From a2a743375e83845b06668f71524e80e1aa661d67 Mon Sep 17 00:00:00 2001 From: 1820893135-pixel <1820893135@qq.com> Date: Wed, 23 Sep 2026 17:08:16 +0800 Subject: [PATCH] Do not trust argument_end to select the shared frame layout vm_init_exec() decided whether shared_p carries an argument list by looking at argument_end, a field taken from the bytecode header, and then cast shared_p to vm_frame_ctx_shared_args_t: if (argument_end > 0) { JERRY_ASSERT (shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST); const ecma_value_t *arg_list_p = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_p; arg_list_len = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_len; The assert is compiled out with NDEBUG, and vm_run_global() only ever passes a plain vm_frame_ctx_shared_t, so a snapshot whose bytecode declares arguments makes this read go past the 24-byte shared struct: ==ERROR: AddressSanitizer: stack-buffer-overflow READ of size 8 #0 vm_init_exec vm.c:5190 #1 vm_run vm.c:5330 #2 vm_run_global vm.c:286 #3 jerry_exec_snapshot jerry-snapshot.c:1024 [32, 56) 'shared' (line 272) <== Memory access at offset 56 overflows The layout is described by VM_FRAME_CTX_SHARED_HAS_ARG_LIST, which is set by the callers that actually build the args variant, so test that flag instead of argument_end. A snapshot that cannot provide an argument list now leaves the registers undefined, exactly as it does when argument_end is zero. JerryScript-DCO-1.0-Signed-off-by: 1820893135-pixel <1820893135@qq.com> --- jerry-core/vm/vm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index b6e1e88fb8..fd4caa9b30 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -5183,10 +5183,12 @@ vm_init_exec (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ uint32_t arg_list_len = 0; - if (argument_end > 0) + /* shared_p only has the vm_frame_ctx_shared_args_t layout - the one carrying the + * argument list - when VM_FRAME_CTX_SHARED_HAS_ARG_LIST is set. argument_end comes + * from the bytecode header and must not be used on its own to decide this, or a + * crafted snapshot makes us read past a plain vm_frame_ctx_shared_t. */ + if (argument_end > 0 && (shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST)) { - JERRY_ASSERT (shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST); - const ecma_value_t *arg_list_p = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_p; arg_list_len = ((vm_frame_ctx_shared_args_t *) shared_p)->arg_list_len;