Skip to content

Commit 82e2816

Browse files
author
Dave Bartolomeo
committed
C++: Fix handling of std::va_list that is used as a function parameter
In the Unix ABI, `std::va_list` is defined as `typedef struct __va_list_tag { ... } va_list[1];`, which means that any `std::va_list` used as a function parameter decays to `struct __va_list_tag*`. Handling this actually made the QL code slightly cleaner. The only tricky bit is that we have to determine what type to use as the actual `va_list` type when loading, storing, or modifying a `std::va_list`. To do this, we look at the type of the argument to the `va_*` macro. A detailed QLDoc comment explains the details. I added a test case for passing a `va_list` as an argument, and then manipulating that `va_list` in the callee.
1 parent bf28451 commit 82e2816

File tree

5 files changed

+3787
-3641
lines changed

5 files changed

+3787
-3641
lines changed

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

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,6 @@ private predicate ignoreExprOnly(Expr expr) {
113113
exists(DeleteExpr deleteExpr | deleteExpr.getDestructorCall() = expr)
114114
or
115115
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-
)
132116
}
133117

134118
/**

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

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,6 +2079,54 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
20792079
}
20802080
}
20812081

2082+
/**
2083+
* Holds if the expression `expr` is one of the `va_list` operands to a `va_*` macro.
2084+
*/
2085+
private predicate isVAListExpr(Expr expr) {
2086+
exists(VarArgsExpr parent, Expr originalExpr |
2087+
(
2088+
originalExpr = parent.(BuiltInVarArgsStart).getVAList()
2089+
or
2090+
originalExpr = parent.(BuiltInVarArgsEnd).getVAList()
2091+
or
2092+
originalExpr = parent.(BuiltInVarArg).getVAList()
2093+
or
2094+
originalExpr = parent.(BuiltInVarArgCopy).getSourceVAList()
2095+
or
2096+
originalExpr = parent.(BuiltInVarArgCopy).getDestinationVAList()
2097+
) and
2098+
expr = originalExpr.getFullyConverted()
2099+
)
2100+
}
2101+
2102+
/**
2103+
* Gets the type of the `va_list` being accessed by `expr`, where `expr` is a `va_list` operand of a
2104+
* `va_*` macro.
2105+
*
2106+
* In the Unix ABI, `va_list` is declared as `typedef struct __va_list_tag va_list[1];`. When used
2107+
* as the type of a local variable, this gets an implicit array-to-pointer conversion, so that the
2108+
* actual argument to the `va_*` macro is a prvalue of type `__va_list_tag*`. When used as the type
2109+
* of a function parameter, the parameter's type decays to `__va_list_tag*`, so that the argument
2110+
* to the `va_*` macro is still a prvalue of type `__va_list_tag*`, with no implicit conversion
2111+
* necessary. In either case, we treat `__va_list_tag` as the representative type of the `va_list`.
2112+
*
2113+
* In the Windows ABI, `va_list` is declared as a pointer type (usually `char*`). Whether used as
2114+
* the type of a local variable or of a parameter, this means that the argument to the `va_*` macro
2115+
* is always an _lvalue_ of type `char*`. We treat `char*` as the representative type of the
2116+
* `va_list`.
2117+
*/
2118+
private Type getVAListType(Expr expr) {
2119+
isVAListExpr(expr) and
2120+
if expr.isPRValueCategory()
2121+
then
2122+
// In the Unix ABI, this will be a prvalue of type `__va_list_tag*`. We want the `__va_list_tag`
2123+
// type.
2124+
result = expr.getType().getUnderlyingType().(PointerType).getBaseType()
2125+
else
2126+
// In the Windows ABI, this will be an lvalue of some pointer type. We want that pointer type.
2127+
result = expr.getType()
2128+
}
2129+
20822130
/**
20832131
* The IR translation of a `BuiltInVarArgsStart` expression.
20842132
*/
@@ -2092,32 +2140,23 @@ class TranslatedVarArgsStart extends TranslatedNonConstantExpr {
20922140
or
20932141
tag = VarArgsStartTag() and
20942142
opcode instanceof Opcode::VarArgsStart and
2095-
// Intentionally skip calling `getFullyConverted()`, because we want the type of the
2096-
// `VariableAccess`, even if it has undergone the array-to-pointer conversion that gets applied
2097-
// for the Unix ABI.
2098-
resultType = getTypeForPRValue(expr.getVAList().getType())
2143+
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
20992144
or
21002145
tag = VarArgsVAListStoreTag() and
21012146
opcode instanceof Opcode::Store and
2102-
resultType = getTypeForPRValue(expr.getVAList().getType())
2147+
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
21032148
}
21042149

21052150
final override Instruction getFirstInstruction() {
21062151
result = getInstruction(VarArgsStartEllipsisAddressTag())
21072152
}
21082153

2109-
final override Instruction getResult() {
2110-
none()
2111-
}
2154+
final override Instruction getResult() { none() }
21122155

2113-
final override TranslatedElement getChild(int id) {
2114-
id = 0 and result = getVAList()
2115-
}
2156+
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
21162157

21172158
private TranslatedExpr getVAList() {
2118-
// Intentionally skip calling `getFullyConverted()`, because we want the `VariableAccess`, even
2119-
// if it has undergone the array-to-pointer conversion that gets applied for the Unix ABI.
2120-
result = getTranslatedExpr(expr.getVAList())
2159+
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
21212160
}
21222161

