Project: njs (nginx/njs), language: c++
Vulnerability instance: njs.cve-2019-13617
Sanitizer: address

================= Bug Report (1/2) ==================
## Source: GitHub Issue
## URL: https://github.com/nginx/njs/issues/174
## Description:
Issue: nginx/njs#174
Title: heap-buffer-overflow in nxt_sprintf.c:429
State: closed
Created by: xcainiao
Created at: 2019-06-03 14:46:39+00:00
Labels: bug, fuzzer, libFuzzer

Issue Body:
Hello, I am shuoz of alpha lab of topsec. I fuzz njs and found a heap overflow bug.

./njs ./tmp.js

```
fuzz@fuzz:~/Desktop/fuzzproject/njs/build$ ./njs  ./tmp.js
INFO: Seed: 2208882516
INFO: Loaded 1 modules (8780 guards): [0x8a15a0, 0x8a9ed0),
./njs: Running 1 inputs 1 time(s) each.
Running: crash-0c9fced77823aa506239627a5473e8c4bb55ec96
=================================================================
==16711==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61100000039b at pc 0x0000004dc4e2 bp 0x7ffc117a8770 sp 0x7ffc117a7f20
READ of size 217 at 0x61100000039b thread T0
    #0 0x4dc4e1 in __asan_memcpy (/home/fuzz/Desktop/fuzzproject/njs/build/njs+0x4dc4e1)
    #1 0x521ce2 in nxt_vsprintf /home/fuzz/Desktop/fuzzproject/njs/nxt/nxt_sprintf.c:429:15
    #2 0x5ada7b in njs_parser_scope_error /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:2365:9
    #3 0x5ada7b in njs_parser_lexer_error /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:2395
    #4 0x58ab62 in njs_regexp_literal /home/fuzz/Desktop/fuzzproject/njs/njs/njs_regexp.c:364:5
    #5 0x5b55fc in njs_parser_terminal /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_terminal.c:106:17
    #6 0x5bcd95 in njs_parser_call_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:748:17
    #7 0x5bc19b in njs_parser_post_inc_dec_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:689:13
    #8 0x5bc19b in njs_parser_inc_dec_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:649
    #9 0x5bc19b in njs_parser_unary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:553
    #10 0x5bbb9b in njs_parser_exponential_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:476:13
    #11 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #12 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #13 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #14 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #15 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #16 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #17 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #18 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #19 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #20 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #21 0x5bafec in njs_parser_conditional_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:352:13
    #22 0x5bafec in njs_parser_assignment_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:228
    #23 0x5bab67 in njs_parser_binary_expression /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser_expression.c:421:13
    #24 0x5af3f1 in njs_parser_statement /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:413:21
    #25 0x5a9f04 in njs_parser_statement_chain /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:280:13
    #26 0x5b3af6 in njs_parser_block_statement /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:465:17
    #27 0x5ae2a2 in njs_parser_statement /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:365:16
    #28 0x5a9f04 in njs_parser_statement_chain /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:280:13
    #29 0x5a940a in njs_parser /home/fuzz/Desktop/fuzzproject/njs/njs/njs_parser.c:131:17
    #30 0x527ae4 in njs_vm_compile /home/fuzz/Desktop/fuzzproject/njs/njs/njs.c:254:11
    #31 0x517338 in njs_process_script /home/fuzz/Desktop/fuzzproject/njs/njs/njs_shell.c:780:11
    #32 0x516714 in njs_process_file /home/fuzz/Desktop/fuzzproject/njs/njs/njs_shell.c:637:11
    #33 0x516714 in LLVMFuzzerTestOneInput /home/fuzz/Desktop/fuzzproject/njs/njs/njs_shell.c:298
    #34 0x6046a4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerLoop.cpp:451:13
    #35 0x6048d1 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerLoop.cpp:408:3
    #36 0x5fab41 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerDriver.cpp:268:6
    #37 0x5fda01 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerDriver.cpp:585:9
    #38 0x5fa8c0 in main /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerMain.cpp:20:10
    #39 0x7f07973cab96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
    #40 0x41d769 in _start (/home/fuzz/Desktop/fuzzproject/njs/build/njs+0x41d769)

0x61100000039b is located 0 bytes to the right of 219-byte region [0x6110000002c0,0x61100000039b)
allocated by thread T0 here:
    #0 0x4ddaa0 in realloc (/home/fuzz/Desktop/fuzzproject/njs/build/njs+0x4ddaa0)
    #1 0x516488 in njs_process_file /home/fuzz/Desktop/fuzzproject/njs/njs/njs_shell.c:569:20
    #2 0x516488 in LLVMFuzzerTestOneInput /home/fuzz/Desktop/fuzzproject/njs/njs/njs_shell.c:298
    #3 0x6046a4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerLoop.cpp:451:13
    #4 0x6048d1 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerLoop.cpp:408:3
    #5 0x5fab41 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerDriver.cpp:268:6
    #6 0x5fda01 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerDriver.cpp:585:9
    #7 0x5fa8c0 in main /home/fuzz/Desktop/libfuzzer-workshop-master/libFuzzer/Fuzzer/./FuzzerMain.cpp:20:10
    #8 0x7f07973cab96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/fuzz/Desktop/fuzzproject/njs/build/njs+0x4dc4e1) in __asan_memcpy
Shadow bytes around the buggy address:
  0x0c227fff8020: 00 00 00 03 fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff8040: 00 00 00 00 00 00 00 00 00 00 00 03 fa fa fa fa
  0x0c227fff8050: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c227fff8060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c227fff8070: 00 00 00[03]fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff8090: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa
  0x0c227fff80a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff80b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff80c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==16711==ABORTING
```

