Skip to content

Commit 78ae3bb

Browse files
committed
fuzz the parser and planner pipeline; bound parser nesting and boolean operators so hostile queries report parse errors instead of overflowing the stack
1 parent 86b12cf commit 78ae3bb

33 files changed

Lines changed: 220 additions & 0 deletions

‎.github/workflows/ci.yml‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,52 @@ jobs:
2020
run: cmake --build build --parallel
2121
- name: Test
2222
run: ctest --test-dir build --output-on-failure
23+
24+
sanitizers:
25+
runs-on: ubuntu-latest
26+
env:
27+
CC: clang
28+
CXX: clang++
29+
steps:
30+
- uses: actions/checkout@v4
31+
- name: Configure
32+
run: >-
33+
cmake -S . -B build-sanitizers
34+
-DCMAKE_BUILD_TYPE=Debug
35+
-DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer"
36+
- name: Build
37+
run: cmake --build build-sanitizers --parallel 2
38+
- name: Test
39+
run: ctest --test-dir build-sanitizers --output-on-failure
40+
41+
fuzz:
42+
runs-on: ubuntu-latest
43+
env:
44+
CC: clang
45+
CXX: clang++
46+
steps:
47+
- uses: actions/checkout@v4
48+
- name: Configure
49+
run: cmake -S . -B build-fuzz -DCMAKE_BUILD_TYPE=Debug -DSQL_ENGINE_BUILD_FUZZER=ON
50+
- name: Build
51+
run: cmake --build build-fuzz --target sql_parser_planner_fuzz --parallel 2
52+
- name: Replay corpus (regression gate)
53+
run: ./build-fuzz/sql_parser_planner_fuzz -timeout=10 fuzz/corpus/*
54+
- name: Short discovery session (non-blocking)
55+
continue-on-error: true
56+
run: >-
57+
./build-fuzz/sql_parser_planner_fuzz fuzz/corpus
58+
-max_total_time=90
59+
-timeout=10
60+
-rss_limit_mb=2048
61+
-print_final_stats=1
62+
- name: Upload discovery artifacts
63+
if: always()
64+
uses: actions/upload-artifact@v4
65+
with:
66+
name: fuzz-findings
67+
path: |
68+
crash-*
69+
timeout-*
70+
oom-*
71+
if-no-files-found: ignore

‎CMakeLists.txt‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ set(CMAKE_CXX_STANDARD 20)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)
66
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
77

8+
option(SQL_ENGINE_BUILD_FUZZER "Build the parser and planner fuzz target" OFF)
9+
810
add_library(sql_engine
911
src/sql/parser.cpp
1012
src/sql/binder.cpp
@@ -20,6 +22,21 @@ add_library(sql_engine
2022
target_include_directories(sql_engine PUBLIC include)
2123
target_compile_options(sql_engine PRIVATE -Wall -Wextra -Wpedantic)
2224

25+
if(SQL_ENGINE_BUILD_FUZZER)
26+
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
27+
message(FATAL_ERROR "SQL_ENGINE_BUILD_FUZZER requires Clang")
28+
endif()
29+
target_compile_options(sql_engine PRIVATE
30+
-fsanitize=fuzzer-no-link,address,undefined
31+
-fno-sanitize-recover=all
32+
-fno-omit-frame-pointer
33+
)
34+
target_link_options(sql_engine INTERFACE
35+
-fsanitize=address,undefined
36+
-fno-sanitize-recover=all
37+
)
38+
endif()
39+
2340
add_executable(sql_smoke tests/smoke.cpp)
2441
target_link_libraries(sql_smoke PRIVATE sql_engine)
2542

@@ -47,6 +64,20 @@ target_link_libraries(sql_optimizer_cost_model PRIVATE sql_engine)
4764
add_executable(sql_bench benchmarks/sql_bench.cpp)
4865
target_link_libraries(sql_bench PRIVATE sql_engine)
4966

67+
if(SQL_ENGINE_BUILD_FUZZER)
68+
add_executable(sql_parser_planner_fuzz fuzz/parser_planner_fuzz.cpp)
69+
target_link_libraries(sql_parser_planner_fuzz PRIVATE sql_engine)
70+
target_compile_options(sql_parser_planner_fuzz PRIVATE
71+
-fsanitize=fuzzer,address,undefined
72+
-fno-sanitize-recover=all
73+
-fno-omit-frame-pointer
74+
)
75+
target_link_options(sql_parser_planner_fuzz PRIVATE
76+
-fsanitize=fuzzer,address,undefined
77+
-fno-sanitize-recover=all
78+
)
79+
endif()
80+
5081
include(CTest)
5182
add_test(NAME sql_smoke COMMAND sql_smoke)
5283
add_test(NAME sql_golden COMMAND sql_golden)

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ C++20, CMake, Arrow-like in-memory column batches, SIMD-friendly operator interf
1616
cmake -S . -B build && cmake --build build && ctest --test-dir build --output-on-failure
1717
```
1818

19+
Linux CI runs the full test suite with address and undefined-behavior sanitizers.
20+
Continuous parser and planner fuzzing replays known queries before a timed fuzzing run.
21+
1922
## Phase map
2023

2124
1. Parser, binder, logical algebra, and golden-query oracle.

‎fuzz/corpus/aggregate_having.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT a, SUM(b) AS total FROM t GROUP BY a HAVING SUM(b) >= 20 ORDER BY total DESC

‎fuzz/corpus/anti_join.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT a FROM t WHERE a NOT IN (SELECT a FROM t1)

‎fuzz/corpus/basic.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT a FROM t WHERE a = 2

‎fuzz/corpus/boolean_tree.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT a FROM t WHERE a = 1 AND (b = 20 OR a = 3)

‎fuzz/corpus/correlated_exists.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT a FROM t WHERE EXISTS (SELECT t1.b FROM t1 WHERE t1.a = t.a)

‎fuzz/corpus/correlated_not_in.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT b FROM t WHERE b NOT IN (SELECT t1.b FROM t1 WHERE t1.a = t.a)

‎fuzz/corpus/deep_or_chain.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
select a from t where a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1 or a = 1

0 commit comments

Comments
 (0)