Skip to content

Commit bf28451

Browse files
author
Dave Bartolomeo
committed
C++: Better IR for varargs
This PR changes the IR we generate for functions that accept a variable argument list. Rather than simply using `BuiltInOperationInstruction` to model the various `va_*` macros as mysterious function-like operations, we now model them in more detail. The intent is to enable better alias analysis and taint flow through varargs. The `va_start` macro now generates a unary `VarArgsStart` instruction that takes the address of the ellipsis pseudo-parameter as its operand, and returns a value of type `std::va_list`. This value is then stored into the actual `std::va_list` variable via a regular `Store`. The `va_arg` macro now loads the `std::va_list` argument, then emits a `VarArg` instruction on the result. This returns the address of the vararg argument to be loaded. That address is later used as the address operand of a regular `Load` to return the value of the argument. To model the side effect of moving to the next argument, we emit a `NextVarArg` instruction that takes the previous `std::va_list` value and returns an updated one, which is then stored back into the `std::va_list` variable. The `va_end` macro just emits a `VarArgsEnd` unary instruction that takes the address of the `std::va_list` argument and does nothing, since `va_end` doesn't really do anything on most compiler implementations anyway. The `va_copy` macro is just modeled as a plain copy.
1 parent 67cb852 commit bf28451

File tree

10 files changed

+436
-58
lines changed

10 files changed

+436
-58
lines changed

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ abstract class BuiltInOperation extends Expr {
88
override string getCanonicalQLClass() { result = "BuiltInOperation" }
99
}
1010

11+
/**
12+
* A C/C++ built-in operation that is used to support functions with variable numbers of arguments.
13+
* This includes `va_start`, `va_end`, `va_copy`, and `va_arg`.
14+
*/
15+
class VarArgsExpr extends BuiltInOperation {
16+
VarArgsExpr() {
17+
this instanceof BuiltInVarArgsStart
18+
or
19+
this instanceof BuiltInVarArgsEnd
20+
or
21+
this instanceof BuiltInVarArg
22+
or
23+
this instanceof BuiltInVarArgCopy
24+
}
25+
}
26+
1127
/**
1228
* A C/C++ `__builtin_va_start` built-in operation (used by some
1329
* implementations of `va_start`).
@@ -20,6 +36,20 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
2036
override string toString() { result = "__builtin_va_start" }
2137

2238
override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" }
39+
40+
/**
41+
* Gets the `va_list` argument.
42+
*/
43+
final Expr getVAList() {
44+
result = getChild(0)
45+
}
46+
47+
/**
48+
* Gets the argument that specifies the last named parameter before the ellipsis.
49+
*/
50+
final VariableAccess getLastNamedParameter() {
51+
result = getChild(1)
52+
}
2353
}
2454

2555
/**
@@ -35,6 +65,13 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
3565
override string toString() { result = "__builtin_va_end" }
3666

3767
override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" }
68+
69+
/**
70+
* Gets the `va_list` argument.
71+
*/
72+
final Expr getVAList() {
73+
result = getChild(0)
74+
}
3875
}
3976

4077
/**
@@ -48,6 +85,13 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
4885
override string toString() { result = "__builtin_va_arg" }
4986

5087
override string getCanonicalQLClass() { result = "BuiltInVarArg" }
88+
89+
/**
90+
* Gets the `va_list` argument.
91+
*/
92+
final Expr getVAList() {
93+
result = getChild(0)
94+
}
5195
}
5296

5397
/**
@@ -63,6 +107,20 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
63107
override string toString() { result = "__builtin_va_copy" }
64108

65109
override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" }
110+
111+
/**
112+
* Gets the destination `va_list` argument.
113+
*/
114+
final Expr getDestinationVAList() {
115+
result = getChild(0)
116+
}
117+
118+
/**
119+
* Gets the the source `va_list` argument.
120+
*/
121+
final Expr getSourceVAList() {
122+
result = getChild(1)
123+
}
66124
}
67125

