Skip to content

Commit 4d450dc

Browse files
authored
fix: prevent out-of-bounds read in tryBacktrackAddStarExportBinding (#41)
1 parent 61d923b commit 4d450dc

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

src/parser.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,11 +1475,13 @@ class CJSLexer {
14751475
}
14761476

14771477
void tryBacktrackAddStarExportBinding(const char* bPos) {
1478-
while (*bPos == ' ' && bPos > source)
1478+
if (bPos < source) return;
1479+
while (bPos > source && *bPos == ' ')
14791480
bPos--;
14801481
if (*bPos == '=') {
1482+
if (bPos <= source) return;
14811483
bPos--;
1482-
while (*bPos == ' ' && bPos > source)
1484+
while (bPos > source && *bPos == ' ')
14831485
bPos--;
14841486
const char* id_end = bPos;
14851487
bool identifierStart = false;
@@ -1494,7 +1496,7 @@ class CJSLexer {
14941496
if (starExportStack == STAR_EXPORT_STACK_END)
14951497
return;
14961498
starExportStack->id = std::string_view(bPos + 1, static_cast<size_t>(id_end - bPos));
1497-
while (*bPos == ' ' && bPos > source)
1499+
while (bPos > source && *bPos == ' ')
14981500
bPos--;
14991501
switch (*bPos) {
15001502
case 'r':

tests/real_world_tests.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,40 @@ TEST(real_world_tests, line_numbers_reexports) {
12721272
ASSERT_EQ(result->re_exports[0].line, 2);
12731273
}
12741274

1275+
// Regression test for nodejs/node#62212:
1276+
// SIGSEGV when parsing a CJS bundle that starts with require() at position 0.
1277+
// tryBacktrackAddStarExportBinding was passed (source - 1) and dereferenced
1278+
// the pointer before checking it against the source boundary.
1279+
TEST(real_world_tests, require_at_start_of_input) {
1280+
// Minimal case: require() as the very first token triggers
1281+
// tryBacktrackAddStarExportBinding(startPos - 1) where startPos == source.
1282+
auto result = lexer::parse_commonjs("require('./foo')");
1283+
ASSERT_TRUE(result.has_value());
1284+
1285+
// Typical ncc/webpack bundle pattern that starts with require()
1286+
auto result2 = lexer::parse_commonjs(
1287+
"require('./sourcemap-register.js');"
1288+
"(()=>{var __webpack_modules__={"
1289+
"0:(module,exports,__webpack_require__)=>{"
1290+
"\"use strict\";"
1291+
"var _a=__webpack_require__(1);"
1292+
"exports.default=_a;"
1293+
"}"
1294+
"};"
1295+
"var __webpack_module_cache__={};"
1296+
"function __webpack_require__(id){"
1297+
"var c=__webpack_module_cache__[id];"
1298+
"if(c!==undefined)return c.exports;"
1299+
"var m=__webpack_module_cache__[id]={exports:{}};"
1300+
"__webpack_modules__[id](m,m.exports,__webpack_require__);"
1301+
"return m.exports;}"
1302+
"var __webpack_exports__=__webpack_require__(0);"
1303+
"module.exports=__webpack_exports__;"
1304+
"})();"
1305+
);
1306+
ASSERT_TRUE(result2.has_value());
1307+
}
1308+
12751309
TEST(real_world_tests, line_numbers_after_block_comment) {
12761310
auto result = lexer::parse_commonjs(
12771311
"/*\n"

0 commit comments

Comments
 (0)