Skip to content

Commit ce0e2ad

Browse files
authored
Merge pull request #22449 from trail-of-forks/kumarak/cpp-libpq-sql-sinks
cpp: model libpq query functions as SQL-injection sinks
2 parents 973d5b5 + 4796ff4 commit ce0e2ad

4 files changed

Lines changed: 80 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added the PostgreSQL libpq (asynchronous) query-execution functions `PQexec`, `PQexecParams`, `PQprepare`, `PQsendQuery`, `PQsendQueryParams`, `PQsendPrepare` as `sql-injection` sinks.

cpp/ql/lib/ext/Postgres.model.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# partial model of the PostgreSQL libpq C API
2+
extensions:
3+
- addsTo:
4+
pack: codeql/cpp-all
5+
extensible: sinkModel
6+
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
7+
# The `*Params`/`*Prepare` functions interpret their command/query text as SQL,
8+
# so injection into that text is a sink even though the parameter values are
9+
# passed separately. The `*Prepared` functions take a prepared-statement name
10+
# rather than SQL text and are therefore not modeled here.
11+
- ["", "", False, "PQexec", "", "", "Argument[*1]", "sql-injection", "manual"]
12+
- ["", "", False, "PQexecParams", "", "", "Argument[*1]", "sql-injection", "manual"]
13+
- ["", "", False, "PQprepare", "", "", "Argument[*2]", "sql-injection", "manual"]
14+
- ["", "", False, "PQsendQuery", "", "", "Argument[*1]", "sql-injection", "manual"]
15+
- ["", "", False, "PQsendQueryParams", "", "", "Argument[*1]", "sql-injection", "manual"]
16+
- ["", "", False, "PQsendPrepare", "", "", "Argument[*2]", "sql-injection", "manual"]

cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
| 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) |
77
| 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) |
88
| 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) |
9+
| 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) |
10+
| 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) |
11+
| 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) |
12+
| test_libpq.c:29:21:29:29 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:29:21:29:29 | 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) |
13+
| test_libpq.c:30:27:30:35 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:30:27:30: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) |
14+
| test_libpq.c:31:31:31:39 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:31:31:31:39 | 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) |
915
edges
1016
| test.c:14:27:14:30 | **argv | test.c:15:20:15:26 | *access to array | provenance | |
1117
| test.c:15:20:15:26 | *access to array | test.c:21:18:21:23 | *query1 | provenance | TaintFunction |
@@ -20,9 +26,21 @@ edges
2026
| test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | provenance | TaintFunction Sink:MaD:2 |
2127
| test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | provenance | TaintFunction Sink:MaD:1 |
2228
| test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | provenance | |
29+
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | provenance | Sink:MaD:3 |
30+
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | provenance | Sink:MaD:4 |
31+
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | provenance | Sink:MaD:5 |
32+
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:29:21:29:29 | userInput | provenance | Sink:MaD:7 |
33+
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:30:27:30:35 | userInput | provenance | Sink:MaD:8 |
34+
| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:31:31:31:39 | userInput | provenance | Sink:MaD:6 |
2335
models
2436
| 1 | Sink: ; ; false; OCIStmtPrepare2; ; ; Argument[*3]; sql-injection; manual |
2537
| 2 | Sink: ; ; false; OCIStmtPrepare; ; ; Argument[*2]; sql-injection; manual |
38+
| 3 | Sink: ; ; false; PQexec; ; ; Argument[*1]; sql-injection; manual |
39+
| 4 | Sink: ; ; false; PQexecParams; ; ; Argument[*1]; sql-injection; manual |
40+
| 5 | Sink: ; ; false; PQprepare; ; ; Argument[*2]; sql-injection; manual |
41+
| 6 | Sink: ; ; false; PQsendPrepare; ; ; Argument[*2]; sql-injection; manual |
42+
| 7 | Sink: ; ; false; PQsendQuery; ; ; Argument[*1]; sql-injection; manual |
43+
| 8 | Sink: ; ; false; PQsendQueryParams; ; ; Argument[*1]; sql-injection; manual |
2644
nodes
2745
| test.c:14:27:14:30 | **argv | semmle.label | **argv |
2846
| test.c:15:20:15:26 | *access to array | semmle.label | *access to array |
@@ -41,4 +59,11 @@ nodes
4159
| test.c:107:28:107:33 | query1 | semmle.label | query1 |
4260
| test.cpp:39:27:39:30 | **argv | semmle.label | **argv |
4361
| test.cpp:43:27:43:33 | *access to array | semmle.label | *access to array |
62+
| test_libpq.c:23:8:23:16 | gets output argument | semmle.label | gets output argument |
63+
| test_libpq.c:26:16:26:24 | userInput | semmle.label | userInput |
64+
| test_libpq.c:27:22:27:30 | userInput | semmle.label | userInput |
65+
| test_libpq.c:28:27:28:35 | userInput | semmle.label | userInput |
66+
| test_libpq.c:29:21:29:29 | userInput | semmle.label | userInput |
67+
| test_libpq.c:30:27:30:35 | userInput | semmle.label | userInput |
68+
| test_libpq.c:31:31:31:39 | userInput | semmle.label | userInput |
4469
subpaths
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
typedef unsigned long size_t;
2+
typedef unsigned int Oid;
3+
typedef struct pg_conn PGconn;
4+
typedef struct pg_result PGresult;
5+
6+
PGresult *PQexec(PGconn *conn, const char *query);
7+
PGresult *PQexecParams(PGconn *conn, const char *command, int nParams,
8+
const Oid *paramTypes, const char *const *paramValues,
9+
const int *paramLengths, const int *paramFormats, int resultFormat);
10+
PGresult *PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams,
11+
const Oid *paramTypes);
12+
int PQsendQuery(PGconn *conn, const char *query);
13+
int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes,
14+
const char *const *paramValues, const int *paramLengths,
15+
const int *paramFormats, int resultFormat);
16+
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams,
17+
const Oid *paramTypes);
18+
19+
char *gets(char *s);
20+
21+
void libpqTests(PGconn *conn) {
22+
char userInput[1000];
23+
gets(userInput); // $ Source
24+
25+
// A user-controlled string is interpreted as SQL.
26+
PQexec(conn, userInput); // $ Alert
27+
PQexecParams(conn, userInput, 0, 0, 0, 0, 0, 0); // $ Alert
28+
PQprepare(conn, "stmt", userInput, 0, 0); // $ Alert
29+
PQsendQuery(conn, userInput); // $ Alert
30+
PQsendQueryParams(conn, userInput, 0, 0, 0, 0, 0, 0); // $ Alert
31+
PQsendPrepare(conn, "stmt", userInput, 0, 0); // $ Alert
32+
33+
// A constant query is safe.
34+
PQexec(conn, "SELECT 1"); // GOOD
35+
}

0 commit comments

Comments
 (0)