Skip to content

Commit 6ff939d

Browse files
authored
Merge pull request #4432 from dbartol/dbartol/temporaries/work
C++: Represent temporary object initialization in AST and IR
2 parents 62cb4ec + 4cc9110 commit 6ff939d

75 files changed

Lines changed: 10883 additions & 4655 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
8383
or
8484
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
8585
or
86+
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
87+
or
8688
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
8789
or
8890
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()

cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
8181
or
8282
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
8383
or
84+
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
85+
or
8486
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
8587
or
8688
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,28 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer {
840840
override predicate mayBeGloballyImpure() { none() }
841841
}
842842

843+
/**
844+
* A node representing a temporary object created as part of an expression.
845+
*
846+
* This is most commonly seen in the following cases:
847+
* ```c++
848+
* // when binding a reference to a prvalue
849+
* const std::string& r = std::string("text");
850+
*
851+
* // when performing member access on a class prvalue
852+
* strlen(std::string("text").c_str());
853+
*
854+
* // when a prvalue of a type with a destructor is discarded
855+
* s.substr(0, 5); // Return value is discarded but requires destruction
856+
* ```
857+
*/
858+
class TemporaryObjectExpr extends Conversion, @temp_init {
859+
/** Gets a textual representation of this conversion. */
860+
override string toString() { result = "temporary object" }
861+
862+
override string getAPrimaryQlClass() { result = "TemporaryObjectExpr" }
863+
}
864+
843865
/**
844866
* A node representing the Cast sub-class of entity `cast`.
845867
*/

cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
8383
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
8484

8585
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
86-
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
86+
commonTaintStep(n1, n2)
8787
}
8888

8989
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
@@ -101,7 +101,7 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
101101
}
102102

103103
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
104-
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
104+
commonTaintStep(n1, n2)
105105
or
106106
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
107107
or
@@ -125,7 +125,7 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
125125
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
126126

127127
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
128-
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
128+
commonTaintStep(n1, n2)
129129
or
130130
// Additional step for flow out of variables. There is no flow _into_
131131
// variables in this configuration, so this step only serves to take flow
@@ -215,19 +215,62 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
215215
}
216216

217217
cached
218-
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
218+
private predicate commonTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
219+
instructionToInstructionTaintStep(fromNode.asInstruction(), toNode.asInstruction())
220+
or
221+
operandToInstructionTaintStep(fromNode.asOperand(), toNode.asInstruction())
222+
or
223+
operandToOperandTaintStep(fromNode.asOperand(), toNode.asOperand())
224+
}
225+
226+
private predicate operandToOperandTaintStep(Operand fromOperand, Operand toOperand) {
227+
exists(ReadSideEffectInstruction readInstr |
228+
fromOperand = readInstr.getArgumentOperand() and
229+
toOperand = readInstr.getSideEffectOperand()
230+
)
231+
}
232+
233+
private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction toInstr) {
219234
// Expressions computed from tainted data are also tainted
220-
exists(CallInstruction call, int argIndex | call = i2 |
235+
exists(CallInstruction call, int argIndex | call = toInstr |
221236
isPureFunction(call.getStaticCallTarget().getName()) and
222-
i1 = getACallArgumentOrIndirection(call, argIndex) and
223-
forall(Instruction arg | arg = call.getAnArgument() |
224-
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
237+
fromOperand = getACallArgumentOrIndirection(call, argIndex) and
238+
forall(Operand argOperand | argOperand = call.getAnArgumentOperand() |
239+
argOperand = getACallArgumentOrIndirection(call, argIndex) or
240+
predictableInstruction(argOperand.getAnyDef())
225241
) and
226242
// flow through `strlen` tends to cause dubious results, if the length is
227243
// bounded.
228244
not call.getStaticCallTarget().getName() = "strlen"
229245
)
230246
or
247+
// Flow from argument to return value
248+
toInstr =
249+
any(CallInstruction call |
250+
exists(int indexIn |
251+
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
252+
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
253+
not predictableOnlyFlow(call.getStaticCallTarget().getName())
254+
)
255+
)
256+
or
257+
// Flow from input argument to output argument
258+
// TODO: This won't work in practice as long as all aliased memory is tracked
259+
// together in a single virtual variable.
260+
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
261+
// is a pointer addition expression?
262+
toInstr =
263+
any(WriteSideEffectInstruction outInstr |
264+
exists(CallInstruction call, int indexIn, int indexOut |
265+
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
266+
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
267+
outInstr.getIndex() = indexOut and
268+
outInstr.getPrimaryInstruction() = call
269+
)
270+
)
271+
}
272+
273+
private predicate instructionToInstructionTaintStep(Instruction i1, Instruction i2) {
231274
// Flow through pointer dereference
232275
i2.(LoadInstruction).getSourceAddress() = i1
233276
or
@@ -291,29 +334,6 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
291334
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
292335
read.getArgumentDef() = i2
293336
)
294-
or
295-
// Flow from argument to return value
296-
i2 =
297-
any(CallInstruction call |
298-
exists(int indexIn |
299-
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
300-
i1 = getACallArgumentOrIndirection(call, indexIn) and
301-
not predictableOnlyFlow(call.getStaticCallTarget().getName())
302-
)
303-
)
304-
or
305-
// Flow from input argument to output argument
306-
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
307-
// is a pointer addition expression?
308-
i2 =
309-
any(WriteSideEffectInstruction outNode |
310-
exists(CallInstruction call, int indexIn, int indexOut |
311-
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
312-
i1 = getACallArgumentOrIndirection(call, indexIn) and
313-
outNode.getIndex() = indexOut and
314-
outNode.getPrimaryInstruction() = call
315-
)
316-
)
317337
}
318338

319339
pragma[noinline]
@@ -329,15 +349,25 @@ private InitializeParameterInstruction getInitializeParameter(IRFunction f, Para
329349
}
330350

331351
/**
332-
* Get an instruction that goes into argument `argumentIndex` of `call`. This
352+
* Returns the index of the side effect instruction corresponding to the specified function output,
353+
* if one exists.
354+
*/
355+
private int getWriteSideEffectIndex(FunctionOutput output) {
356+
output.isParameterDeref(result)
357+
or
358+
output.isQualifierObject() and result = -1
359+
}
360+
361+
/**
362+
* Get an operand that goes into argument `argumentIndex` of `call`. This
333363
* can be either directly or through one pointer indirection.
334364
*/
335-
private Instruction getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
336-
result = call.getPositionalArgument(argumentIndex)
365+
private Operand getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
366+
result = call.getPositionalArgumentOperand(argumentIndex)
337367
or
338368
exists(ReadSideEffectInstruction readSE |
339369
// TODO: why are read side effect operands imprecise?
340-
result = readSE.getSideEffectOperand().getAnyDef() and
370+
result = readSE.getSideEffectOperand() and
341371
readSE.getPrimaryInstruction() = call and
342372
readSE.getIndex() = argumentIndex
343373
)
@@ -351,7 +381,7 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
351381
f.(TaintFunction).hasTaintFlow(modelIn, modelOut)
352382
) and
353383
(modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
354-
modelOut.isParameterDeref(parameterOut)
384+
parameterOut = getWriteSideEffectIndex(modelOut)
355385
)
356386
}
357387