68126
/**

cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private newtype TOpcode =
7070
TVarArgsStart() or
7171
TVarArgsEnd() or
7272
TVarArg() or
73-
TVarArgCopy() or
73+
TNextVarArg() or
7474
TCallSideEffect() or
7575
TCallReadSideEffect() or
7676
TIndirectReadSideEffect() or
@@ -629,20 +629,20 @@ module Opcode {
629629
final override string toString() { result = "BuiltIn" }
630630
}
631631

632-
class VarArgsStart extends BuiltInOperationOpcode, TVarArgsStart {
632+
class VarArgsStart extends UnaryOpcode, TVarArgsStart {
633633
final override string toString() { result = "VarArgsStart" }
634634
}
635635

636-
class VarArgsEnd extends BuiltInOperationOpcode, TVarArgsEnd {
636+
class VarArgsEnd extends UnaryOpcode, TVarArgsEnd {
637637
final override string toString() { result = "VarArgsEnd" }
638638
}
639639

640-
class VarArg extends BuiltInOperationOpcode, TVarArg {
640+
class VarArg extends UnaryOpcode, TVarArg {
641641
final override string toString() { result = "VarArg" }
642642
}
643643

644-
class VarArgCopy extends BuiltInOperationOpcode, TVarArgCopy {
645-
final override string toString() { result = "VarArgCopy" }
644+
class NextVarArg extends UnaryOpcode, TNextVarArg {
645+
final override string toString() { result = "NextVarArg" }
646646
}
647647

648648
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ newtype TInstructionTag =
6464
InitializerElementAddressTag() or
6565
InitializerElementDefaultValueTag() or
6666
InitializerElementDefaultValueStoreTag() or
67+
VarArgsStartEllipsisAddressTag() or
68+
VarArgsStartTag() or
69+
VarArgsVAListLoadTag() or
70+
VarArgsArgAddressTag() or
71+
VarArgsArgLoadTag() or
72+
VarArgsMoveNextTag() or
73+
VarArgsVAListStoreTag() or
6774
AsmTag() or
6875
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) }
6976

@@ -188,6 +195,20 @@ string getInstructionTagId(TInstructionTag tag) {
188195
or
189196
tag = InitializerElementDefaultValueStoreTag() and result = "InitElemDefValStore"
190197
or
198+
tag = VarArgsStartEllipsisAddressTag() and result = "VarArgsStartEllipsisAddr"
199+
or
200+
tag = VarArgsStartTag() and result = "VarArgsStart"
201+
or
202+
tag = VarArgsVAListLoadTag() and result = "VarArgsVAListLoad"
203+
or
204+
tag = VarArgsArgAddressTag() and result = "VarArgsArgAddr"
205+
or
206+
tag = VarArgsArgLoadTag() and result = "VaArgsArgLoad"
207+
or
208+
tag = VarArgsMoveNextTag() and result = "VarArgsMoveNext"
209+
or
210+
tag = VarArgsVAListStoreTag() and result = "VarArgsVAListStore"
211+
or
191212
tag = AsmTag() and result = "Asm"
192213
or
193214
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ private predicate ignoreExprAndDescendants(Expr expr) {
8383
exists(DeleteExpr deleteExpr | deleteExpr.getAllocatorCall() = expr)
8484
or
8585
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getAllocatorCall() = expr)
86+
or
87+
exists(BuiltInVarArgsStart vaStartExpr |
88+
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
89+
)
8690
}
8791

8892
/**
@@ -109,6 +113,22 @@ private predicate ignoreExprOnly(Expr expr) {
109113
exists(DeleteExpr deleteExpr | deleteExpr.getDestructorCall() = expr)
110114
or
111115
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getDestructorCall() = expr)
116+
or
117+
expr instanceof Conversion and
118+
exists(VarArgsExpr parent |
119+
// Ignore any conversions applied to a `va_list` argument to a varargs-related macro. In the
120+
// Unix ABI, the `va_list` undergoes an array-to-pointer conversion, but we only want the
121+
// underlying variable access.
122+
expr = parent.(BuiltInVarArgsStart).getVAList().getFullyConverted()
123+
or
124+
expr = parent.(BuiltInVarArgsEnd).getVAList().getFullyConverted()
125+
or
126+
expr = parent.(BuiltInVarArg).getVAList().getFullyConverted()
127+
or
128+
expr = parent.(BuiltInVarArgCopy).getSourceVAList().getFullyConverted()
129+
or
130+
expr = parent.(BuiltInVarArgCopy).getDestinationVAList().getFullyConverted()
131+
)
112132
}
113133

114134
/**

0 commit comments

Comments
 (0)