pocfile:https://github.com/xcainiao/poc/blob/master/tmp.js


Comments:
Comment by Dor1s on 2019-06-03 17:36:42+00:00:
This was also found as https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=15093 last week.

---

Comment by xeioex on 2019-06-03 17:45:04+00:00:
> Permission denied.

HI @Dor1s , can I get access to the ticket details?



---

Comment by Dor1s on 2019-06-03 17:50:09+00:00:
Yes! Please update the `project.yaml` file as per this comment: https://github.com/google/oss-fuzz/pull/2481#issuecomment-498109267

Or give me the contact emails and I can update it for you :)

After that, OSS-Fuzz will propagate the changes and the specified emails will get access to the bug tracker as well as ClusterFuzz interface.

Commit References:
0c9fced77823aa506239627a5473e8c4bb55ec96

================= Bug Report (2/2) ==================
## Source: Chromium
## URL: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=15093
## Description:
Detailed report: https://oss-fuzz.com/testcase?key=5142265766346752

Project: njs
Fuzzer: libFuzzer_njs_process_script_fuzzer
Fuzz target binary: njs_process_script_fuzzer
Job Type: libfuzzer_asan_njs
Platform Id: linux

Crash Type: Heap-buffer-overflow READ 5
Crash Address: 0x605000000224
Crash State:
  nxt_vsprintf
  njs_parser_scope_error
  njs_parser_lexer_error
 
Sanitizer: address (ASAN)

Recommended Security Severity: Medium

Reproducer Testcase: https://oss-fuzz.com/download?testcase_id=5142265766346752

Issue filed automatically.

See https://github.com/google/oss-fuzz/blob/master/docs/reproducing.md for instructions to reproduce this bug locally.

This bug is subject to a 90 day disclosure deadline. If 90 days elapse
without an upstream patch, then the bug report will automatically
become visible to the public.

When you fix this bug, please
  * mention the fix revision(s).
  * state whether the bug was a short-lived regression or an old bug in any stable releases.
  * add any other useful information.
This information can help downstream consumers.

If you need to contact the OSS-Fuzz team with a question, concern, or any other feedback, please file an issue at https://github.com/google/oss-fuzz/issues.

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
./autogen.sh
CFLAGS="$CFLAGS -fno-use-cxa-atexit" CXXFLAGS="$CXXFLAGS -fno-use-cxa-atexit" ./configure
make -j$(nproc) clean
make -j$(nproc) all
./configure
make njs -j$(nproc)
mkdir -p $SEED_CORPUS_PATH

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool mercurial libpcre3-dev libreadline-dev subversion
RUN git clone https://github.com/nginx/njs njs
RUN git -C njs checkout 7b540a9bf295ba8426cc6c8962e595c0cbc6a295
WORKDIR $SRC/njs
COPY build.sh $SRC/

Additional files:
filename: njs_process_script_fuzzer.c
content: // 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;
}