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 changes: 4 additions & 0 deletions cpp/ql/lib/change-notes/2026-09-14-comdb2-sql-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added SQL-injection sink models for the Comdb2 C API functions `cdb2_run_statement` and `cdb2_run_statement_typed`.
11 changes: 11 additions & 0 deletions cpp/ql/lib/ext/Comdb2.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Partial model of the Comdb2 C API.
# https://bloomberg.github.io/comdb2/c_api.html
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: sinkModel
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
# Both functions interpret the second argument as SQL. The typed variant
# specifies result-column types; bound parameter values are passed separately.
- ["", "", False, "cdb2_run_statement", "", "", "Argument[*1]", "sql-injection", "manual"]
- ["", "", False, "cdb2_run_statement_typed", "", "", "Argument[*1]", "sql-injection", "manual"]
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
| test.c:106:24:106:29 | query1 | test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) |
| test.c:107:28:107:33 | query1 | test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) |
| test.cpp:43:27:43:33 | *access to array | test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | This argument to a SQL query function is derived from $@ and then passed to pqxx::work::exec1((unnamed parameter 0)). | test.cpp:39:27:39:30 | **argv | user input (a command-line argument) |
| test_comdb2.c:22:28:22:36 | userInput | test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:22:28:22:36 | userInput | This argument to a SQL query function is derived from $@. | test_comdb2.c:18:8:18:16 | gets output argument | user input (string read by gets) |
| test_comdb2.c:23:34:23:42 | userInput | test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:23:34:23:42 | userInput | This argument to a SQL query function is derived from $@. | test_comdb2.c:18:8:18:16 | gets output argument | user input (string read by gets) |
| test_libpq.c:26:16:26:24 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
| test_libpq.c:27:22:27:30 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
| test_libpq.c:28:27:28:35 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
Expand All @@ -26,6 +28,8 @@ edges
| test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | provenance | TaintFunction Sink:MaD:2 |
| test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | provenance | TaintFunction Sink:MaD:1 |
| test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | provenance | |
| test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:22:28:22:36 | userInput | provenance | Sink:MaD:9 |
| test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:23:34:23:42 | userInput | provenance | Sink:MaD:10 |
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | provenance | Sink:MaD:3 |
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | provenance | Sink:MaD:4 |
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | provenance | Sink:MaD:5 |
Expand All @@ -41,6 +45,8 @@ models
| 6 | Sink: ; ; false; PQsendPrepare; ; ; Argument[*2]; sql-injection; manual |
| 7 | Sink: ; ; false; PQsendQuery; ; ; Argument[*1]; sql-injection; manual |
| 8 | Sink: ; ; false; PQsendQueryParams; ; ; Argument[*1]; sql-injection; manual |
| 9 | Sink: ; ; false; cdb2_run_statement; ; ; Argument[*1]; sql-injection; manual |
| 10 | Sink: ; ; false; cdb2_run_statement_typed; ; ; Argument[*1]; sql-injection; manual |
nodes
| test.c:14:27:14:30 | **argv | semmle.label | **argv |
| test.c:15:20:15:26 | *access to array | semmle.label | *access to array |
Expand All @@ -59,6 +65,9 @@ nodes
| test.c:107:28:107:33 | query1 | semmle.label | query1 |
| test.cpp:39:27:39:30 | **argv | semmle.label | **argv |
| test.cpp:43:27:43:33 | *access to array | semmle.label | *access to array |
| test_comdb2.c:18:8:18:16 | gets output argument | semmle.label | gets output argument |
| test_comdb2.c:22:28:22:36 | userInput | semmle.label | userInput |
| test_comdb2.c:23:34:23:42 | userInput | semmle.label | userInput |
| test_libpq.c:23:8:23:16 | gets output argument | semmle.label | gets output argument |
| test_libpq.c:26:16:26:24 | userInput | semmle.label | userInput |
| test_libpq.c:27:22:27:30 | userInput | semmle.label | userInput |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Minimal declarations from the public Comdb2 C API:
// https://github.com/bloomberg/comdb2/blob/dfb15415b48cbe67220d76964243086051813676/cdb2api/cdb2api.h
typedef struct cdb2_hndl cdb2_hndl_tp;

enum { CDB2_CSTRING = 3 };

int cdb2_run_statement(cdb2_hndl_tp *hndl, const char *sql);
int cdb2_run_statement_typed(cdb2_hndl_tp *hndl, const char *sql, int ntypes, const int *types);
int cdb2_bind_param(cdb2_hndl_tp *hndl, const char *name, int type,
const void *varaddr, int length);

char *gets(char *s);
typedef unsigned long size_t;
size_t strlen(const char *s);

void comdb2Tests(cdb2_hndl_tp *hndl) {
char userInput[1000];
gets(userInput); // $ Source
int types[] = {CDB2_CSTRING};

// Both execution functions interpret user-controlled query text as SQL.
cdb2_run_statement(hndl, userInput); // $ Alert
cdb2_run_statement_typed(hndl, userInput, 1, types); // $ Alert

cdb2_run_statement(hndl, "SELECT 1"); // GOOD
cdb2_run_statement_typed(hndl, "SELECT 'constant'", 1, types); // GOOD

// Bound values are data, not SQL text, even when controlled by the user.
cdb2_bind_param(hndl, "value", CDB2_CSTRING, userInput, strlen(userInput) + 1); // GOOD
cdb2_run_statement(hndl, "SELECT @value"); // GOOD
cdb2_run_statement_typed(hndl, "SELECT @value", 1, types); // GOOD
}