21232162
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -2167,37 +2206,29 @@ class TranslatedVarArg extends TranslatedNonConstantExpr {
21672206
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
21682207
tag = VarArgsVAListLoadTag() and
21692208
opcode instanceof Opcode::Load and
2170-
resultType = getTypeForPRValue(expr.getVAList().getType())
2209+
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
21712210
or
21722211
tag = VarArgsArgAddressTag() and
21732212
opcode instanceof Opcode::VarArg and
21742213
resultType = getResultType()
21752214
or
21762215
tag = VarArgsMoveNextTag() and
21772216
opcode instanceof Opcode::NextVarArg and
2178-
resultType = getTypeForPRValue(expr.getVAList().getType())
2217+
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
21792218
or
21802219
tag = VarArgsVAListStoreTag() and
21812220
opcode instanceof Opcode::Store and
2182-
resultType = getTypeForPRValue(expr.getVAList().getType())
2221+
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
21832222
}
21842223

2185-
final override Instruction getFirstInstruction() {
2186-
result = getVAList().getFirstInstruction()
2187-
}
2224+
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
21882225

2189-
final override Instruction getResult() {
2190-
result = getInstruction(VarArgsArgAddressTag())
2191-
}
2226+
final override Instruction getResult() { result = getInstruction(VarArgsArgAddressTag()) }
21922227

2193-
final override TranslatedElement getChild(int id) {
2194-
id = 0 and result = getVAList()
2195-
}
2228+
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
21962229

21972230
private TranslatedExpr getVAList() {
2198-
// Intentionally skip calling `getFullyConverted()`, because we want the `VariableAccess`, even
2199-
// if it has undergone the array-to-pointer conversion that gets applied for the Unix ABI.
2200-
result = getTranslatedExpr(expr.getVAList())
2231+
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
22012232
}
22022233

22032234
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -2262,22 +2293,14 @@ class TranslatedVarArgsEnd extends TranslatedNonConstantExpr {
22622293
resultType = getVoidType()
22632294
}
22642295

2265-
final override Instruction getFirstInstruction() {
2266-
result = getVAList().getFirstInstruction()
2267-
}
2296+
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
22682297

2269-
final override Instruction getResult() {
2270-
none()
2271-
}
2298+
final override Instruction getResult() { none() }
22722299

2273-
final override TranslatedElement getChild(int id) {
2274-
id = 0 and result = getVAList()
2275-
}
2300+
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
22762301

22772302
private TranslatedExpr getVAList() {
2278-
// Intentionally skip calling `getFullyConverted()`, because we want the `VariableAccess`, even
2279-
// if it has undergone the array-to-pointer conversion that gets applied for the Unix ABI.
2280-
result = getTranslatedExpr(expr.getVAList())
2303+
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
22812304
}
22822305

22832306
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -2307,20 +2330,18 @@ class TranslatedVarArgCopy extends TranslatedNonConstantExpr {
23072330
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
23082331
tag = VarArgsVAListLoadTag() and
23092332
opcode instanceof Opcode::Load and
2310-
resultType = getTypeForPRValue(expr.getSourceVAList().getType())
2333+
resultType = getTypeForPRValue(getVAListType(expr.getSourceVAList().getFullyConverted()))
23112334
or
23122335
tag = VarArgsVAListStoreTag() and
23132336
opcode instanceof Opcode::Store and
2314-
resultType = getTypeForPRValue(expr.getDestinationVAList().getType())
2337+
resultType = getTypeForPRValue(getVAListType(expr.getDestinationVAList().getFullyConverted()))
23152338
}
23162339

23172340
final override Instruction getFirstInstruction() {
23182341
result = getSourceVAList().getFirstInstruction()
23192342
}
23202343

2321-
final override Instruction getResult() {
2322-
result = getInstruction(VarArgsVAListStoreTag())
2323-
}
2344+
final override Instruction getResult() { result = getInstruction(VarArgsVAListStoreTag()) }
23242345

23252346
final override TranslatedElement getChild(int id) {
23262347
id = 0 and result = getDestinationVAList()
@@ -2329,15 +2350,11 @@ class TranslatedVarArgCopy extends TranslatedNonConstantExpr {
23292350
}
23302351

23312352
private TranslatedExpr getDestinationVAList() {
2332-
// Intentionally skip calling `getFullyConverted()`, because we want the `VariableAccess`, even
2333-
// if it has undergone the array-to-pointer conversion that gets applied for the Unix ABI.
2334-
result = getTranslatedExpr(expr.getDestinationVAList())
2353+
result = getTranslatedExpr(expr.getDestinationVAList().getFullyConverted())
23352354
}
23362355

23372356
private TranslatedExpr getSourceVAList() {
2338-
// Intentionally skip calling `getFullyConverted()`, because we want the `VariableAccess`, even
2339-
// if it has undergone the array-to-pointer conversion that gets applied for the Unix ABI.
2340-
result = getTranslatedExpr(expr.getSourceVAList())
2357+
result = getTranslatedExpr(expr.getSourceVAList().getFullyConverted())
23412358
}
23422359

23432360
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {

0 commit comments

Comments
 (0)