diff options
author | Max Moroz <mmoroz@chromium.org> | 2019-05-31 16:59:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-31 16:59:45 -0700 |
commit | 230751d78eaa74518d48e0c6e0e5003a68e2e7e7 (patch) | |
tree | da78aed1b55a505ed8231db7d55e5c0720e462a7 /projects/njs | |
parent | 3be06d5e78f865ea737b21ce5236b069c0f8f018 (diff) | |
download | oss-fuzz-230751d78eaa74518d48e0c6e0e5003a68e2e7e7.tar.gz |
[njs] Add nginx/njs project and njs_process_script_fuzzer. (#2481)
* [njs] Add nginx/njs project and njs_process_script_fuzzer.
* Add copyright, disable logging, disable leaks detection.
* fix memory leaks
* use $LIB_FUZZING_ENGINE
* list myself as a primary contact for now
* enable all sanitizers
Diffstat (limited to 'projects/njs')
-rw-r--r-- | projects/njs/Dockerfile | 23 | ||||
-rwxr-xr-x | projects/njs/build.sh | 45 | ||||
-rw-r--r-- | projects/njs/njs_process_script_fuzzer.c | 709 | ||||
-rw-r--r-- | projects/njs/project.yaml | 7 |
4 files changed, 784 insertions, 0 deletions
diff --git a/projects/njs/Dockerfile b/projects/njs/Dockerfile new file mode 100644 index 000000000..defa36240 --- /dev/null +++ b/projects/njs/Dockerfile @@ -0,0 +1,23 @@ +# Copyright 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder +MAINTAINER mmoroz@chromium.org +RUN apt-get update && apt-get install -y make autoconf automake libtool mercurial libpcre3-dev libreadline-dev +RUN hg clone http://hg.nginx.org/njs + +WORKDIR njs +COPY build.sh njs_process_script_fuzzer.c $SRC/ diff --git a/projects/njs/build.sh b/projects/njs/build.sh new file mode 100755 index 000000000..a9539c97b --- /dev/null +++ b/projects/njs/build.sh @@ -0,0 +1,45 @@ +#!/bin/bash -eu +# Copyright 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# build project +rm -rf build + +./configure +make njs + +# build fuzzers +# e.g. +$CC $CFLAGS -Inxt -Ibuild -Injs -c \ + $SRC/njs_process_script_fuzzer.c -o build/njs_process_script_fuzzer.o + +$CXX $CXXFLAGS build/njs_process_script_fuzzer.o -o $OUT/njs_process_script_fuzzer \ + $LIB_FUZZING_ENGINE build/libnxt.a build/libnjs.a -lm -lpcre -lreadline + +SEED_CORPUS_PATH=$OUT/njs_process_script_fuzzer_seed_corpus +mkdir -p $SEED_CORPUS_PATH + +set +x +cat njs/test/njs_interactive_test.c njs/test/njs_unit_test.c \ + | egrep -o '".*"' | awk '{print substr($0,2,length($0)-2)}' | sort | uniq \ + | while IFS= read -r line; do + echo $line > $SEED_CORPUS_PATH/$(echo $line | sha1sum | awk '{ print $1 }'); + done +set -x + +zip -q $SEED_CORPUS_PATH.zip $SEED_CORPUS_PATH +rm -rf $SEED_CORPUS_PATH + diff --git a/projects/njs/njs_process_script_fuzzer.c b/projects/njs/njs_process_script_fuzzer.c new file mode 100644 index 000000000..b90425d9f --- /dev/null +++ b/projects/njs/njs_process_script_fuzzer.c @@ -0,0 +1,709 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stddef.h> +#include <stdint.h> + +#include <njs_core.h> +#include <njs_builtin.h> + +// The vast majority of the code was copied from njs/njs_shell.c. + +typedef struct { + uint8_t disassemble; + uint8_t interactive; + uint8_t module; + uint8_t quiet; + uint8_t sandbox; + uint8_t version; + + char *file; + char *command; + size_t n_paths; + char **paths; +} njs_opts_t; + + +typedef struct { + size_t index; + size_t length; + nxt_array_t *completions; + nxt_array_t *suffix_completions; + nxt_lvlhsh_each_t lhe; + + enum { + NJS_COMPLETION_VAR = 0, + NJS_COMPLETION_SUFFIX, + NJS_COMPLETION_GLOBAL + } phase; +} njs_completion_t; + + +typedef struct { + njs_vm_event_t vm_event; + nxt_queue_link_t link; +} njs_ev_t; + + +typedef struct { + njs_vm_t *vm; + + nxt_lvlhsh_t events; /* njs_ev_t * */ + nxt_queue_t posted_events; + + uint64_t time; + + njs_completion_t completion; +} njs_console_t; + + +static nxt_int_t njs_console_init(njs_vm_t *vm, njs_console_t *console); +static nxt_int_t njs_externals_init(njs_vm_t *vm, njs_console_t *console); +static nxt_int_t njs_interactive_shell(njs_opts_t *opts, + njs_vm_opt_t *vm_options, nxt_str_t *line); +static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options); +static nxt_int_t njs_process_script(njs_console_t *console, njs_opts_t *opts, + const nxt_str_t *script); + +static njs_ret_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t njs_ext_console_dump(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); + +static njs_host_event_t njs_console_set_timer(njs_external_ptr_t external, + uint64_t delay, njs_vm_event_t vm_event); +static void njs_console_clear_timer(njs_external_ptr_t external, + njs_host_event_t event); + +static nxt_int_t lvlhsh_key_test(nxt_lvlhsh_query_t *lhq, void *data); +static void *lvlhsh_pool_alloc(void *pool, size_t size, nxt_uint_t nalloc); +static void lvlhsh_pool_free(void *pool, void *p, size_t size); + + +static njs_external_t njs_ext_console[] = { + + { nxt_string("log"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_log, + 0 }, + + { nxt_string("dump"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_dump, + 0 }, + + { nxt_string("help"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_help, + 0 }, + + { nxt_string("time"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_time, + 0 }, + + { nxt_string("timeEnd"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_time_end, + 0 }, +}; + +static njs_external_t njs_externals[] = { + + { nxt_string("console"), + NJS_EXTERN_OBJECT, + njs_ext_console, + nxt_nitems(njs_ext_console), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, +}; + + +static const nxt_lvlhsh_proto_t lvlhsh_proto nxt_aligned(64) = { + NXT_LVLHSH_LARGE_SLAB, + 0, + lvlhsh_key_test, + lvlhsh_pool_alloc, + lvlhsh_pool_free, +}; + + +static njs_vm_ops_t njs_console_ops = { + njs_console_set_timer, + njs_console_clear_timer +}; + + +static njs_console_t njs_console; + + +static nxt_int_t +njs_console_init(njs_vm_t *vm, njs_console_t *console) +{ + console->vm = vm; + + nxt_lvlhsh_init(&console->events); + nxt_queue_init(&console->posted_events); + + console->time = UINT64_MAX; + + console->completion.completions = njs_vm_completions(vm, NULL); + if (console->completion.completions == NULL) { + return NXT_ERROR; + } + + return NXT_OK; +} + + +static nxt_int_t +njs_externals_init(njs_vm_t *vm, njs_console_t *console) +{ + nxt_uint_t ret; + njs_value_t *value; + const njs_extern_t *proto; + + static const nxt_str_t name = nxt_string("console"); + + proto = njs_vm_external_prototype(vm, &njs_externals[0]); + if (proto == NULL) { + nxt_error("failed to add console proto\n"); + return NXT_ERROR; + } + + value = nxt_mp_zalloc(vm->mem_pool, sizeof(njs_opaque_value_t)); + if (value == NULL) { + return NXT_ERROR; + } + + ret = njs_vm_external_create(vm, value, proto, console); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + ret = njs_vm_external_bind(vm, &name, value); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + ret = njs_console_init(vm, console); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + return NXT_OK; +} + +static nxt_int_t +njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options, nxt_str_t *line) +{ + njs_vm_t *vm; + + vm = njs_create_vm(opts, vm_options); + if (vm == NULL) { + return NXT_ERROR; + } + + njs_process_script(vm_options->external, opts, line); + njs_vm_destroy(vm); + vm = NULL; + + return NXT_OK; +} + +static njs_vm_t * +njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options) +{ + u_char *p, *start; + njs_vm_t *vm; + nxt_int_t ret; + nxt_str_t path; + nxt_uint_t i; + + vm = njs_vm_create(vm_options); + if (vm == NULL) { + nxt_error("failed to create vm\n"); + return NULL; + } + + if (njs_externals_init(vm, vm_options->external) != NXT_OK) { + nxt_error("failed to add external protos\n"); + return NULL; + } + + for (i = 0; i < opts->n_paths; i++) { + path.start = (u_char *) opts->paths[i]; + path.length = nxt_strlen(opts->paths[i]); + + ret = njs_vm_add_path(vm, &path); + if (ret != NXT_OK) { + nxt_error("failed to add path\n"); + return NULL; + } + } + + start = (u_char *) getenv("NJS_PATH"); + if (start == NULL) { + return vm; + } + + for ( ;; ) { + p = nxt_strchr(start, ':'); + + path.start = start; + path.length = (p != NULL) ? (size_t) (p - start) : nxt_strlen(start); + + ret = njs_vm_add_path(vm, &path); + if (ret != NXT_OK) { + nxt_error("failed to add path\n"); + return NULL; + } + + if (p == NULL) { + break; + } + + start = p + 1; + } + + return vm; +} + + +static nxt_int_t +njs_process_events(njs_console_t *console, njs_opts_t *opts) +{ + njs_ev_t *ev; + nxt_queue_t *events; + nxt_queue_link_t *link; + + events = &console->posted_events; + + for ( ;; ) { + link = nxt_queue_first(events); + + if (link == nxt_queue_tail(events)) { + break; + } + + ev = nxt_queue_link_data(link, njs_ev_t, link); + + nxt_queue_remove(&ev->link); + ev->link.prev = NULL; + ev->link.next = NULL; + + njs_vm_post_event(console->vm, ev->vm_event, NULL, 0); + } + + return NXT_OK; +} + + +static nxt_int_t +njs_process_script(njs_console_t *console, njs_opts_t *opts, + const nxt_str_t *script) +{ + u_char *start; + njs_vm_t *vm; + nxt_int_t ret; + + vm = console->vm; + start = script->start; + + ret = njs_vm_compile(vm, &start, start + script->length); + + if (ret == NXT_OK) { + if (opts->disassemble) { + njs_disassembler(vm); + nxt_printf("\n"); + } + + ret = njs_vm_start(vm); + } + + for ( ;; ) { + if (!njs_vm_pending(vm)) { + break; + } + + ret = njs_process_events(console, opts); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_error("njs_process_events() failed\n"); + ret = NJS_ERROR; + break; + } + + if (njs_vm_waiting(vm) && !njs_vm_posted(vm)) { + /*TODO: async events. */ + + nxt_error("njs_process_script(): async events unsupported\n"); + ret = NJS_ERROR; + break; + } + + ret = njs_vm_run(vm); + } + + return ret; +} + + +static njs_ret_t +njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_str_t msg; + nxt_uint_t n; + + n = 1; + + while (n < nargs) { + if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, 0) + == NJS_ERROR) + { + return NJS_ERROR; + } + + nxt_printf("%s", (n != 1) ? " " : ""); + nxt_print(msg.start, msg.length); + + n++; + } + + if (nargs > 1) { + nxt_printf("\n"); + } + + vm->retval = njs_value_undefined; + + return NJS_OK; +} + + +static njs_ret_t +njs_ext_console_dump(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_str_t msg; + nxt_uint_t n; + + n = 1; + + while (n < nargs) { + if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, 1) + == NJS_ERROR) + { + return NJS_ERROR; + } + + nxt_printf("%s", (n != 1) ? " " : ""); + nxt_print(msg.start, msg.length); + + n++; + } + + if (nargs > 1) { + nxt_printf("\n"); + } + + vm->retval = njs_value_undefined; + + return NJS_OK; +} + + +static njs_ret_t +njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + const njs_object_init_t *obj, **objpp; + + nxt_printf("VM built-in objects:\n"); + + for (objpp = njs_constructor_init; *objpp != NULL; objpp++) { + obj = *objpp; + + nxt_printf(" %V\n", &obj->name); + } + + for (objpp = njs_object_init; *objpp != NULL; objpp++) { + obj = *objpp; + + nxt_printf(" %V\n", &obj->name); + } + + nxt_printf("\nEmbedded objects:\n"); + nxt_printf(" console\n"); + + nxt_printf("\n"); + + vm->retval = njs_value_undefined; + + return NJS_OK; +} + + +static njs_ret_t +njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + njs_console_t *console; + + if (!njs_value_is_undefined(njs_arg(args, nargs, 1))) { + njs_vm_error(vm, "labels not implemented"); + return NJS_ERROR; + } + + console = njs_vm_external(vm, njs_arg(args, nargs, 0)); + if (nxt_slow_path(console == NULL)) { + return NJS_ERROR; + } + + console->time = nxt_time(); + + vm->retval = njs_value_undefined; + + return NJS_OK; +} + + +static njs_ret_t +njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + uint64_t ns, ms; + njs_console_t *console; + + ns = nxt_time(); + + if (!njs_value_is_undefined(njs_arg(args, nargs, 1))) { + njs_vm_error(vm, "labels not implemented"); + return NJS_ERROR; + } + + console = njs_vm_external(vm, njs_arg(args, nargs, 0)); + if (nxt_slow_path(console == NULL)) { + return NJS_ERROR; + } + + if (nxt_fast_path(console->time != UINT64_MAX)) { + + ns = ns - console->time; + + ms = ns / 1000000; + ns = ns % 1000000; + + nxt_printf("default: %uL.%06uLms\n", ms, ns); + + console->time = UINT64_MAX; + + } else { + nxt_printf("Timer \"default\" doesn’t exist.\n"); + } + + vm->retval = njs_value_undefined; + + return NJS_OK; +} + + +static njs_host_event_t +njs_console_set_timer(njs_external_ptr_t external, uint64_t delay, + njs_vm_event_t vm_event) +{ + njs_ev_t *ev; + njs_vm_t *vm; + nxt_int_t ret; + njs_console_t *console; + nxt_lvlhsh_query_t lhq; + + if (delay != 0) { + nxt_error("njs_console_set_timer(): async timers unsupported\n"); + return NULL; + } + + console = external; + vm = console->vm; + + ev = nxt_mp_alloc(vm->mem_pool, sizeof(njs_ev_t)); + if (nxt_slow_path(ev == NULL)) { + return NULL; + } + + ev->vm_event = vm_event; + + lhq.key.start = (u_char *) &ev->vm_event; + lhq.key.length = sizeof(njs_vm_event_t); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + + lhq.replace = 0; + lhq.value = ev; + lhq.proto = &lvlhsh_proto; + lhq.pool = vm->mem_pool; + + ret = nxt_lvlhsh_insert(&console->events, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + nxt_queue_insert_tail(&console->posted_events, &ev->link); + + return (njs_host_event_t) ev; +} + + +static void +njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event) +{ + njs_vm_t *vm; + njs_ev_t *ev; + nxt_int_t ret; + njs_console_t *console; + nxt_lvlhsh_query_t lhq; + + ev = event; + console = external; + vm = console->vm; + + lhq.key.start = (u_char *) &ev->vm_event; + lhq.key.length = sizeof(njs_vm_event_t); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + + lhq.proto = &lvlhsh_proto; + lhq.pool = vm->mem_pool; + + if (ev->link.prev != NULL) { + nxt_queue_remove(&ev->link); + } + + ret = nxt_lvlhsh_delete(&console->events, &lhq); + if (ret != NXT_OK) { + nxt_error("nxt_lvlhsh_delete() failed\n"); + } + + nxt_mp_free(vm->mem_pool, ev); +} + + +static nxt_int_t +lvlhsh_key_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + njs_ev_t *ev; + + ev = data; + + if (memcmp(&ev->vm_event, lhq->key.start, sizeof(njs_vm_event_t)) == 0) { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +static void * +lvlhsh_pool_alloc(void *pool, size_t size, nxt_uint_t nalloc) +{ + return nxt_mp_align(pool, size, size); +} + + +static void +lvlhsh_pool_free(void *pool, void *p, size_t size) +{ + nxt_mp_free(pool, p); +} + + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size == 0) return 0; + + char* input = malloc(size); + memcpy(input, data, size); + nxt_str_t line = {size, input}; + + njs_vm_t *vm; + nxt_int_t ret; + njs_opts_t opts; + nxt_str_t command; + njs_vm_opt_t vm_options; + + nxt_memzero(&opts, sizeof(njs_opts_t)); + opts.interactive = 1; + + nxt_memzero(&vm_options, sizeof(njs_vm_opt_t)); + + vm_options.init = !opts.interactive; + vm_options.accumulative = opts.interactive; + vm_options.backtrace = 1; + vm_options.quiet = opts.quiet; + vm_options.sandbox = opts.sandbox; + vm_options.module = opts.module; + + vm_options.ops = &njs_console_ops; + vm_options.external = &njs_console; + + ret = njs_interactive_shell(&opts, &vm_options, &line); + free(input); + + if (ret != NXT_OK) + return 0; + + return 0; +} diff --git a/projects/njs/project.yaml b/projects/njs/project.yaml new file mode 100644 index 000000000..8a659c226 --- /dev/null +++ b/projects/njs/project.yaml @@ -0,0 +1,7 @@ +homepage: "https://nginx.org/en/docs/njs/" +primary_contact: "mmoroz@google.com" +experimental: True +sanitizers: + - address + - undefined + - memory |