add libFuzzer integration, r_run_parseline test ##fuzz

This commit is contained in:
Richard Patel 2022-08-21 10:30:58 +02:00 committed by pancake
parent 52b9b7b703
commit e319a7a71e
8 changed files with 129 additions and 3 deletions

5
.gitignore vendored
View File

@ -140,4 +140,7 @@ libr/include/sdb
**/d/*.out
**/d/*.inc
# Artifacts
/dist/artifacts
/dist/artifacts
# libFuzzer
crash-*
corpus*

View File

@ -622,6 +622,10 @@ R_API bool r_sys_aslr(int val) {
#if __UNIX__ && HAVE_SYSTEM
R_API int r_sys_cmd_str_full(const char *cmd, const char *input, int ilen, char **output, int *len, char **sterr) {
if (!r_sandbox_check (R_SANDBOX_GRAIN_EXEC)) {
return false;
}
char *mysterr = NULL;
if (!sterr) {
sterr = &mysterr;

View File

@ -645,6 +645,7 @@ if get_option('use_webui')
endif
subdir('test/unit')
subdir('test/fuzz')
install_data(
'doc/fortunes.fun',

View File

@ -47,4 +47,5 @@ option('want_ptrace_wrap', type: 'boolean', value: true)
option('nogpl', type: 'boolean', value: false)
option('use_webui', type: 'boolean', value: true, description: 'install different WebUIs for radare2')
option('enable_tests', type: 'boolean', value: true, description: 'Build unit tests in test/unit')
option('enable_libfuzzer', type: 'boolean', value: false, description: 'Build libFuzzer targets in test/fuzz')
option('enable_r2r', type: 'boolean', value: true, description: 'Build r2r executable for regression ')

View File

@ -168,6 +168,8 @@ def build(args):
options.append('-Duse_webui=true')
if args.local:
options.append('-Dlocal=true')
if args.fuzz:
options.append('-Denable_libfuzzer=true')
if not os.path.exists(r2_builddir):
meson('setup', builddir=r2_builddir, prefix=args.prefix, backend=args.backend,
release=args.release, shared=args.shared, options=options)
@ -198,6 +200,8 @@ def main():
parser.add_argument('--sanitize', nargs='?',
const='address,undefined,signed-integer-overflow', metavar='sanitizers',
help='Build radare2 with sanitizer support (default: %(const)s)')
parser.add_argument('--fuzz', action='store_true',
help='Build radare2 with libFuzzer support')
parser.add_argument('--project', action='store_true',
help='Create a visual studio project and do not build.')
parser.add_argument('--release', action='store_true',
@ -237,15 +241,18 @@ def main():
if os.uname().sysname == 'OpenBSD':
log.error("Sanitizers unsupported under OpenBSD")
sys.exit(1)
sanitizers = args.sanitize
if args.fuzz and 'fuzzer' not in sanitizers:
sanitizers = "fuzzer," + sanitizers
cflags = os.environ.get('CFLAGS')
if not cflags:
cflags = ''
os.environ['CFLAGS'] = cflags + ' -fsanitize=' + args.sanitize
os.environ['CFLAGS'] = cflags + ' -fsanitize=' + sanitizers
if os.uname().sysname != 'Darwin':
ldflags = os.environ.get('LDFLAGS')
if not ldflags:
ldflags = ''
os.environ['LDFLAGS'] = ldflags + ' -fsanitize=' + args.sanitize
os.environ['LDFLAGS'] = ldflags + ' -fsanitize=' + sanitizers
# Check arguments
if args.pull:

61
test/fuzz/README.md Normal file
View File

@ -0,0 +1,61 @@
# libFuzzer tests
## Setup
Get libFuzzer-capable clang
```shell
# Linux
export CC=clang-14
# macOS
export CC="$(brew --prefix llvm@14)/bin/clang"
```
Clean project
```shell
rm -rf build
```
Build project with libFuzzer and sanitizers
```shell
# If you want to debug crashes
export CFLAGS="-g"
# Build project with test/fuzz
python3 ./sys/meson.py --fuzz --sanitize address,leak
```
## Run
Refer to https://llvm.org/docs/LibFuzzer.html
**Show help**
```
./build/test/fuzz/fuzz_r_run_parseline -help=1
```
**Run fuzzer**
```
mkdir corpus_parseline
./build/test/fuzz/fuzz_r_run_parseline \
-workers=1 -runs=50000 -timeout=3 \
corpus_parseline
```
**Replay crashes**
```
./build/test/fuzz/fuzz_r_run_parseline crash-*
```
## Adding a new target
- add your test to /test/fuzz/meson.build
- add `/test/fuzz/fuzz_<name>.c` file
- add system setup to `LLVMFuzzerInitialize` (disable logging, enable sandbox, etc)
- add fuzz target to `LLVMFuzzerTestOneInput`
- make sure input is short (ideally no longer than 256 bytes)
- make sure no memory leaks are present

View File

@ -0,0 +1,32 @@
#include <stdlib.h>
#include <string.h>
#include <r_types.h>
#include <r_socket.h>
#include <r_util/r_log.h>
#include <r_util/r_sys.h>
#include <r_util/r_sandbox.h>
int LLVMFuzzerInitialize(int *argc, char ***argv) {
r_sys_clearenv ();
r_sandbox_enable (true);
r_sandbox_grain (R_SANDBOX_GRAIN_NONE);
r_log_set_quiet (true);
return 0;
}
int LLVMFuzzerTestOneInput(const ut8 *data, size_t len) {
r_sys_clearenv ();
r_sandbox_enable (true);
char *str = malloc (len + 1);
memcpy (str, data, len);
str[len] = 0;
RRunProfile *p = r_run_new (NULL);
r_run_parseline (p, str);
free (str);
r_run_free (p);
r_sys_clearenv ();
return 0;
}

17
test/fuzz/meson.build Normal file
View File

@ -0,0 +1,17 @@
if get_option('enable_libfuzzer')
targets = [
'r_run_parseline',
]
foreach target : targets
exe = executable('fuzz_@0@'.format(target), 'fuzz_@0@.c'.format(target),
include_directories: [platform_inc],
dependencies: [
r_util_dep,
r_socket_dep,
],
install: false,
implicit_include_directories: false,
)
endforeach
endif