Skip to content
Open
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
4,835 changes: 2,502 additions & 2,333 deletions src/parser/bison_parser.cpp

Large diffs are not rendered by default.

359 changes: 183 additions & 176 deletions src/parser/bison_parser.h

Large diffs are not rendered by default.

21 changes: 19 additions & 2 deletions src/parser/bison_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,31 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
%destructor { free( ($$.name) ); free( ($$.schema) ); } <table_name>
%destructor { free( ($$.name) ); } <db_name>
%destructor { free( ($$) ); } <sval>
// <str_vec> elements are char* allocated by the lexer with strdup() (unquoted
// IDENTIFIER) or hsql::substr() (quoted IDENTIFIER), both backed by malloc.
// They must be released with free(); calling delete on them is undefined
// behavior. Under tcmalloc + -fsized-deallocation that UB becomes a freelist
// corruption that crashes after enough error-recovery invocations.
// See envoyproxy/envoy#36471.
%destructor {
if (($$) != nullptr) {
for (auto ptr : *($$)) {
free(ptr);
}
}
delete ($$);
} <str_vec>

// The remaining vector types hold pointers to heap objects allocated with new
// in their respective grammar actions; delete is correct for them.
%destructor {
if (($$) != nullptr) {
for (auto ptr : *($$)) {
delete ptr;
}
}
delete ($$);
} <str_vec> <table_vec> <column_vec> <update_vec> <expr_vec> <order_vec> <stmt_vec>
} <table_vec> <column_vec> <update_vec> <expr_vec> <order_vec> <stmt_vec>
%destructor { delete ($$); } <*>


Expand All @@ -171,7 +188,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
%token DOUBLE ESCAPE EXCEPT EXISTS EXTRACT GLOBAL HAVING IMPORT
%token INSERT ISNULL OFFSET RENAME SCHEMA SELECT SORTED
%token TABLES UNIQUE UNLOAD UPDATE VALUES AFTER ALTER CROSS
%token DELTA FLOAT GROUP INDEX INNER LIMIT LOCAL MERGE MINUS ORDER
%token FLOAT GROUP INDEX INNER LIMIT LOCAL MERGE MINUS ORDER
%token OUTER RIGHT TABLE UNION USING WHERE CALL CASE CHAR DATE
%token DESC DROP ELSE FILE FROM FULL HASH HINT INTO JOIN
%token LEFT LIKE LOAD LONG NULL PLAN SHOW TEXT THEN TIME
Expand Down
2,511 changes: 1,249 additions & 1,262 deletions src/parser/flex_lexer.cpp

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/parser/flex_lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
#define hsql_HEADER_H 1
#define hsql_IN_HEADER 1

#line 5 "flex_lexer.h"
#line 6 "flex_lexer.h"

#line 7 "flex_lexer.h"
#line 8 "flex_lexer.h"

#define YY_INT_ALIGNED short int

Expand Down Expand Up @@ -730,9 +730,9 @@ extern int yylex \
#undef yyTABLES_NAME
#endif

#line 264 "flex_lexer.l"
#line 263 "flex_lexer.l"


#line 736 "flex_lexer.h"
#line 737 "flex_lexer.h"
#undef hsql_IN_HEADER
#endif /* hsql_HEADER_H */
1 change: 0 additions & 1 deletion src/parser/flex_lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ AFTER TOKEN(AFTER)
ALTER TOKEN(ALTER)
ARRAY TOKEN(ARRAY)
CROSS TOKEN(CROSS)
DELTA TOKEN(DELTA)
FLOAT TOKEN(FLOAT)
GROUP TOKEN(GROUP)
INDEX TOKEN(INDEX)
Expand Down
1 change: 0 additions & 1 deletion src/parser/sql_keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ TRUNCATE

MERGE
HISTORY
DELTA
OF

LOAD
Expand Down
59 changes: 59 additions & 0 deletions test/regression_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* regression_tests.cpp
*
* Regression tests for envoyproxy/envoy#36471.
*/

#include "thirdparty/microtest/microtest.h"

#include "SQLParser.h"
#include "sql_asserts.h"

using namespace hsql;

// Verifies that `delta` is a usable identifier. It used to be a dead reserved
// token (declared in bison_parser.y / sql_keywords.txt but referenced by no
// grammar rule), so any INSERT mentioning a `delta` column failed to parse.
TEST(DeltaIsAValidIdentifier) {
SQLParserResult result;
SQLParser::parse(
"INSERT INTO pgbench_history (tid, bid, aid, delta) VALUES (1, 2, 3, 4);",
&result);

ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT_EQ(result.getStatement(0)->type(), kStmtInsert);

const InsertStatement* stmt = (const InsertStatement*) result.getStatement(0);
ASSERT_STREQ(stmt->tableName, "pgbench_history");
ASSERT_NOTNULL(stmt->columns);
ASSERT_EQ(stmt->columns->size(), 4);
ASSERT_STREQ(stmt->columns->at(0), "tid");
ASSERT_STREQ(stmt->columns->at(1), "bid");
ASSERT_STREQ(stmt->columns->at(2), "aid");
ASSERT_STREQ(stmt->columns->at(3), "delta");
}

// Stresses the failed-parse cleanup path: when an ident_commalist contains a
// reserved keyword, Bison's error recovery invokes the <str_vec> %destructor
// on the partial vector. The destructor used to call `delete` on the strdup'd
// char* elements (UB: mixing free/delete). Under tcmalloc + sized
// deallocation that UB poisoned the freelist and crashed after a few hundred
// iterations.
//
// This test is deterministic only under AddressSanitizer, where the very first
// mismatched delete is reported as `alloc-dealloc-mismatch`. In release builds
// the test will usually pass either way; the value is in catching regressions
// once CI runs it under ASAN.
TEST(RepeatedFailedInsertParseDoesNotCorruptHeap) {
// SELECT is a hard reserved keyword in any plausible future of this grammar,
// so this stays a parse-error case independent of keyword-list churn.
const char* kBad =
"INSERT INTO t (a, b, c, SELECT) VALUES (1, 2, 3, 4);";

for (int i = 0; i < 1000; ++i) {
SQLParserResult result;
SQLParser::parse(kBad, &result);
ASSERT_FALSE(result.isValid());
}
}