From 4531224989733303a90bf8c98de84781072fb251 Mon Sep 17 00:00:00 2001 From: PGZXB Date: Fri, 25 Sep 2026 21:17:14 +0800 Subject: [PATCH] Fix NULL pointer dereference in the module linker on a duplicate binding A module whose top-level binding is declared with `var`/`function` and then re-declared by a namespace import (`import * as name`) is accepted, although ECMA-262 makes duplicate top-level module binding names an early SyntaxError. The scanner already records `SCANNER_TYPE_ERR_REDECLARED` for the imported name and the named-import clause rejects it, but the namespace-import branch of `parser_parse_import_statement()` never performed that check. The name is then referenced from the module scope while absent from `module_p->imports_p`, and during linking `ecma_module_resolve_export()` falls through to `ecma_module_resolve_import()`. That function walks `imports_p` with an unbounded `while (true)` loop whose only terminating guard is a `JERRY_ASSERT` (compiled out in release builds), so it dereferences `import_node_p->module_names_p` after `import_node_p` has reached NULL. The namespace-import path now performs the same redeclaration check that `parser_module_parse_import_clause()` already uses, so the duplicate binding is rejected as a SyntaxError while the module is parsed. As defence in depth, `ecma_module_resolve_import()` now stops when it runs out of import nodes and returns `false` (resolution failure) instead of relying on an assertion; the caller already handles an unsuccessful resolution. Fixes #5292 JerryScript-DCO-1.0-Signed-off-by: PGZXB --- jerry-core/ecma/base/ecma-module.c | 6 +++--- jerry-core/parser/js/js-parser-statm.c | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index 21fc52fd70..1f6917eed9 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -335,10 +335,8 @@ ecma_module_resolve_import (ecma_module_resolve_result_t *resolve_result_p, /**< { ecma_module_node_t *import_node_p = module_p->imports_p; - while (true) + while (import_node_p != NULL) { - JERRY_ASSERT (import_node_p != NULL); - for (ecma_module_names_t *import_names_p = import_node_p->module_names_p; import_names_p != NULL; import_names_p = import_names_p->next_p) { @@ -368,6 +366,8 @@ ecma_module_resolve_import (ecma_module_resolve_result_t *resolve_result_p, /**< import_node_p = import_node_p->next_p; } + + return false; } /* ecma_module_resolve_import */ /** diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 10df37e514..2a4f098a02 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -2327,6 +2327,12 @@ parser_parse_import_statement (parser_context_t *context_p) /**< parser context parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); + parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); + } + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); ecma_string_t *local_name_p = parser_new_ecma_string_from_literal (context_p->lit_object.literal_p);