@@ -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 /**
0 commit comments