summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fuzzing/README.md51
-rw-r--r--fuzzing/corpuses/generic-cache-dirbin0 -> 1872 bytes
-rw-r--r--fuzzing/driver.c45
-rw-r--r--fuzzing/meson.build43
-rw-r--r--fuzzing/setup.c133
-rw-r--r--fuzzing/setup.h15
-rw-r--r--meson.build8
-rw-r--r--meson_options.txt4
-rw-r--r--src/meson.build1
9 files changed, 300 insertions, 0 deletions
diff --git a/fuzzing/README.md b/fuzzing/README.md
new file mode 100644
index 0000000..49119cc
--- /dev/null
+++ b/fuzzing/README.md
@@ -0,0 +1,51 @@
+Fuzz targets used by [oss-fuzz](https://github.com/google/oss-fuzz/).
+
+Useful links: [Dashboard](https://oss-fuzz.com/) _(requires access)_, [Build logs](https://oss-fuzz-build-logs.storage.googleapis.com/index.html), [Coverage](https://oss-fuzz.com/coverage-report/job/libfuzzer_asan_xdgmime/latest)
+
+## How to add new targets
+
+Add **fuzz_target_name.c** and edit `meson.build` accordingly.
+
+New targets are picked up by oss-fuzz automatically within a day. Targets must not be renamed once added.
+
+Add (optional) **fuzz_target_name.dict** containing keywords and magic bytes.
+
+Add (optional) **fuzz_target_name.corpus** with file names on separate lines. Wildcards `?`, `*` and `**` are supported. Examples below.
+
+```bash
+xdgmime/* # all files in directory xdgmime
+xdgmime/** # all files in directory xdgmime and sub-directories
+**.xbel # all files ending with .xbel in the repository
+```
+
+Recommended reading: [Fuzz Target](https://llvm.org/docs/LibFuzzer.html#fuzz-target), [Dictionaries](https://llvm.org/docs/LibFuzzer.html#dictionaries), [Corpus](https://llvm.org/docs/LibFuzzer.html#corpus)
+
+## How to reproduce oss-fuzz bugs locally
+
+Build with at least the following flags, choosing a sanitizer as needed. A somewhat recent version of [clang](http://clang.llvm.org/) is recommended.
+
+```bash
+$ CC=clang CXX=clang++ meson DIR -Db_sanitize=<address|undefined> -Db_lundef=false
+```
+
+Afterwards run the affected target against the provided test case.
+
+```bash
+$ DIR/fuzzing/fuzz_target_name FILE
+```
+
+#### FAQs
+
+###### What about Memory Sanitizer (MSAN)?
+
+Correct MSAN instrumentation is [difficult to achieve](https://clang.llvm.org/docs/MemorySanitizer.html#handling-external-code) locally, so false positives are very likely to mask the actual bug.
+
+If need be, [you can still reproduce](https://google.github.io/oss-fuzz/advanced-topics/reproducing/#building-using-docker) those bugs with the oss-fuzz provided docker images.
+
+###### There are no file/function names in the stack trace.
+
+`llvm-symbolizer` must be in `PATH`.
+
+###### UndefinedBehavior Sanitizer (UBSAN) doesn't provide a stack trace.
+
+Set environment variable `UBSAN_OPTIONS` to `print_stacktrace=1` prior to running the target.
diff --git a/fuzzing/corpuses/generic-cache-dir b/fuzzing/corpuses/generic-cache-dir
new file mode 100644
index 0000000..30f640f
--- /dev/null
+++ b/fuzzing/corpuses/generic-cache-dir
Binary files differ
diff --git a/fuzzing/driver.c b/fuzzing/driver.c
new file mode 100644
index 0000000..d895007
--- /dev/null
+++ b/fuzzing/driver.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 LLVM contributors
+ *
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ */
+
+/* Simpler gnu89 version of StandaloneFuzzTargetMain.c from LLVM */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int LLVMFuzzerTestOneInput (const unsigned char *data, size_t size);
+
+int
+main (int argc, char **argv)
+{
+ FILE *f;
+ long tell_result;
+ size_t n_read, len;
+ unsigned char *buf;
+
+ if (argc < 2)
+ return 1;
+
+ f = fopen (argv[1], "r");
+ assert (f);
+ fseek (f, 0, SEEK_END);
+ tell_result = ftell (f);
+ assert (tell_result >= 0);
+ len = (size_t) tell_result;
+ fseek (f, 0, SEEK_SET);
+ buf = (unsigned char*) malloc (len);
+ n_read = fread (buf, 1, len, f);
+ assert (n_read == len);
+ LLVMFuzzerTestOneInput (buf, len);
+
+ free (buf);
+ fclose (f);
+ printf ("Done!\n");
+ return 0;
+}
diff --git a/fuzzing/meson.build b/fuzzing/meson.build
new file mode 100644
index 0000000..7b29e6a
--- /dev/null
+++ b/fuzzing/meson.build
@@ -0,0 +1,43 @@
+# Copyright 2018 pdknsk
+# Copyright 2020, 2021, 2022 Endless OS Foundation, LLC
+# Copyright 2023 GNOME Foundation Inc.
+#
+# SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
+
+fuzz_targets = [
+]
+
+deps = [libxdgmime_dep]
+
+extra_sources = ['setup.c']
+extra_c_args = cc.get_supported_arguments('-Werror=unused-function')
+
+# Links in a static library provided by oss-fuzz, else a standalone driver.
+# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#buildsh-script-environment
+have_fuzzing_engine = false
+if have_cxx
+ fuzzing_engine = cxx.find_library('FuzzingEngine', required : get_option('oss_fuzz'))
+ have_fuzzing_engine = fuzzing_engine.found()
+endif
+if have_fuzzing_engine
+ deps += fuzzing_engine
+else
+ extra_sources += 'driver.c'
+endif
+
+foreach target_name : fuzz_targets
+ exe = executable(target_name, [extra_sources, target_name + '.c'],
+ c_args : extra_c_args,
+ dependencies : deps,
+ )
+
+ # If the FuzzingEngine isn’t available, build some unit tests to check that
+ # the fuzzing files do basically work. This doesn’t do any actual fuzzing though.
+ # Pass in the README as an arbitrary fuzzing input, just so we have something.
+ if not have_fuzzing_engine
+ test(target_name, exe,
+ args : files('README.md'),
+ suite : 'fuzzing',
+ )
+ endif
+endforeach \ No newline at end of file
diff --git a/fuzzing/setup.c b/fuzzing/setup.c
new file mode 100644
index 0000000..d9b2065
--- /dev/null
+++ b/fuzzing/setup.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 GNOME Foundation Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
+ */
+
+/* for TEMP_FAILURE_RETRY */
+#define _GNU_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "setup.h"
+
+static const char *mime_dir_filenames[] =
+ {
+ "mime.cache",
+ "globs2",
+ "globs",
+ "magic",
+ "aliases",
+ "subclasses",
+ "icons",
+ "generic-icons",
+ };
+
+void
+fuzz_teardown (int working_dir_fd)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof (mime_dir_filenames) / sizeof (*mime_dir_filenames); i++)
+ unlinkat (working_dir_fd, mime_dir_filenames[i], 0);
+
+ unlinkat (working_dir_fd, ".", AT_REMOVEDIR);
+
+ close (working_dir_fd);
+}
+
+int
+fuzz_setup (const unsigned char *data,
+ size_t data_len,
+ int *working_dir_fd_out)
+{
+ const char *blob_separator = "~~ fuzz separator ~~";
+ const size_t blob_separator_len = strlen (blob_separator);
+ const unsigned char *separator;
+ size_t i;
+ char tmp_path[] = "/tmp/fuzz_xdgmime_XXXXXX";
+ int dirfd = -1;
+
+ /* libxdgmime loads its inputs from disk, rather than from memory, so we need
+ * to split the fuzz input and save it to disk as multiple files.
+ *
+ * See https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#magic-separator
+ *
+ * You can build a sample input file in this format using:
+ * ```sh
+ * $ pushd ~/.local/share/mime/
+ * $ cat ./mime.cache ^C
+ * $ echo -n "~~ fuzz separator ~~" > separator
+ * $ cat mime.cache separator globs2 separator globs separator magic separator aliases separator subclasses separator icons separator generic-icons > test.corpus
+ * $ rm separator
+ * $ popd
+ * ```
+ */
+
+ *working_dir_fd_out = -1;
+
+ if (mkdtemp (tmp_path) == NULL)
+ return 0;
+
+ dirfd = TEMP_FAILURE_RETRY (open (tmp_path, O_PATH | O_CLOEXEC | O_DIRECTORY));
+ if (dirfd < 0)
+ {
+ rmdir (tmp_path);
+ return 0;
+ }
+
+ for (i = 0; i < sizeof (mime_dir_filenames) / sizeof (*mime_dir_filenames); i++)
+ {
+ const unsigned char *file_data;
+ size_t file_data_len;
+ int fd = -1;
+
+ file_data = data;
+ separator = memmem (data, data_len, blob_separator, blob_separator_len);
+ file_data_len = (separator != NULL) ? (size_t) (separator - data) : data_len;
+
+ if (separator != NULL)
+ {
+ data = separator + blob_separator_len;
+ data_len -= file_data_len + blob_separator_len;
+ }
+
+ fd = TEMP_FAILURE_RETRY (openat (dirfd, mime_dir_filenames[i], O_CREAT | O_WRONLY | O_CLOEXEC));
+ if (fd < 0)
+ {
+ fuzz_teardown (dirfd);
+ return 0;
+ }
+
+ while (file_data_len > 0)
+ {
+ ssize_t s;
+
+ s = TEMP_FAILURE_RETRY (write (fd, file_data, file_data_len));
+ if (s < 0)
+ {
+ close (fd);
+ fuzz_teardown (dirfd);
+ return 0;
+ }
+
+ file_data += s;
+ file_data_len -= s;
+ }
+
+ close (fd);
+
+ /* Skip the rest of the files if this was the last separator. */
+ if (separator == NULL)
+ break;
+ }
+
+ *working_dir_fd_out = dirfd;
+
+ return 1;
+}
diff --git a/fuzzing/setup.h b/fuzzing/setup.h
new file mode 100644
index 0000000..e67a336
--- /dev/null
+++ b/fuzzing/setup.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2023 GNOME Foundation Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
+ */
+
+#include <stddef.h>
+
+void fuzz_teardown (int working_dir_fd);
+int fuzz_setup (const unsigned char *data,
+ size_t data_len,
+ int *working_dir_fd_out);
+
+int LLVMFuzzerTestOneInput (const unsigned char *data,
+ size_t size);
diff --git a/meson.build b/meson.build
index 9b71f5f..ef9a4cf 100644
--- a/meson.build
+++ b/meson.build
@@ -27,4 +27,12 @@ add_project_arguments(
language: 'c',
)
+cc = meson.get_compiler('c')
+
+have_cxx = add_languages('cpp', native: false, required: get_option('oss_fuzz').enabled())
+if have_cxx
+ cxx = meson.get_compiler('cpp')
+endif
+
subdir('src')
+subdir('fuzzing') \ No newline at end of file
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..c2d3b0f
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,4 @@
+option('oss_fuzz',
+ type : 'feature',
+ value : 'disabled',
+ description : 'Indicate oss-fuzz build environment')
diff --git a/src/meson.build b/src/meson.build
index d8e4777..6273c96 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -15,6 +15,7 @@ libxdgmime = static_library('xdgmime',
libxdgmime_dep = declare_dependency(
link_with : libxdgmime,
+ include_directories : ['.'],
)
meson.override_dependency('xdgmime', libxdgmime_dep)