Skip to content

Commit 9cc3cda

Browse files
author
Dave Bartolomeo
committed
C++: Model varargs in IR, Part I
This change introduces a new synthesized `IRVariable` in every varargs function. This variable represents the entire set of arguments passed to the ellipsis by the caller. We give it an opaque type big enough hold all of the arguments passed by the largest vararg call in the database. It is treated just like any other parameter. It is initialized the same, it has indirect buffers, etc. I had to introduce a couple new APIs to `Call` and `Function`. The QLDoc comments should explain these. I added tests for these new APIs as well. The next step will be to change the IR generation for the `va_*` macros to manipulate the ellipsis parameter.
1 parent 17c57dc commit 9cc3cda

File tree

16 files changed

+361
-69
lines changed

16 files changed

+361
-69
lines changed

cpp/ql/src/semmle/code/cpp/Function.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,12 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
364364
/** Holds if this function is a varargs function. */
365365
predicate isVarargs() { hasSpecifier("varargs") }
366366

367+
/**
368+
* Gets the index of the `...` parameter, if any. If present, the value will always be equal to
369+
* `getNumberOfParameters()`.
370+
*/
371+
int getEllipsisParameterIndex() { isVarargs() and result = getNumberOfParameters() }
372+
367373
/** Gets a type that is specified to be thrown by the function. */
368374
Type getAThrownType() { result = getADeclarationEntry().getAThrownType() }
369375

cpp/ql/src/semmle/code/cpp/exprs/Call.qll

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import semmle.code.cpp.exprs.Expr
22
import semmle.code.cpp.Function
33
private import semmle.code.cpp.dataflow.EscapesTree
44

5+
/**
6+
* Gets the index of the `...` parameter, if any.
7+
*/
8+
private int getEllipsisParameterIndex(RoutineType type) {
9+
type.getParameterType(result) instanceof UnknownType
10+
}
11+
512
/**
613
* A C/C++ call.
714
*
@@ -78,6 +85,32 @@ abstract class Call extends Expr, NameQualifiableElement {
7885

7986
override string toString() { none() }
8087

88+
/**
89+
* Gets the index of the `...` parameter, if any. This will be one greater than the index of the
90+
* last declared positional parameter.
91+
*/
92+
abstract int getEllipsisParameterIndex();
93+
94+
/**
95+
* Gets the index of the parameter that will be initialized with the value of the argument
96+
* specified by `argIndex`. For ordinary positional parameters, the argument and parameter indices
97+
* will be equal. For a call to a varargs function, all arguments passed to the `...` will be
98+
* mapped to the index returned by `getEllipsisParameterIndex()`.
99+
*/
100+
final int getParameterIndexForArgument(int argIndex) {
101+
exists(getArgument(argIndex)) and
102+
if argIndex >= getEllipsisParameterIndex()
103+
then result = getEllipsisParameterIndex()
104+
else result = argIndex
105+
}
106+
107+
/**
108+
* Holds if the argument specified by `index` is an argument to the `...` of a varargs function.
109+
*/
110+
final predicate isEllipsisArgumentIndex(int index) {
111+
exists(getArgument(index)) and index >= getEllipsisParameterIndex()
112+
}
113+
81114
/**
82115
* Holds if this call passes the variable accessed by `va` by
83116
* reference as the `i`th argument.
@@ -259,6 +292,12 @@ class FunctionCall extends Call, @funbindexpr {
259292
else result = "call to unknown function"
260293
}
261294

295+
final override int getEllipsisParameterIndex() {
296+
if getTargetType() instanceof RoutineType
297+
then result = getEllipsisParameterIndex(getTargetType())
298+
else result = getTarget().getEllipsisParameterIndex()
299+
}
300+
262301
override predicate mayBeImpure() {
263302
this.getChild(_).mayBeImpure() or
264303
this.getTarget().mayHaveSideEffects() or
@@ -378,6 +417,10 @@ class ExprCall extends Call, @callexpr {
378417
override string toString() { result = "call to expression" }
379418

380419
override Function getTarget() { none() }
420+
421+
final override int getEllipsisParameterIndex() {
422+
result = getEllipsisParameterIndex(getExpr().getType().stripType())
423+
}
381424
}
382425

383426
/**

cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ deprecated class MessageExpr extends Expr, Call {
6363
override Expr getArgument(int n) { none() }
6464

6565
override int getPrecedence() { none() }
66+
67+
final override int getEllipsisParameterIndex() {
68+
none()
69+
}
6670
}
6771

6872
/**

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ class IRThrowVariable extends IRTempVariable {
211211
override string getBaseString() { result = "#throw" }
212212
}
213213

214+
/**
215+
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
216+
* function that accepts a variable number of arguments.
217+
*/
218+
class IREllipsisVariable extends IRTempVariable {
219+
IREllipsisVariable() { tag = EllipsisTempVar() }
220+
221+
final override string toString() { result = "#ellipsis" }
222+
}
223+
214224
/**
215225
* A variable generated to represent the contents of a string literal. This variable acts much like
216226
* a read-only global variable.

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ class IRThrowVariable extends IRTempVariable {
211211
override string getBaseString() { result = "#throw" }
212212
}
213213

214+
/**
215+
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
216+
* function that accepts a variable number of arguments.
217+
*/
218+
class IREllipsisVariable extends IRTempVariable {
219+
IREllipsisVariable() { tag = EllipsisTempVar() }
220+
221+
final override string toString() { result = "#ellipsis" }
222+
}
223+
214224
/**
215225
* A variable generated to represent the contents of a string literal. This variable acts much like
216226
* a read-only global variable.

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ newtype TTranslatedElement =
382382
translateFunction(func)
383383
)
384384
} or
385+
TTranslatedEllipsisParameter(Function func) {
386+
translateFunction(func) and func.isVarargs()
387+
} or
385388
TTranslatedReadEffects(Function func) { translateFunction(func) } or
386389
// The read side effects in a function's return block
387390
TTranslatedReadEffect(Parameter param) {

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll

Lines changed: 119 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ private import TranslatedStmt
1616
*/
1717
TranslatedFunction getTranslatedFunction(Function func) { result.getAST() = func }
1818

