]> git.kaiwu.me - njs.git/commitdiff
Modules: reject asynchronous work during module initialization
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 5 Nov 2026 00:49:18 +0000 (16:49 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Wed, 11 Nov 2026 21:33:45 +0000 (13:33 -0800)
Global code is intended for synchronous initialization, but Fetch and
timers could start request-bound work before clone publication.  QuickJS
Fetch dereferenced the unpublished engine, and module-level timers could
escape initialization.

Reject Fetch, setTimeout(), and setImmediate() before argument conversion
or resource allocation in both engines.  This does not prohibit ordinary
promise jobs or synchronous module initialization.

nginx/ngx_js.c
nginx/ngx_js_fetch.c
nginx/ngx_qjs_fetch.c

index 523799c3c269fb78db4dc5896c96b13ee8190a8b..282af63185bc46e397b84da9a9c79b9f7be3170f 100644 (file)
@@ -1832,6 +1832,13 @@ ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val, int argc,
     ngx_qjs_event_t   *event;
     ngx_connection_t  *c;
 
+    external = JS_GetContextOpaque(cx);
+    ctx = ngx_qjs_external_ctx(cx, external);
+    if (ctx->engine == NULL) {
+        return JS_ThrowTypeError(cx, "async operations are not allowed "
+                                    "during module initialization");
+    }
+
     if (!JS_IsFunction(cx, argv[0])) {
         return JS_ThrowTypeError(cx, "first arg must be a function");
     }
@@ -1846,9 +1853,6 @@ ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val, int argc,
 
     n = immediate ? 1 : 2;
     argc = (argc >= n) ? argc - n : 0;
-    external = JS_GetContextOpaque(cx);
-    ctx = ngx_qjs_external_ctx(cx, external);
-
     event = ngx_pcalloc(ngx_qjs_external_pool(cx, external),
                         sizeof(ngx_qjs_event_t) + sizeof(JSValue) * argc);
     if (event == NULL) {
@@ -3285,6 +3289,13 @@ njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_js_event_t    *event;
     ngx_connection_t  *c;
 
+    ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+    if (ctx->engine == NULL) {
+        njs_vm_type_error(vm, "async operations are not allowed "
+                             "during module initialization");
+        return NJS_ERROR;
+    }
+
     if (njs_slow_path(nargs < 2)) {
         njs_vm_type_error(vm, "too few arguments");
         return NJS_ERROR;
@@ -3320,7 +3331,6 @@ njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     event->args = (njs_opaque_value_t *) &event[1];
     event->destructor = ngx_js_clear_timer;
 
-    ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
     event->fd = ctx->event_id++;
 
     c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
index 003ded35d6b44904711cd2e5ff3eece1e65df0ac..73abe8ff7a8cb9781ad3e88c2aa1b680609eadb3 100644 (file)
@@ -510,6 +510,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ngx_str_t           *resolve_host;
     ngx_pool_t          *pool;
     njs_value_t         *init, *value;
+    ngx_js_ctx_t        *jsctx;
     ngx_js_http_t       *http;
     ngx_js_fetch_t      *fetch;
     ngx_js_request_t     request;
@@ -525,6 +526,13 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 #endif
 
     external = njs_vm_external_ptr(vm);
+    jsctx = ngx_external_ctx(vm, external);
+    if (jsctx->engine == NULL) {
+        njs_vm_type_error(vm, "async operations are not allowed "
+                             "during module initialization");
+        return NJS_ERROR;
+    }
+
     c = ngx_external_connection(vm, external);
     pool = ngx_external_pool(vm, external);
 
index 7149b976b8dba41f302addf17d79602f05cd2639..ff34dd55a3a64c344c7542ed3d0aa800cc438660 100644 (file)
@@ -257,6 +257,12 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
     ngx_resolver_ctx_t  *rs;
 
     external = JS_GetContextOpaque(cx);
+    ctx = ngx_qjs_external_ctx(cx, external);
+    if (ctx->engine == NULL) {
+        return JS_ThrowTypeError(cx, "async operations are not allowed "
+                                    "during module initialization");
+    }
+
     c = ngx_qjs_external_connection(cx, external);
     pool = ngx_qjs_external_pool(cx, external);
 
@@ -353,8 +359,6 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
         http->header_only = 1;
     }
 
-    ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx));
-
     NJS_CHB_MP_INIT(&http->chain, ctx->engine->pool);
     NJS_CHB_MP_INIT(&http->response.chain, ctx->engine->pool);