diff --git a/quickjs.c b/quickjs.c index 535925fe3..aa405220d 100644 --- a/quickjs.c +++ b/quickjs.c @@ -23071,10 +23071,16 @@ static int update_label(JSFunctionDef *s, int label, int delta) { LabelSlot *ls; - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; ls = &s->label_slots[label]; ls->ref_count += delta; - assert(ls->ref_count >= 0); + if (ls->ref_count < 0) + ls->ref_count = 0; return ls->ref_count; } @@ -33630,7 +33636,12 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) LabelSlot *ls; label = get_u32(bc_buf + pos + 1); - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; ls = &s->label_slots[label]; if (code_match(&cc, ls->pos, OP_ret, -1)) { ls->ref_count--; @@ -33695,7 +33706,12 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) LabelSlot *ls; label = get_u32(bc_buf + pos + 1); - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; ls = &s->label_slots[label]; ls->pos2 = bc_out.size + opcode_info[op].size; } @@ -33968,7 +33984,12 @@ static int find_jump_target(JSFunctionDef *s, int label, int *pop) update_label(s, label, -1); for (i = 0; i < 10; i++) { - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; pos = s->label_slots[label].pos2; for (;;) { switch(op = s->byte_code.buf[pos]) { @@ -34192,7 +34213,12 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) case OP_label: { label = get_u32(bc_buf + pos + 1); - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; ls = &label_slots[label]; assert(ls->addr == -1); ls->addr = bc_out.size; @@ -34317,7 +34343,12 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num, &col_num); } - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; ls = &label_slots[label]; jp = &s->jump_slots[s->jump_count++]; jp->op = op; @@ -34384,7 +34415,12 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) label = get_u32(bc_buf + pos + 5); is_with = bc_buf[pos + 9]; label = find_jump_target(s, label, &op1); - assert(label >= 0 && label < s->label_count); + /* Bounds check: untrusted JS can trigger invalid labels during bytecode + * optimization. Returning -1 is strictly safer than aborting the host + * process via assert. Discovered during adversarial testing of embedded + * QuickJS sandboxes processing untrusted web content. */ + if (label < 0 || label >= s->label_count) + return -1; ls = &label_slots[label]; add_pc2line_info(s, bc_out.size, line_num, col_num); jp = &s->jump_slots[s->jump_count++];