Skip to content

Commit edc33b6

Browse files
committed
C++: Add getOutputParameterIndex override to UserDefinedFormattingFunction and accept test changes
1 parent d711c22 commit edc33b6

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

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

Lines changed: 33 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,25 @@ 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 writes formatted
82+
* output to buffer given as a parameter at index `outputParamIndex`, if any.
83+
*/
84+
private predicate variadicFormatterOutput(Function f, int outputParamIndex) {
85+
primitiveVariadicFormatterOutput(f, outputParamIndex)
86+
or
87+
not f.isVarargs() and
88+
callsVariadicFormatterOutput(f, outputParamIndex)
89+
}
90+
6091
/**
6192
* Holds if `f` is a function such as `vprintf` that has a format parameter
6293
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
@@ -78,6 +109,8 @@ class UserDefinedFormattingFunction extends FormattingFunction {
78109
UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _) }
79110

80111
override int getFormatParameterIndex() { callsVariadicFormatter(this, result) }
112+
113+
override int getOutputParameterIndex() { callsVariadicFormatterOutput(this, result) }
81114
}
82115

83116
/**

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,6 @@ namespace custom_sprintf_impl {
305305
void regression_test1()
306306
{
307307
char buffer8[8];
308-
sprintf(buffer8, "12345678"); // BAD: potential buffer overflow [NOT DETECTED]
308+
sprintf(buffer8, "12345678"); // BAD: potential buffer overflow
309309
}
310310
}

0 commit comments

Comments
 (0)