diff --git a/meson.build b/meson.build index c045b3bf..b6fc1243 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,8 @@ libalpm_version = '15.0.0' cc = meson.get_compiler('c') +add_project_arguments(['-fsanitize=address', '-fno-omit-frame-pointer', '-ggdb', '-O0'], language : 'c') + # commandline options PREFIX = get_option('prefix') DATAROOTDIR = join_paths(PREFIX, get_option('datarootdir')) @@ -307,6 +309,8 @@ subdir('src/pacman') subdir('src/util') subdir('scripts') +subdir('src/fuzzing') + # Internationalization if get_option('i18n') i18n = import('i18n') @@ -400,6 +404,45 @@ executable( install : true, ) +# Note: fuzz targets below must be built with Clang compiler +executable( + 'fuzz_wordsplit', + fuzz_wordsplit_sources, + include_directories : includes, + link_with : [libcommon], + dependencies : [], + c_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer'], + link_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer'], +) + +executable( + 'fuzz_string_length', + [fuzz_string_length_sources, pacman_sources], + include_directories : includes, + link_with : [libalpm_a, libcommon], + dependencies : [], + c_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer', '-DFUZZING_PACMAN'], + link_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer'], +) +executable( + 'fuzz_alpm_extract_keyid', + [fuzz_alpm_extract_keyid_sources, pacman_sources], + include_directories : includes, + link_with : [libalpm_a, libcommon], + dependencies : [], + c_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer', '-DFUZZING_PACMAN'], + link_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer'], +) +executable( + 'fuzz_parseconfigfile', + [fuzz_parseconfigfile_sources, pacman_sources], + include_directories : includes, + link_with : [libalpm_a], + dependencies : [], + c_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer', '-DFUZZING_PACMAN'], + link_args : ['-fsanitize=fuzzer,address', '-ggdb', '-O0', '-fno-omit-frame-pointer'], +) + foreach wrapper : script_wrappers cdata = configuration_data() cdata.set_quoted('BASH', BASH.full_path()) diff --git a/src/fuzzing/fuzz_alpm_extract_keyid.c b/src/fuzzing/fuzz_alpm_extract_keyid.c new file mode 100644 index 00000000..febbd57a --- /dev/null +++ b/src/fuzzing/fuzz_alpm_extract_keyid.c @@ -0,0 +1,26 @@ +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include + +/* libalpm */ +#include "alpm.h" +#include "alpm_list.h" +#include "handle.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == 0) + return 0; + + alpm_handle_t handle; // TODO/FIXME? + const char* filename = "/dev/null"; // TODO/FIXME? + + alpm_list_t *keys = NULL; + alpm_extract_keyid(&handle, filename, /* sig */ Data, /* len */ Size, &keys); + + return 0; +} diff --git a/src/fuzzing/fuzz_parseconfigfile.c b/src/fuzzing/fuzz_parseconfigfile.c new file mode 100644 index 00000000..4746141d --- /dev/null +++ b/src/fuzzing/fuzz_parseconfigfile.c @@ -0,0 +1,43 @@ +#include +#include +#include +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include + +// TODO/FIXME: Fix the util.h include +//#include "conf.h" +// And remove that function header from here +int parseconfigfile(const char *s); +extern void *config; +void *config_new(void); + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// TODO/FIXME: This fuzzer should always be run from a chroot +// without any other files in it; otherwise the configfile may refer +// to other files +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static void* config_object = 0; + + // TODO/FIXME: The harness needs to be run with -detect_leaks=0 + // because the config object here is detected as a leak + if (!config_object) { + config = config_object = config_new(); + } + + if (Size == 0) + return 0; + + int fd = memfd_create("input", 0); + write(fd, Data, Size); + + char path[64] = {0}; + sprintf(path, "/proc/self/fd/%d", fd); + + parseconfigfile(path); + + close(fd); + + return 0; +} diff --git a/src/fuzzing/fuzz_string_length.c b/src/fuzzing/fuzz_string_length.c new file mode 100644 index 00000000..8991b476 --- /dev/null +++ b/src/fuzzing/fuzz_string_length.c @@ -0,0 +1,26 @@ +#include +#include +#include + +// TODO/FIXME: Fix the util.h include +//#include "util.h" +// And remove that function header from here +size_t string_length(const char *s); + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == 0) + return 0; + + // Prepare a null terminated string + char* cstring = malloc(Size+1); + memcpy(cstring, Data, Size); + cstring[Size] = 0; + + string_length(cstring); + + free(cstring); + + return 0; +} diff --git a/src/fuzzing/fuzz_wordsplit.c b/src/fuzzing/fuzz_wordsplit.c new file mode 100644 index 00000000..e2e10210 --- /dev/null +++ b/src/fuzzing/fuzz_wordsplit.c @@ -0,0 +1,36 @@ +#define _XOPEN_SOURCE +#include +#include +#include + +#include "util-common.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == 0) + return 0; + + // Prepare a null terminated string + char* cstring = malloc(Size+1); + memcpy(cstring, Data, Size); + cstring[Size] = 0; + + char** ptr = wordsplit(cstring); + + // Free the memory allocated by wordsplit + if (ptr) { + int i = 0; + char* p = ptr[i++]; + while (p) { + free(p); + p = ptr[i++]; + } + free(ptr); + } + + // Free the allocated cstring + free(cstring); + + return 0; +} diff --git a/src/fuzzing/meson.build b/src/fuzzing/meson.build new file mode 100644 index 00000000..9a8555c2 --- /dev/null +++ b/src/fuzzing/meson.build @@ -0,0 +1,15 @@ +fuzz_wordsplit_sources = files(''' + fuzz_wordsplit.c +'''.split()) + +fuzz_string_length_sources = files(''' + fuzz_string_length.c +'''.split()) + +fuzz_alpm_extract_keyid_sources = files(''' + fuzz_alpm_extract_keyid.c +'''.split()) + +fuzz_parseconfigfile_sources = files(''' + fuzz_parseconfigfile.c +'''.split()) \ No newline at end of file diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 2866fc98..d612c312 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -1115,6 +1115,7 @@ static void cl_to_log(int argc, char *argv[]) } } +#ifndef FUZZING_PACMAN /** Main function. * @param argc * @param argv @@ -1303,3 +1304,4 @@ int main(int argc, char *argv[]) /* not reached */ return EXIT_SUCCESS; } +#endif //FUZZING_PACMAN diff --git a/src/pacman/util.c b/src/pacman/util.c index 3b96568d..5367128b 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -451,7 +451,7 @@ static char *concat_list(alpm_list_t *lst, formatfn fn) return output; } -static size_t string_length(const char *s) +size_t string_length(const char *s) { size_t len; wchar_t *wcstr; diff --git a/src/pacman/util.h b/src/pacman/util.h index cca816a5..1330cd4a 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -47,6 +47,7 @@ typedef struct _pm_target_t { int is_explicit; } pm_target_t; +size_t string_length(const char *s); void trans_init_error(void); /* flags is a bitfield of alpm_transflag_t flags */ int trans_init(int flags, int check_valid);