Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
badb117
AST and IR support for `TemporaryObjectExpr`
Oct 5, 2020
80b832e
Fix test expectations
Oct 6, 2020
22638fd
Merge remote-tracking branch 'upstream/main' into work
Oct 6, 2020
bf8340f
Fix test expectations in `syntax-zoo`
Oct 7, 2020
1e455f0
Fix test expectations
Oct 7, 2020
e4bfb75
C++: Fix pointer flow through temporary objects
Oct 8, 2020
dfe69d8
Update taint test to propagate through string constructor
Oct 13, 2020
93f5ae4
Clean up test formatting and accept new lines in results
Oct 13, 2020
fba4313
Merge remote-tracking branch 'upstream/main' into work
Oct 13, 2020
794a672
C++: Add ability to dump local dataflow info in IR dumps
Oct 14, 2020
ceea5b3
Fix test code that returns reference to local
Oct 15, 2020
3767a52
Fix ODR violations in test code
Oct 15, 2020
14ac985
Remove more ODR violations from test code.
Oct 16, 2020
6a9ecf7
Dump static call target for Call instructions
Oct 16, 2020
9afddf0
Insert a load of the temporary object for arguments passed by value
Oct 16, 2020
cf19fcf
C++: Improve dataflow model for copy/move constructors
Oct 16, 2020
af799a7
Accept good test diffs
Oct 16, 2020
675256a
Accept test diffs from `set.cpp` (50 new good results!)
Oct 16, 2020
7da4eef
Fix subtle typing issue with `std::makr_pair`
Oct 17, 2020
686f5aa
Handle parameter indirections in `make_shared` and `make_unique`
Oct 17, 2020
1b53c46
Fix test expectations due to `pair`/`make_pair` fixes
Oct 17, 2020
1dae8f6
Model copy-ish constructors for `std::pair`
Oct 17, 2020
4814dcf
Print partial flow info in `PrintIRLocalFlow.qll`
Oct 17, 2020
e4fdf69
Accept improved test results
Oct 17, 2020
4e0afb0
Print targets of `Load` and `Store` instructions in IR dump
Oct 17, 2020
40cd96e
Merge from `main`
Oct 17, 2020
eb9cea4
Fix modeling of `std::set::emplace`
Oct 17, 2020
129e250
Update test expectations
Oct 17, 2020
5f6ae32
Accept test output after merge
Oct 17, 2020
939bfae
Fix formatting
Oct 18, 2020
0b2acff
Add upgrade script
Oct 18, 2020
2f34c78
Fix formatting
Oct 18, 2020
b73cb3a
Accept C# IR diffs
Oct 18, 2020
ece20cd
Merge branch 'main' into dbartol/temporaries/work
Oct 18, 2020
2ba1ef9
Merge remote-tracking branch 'upstream/main' into work
Oct 19, 2020
d0b93df
Merge from `main`
Oct 19, 2020
2eaa4a4
Merge remote-tracking branch 'upstream/main' into work
Oct 19, 2020
ade6d10
Merge remote-tracking branch 'upstream/main' into work
Oct 20, 2020
7de6415
Accept test diffs after merge
Oct 20, 2020
735c657
IR consistency checks for `FieldAddress` and `this` arguments that ar…
Oct 20, 2020
4ba2817
Fix IR generation for member access with a prvalue on the RHS
Oct 20, 2020
8c8daa3
Update stats
Oct 20, 2020
c739f98
Merge remote-tracking branch 'upstream/main' into work
Oct 20, 2020
08af080
Add examples to QLDoc comment
Oct 20, 2020
98e0ae4
Add tests for member accesses on temporary objects
Oct 20, 2020
ee18db7
Fix IR for member accesses on prvalues
Oct 21, 2020
5259f86
Accept diff (needs further investigation, though)
Oct 21, 2020
1de1ab6
Merge remote-tracking branch 'upstream/main' into work
Oct 21, 2020
f7eeada
Accept more diffs
Oct 21, 2020
b62bda6
Fix regression due to primary instructions for side effects not being…
Oct 22, 2020
9907248
Fix PR feedback
Oct 22, 2020
bace0dc
Handle more cases that require synthesizing temporary objects
Oct 23, 2020
35abcae
Fix formatting
Oct 23, 2020
1e96404
Revert bad changes to `basic_string`
Oct 23, 2020
4d2f658
Don't treat allocator argument as a string input
Oct 23, 2020
8666805
Avoid ODR violation in test code
Oct 23, 2020
3fce971
Fix taint propagation to qualifier objects and update test expectations
Oct 23, 2020
5a6cd4a
Fix test expectations for new nodes and edges in path queries
Oct 28, 2020
7a2c59c
Merge from main
Oct 28, 2020
c49e33f
Fixup after merge
Oct 28, 2020
4237341
Merge from `main`
Oct 30, 2020
ec398b2
Merge remote-tracking branch 'upstream/main' into work
Oct 30, 2020
be180aa
Fixup after merge
Oct 30, 2020
69dee15
Fix PR feedback
Oct 31, 2020
e9d1f0d
Merge remote-tracking branch 'upstream/main' into work
Nov 2, 2020
0d1fbd1
Fix annotations
Nov 2, 2020
f0b9794
Merge remote-tracking branch 'upstream/main' into work
Nov 3, 2020
4cc9110
Fix test expectation
Nov 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
or
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
or
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
or
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
or
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
or
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
or
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
or
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
or
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
Expand Down
22 changes: 22 additions & 0 deletions cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,28 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer {
override predicate mayBeGloballyImpure() { none() }
}

