Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/ccompiler/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ typedef struct {
size_t typedef_scope_count;
size_t typedef_scope_capacity;
size_t expression_depth;
size_t statement_depth;
} CCParser;

static const char *const cc_ast_kind_names[] = {
Expand Down Expand Up @@ -271,6 +272,25 @@ static void cc_leave_expression_nesting(CCParser *parser) {
parser->expression_depth--;
}

/* Statement parsing recurses per nesting level too, through compound blocks
* and the bodies of if/while/for/do/switch. Nested braces alone are enough to
* exhaust the stack, so the same cap applies one level up. */
#define CC_MAX_STATEMENT_NESTING 200

static bool cc_enter_statement_nesting(CCParser *parser) {
if (parser->statement_depth >= CC_MAX_STATEMENT_NESTING) {
cc_expected(parser, "statement nesting too deep");
return false;
}

parser->statement_depth++;
return true;
}

static void cc_leave_statement_nesting(CCParser *parser) {
parser->statement_depth--;
}

static CCAstNode *cc_new_ast_node(CCAstKind kind, CCSpan span, const char *text) {
CCAstNode *node;

Expand Down Expand Up @@ -579,6 +599,7 @@ static void cc_synchronize_to_semicolon_or_brace(CCParser *parser) {
static CCAstNode *cc_parse_expression(CCParser *parser);
static CCAstNode *cc_parse_assignment_expression(CCParser *parser);
static CCAstNode *cc_parse_statement(CCParser *parser);
static CCAstNode *cc_parse_statement_body(CCParser *parser);
static CCAstNode *cc_parse_declaration(CCParser *parser);
static CCParsedDeclarator cc_parse_declarator(CCParser *parser, bool allow_abstract);

Expand Down Expand Up @@ -1809,6 +1830,18 @@ static CCAstNode *cc_parse_expression_statement(CCParser *parser) {
}

static CCAstNode *cc_parse_statement(CCParser *parser) {
CCAstNode *statement;

if (!cc_enter_statement_nesting(parser)) {
return NULL;
}

statement = cc_parse_statement_body(parser);
cc_leave_statement_nesting(parser);
return statement;
}

static CCAstNode *cc_parse_statement_body(CCParser *parser) {
if (cc_current_token(parser)->kind == CC_TOKEN_IDENTIFIER && cc_peek_token(parser, 1)->kind == CC_TOKEN_COLON) {
return cc_parse_labeled_statement(parser);
}
Expand Down
8 changes: 5 additions & 3 deletions src/ccompiler/sema_const.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@ static bool cc_parse_integer_literal(const char *text, long long *value) {
copy[length] = '\0';

if (length > 2 && copy[0] == '0' && (copy[1] == 'b' || copy[1] == 'B')) {
long long parsed;
unsigned long long parsed;

parsed = 0;
for (index = 2; index < length; index++) {
if (copy[index] != '0' && copy[index] != '1') {
free(copy);
return false;
}
parsed = (parsed << 1) | (copy[index] - '0');
/* Accumulate unsigned: shifting a signed value up into the sign bit
* is undefined, and a 64-bit literal does exactly that. */
parsed = (parsed << 1) | (unsigned long long)(copy[index] - '0');
}
*value = parsed;
*value = (long long)parsed;
free(copy);
return true;
}
Expand Down
6 changes: 6 additions & 0 deletions tests/binary_literal_overflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* Regression test: the 0b fold accumulated into a signed long long, so a
* literal whose top bit lands in the sign position shifted into it. That is
* undefined behaviour, not a wrapped value, and UBSan flagged it. The fold now
* accumulates unsigned and converts once on the way out.
*/
int main(void){ switch (0) { case 0b1111111111111111111111111111111111111111111111111111111111111111: return 1; } return 0; }
5 changes: 5 additions & 0 deletions tests/data/smoke_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ parse|tests/pathological_statement.c|1|expected expression
parse|tests/pathological_conditional.c|1|expected expression
# Regression: deep nesting is capped instead of overflowing the stack
parse|tests/pathological_nesting.c|1|expression nesting too deep
# Regression: statement nesting is capped too, whether or not blocks are involved
parse|tests/pathological_statement_nesting.c|1|statement nesting too deep
parse|tests/pathological_dangling_if.c|1|statement nesting too deep
# Regression: self-including headers hit the include-depth cap, not the stack
preprocess|tests/include_cycle_a.c|1|include nesting too deep
# Regression: const folding must reject UB shifts/overflow instead of tripping UBSan
check|tests/shift_too_large.c|1|case label must be an integer constant expression
check|tests/const_overflow.c|1|case label must be an integer constant expression
# Regression: a 0b literal wide enough to reach the sign bit must fold without UB
check|tests/binary_literal_overflow.c|0|semantic analysis succeeded
8 changes: 8 additions & 0 deletions tests/pathological_dangling_if.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* Regression test: the depth cap covers brace-less nesting too. A chain of
* dangling ifs recurses through the statement parser without ever opening a
* block, so a cap on compound statements alone would not have caught it.
*/
int main(void) {
if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) if (1) return 0;
return 0;
}
9 changes: 9 additions & 0 deletions tests/pathological_statement_nesting.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* Regression test: statement nesting is capped the way expression nesting
* already is. Nested blocks recurse through the statement and
* compound-statement parsers with no other bound, which overflows the stack
* on deep input. The parser now reports "statement nesting too deep".
*/
int main(void) {
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
return 0;
}
Loading