Skip to content

Commit 2e5af67

Browse files
authored
Merge pull request github#3952 from MathiasVP/output-parameter-index-for-UserDefinedFormattingFunction
C++: Add getOutputParameterIndex override to UserDefinedFormattingFunction class.
2 parents c7b6681 + 289a908 commit 2e5af67

File tree

7 files changed

+59
-1
lines changed

7 files changed

+59
-1
lines changed

cpp/ql/src/semmle/code/cpp/commons/Printf.qll

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) {
4949
)
5050
}
5151

52+
/**
53+
* A standard function such as `vsprintf` that has an output parameter
54+
* and a variable argument list of type `va_arg`.
55+
*/
56+
private predicate primitiveVariadicFormatterOutput(TopLevelFunction f, int outputParamIndex) {
57+
// note: this might look like the regular expression in `primitiveVariadicFormatter`, but
58+
// there is one important difference: the [fs] part is not optional, as these classify
59+
// the `printf` variants that write to a buffer.
60+
// Conveniently, these buffer parameters are all at index 0.
61+
f.getName().regexpMatch("_?_?va?[fs]n?w?printf(_s)?(_p)?(_l)?") and outputParamIndex = 0
62+
}
63+
5264
private predicate callsVariadicFormatter(Function f, int formatParamIndex) {
5365
exists(FunctionCall fc, int i |
5466
variadicFormatter(fc.getTarget(), i) and
@@ -57,6 +69,26 @@ private predicate callsVariadicFormatter(Function f, int formatParamIndex) {
5769
)
5870
}
5971

72+
private predicate callsVariadicFormatterOutput(Function f, int outputParamIndex) {
73+
exists(FunctionCall fc, int i |
74+
fc.getEnclosingFunction() = f and
75+
variadicFormatterOutput(fc.getTarget(), i) and
76+
fc.getArgument(i) = f.getParameter(outputParamIndex).getAnAccess()
77+
)
78+
}
79+
80+
/**
81+
* Holds if `f` is a function such as `vprintf` that takes variable argument list
82+
* of type `va_arg` and writes formatted output to a buffer given as a parameter at
83+
* index `outputParamIndex`, if any.
84+
*/
85+
private predicate variadicFormatterOutput(Function f, int outputParamIndex) {
86+
primitiveVariadicFormatterOutput(f, outputParamIndex)
87+
or
88+
not f.isVarargs() and
89+
callsVariadicFormatterOutput(f, outputParamIndex)
90+
}
91+
6092
/**
6193
* Holds if `f` is a function such as `vprintf` that has a format parameter
6294
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
@@ -78,6 +110,8 @@ class UserDefinedFormattingFunction extends FormattingFunction {
78110
UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _) }
79111

80112
override int getFormatParameterIndex() { callsVariadicFormatter(this, result) }
113+
114+
override int getOutputParameterIndex() { callsVariadicFormatterOutput(this, result) }
81115
}
82116

83117
/**

cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ void test1()
112112
{
113113
char buffer[256] = {0};
114114
sink(mysprintf(buffer, 256, "%s", string::source()));
115-
sink(buffer); // tainted [NOT DETECTED - implement UserDefinedFormattingFunction.getOutputParameterIndex()]
115+
sink(buffer); // tainted
116116
}
117117

118118
{

cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@
206206
| format.cpp:113:21:113:24 | {...} | format.cpp:115:8:115:13 | buffer | |
207207
| format.cpp:113:23:113:23 | 0 | format.cpp:113:21:113:24 | {...} | TAINT |
208208
| format.cpp:114:18:114:23 | ref arg buffer | format.cpp:115:8:115:13 | buffer | |
209+
| format.cpp:114:31:114:34 | %s | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
210+
| format.cpp:114:37:114:50 | call to source | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
209211
| format.cpp:119:10:119:11 | 0 | format.cpp:120:29:120:29 | i | |
210212
| format.cpp:119:10:119:11 | 0 | format.cpp:121:8:121:8 | i | |
211213
| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:120:29:120:29 | i [inner post update] | |

cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
| format.cpp:100:8:100:13 | buffer | format.cpp:99:30:99:43 | call to source |
2323
| format.cpp:105:8:105:13 | buffer | format.cpp:104:31:104:45 | call to source |
2424
| format.cpp:110:8:110:14 | wbuffer | format.cpp:109:38:109:52 | call to source |
25+
| format.cpp:115:8:115:13 | buffer | format.cpp:114:37:114:50 | call to source |
2526
| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
2627
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
2728
| movableclass.cpp:44:8:44:9 | s1 | movableclass.cpp:39:21:39:26 | call to source |

cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
| format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only |
2323
| format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only |
2424
| format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only |
25+
| format.cpp:115:8:115:13 | format.cpp:114:37:114:50 | AST only |
2526
| movableclass.cpp:44:8:44:9 | movableclass.cpp:39:21:39:26 | AST only |
2627
| movableclass.cpp:45:8:45:9 | movableclass.cpp:40:23:40:28 | AST only |
2728
| movableclass.cpp:46:8:46:9 | movableclass.cpp:42:8:42:13 | AST only |

cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/OverrunWrite.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
| tests.cpp:259:2:259:8 | call to sprintf | This 'call to sprintf' operation requires 17 bytes but the destination is only 10 bytes. |
33
| tests.cpp:272:2:272:8 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. |
44
| tests.cpp:273:2:273:8 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. |
5+
| tests.cpp:308:3:308:9 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. |

cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/tests.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,22 @@ void test5(va_list args, float f)
289289
vsprintf(buffer4, "123", args); // GOOD
290290
vsprintf(buffer4, "1234", args); // BAD: buffer overflow [NOT DETECTED]
291291
}
292+
293+
namespace custom_sprintf_impl {
294+
int sprintf(char *buf, const char *format, ...)
295+
{
296+
__builtin_va_list args;
297+
int i;
298+
299+
__builtin_va_start(args, format);
300+
i = vsprintf(buf, format, args);
301+
__builtin_va_end(args);
302+
return i;
303+
}
304+
305+
void regression_test1()
306+
{
307+
char buffer8[8];
308+
sprintf(buffer8, "12345678"); // BAD: potential buffer overflow
309+
}
310+
}

0 commit comments

Comments
 (0)