/**
* A node representing a temporary object created as part of an expression.
*
* This is most commonly seen in the following cases:
* ```c++
* // when binding a reference to a prvalue
* const std::string& r = std::string("text");
*
* // when performing member access on a class prvalue
* strlen(std::string("text").c_str());
*
* // when a prvalue of a type with a destructor is discarded
* s.substr(0, 5); // Return value is discarded but requires destruction
* ```
*/
class TemporaryObjectExpr extends Conversion, @temp_init {
/** Gets a textual representation of this conversion. */
override string toString() { result = "temporary object" }

override string getAPrimaryQlClass() { result = "TemporaryObjectExpr" }
}

/**
* A node representing the Cast sub-class of entity `cast`.
*/
Expand Down
104 changes: 67 additions & 37 deletions cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }

override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
commonTaintStep(n1, n2)
}

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

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

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

cached
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
private predicate commonTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
instructionToInstructionTaintStep(fromNode.asInstruction(), toNode.asInstruction())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with how we interleave instruction -> operand steps in dataflow we should probably convert this to pure instruction -> operand and operand -> instruction flow at some point once this PR has been merged. But I'm fine with these changes as they are now!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I just didn't want to change more than I already had to.

or
operandToInstructionTaintStep(fromNode.asOperand(), toNode.asInstruction())
or
operandToOperandTaintStep(fromNode.asOperand(), toNode.asOperand())
}

private predicate operandToOperandTaintStep(Operand fromOperand, Operand toOperand) {
exists(ReadSideEffectInstruction readInstr |
fromOperand = readInstr.getArgumentOperand() and
toOperand = readInstr.getSideEffectOperand()
)
}

private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction toInstr) {
// Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 |
exists(CallInstruction call, int argIndex | call = toInstr |
isPureFunction(call.getStaticCallTarget().getName()) and
i1 = getACallArgumentOrIndirection(call, argIndex) and
forall(Instruction arg | arg = call.getAnArgument() |
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
fromOperand = getACallArgumentOrIndirection(call, argIndex) and
forall(Operand argOperand | argOperand = call.getAnArgumentOperand() |
argOperand = getACallArgumentOrIndirection(call, argIndex) or
predictableInstruction(argOperand.getAnyDef())
) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getStaticCallTarget().getName() = "strlen"
)
or
// Flow from argument to return value
toInstr =
any(CallInstruction call |
exists(int indexIn |
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
not predictableOnlyFlow(call.getStaticCallTarget().getName())
)
)
or
// Flow from input argument to output argument
// TODO: This won't work in practice as long as all aliased memory is tracked
// together in a single virtual variable.
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
// is a pointer addition expression?
toInstr =
any(WriteSideEffectInstruction outInstr |
exists(CallInstruction call, int indexIn, int indexOut |
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
outInstr.getIndex() = indexOut and
outInstr.getPrimaryInstruction() = call
)
)
}

private predicate instructionToInstructionTaintStep(Instruction i1, Instruction i2) {
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
or
Expand Down Expand Up @@ -291,29 +334,6 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
read.getArgumentDef() = i2
)
or
// Flow from argument to return value
i2 =
any(CallInstruction call |
exists(int indexIn |
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
i1 = getACallArgumentOrIndirection(call, indexIn) and
not predictableOnlyFlow(call.getStaticCallTarget().getName())
)
)
or
// Flow from input argument to output argument
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
// is a pointer addition expression?
i2 =
any(WriteSideEffectInstruction outNode |
exists(CallInstruction call, int indexIn, int indexOut |
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
i1 = getACallArgumentOrIndirection(call, indexIn) and
outNode.getIndex() = indexOut and
outNode.getPrimaryInstruction() = call
)
)
}

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