19+
/**
20+
* Gets the size, in bytes, of the variable used to represent the `...` parameter in a varargs
21+
* function. This is determined by finding the total size of all of the arguments passed to the
22+
* `...` in each call in the program, and choosing the maximum of those, with a minimum of 8 bytes.
23+
*/
24+
private int getEllipsisVariableByteSize() {
25+
result =
26+
max(int variableSize |
27+
variableSize =
28+
max(Call call, int callSize |
29+
callSize =
30+
sum(int argIndex |
31+
call.isEllipsisArgumentIndex(argIndex)
32+
|
33+
call.getArgument(argIndex).getType().getSize()
34+
)
35+
|
36+
callSize
37+
)
38+
or
39+
variableSize = 8
40+
|
41+
variableSize
42+
)
43+
}
44+
1945
/**
2046
* Represents the IR translation of a function. This is the root elements for
2147
* all other elements associated with this function.
@@ -60,6 +86,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
6086

6187
final private TranslatedParameter getParameter(int index) {
6288
result = getTranslatedParameter(func.getParameter(index))
89+
or
90+
index = func.getEllipsisParameterIndex() and
91+
result = getTranslatedEllipsisParameter(func)
6392
}
6493

6594
final override Instruction getFirstInstruction() { result = getInstruction(EnterFunctionTag()) }
@@ -113,7 +142,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
113142
final override Instruction getChildSuccessor(TranslatedElement child) {
114143
exists(int paramIndex |
115144
child = getParameter(paramIndex) and
116-
if exists(func.getParameter(paramIndex + 1))
145+
if
146+
exists(func.getParameter(paramIndex + 1)) or
147+
func.getEllipsisParameterIndex() = paramIndex + 1
117148
then result = getParameter(paramIndex + 1).getFirstInstruction()
118149
else result = getConstructorInitList().getFirstInstruction()
119150
)
@@ -237,10 +268,18 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
237268
result = getReturnVariable()
238269
}
239270

271+
final override predicate needsUnknownOpaqueType(int byteSize) {
272+
byteSize = getEllipsisVariableByteSize()
273+
}
274+
240275
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
241276
tag = ReturnValueTempVar() and
242277
hasReturnValue() and
243278
type = getTypeForPRValue(getReturnType())
279+
or
280+
tag = EllipsisTempVar() and
281+
func.isVarargs() and
282+
type = getUnknownOpaqueType(getEllipsisVariableByteSize())
244283
}
245284

246285
/**
@@ -316,34 +355,29 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
316355
}
317356

318357
/**
319-
* Gets the `TranslatedParameter` that represents parameter `param`.
358+
* Gets the `TranslatedPositionalParameter` that represents parameter `param`.
320359
*/
321-
TranslatedParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
360+
TranslatedPositionalParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
322361

323362
/**
324-
* Represents the IR translation of a function parameter, including the
325-
* initialization of that parameter with the incoming argument.
363+
* Gets the `TranslatedEllipsisParameter` for function `func`, if one exists.
326364
*/
327-
class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
328-
Parameter param;
329-
330-
TranslatedParameter() { this = TTranslatedParameter(param) }
331-
332-
final override string toString() { result = param.toString() }
333-
334-
final override Locatable getAST() { result = param }
365+
TranslatedEllipsisParameter getTranslatedEllipsisParameter(Function func) {
366+
result.getFunction() = func
367+
}
335368

