Skip to content

Commit fe678a9

Browse files
committed
C++: Handle size arguments in 'getOutputArgument'.
1 parent fb1b293 commit fe678a9

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ abstract class ScanfFunction extends Function {
2525
* (rather than a `char*`).
2626
*/
2727
predicate isWideCharDefault() { exists(this.getName().indexOf("wscanf")) }
28+
29+
/** Holds if this is one of the `scanf_s` variants. */
30+
predicate isSVariant() {
31+
exists(string name | name = this.getName() |
32+
name.matches("%\\_s")
33+
or
34+
name.matches("%\\_s\\_l")
35+
)
36+
}
2837
}
2938

3039
/**
@@ -103,6 +112,14 @@ class Snscanf extends ScanfFunction instanceof TopLevelFunction {
103112
int getInputLengthParameterIndex() { result = 1 }
104113
}
105114

115+
private predicate isCharLike(Type t) { t instanceof CharType or t instanceof Wchar_t }
116+
117+
private predicate isStringLike(Type t) {
118+
isCharLike(t.(PointerType).getBaseType())
119+
or
120+
isCharLike(t.(ArrayType).getBaseType())
121+
}
122+
106123
/**
107124
* A call to one of the `scanf` functions.
108125
*/
@@ -136,14 +153,40 @@ class ScanfFunctionCall extends FunctionCall {
136153
*/
137154
predicate isWideCharDefault() { this.getScanfFunction().isWideCharDefault() }
138155

156+
bindingset[this, k]
157+
pragma[inline_late]
158+
private predicate isSizeArgument(int k) {
159+
// The first vararg is never the size argument since a size argument must
160+
// always follow a string buffer argument.
161+
k > 0 and
162+
isStringLike(this.getArgument(this.getScanfFunction().getNumberOfParameters() + k - 1)
163+
.getUnspecifiedType())
164+
}
165+
139166
/**
140167
* Gets the output argument at position `n` in the vararg list of this call.
141168
*
142169
* The range of `n` is from `0` to `this.getNumberOfOutputArguments() - 1`.
143170
*/
144171
Expr getOutputArgument(int n) {
145-
result = this.getArgument(this.getTarget().getNumberOfParameters() + n) and
146-
n >= 0
172+
exists(ScanfFunction target | target = this.getScanfFunction() |
173+
// If this is an S variant then every string buffer argument has a
174+
// corresponding size argument immediately following it, so we need to
175+
// skip over those size arguments when counting the output arguments.
176+
if target.isSVariant()
177+
then
178+
result =
179+
rank[n + 1](Expr arg, int k |
180+
k >= 0 and
181+
arg = this.getArgument(target.getNumberOfParameters() + k) and
182+
not this.isSizeArgument(k)
183+
|
184+
arg order by k
185+
)
186+
else (
187+
n >= 0 and result = this.getArgument(target.getNumberOfParameters() + n)
188+
)
189+
)
147190
}
148191

149192
/**

cpp/ql/test/library-tests/scanf/scanfFunctionCallOutput.expected

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@
66
| test.c:22:2:22:8 | call to swscanf | test.c:22:29:22:35 | wbuffer | 0 |
77
| test.c:23:2:23:8 | call to scanf_s | test.c:23:22:23:23 | & ... | 0 |
88
| test.c:23:2:23:8 | call to scanf_s | test.c:23:26:23:31 | buffer | 1 |
9-
| test.c:23:2:23:8 | call to scanf_s | test.c:23:34:23:35 | 10 | 2 |
10-
| test.c:23:2:23:8 | call to scanf_s | test.c:23:38:23:40 | & ... | 3 |
9+
| test.c:23:2:23:8 | call to scanf_s | test.c:23:38:23:40 | & ... | 2 |

0 commit comments

Comments
 (0)