@@ -540,7 +570,7 @@ module TaintedWithPath {
540570
}
541571

542572
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
543-
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
573+
commonTaintStep(n1, n2)
544574
or
545575
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
546576
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,4 +494,34 @@ module InstructionConsistency {
494494
irFunc = getInstructionIRFunction(instr, irFuncText)
495495
)
496496
}
497+
498+
/**
499+
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
500+
* address type.
501+
*/
502+
query predicate fieldAddressOnNonPointer(
503+
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
504+
) {
505+
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
506+
message =
507+
"FieldAddress instruction '" + instr.toString() +
508+
"' has an object address operand that is not an address, in function '$@'." and
509+
irFunc = getInstructionIRFunction(instr, irFuncText)
510+
}
511+
512+
/**
513+
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
514+
* type.
515+
*/
516+
query predicate thisArgumentIsNonPointer(
517+
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
518+
) {
519+
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
520+
not thisOperand.getIRType() instanceof IRAddressType
521+
) and
522+
message =
523+
"Call instruction '" + instr.toString() +
524+
"' has a `this` argument operand that is not an address, in function '$@'." and
525+
irFunc = getInstructionIRFunction(instr, irFuncText)
526+
}
497527
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,4 +494,34 @@ module InstructionConsistency {
494494
irFunc = getInstructionIRFunction(instr, irFuncText)
495495
)
496496
}
497+
498+
/**
499+
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
500+
* address type.
501+
*/
502+
query predicate fieldAddressOnNonPointer(
503+
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
504+
) {
505+
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
506+
message =
507+
"FieldAddress instruction '" + instr.toString() +
508+
"' has an object address operand that is not an address, in function '$@'." and
509+
irFunc = getInstructionIRFunction(instr, irFuncText)
510+
}
511+
512+
/**
513+
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
514+
* type.
515+
*/
516+
query predicate thisArgumentIsNonPointer(
517+
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
518+
) {
519+
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
520+
not thisOperand.getIRType() instanceof IRAddressType
521+
) and
522+
message =
523+
"Call instruction '" + instr.toString() +
524+
"' has a `this` argument operand that is not an address, in function '$@'." and
525+
irFunc = getInstructionIRFunction(instr, irFuncText)
526+
}
497527
}

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ private import TranslatedElement
88
private import TranslatedExpr
99
private import TranslatedFunction
1010

11+
/**
12+
* Gets the `CallInstruction` from the `TranslatedCallExpr` for the specified expression.
13+
*/
14+
private CallInstruction getTranslatedCallInstruction(Call call) {
15+
exists(TranslatedCallExpr translatedCall |
16+
translatedCall.getExpr() = call and
17+
result = translatedCall.getInstruction(CallTag())
18+
)
19+
}
20+
1121
/**
1222
* The IR translation of a call to a function. The call may be from an actual
1323
* call in the source code, or could be a call that is part of the translation
@@ -388,7 +398,7 @@ class TranslatedAllocationSideEffects extends TranslatedSideEffects,
388398
tag = OnlyInstructionTag() and
389399
if expr instanceof NewOrNewArrayExpr
390400
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
391-
else result = getTranslatedExpr(expr).getInstruction(CallTag())
401+
else result = getTranslatedCallInstruction(expr)
392402
}
393403
}
394404

@@ -409,7 +419,7 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
409419

410420
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
411421
tag = OnlyInstructionTag() and
412-
result = getTranslatedExpr(expr).getInstruction(CallTag())
422+
result = getTranslatedCallInstruction(expr)
413423
}
414424
}
415425

@@ -599,7 +609,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
599609

600610
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
601611
tag = OnlyInstructionTag() and
602-
result = getTranslatedExpr(call).getInstruction(CallTag())
612+
result = getTranslatedCallInstruction(call)
603613
}
604614

605615
final override int getInstructionIndex(InstructionTag tag) {

0 commit comments

Comments
 (0)