/**
* Get an instruction that goes into argument `argumentIndex` of `call`. This
* Returns the index of the side effect instruction corresponding to the specified function output,
* if one exists.
*/
private int getWriteSideEffectIndex(FunctionOutput output) {
output.isParameterDeref(result)
or
output.isQualifierObject() and result = -1
}

/**
* Get an operand that goes into argument `argumentIndex` of `call`. This
* can be either directly or through one pointer indirection.
*/
private Instruction getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
result = call.getPositionalArgument(argumentIndex)
private Operand getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
result = call.getPositionalArgumentOperand(argumentIndex)
or
exists(ReadSideEffectInstruction readSE |
// TODO: why are read side effect operands imprecise?
Comment thread
geoffw0 marked this conversation as resolved.
result = readSE.getSideEffectOperand().getAnyDef() and
result = readSE.getSideEffectOperand() and
readSE.getPrimaryInstruction() = call and
readSE.getIndex() = argumentIndex
)
Expand All @@ -351,7 +381,7 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
f.(TaintFunction).hasTaintFlow(modelIn, modelOut)
) and
(modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
modelOut.isParameterDeref(parameterOut)
parameterOut = getWriteSideEffectIndex(modelOut)
)
}

Expand Down Expand Up @@ -540,7 +570,7 @@ module TaintedWithPath {
}

override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
commonTaintStep(n1, n2)
or
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,4 +494,34 @@ module InstructionConsistency {
irFunc = getInstructionIRFunction(instr, irFuncText)
)
}

/**
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
* address type.
*/
query predicate fieldAddressOnNonPointer(
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
message =
"FieldAddress instruction '" + instr.toString() +
"' has an object address operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}

/**
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
* type.
*/
query predicate thisArgumentIsNonPointer(
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
not thisOperand.getIRType() instanceof IRAddressType
) and
message =
"Call instruction '" + instr.toString() +
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -494,4 +494,34 @@ module InstructionConsistency {
irFunc = getInstructionIRFunction(instr, irFuncText)
)
}

/**
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
* address type.
*/
query predicate fieldAddressOnNonPointer(
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
message =
"FieldAddress instruction '" + instr.toString() +
"' has an object address operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}

/**
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
* type.
*/
query predicate thisArgumentIsNonPointer(
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
not thisOperand.getIRType() instanceof IRAddressType
) and
message =
"Call instruction '" + instr.toString() +
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction

/**
* Gets the `CallInstruction` from the `TranslatedCallExpr` for the specified expression.
*/
private CallInstruction getTranslatedCallInstruction(Call call) {
exists(TranslatedCallExpr translatedCall |
translatedCall.getExpr() = call and
result = translatedCall.getInstruction(CallTag())
)
}

/**
* The IR translation of a call to a function. The call may be from an actual
* call in the source code, or could be a call that is part of the translation
Expand Down Expand Up @@ -388,7 +398,7 @@ class TranslatedAllocationSideEffects extends TranslatedSideEffects,
tag = OnlyInstructionTag() and
if expr instanceof NewOrNewArrayExpr
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
else result = getTranslatedExpr(expr).getInstruction(CallTag())
else result = getTranslatedCallInstruction(expr)
}
}

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

override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getTranslatedExpr(expr).getInstruction(CallTag())
result = getTranslatedCallInstruction(expr)
}
}

Expand Down Expand Up @@ -599,7 +609,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff

override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getTranslatedExpr(call).getInstruction(CallTag())
result = getTranslatedCallInstruction(call)
}

final override int getInstructionIndex(InstructionTag tag) {
Expand Down
Loading