Skip to content

Commit 6cbfdd6

Browse files
ashketchumwasherearcady-lunarg
authored andcommitted
preprocessor: add macroExpandDepth guard to bound MacroExpand recursion
MacroExpand can call PrescanMacroArg (to pre-expand function-like macro arguments), which in turn calls MacroExpand for any macros found in the argument token stream. With deeply nested macro invocations in the source, this creates a call chain of depth proportional to the nesting level. At 200+ levels the native C++ stack overflows. Add macroExpandDepth to TPpContext (initialized to 0) and a RAII DepthGuard in MacroExpand that increments it on entry and decrements on exit. If the depth reaches maxMacroExpandDepth (200) MacroExpand emits a preprocessor error and returns MacroExpandNotStarted, unwinding the recursion cleanly instead of crashing. Add test preprocessor.macro.recursion.vert: F(x)=x nested 210 times triggers the guard 10 times with 'macro expansion depth limit exceeded' errors; without the guard the same input causes a stack overflow.
1 parent 598bd84 commit 6cbfdd6

6 files changed

Lines changed: 49 additions & 0 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
2+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
3+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
4+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
5+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
6+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
7+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
8+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
9+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
10+
ERROR: 0:15: 'macro expansion' : macro expansion depth limit exceeded F
11+
ERROR: 10 compilation errors. No code generated.
12+
13+

Test/baseResults/preprocessor.macro.recursion.vert.out

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test: macroExpandDepth guard prevents stack overflow via
2+
// deeply nested function-like macro expansion.
3+
//
4+
// Without the macroExpandDepth counter (and macro->busy set before
5+
// PrescanMacroArg), F(F(F(...F(1)...))) causes MacroExpand to call
6+
// PrescanMacroArg which calls MacroExpand again for each nested F,
7+
// growing the C stack without bound until stack overflow.
8+
//
9+
// With the fix, expansion is terminated at depth 200 with an error.
10+
11+
#version 450
12+
13+
#define F(x) x
14+
15+
int a = F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))));
16+
17+
void main() {}

glslang/MachineIndependent/preprocessor/Pp.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,20 @@ MacroExpandResult TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, b
12371237
{
12381238
ppToken->space = false;
12391239
int macroAtom = atomStrings.getAtom(ppToken->name);
1240+
1241+
// Bound total MacroExpand nesting depth to prevent stack overflow
1242+
// via unbounded MacroExpand <-> PrescanMacroArg mutual recursion.
1243+
if (macroExpandDepth >= maxMacroExpandDepth) {
1244+
parseContext.ppError(ppToken->loc, "macro expansion depth limit exceeded",
1245+
"macro expansion", ppToken->name);
1246+
return MacroExpandNotStarted;
1247+
}
1248+
struct DepthGuard {
1249+
int& d;
1250+
explicit DepthGuard(int& d_) : d(d_) { ++d; }
1251+
~DepthGuard() { --d; }
1252+
} guard(macroExpandDepth);
1253+
12401254
if (ppToken->fullyExpanded)
12411255
return MacroExpandNotStarted;
12421256

glslang/MachineIndependent/preprocessor/PpContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ class TPpContext {
466466
std::vector<tInput*> inputStack;
467467
bool errorOnVersion;
468468
bool versionSeen;
469+
// Current nesting depth of MacroExpand() calls; bounded to prevent
470+
// stack overflow via unbounded MacroExpand <-> PrescanMacroArg recursion.
471+
int macroExpandDepth = 0;
472+
static const int maxMacroExpandDepth = 200;
469473

470474
//
471475
// from Pp.cpp

gtests/Pp.FromFile.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ INSTANTIATE_TEST_SUITE_P(
7373
"preprocessor.paste_stringify.vert",
7474
"preprocessor.stringify_invalid.vert",
7575
"preprocessor.elseseen.oob.vert",
76+
"preprocessor.macro.recursion.vert",
7677
})),
7778
FileNameAsCustomTestSuffix
7879
);

0 commit comments

Comments
 (0)