From: Fabrice Bellard Date: Mon, 2 Nov 2026 10:37:16 +0000 (+0100) Subject: ensure that the maximum number of variable references fits on 16 bits (#589) - added... X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=e8b69194b3b60686ce32b1df65ded68bfd519bd4;p=quickjs.git ensure that the maximum number of variable references fits on 16 bits (#589) - added more error handling in the compiler --- diff --git a/quickjs.c b/quickjs.c index be2e8f0..61e4977 100644 --- a/quickjs.c +++ b/quickjs.c @@ -33889,12 +33889,18 @@ static void var_object_test(JSContext *ctx, JSFunctionDef *s, s->jump_size++; } -static inline void capture_var(JSFunctionDef *s, JSVarDef *vd) +static inline __exception int capture_var(JSFunctionDef *s, JSVarDef *vd) { if (!vd->is_captured) { + /* the variable reference indexes are currently stored on 16 bits */ + if (unlikely(s->var_ref_count >= JS_MAX_LOCAL_VARS)) { + JS_ThrowInternalError(s->ctx, "too variable references"); + return -1; + } vd->is_captured = 1; vd->var_ref_idx = s->var_ref_count++; } + return 0; } /* return the position of the next opcode or -1 if error */ @@ -34006,12 +34012,14 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, /* Create a dummy object with a named slot that is a reference to the local variable */ if (var_idx & ARGUMENT_VAR_OFFSET) { - capture_var(s, &s->args[var_idx - ARGUMENT_VAR_OFFSET]); + if (capture_var(s, &s->args[var_idx - ARGUMENT_VAR_OFFSET])) + return -1; dbuf_putc(bc, OP_make_arg_ref); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); } else { - capture_var(s, &s->vars[var_idx]); + if (capture_var(s, &s->vars[var_idx])) + return -1; dbuf_putc(bc, OP_make_loc_ref); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_put_u16(bc, var_idx); @@ -34105,7 +34113,8 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, var_idx = idx; break; } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { - capture_var(fd, vd); + if (capture_var(fd, vd)) + return -1; idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); if (idx >= 0) { dbuf_putc(bc, OP_get_var_ref); @@ -34142,7 +34151,8 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, /* check eval object */ if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { vd = &fd->vars[fd->var_object_idx]; - capture_var(fd, vd); + if (capture_var(fd, vd)) + return -1; idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, fd->var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); @@ -34154,7 +34164,8 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, /* check eval object in argument scope */ if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { vd = &fd->vars[fd->arg_var_object_idx]; - capture_var(fd, vd); + if (capture_var(fd, vd)) + return -1; idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, fd->arg_var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); @@ -34269,12 +34280,14 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } else { /* find the corresponding closure variable */ if (var_idx & ARGUMENT_VAR_OFFSET) { - capture_var(fd, &fd->args[var_idx - ARGUMENT_VAR_OFFSET]); + if (capture_var(fd, &fd->args[var_idx - ARGUMENT_VAR_OFFSET])) + return -1; idx = get_closure_var(ctx, s, fd, JS_CLOSURE_ARG, var_idx - ARGUMENT_VAR_OFFSET, var_name, FALSE, FALSE, JS_VAR_NORMAL); } else { - capture_var(fd, &fd->vars[var_idx]); + if (capture_var(fd, &fd->vars[var_idx])) + return -1; idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, var_idx, var_name, @@ -34421,7 +34434,8 @@ static int resolve_scope_private_field1(JSContext *ctx, if (idx >= 0) { var_kind = fd->vars[idx].var_kind; if (is_ref) { - capture_var(fd, &fd->vars[idx]); + if (capture_var(fd, &fd->vars[idx])) + return -1; idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, var_name, TRUE, TRUE, JS_VAR_NORMAL); if (idx < 0) @@ -34568,17 +34582,19 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, return 0; } -static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, - int scope_level) +static __exception int mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, + int scope_level) { int idx; JSVarDef *vd; for (idx = s->scopes[scope_level].first; idx >= 0;) { vd = &s->vars[idx]; - capture_var(s, vd); + if (capture_var(s, vd)) + return -1; idx = vd->scope_next; } + return 0; } /* XXX: should handle the argument scope generically */ @@ -34592,7 +34608,7 @@ static BOOL is_var_in_arg_scope(JSAtom var_name, JSVarKindEnum var_kind) var_kind == JS_VAR_FUNCTION_NAME); } -static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) +static __exception int add_eval_variables(JSContext *ctx, JSFunctionDef *s) { JSFunctionDef *fd; JSVarDef *vd; @@ -34636,7 +34652,8 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) for(i = 0; i < s->arg_count; i++) { vd = &s->args[i]; - capture_var(s, vd); + if (capture_var(s, vd)) + return -1; } for(i = 0; i < s->var_count; i++) { vd = &s->vars[i]; @@ -34644,7 +34661,8 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_ && vd->var_name != JS_ATOM_NULL) { - capture_var(s, vd); + if (capture_var(s, vd)) + return -1; } } @@ -34686,9 +34704,11 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) scope_idx = fd->scopes[scope_level].first; while (scope_idx >= 0) { vd = &fd->vars[scope_idx]; - capture_var(fd, vd); - get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, scope_idx, - vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); + if (capture_var(fd, vd)) + return -1; + if (get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, scope_idx, + vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind) < 0) + return -1; scope_idx = vd->scope_next; } is_arg_scope = (scope_idx == ARG_SCOPE_END); @@ -34698,10 +34718,12 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) for(i = 0; i < fd->arg_count; i++) { vd = &fd->args[i]; if (vd->var_name != JS_ATOM_NULL) { - capture_var(fd, vd); - get_closure_var(ctx, s, fd, - JS_CLOSURE_ARG, i, vd->var_name, FALSE, - vd->is_lexical, JS_VAR_NORMAL); + if (capture_var(fd, vd)) + return -1; + if (get_closure_var(ctx, s, fd, + JS_CLOSURE_ARG, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL) < 0) + return -1; } } for(i = 0; i < fd->var_count; i++) { @@ -34710,10 +34732,12 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_ && vd->var_name != JS_ATOM_NULL) { - capture_var(fd, vd); - get_closure_var(ctx, s, fd, - JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, - vd->is_lexical, JS_VAR_NORMAL); + if (capture_var(fd, vd)) + return -1; + if (get_closure_var(ctx, s, fd, + JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL) < 0) + return -1; } } } else { @@ -34721,10 +34745,12 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) vd = &fd->vars[i]; /* do not close top level last result */ if (vd->scope_level == 0 && is_var_in_arg_scope(vd->var_name, vd->var_kind)) { - capture_var(fd, vd); - get_closure_var(ctx, s, fd, - JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, - vd->is_lexical, JS_VAR_NORMAL); + if (capture_var(fd, vd)) + return -1; + if (get_closure_var(ctx, s, fd, + JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL) < 0) + return -1; } } } @@ -34747,6 +34773,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } } } + return 0; } static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, @@ -35233,7 +35260,8 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { int call_argc = get_u16(bc_buf + pos + 1); scope = get_u16(bc_buf + pos + 1 + 2); - mark_eval_captured_variables(ctx, s, scope); + if (mark_eval_captured_variables(ctx, s, scope)) + goto fail; dbuf_putc(&bc_out, op); dbuf_put_u16(&bc_out, call_argc); dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); @@ -35241,7 +35269,8 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) break; case OP_apply_eval: /* convert scope index to adjusted variable index */ scope = get_u16(bc_buf + pos + 1); - mark_eval_captured_variables(ctx, s, scope); + if (mark_eval_captured_variables(ctx, s, scope)) + goto fail; dbuf_putc(&bc_out, op); dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); break; @@ -35256,6 +35285,8 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) scope = get_u16(bc_buf + pos + 5); pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, NULL, NULL, pos_next); + if (pos_next < 0) + goto fail; JS_FreeAtom(ctx, var_name); break; case OP_scope_make_ref: @@ -35269,6 +35300,8 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) ls->ref_count--; /* always remove label reference */ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, bc_buf, ls, pos_next); + if (pos_next < 0) + goto fail; JS_FreeAtom(ctx, var_name); } break; @@ -35803,14 +35836,14 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) if (s->jump_size) { s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size); if (s->jump_slots == NULL) - return -1; + goto fail; } #endif /* XXX: Should skip this phase if not generating SHORT_OPCODES */ if (s->line_number_size && !s->strip_debug) { s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size); if (s->line_number_slots == NULL) - return -1; + goto fail; s->line_number_last = s->source_pos; s->line_number_last_pc = 0; } @@ -35855,8 +35888,10 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) /* the arguments are implicitly captured because references to them are created with the 'argument' object */ - for(i = 0; i < s->arg_count; i++) - capture_var(s, &s->args[i]); + for(i = 0; i < s->arg_count; i++) { + if (capture_var(s, &s->args[i])) + goto fail; + } } if (s->arguments_arg_idx >= 0) put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); @@ -37081,8 +37116,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) are used to compile the eval and they must be ordered by scope, so it is necessary to create the closure variables before any other variable lookup is done. */ - if (fd->has_eval_call) - add_eval_variables(ctx, fd); + if (fd->has_eval_call) { + if (add_eval_variables(ctx, fd)) + goto fail; + } /* add the module global variables in the closure */ if (fd->is_eval) {