From 850d1aaa7b4cf2fecb39451cb79bec8fa53c7cd0 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Mon, 11 Mar 2019 20:56:21 -0700 Subject: [PATCH] Add mb_regex fuzzer. Also refactor callers code a bit and disable LSAN. --- Makefile.frag | 3 ++ config.m4 | 5 ++- fuzzer-exif.c | 24 ++------------ fuzzer-mbstring.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++ fuzzer-sapi.c | 55 +++++++++++++++++++++++++++++++++ fuzzer-sapi.h | 2 ++ 6 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 fuzzer-mbstring.c diff --git a/Makefile.frag b/Makefile.frag index 35ca37c..fd57ec2 100644 --- a/Makefile.frag +++ b/Makefile.frag @@ -13,3 +13,6 @@ $(SAPI_FUZZER_PATH)/php-fuzz-json: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZ $(SAPI_FUZZER_PATH)/php-fuzz-exif: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_EXIF_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_EXIF_OBJS) -o $@ + +$(SAPI_FUZZER_PATH)/php-fuzz-mbstring: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_MBSTRING_OBJS) + $(FUZZER_BUILD) $(PHP_FUZZER_MBSTRING_OBJS) -o $@ diff --git a/config.m4 b/config.m4 index 0a937d4..5cf37e3 100644 --- a/config.m4 +++ b/config.m4 @@ -47,11 +47,14 @@ if test "$PHP_FUZZER" != "no"; then FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS) FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS) FUZZER_TARGET([exif], PHP_FUZZER_EXIF_OBJS) - + dnl This check doesn't work, as PHP_ARG_ENABLE(json) comes later ... if test "$PHP_JSON" != "no"; then FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS) fi + if test "$PHP_MBSTRING" != "no"; then + FUZZER_TARGET([mbstring], PHP_FUZZER_MBSTRING_OBJS) + fi PHP_SUBST(PHP_FUZZER_BINARIES) fi diff --git a/fuzzer-exif.c b/fuzzer-exif.c index 296f9fc..e8bab0a 100644 --- a/fuzzer-exif.c +++ b/fuzzer-exif.c @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | Copyright (c) 2017, Johannes Schlüter | + | Copyright (c) 2019, Stanislav Malyshev | | All rights reserved. | +----------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | @@ -29,10 +29,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { char *filename; - int filedes, result; - zend_fcall_info fci = {0}; - zend_fcall_info_cache fci_cache = {0}; - zval args[1], retval, func; + int filedes; if (php_request_startup()==FAILURE) { php_module_shutdown(); @@ -45,24 +42,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { write(filedes, Data, Size); close(filedes); - ZVAL_STRING(&func, "exif_read_data"); - - zend_fcall_info_init(&func, 0, &fci, &fci_cache, NULL, NULL); - - ZVAL_STRING(&args[0], filename); - fci.retval = &retval; - fci.param_count = 1; - fci.params = args; - fci.no_separation = 0; - - result = zend_call_function(&fci, &fci_cache); - /* to ensure retval is not broken */ - php_var_dump(&retval, 0); + fuzzer_call_php_func("exif_read_data", 1, &filename); /* cleanup */ - ZVAL_UNDEF(&retval); - ZVAL_UNDEF(&args[0]); - ZVAL_UNDEF(&func); unlink(filename); php_request_shutdown(NULL); diff --git a/fuzzer-mbstring.c b/fuzzer-mbstring.c new file mode 100644 index 0000000..870f091 --- /dev/null +++ b/fuzzer-mbstring.c @@ -0,0 +1,79 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) 2019, Stanislav Malyshev | + | All rights reserved. | + +----------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions which are | + | bundled with this package in the file LICENSE. | + | This product includes PHP software, freely available from | + | | + +----------------------------------------------------------------------+ +*/ + +#include "fuzzer.h" + +#include "Zend/zend.h" +#include "main/php_config.h" +#include "main/php_main.h" + +#include +#include +#include + +#include "fuzzer-sapi.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { +#ifdef HAVE_MBREGEX + char *args[2]; + char *data = malloc(Size+1); + memcpy(data, Data, Size); + data[Size] = '\0'; + + if (php_request_startup()==FAILURE) { + php_module_shutdown(); + return 0; + } + + args[0] = data; + args[1] = "test123"; + fuzzer_call_php_func("mb_ereg", 2, args); + + args[0] = data; + args[1] = "test123"; + fuzzer_call_php_func("mb_eregi", 2, args); + + args[0] = data; + args[1] = data; + fuzzer_call_php_func("mb_ereg", 2, args); + + args[0] = data; + args[1] = data; + fuzzer_call_php_func("mb_eregi", 2, args); + + php_request_shutdown(NULL); + + free(data); +#else + fprintf(stderr, "\n\nERROR:\nPHP built without mbstring, recompile with --enable-mbstring to use this fuzzer\n"); + exit(1); +#endif + return 0; +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + fuzzer_init_php(); + + /* fuzzer_shutdown_php(); */ + return 0; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/fuzzer-sapi.c b/fuzzer-sapi.c index a303202..9f6b833 100644 --- a/fuzzer-sapi.c +++ b/fuzzer-sapi.c @@ -1,6 +1,7 @@ /* +----------------------------------------------------------------------+ | Copyright (c) 2017, Johannes Schlüter | + | Copyright (c) 2019, Stanislav Malyshev | | All rights reserved. | +----------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | @@ -108,8 +109,17 @@ int fuzzer_init_php() fuzzer_module.ini_entries = malloc(sizeof(HARDCODED_INI)); memcpy(fuzzer_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI)); + /* + * TODO: we might want to test both Zend and malloc MM, but testing with malloc + * is more likely to find bugs, so use that for now. + */ putenv("USE_ZEND_ALLOC=0"); +#ifdef __SANITIZE_ADDRESS__ + /* Not very interested in memory leak detection, since Zend MM does that */ + __lsan_disable(); +#endif + if (fuzzer_module.startup(&fuzzer_module)==FAILURE) { return FAILURE; } @@ -195,6 +205,51 @@ int fuzzer_do_request_d(char *filename, char *data, size_t data_len) return fuzzer_do_request(&file_handle, filename); } +// Call named PHP function with N zval arguments +void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args) { + zend_fcall_info fci = {0}; + zend_fcall_info_cache fci_cache = {0}; + zval retval, func; + int result; + + ZVAL_STRING(&func, func_name); + ZVAL_UNDEF(&retval); + zend_fcall_info_init(&func, 0, &fci, &fci_cache, NULL, NULL); + + fci.retval = &retval; + fci.param_count = nargs; + fci.params = args; + fci.no_separation = 0; + + result = zend_call_function(&fci, &fci_cache); + // TODO: check result? + /* to ensure retval is not broken */ + php_var_dump(&retval, 0); + + /* cleanup */ + zval_ptr_dtor(&retval); + ZVAL_UNDEF(&retval); + zval_ptr_dtor(&func); + ZVAL_UNDEF(&func); +} + +// Call named PHP function with N string arguments +void fuzzer_call_php_func(const char *func_name, int nargs, char **params) { + zval args[nargs]; + int i; + + for(i=0;i