diff --git a/src/parser.cpp b/src/parser.cpp index 9f06072..da67699 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1475,11 +1475,13 @@ class CJSLexer { } void tryBacktrackAddStarExportBinding(const char* bPos) { - while (*bPos == ' ' && bPos > source) + if (bPos < source) return; + while (bPos > source && *bPos == ' ') bPos--; if (*bPos == '=') { + if (bPos <= source) return; bPos--; - while (*bPos == ' ' && bPos > source) + while (bPos > source && *bPos == ' ') bPos--; const char* id_end = bPos; bool identifierStart = false; @@ -1494,7 +1496,7 @@ class CJSLexer { if (starExportStack == STAR_EXPORT_STACK_END) return; starExportStack->id = std::string_view(bPos + 1, static_cast(id_end - bPos)); - while (*bPos == ' ' && bPos > source) + while (bPos > source && *bPos == ' ') bPos--; switch (*bPos) { case 'r': diff --git a/tests/real_world_tests.cpp b/tests/real_world_tests.cpp index dac0568..f320bc3 100644 --- a/tests/real_world_tests.cpp +++ b/tests/real_world_tests.cpp @@ -1272,6 +1272,40 @@ TEST(real_world_tests, line_numbers_reexports) { ASSERT_EQ(result->re_exports[0].line, 2); } +// Regression test for nodejs/node#62212: +// SIGSEGV when parsing a CJS bundle that starts with require() at position 0. +// tryBacktrackAddStarExportBinding was passed (source - 1) and dereferenced +// the pointer before checking it against the source boundary. +TEST(real_world_tests, require_at_start_of_input) { + // Minimal case: require() as the very first token triggers + // tryBacktrackAddStarExportBinding(startPos - 1) where startPos == source. + auto result = lexer::parse_commonjs("require('./foo')"); + ASSERT_TRUE(result.has_value()); + + // Typical ncc/webpack bundle pattern that starts with require() + auto result2 = lexer::parse_commonjs( + "require('./sourcemap-register.js');" + "(()=>{var __webpack_modules__={" + "0:(module,exports,__webpack_require__)=>{" + "\"use strict\";" + "var _a=__webpack_require__(1);" + "exports.default=_a;" + "}" + "};" + "var __webpack_module_cache__={};" + "function __webpack_require__(id){" + "var c=__webpack_module_cache__[id];" + "if(c!==undefined)return c.exports;" + "var m=__webpack_module_cache__[id]={exports:{}};" + "__webpack_modules__[id](m,m.exports,__webpack_require__);" + "return m.exports;}" + "var __webpack_exports__=__webpack_require__(0);" + "module.exports=__webpack_exports__;" + "})();" + ); + ASSERT_TRUE(result2.has_value()); +} + TEST(real_world_tests, line_numbers_after_block_comment) { auto result = lexer::parse_commonjs( "/*\n"