336-
final override Function getFunction() {
337-
result = param.getFunction() or
338-
result = param.getCatchBlock().getEnclosingFunction()
339-
}
369+
/**
370+
* The IR translation of a parameter to a function. This can be either a user-declared parameter
371+
* (`TranslatedPositionParameter`) or the synthesized parameter used to represent a `...` in a
372+
* varargs function (`TranslatedEllipsisParameter`).
373+
*/
374+
abstract class TranslatedParameter extends TranslatedElement {
375+
final override TranslatedElement getChild(int id) { none() }
340376

341377
final override Instruction getFirstInstruction() {
342378
result = getInstruction(InitializerVariableAddressTag())
343379
}
344380

345-
final override TranslatedElement getChild(int id) { none() }
346-
347381
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
348382
kind instanceof GotoEdge and
349383
(
@@ -368,16 +402,16 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
368402
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
369403
tag = InitializerVariableAddressTag() and
370404
opcode instanceof Opcode::VariableAddress and
371-
resultType = getTypeForGLValue(getVariableType(param))
405+
resultType = getGLValueType()
372406
or
373407
tag = InitializerStoreTag() and
374408
opcode instanceof Opcode::InitializeParameter and
375-
resultType = getTypeForPRValue(getVariableType(param))
409+
resultType = getPRValueType()
376410
or
377411
hasIndirection() and
378412
tag = InitializerIndirectAddressTag() and
379413
opcode instanceof Opcode::Load and
380-
resultType = getTypeForPRValue(getVariableType(param))
414+
resultType = getPRValueType()
381415
or
382416
hasIndirection() and
383417
tag = InitializerIndirectStoreTag() and
@@ -391,7 +425,7 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
391425
tag = InitializerVariableAddressTag() or
392426
tag = InitializerIndirectStoreTag()
393427
) and
394-
result = getIRUserVariable(getFunction(), param)
428+
result = getIRVariable()
395429
}
396430

397431
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -416,13 +450,74 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
416450
result = getInstruction(InitializerIndirectAddressTag())
417451
}
418452

419-
predicate hasIndirection() {
453+
abstract predicate hasIndirection();
454+
455+
abstract CppType getGLValueType();
456+
457+
abstract CppType getPRValueType();
458+
459+
abstract IRAutomaticVariable getIRVariable();
460+
}
461+
462+
/**
463+
* Represents the IR translation of a function parameter, including the
464+
* initialization of that parameter with the incoming argument.
465+
*/
466+
class TranslatedPositionalParameter extends TranslatedParameter, TTranslatedParameter {
467+
Parameter param;
468+
469+
TranslatedPositionalParameter() { this = TTranslatedParameter(param) }
470+
471+
final override string toString() { result = param.toString() }
472+
473+
final override Locatable getAST() { result = param }
474+
475+
final override Function getFunction() {
476+
result = param.getFunction() or
477+
result = param.getCatchBlock().getEnclosingFunction()
478+
}
479+
480+
final override predicate hasIndirection() {
420481
exists(Type t | t = param.getUnspecifiedType() |
421482
t instanceof ArrayType or
422483
t instanceof PointerType or
423484
t instanceof ReferenceType
424485
)
425486
}
487+
488+
final override CppType getGLValueType() { result = getTypeForGLValue(getVariableType(param)) }
489+
490+
final override CppType getPRValueType() { result = getTypeForPRValue(getVariableType(param)) }
491+
492+
final override IRAutomaticUserVariable getIRVariable() {
493+
result = getIRUserVariable(getFunction(), param)
494+
}
495+
}
496+
497+
/**
498+
* The IR translation of the synthesized parameter used to represent the `...` in a varargs
499+
* function.
500+
*/
501+
class TranslatedEllipsisParameter extends TranslatedParameter, TTranslatedEllipsisParameter {
502+
Function func;
503+
504+
TranslatedEllipsisParameter() { this = TTranslatedEllipsisParameter(func) }
505+
506+
final override string toString() { result = "..." }
507+
508+
final override Locatable getAST() { result = func }
509+
510+
final override Function getFunction() { result = func }
511+
512+
final override predicate hasIndirection() { any() }
513+
514+
final override CppType getGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
515+
516+
final override CppType getPRValueType() {
517+
result = getUnknownOpaqueType(getEllipsisVariableByteSize())
518+
}
519+
520+
final override IREllipsisVariable getIRVariable() { result.getEnclosingFunction() = func }
426521
}
427522

428523
private TranslatedConstructorInitList getTranslatedConstructorInitList(Function func) {

cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ class IRThrowVariable extends IRTempVariable {
211211
override string getBaseString() { result = "#throw" }
212212
}
213213

214+
/**
215+
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
216+
* function that accepts a variable number of arguments.
217+
*/
218+
class IREllipsisVariable extends IRTempVariable {
219+
IREllipsisVariable() { tag = EllipsisTempVar() }
220+
221+
final override string toString() { result = "#ellipsis" }
222+
}
223+
214224
/**
215225
* A variable generated to represent the contents of a string literal. This variable acts much like
216226
* a read-only global variable.

cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ newtype TTempVariableTag =
22
ConditionValueTempVar() or
33
ReturnValueTempVar() or
44
ThrowTempVar() or
5-
LambdaTempVar()
5+
LambdaTempVar() or
6+
EllipsisTempVar()
67

78
string getTempVariableTagId(TTempVariableTag tag) {
89
tag = ConditionValueTempVar() and result = "CondVal"
@@ -12,4 +13,6 @@ string getTempVariableTagId(TTempVariableTag tag) {
1213
tag = ThrowTempVar() and result = "Throw"
1314
or
1415
tag = LambdaTempVar() and result = "Lambda"
16+
or
17+
tag = EllipsisTempVar() and result = "Ellipsis"
1518
}

0 commit comments

Comments
 (0)