Merge pull request #5737 from dbartol/dbartol/smart-pointers/work

C++: IR Alias Analysis for smart pointers
This commit is contained in:
Mathias Vorreiter Pedersen
2021-04-27 21:40:14 +02:00
committed by GitHub
21 changed files with 1152 additions and 292 deletions

View File

@@ -694,7 +694,12 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
fromExpr = call.getQualifier()
) and
call.getTarget() = f and
outModel.isReturnValue()
// AST dataflow treats a reference as if it were the referred-to object, while the dataflow
// models treat references as pointers. If the return type of the call is a reference, then
// look for data flow the the referred-to object, rather than the reference itself.
if call.getType().getUnspecifiedType() instanceof ReferenceType
then outModel.isReturnValueDeref()
else outModel.isReturnValue()
)
)
}

View File

@@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction {
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
/**
* Holds if the result is a side effect for the argument at the specified index, or `this` if
* `index` is `-1`.
*
* This helper predicate makes it easy to join on both of these columns at once, avoiding
* pathological join orders in case the argument index should get joined first.
*/
pragma[noinline]
final SideEffectInstruction getAParameterSideEffect(int index) {
this = result.getPrimaryInstruction() and
index = result.(IndexedInstruction).getIndex()
}
}
/**

View File

@@ -4,6 +4,71 @@ private import AliasAnalysisImports
private class IntValue = Ints::IntValue;
/**
* If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side
* effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`.
*/
private CallInstruction getPrimaryCall(Instruction instr) {
result = instr
or
result = instr.(SideEffectInstruction).getPrimaryInstruction()
}
/**
* Holds if `operand` serves as an input argument (or indirection) to `call`, in the position
* specified by `input`.
*/
private predicate isCallInput(
CallInstruction call, Operand operand, AliasModels::FunctionInput input
) {
call = getPrimaryCall(operand.getUse()) and
(
exists(int index |
input.isParameterOrQualifierAddress(index) and
operand = call.getArgumentOperand(index)
)
or
exists(int index, ReadSideEffectInstruction read |
input.isParameterDerefOrQualifierObject(index) and
read = call.getAParameterSideEffect(index) and
operand = read.getSideEffectOperand()
)
)
}
/**
* Holds if `instr` serves as a return value or output argument indirection for `call`, in the
* position specified by `output`.
*/
private predicate isCallOutput(
CallInstruction call, Instruction instr, AliasModels::FunctionOutput output
) {
call = getPrimaryCall(instr) and
(
output.isReturnValue() and instr = call
or
exists(int index, WriteSideEffectInstruction write |
output.isParameterDerefOrQualifierObject(index) and
write = call.getAParameterSideEffect(index) and
instr = write
)
)
}
/**
* Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled
* address flow through a function call.
*/
private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) {
exists(
CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output
|
call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and
isCallInput(call, operand, input) and
isCallOutput(call, resultInstr, output)
)
}
/**
* Holds if the operand `tag` of instruction `instr` is used in a way that does
* not result in any address held in that operand from escaping beyond the
@@ -34,7 +99,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
private predicate operandEscapesDomain(Operand operand) {
not operandIsConsumedWithoutEscaping(operand) and
not operandIsPropagated(operand, _) and
not operandIsPropagated(operand, _, _) and
not isArgumentForParameter(_, operand, _) and
not isOnlyEscapesViaReturnArgument(operand) and
not operand.getUse() instanceof ReturnValueInstruction and
@@ -69,67 +134,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
}
/**
* Holds if any address held in operand `tag` of instruction `instr` is
* propagated to the result of `instr`, offset by the number of bits in
* `bitOffset`. If the address is propagated, but the offset is not known to be
* a constant, then `bitOffset` is unknown.
* Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by
* the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to
* be a constant, then `bitOffset` is `unknown()`.
*/
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
exists(Instruction instr |
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)
or
// Conversion using dynamic_cast results in an unknown offset
instr instanceof CheckedConvertOrNullInstruction and
bitOffset = Ints::unknown()
or
// Converting to a derived class subtracts the offset of the base class.
exists(ConvertToDerivedInstruction convert |
convert = instr and
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
)
or
// Converting to a virtual base class adds an unknown offset.
instr instanceof ConvertToVirtualBaseInstruction and
bitOffset = Ints::unknown()
or
// Conversion to another pointer type propagates the source address.
exists(ConvertInstruction convert, IRType resultType |
convert = instr and
resultType = convert.getResultIRType() and
resultType instanceof IRAddressType and
bitOffset = 0
)
or
// Adding an integer to or subtracting an integer from a pointer propagates
// the address with an offset.
exists(PointerOffsetInstruction ptrOffset |
ptrOffset = instr and
operand = ptrOffset.getLeftOperand() and
bitOffset = getPointerBitOffset(ptrOffset)
)
or
// Computing a field address from a pointer propagates the address plus the
// offset of the field.
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
or
// A copy propagates the source value.
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
or
// Some functions are known to propagate an argument
isAlwaysReturnedArgument(operand) and bitOffset = 0
private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) {
// Some functions are known to propagate an argument
hasAddressFlowThroughCall(operand, instr) and
bitOffset = 0
or
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)
or
// Conversion using dynamic_cast results in an unknown offset
instr instanceof CheckedConvertOrNullInstruction and
bitOffset = Ints::unknown()
or
// Converting to a derived class subtracts the offset of the base class.
exists(ConvertToDerivedInstruction convert |
convert = instr and
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
)
or
// Converting to a virtual base class adds an unknown offset.
instr instanceof ConvertToVirtualBaseInstruction and
bitOffset = Ints::unknown()
or
// Conversion to another pointer type propagates the source address.
exists(ConvertInstruction convert, IRType resultType |
convert = instr and
resultType = convert.getResultIRType() and
resultType instanceof IRAddressType and
bitOffset = 0
)
or
// Adding an integer to or subtracting an integer from a pointer propagates
// the address with an offset.
exists(PointerOffsetInstruction ptrOffset |
ptrOffset = instr and
operand = ptrOffset.getLeftOperand() and
bitOffset = getPointerBitOffset(ptrOffset)
)
or
// Computing a field address from a pointer propagates the address plus the
// offset of the field.
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
or
// A copy propagates the source value.
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
)
}
private predicate operandEscapesNonReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse())
exists(Instruction instr |
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr)
)
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -151,9 +216,11 @@ private predicate operandEscapesNonReturn(Operand operand) {
}
private predicate operandMayReachReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and
resultMayReachReturn(operand.getUse())
exists(Instruction instr |
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _, instr) and
resultMayReachReturn(instr)
)
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -173,9 +240,9 @@ private predicate operandMayReachReturn(Operand operand) {
private predicate operandReturned(Operand operand, IntValue bitOffset) {
// The address is propagated to the result of the instruction, and that result itself is returned
exists(IntValue bitOffset1, IntValue bitOffset2 |
operandIsPropagated(operand, bitOffset1) and
resultReturned(operand.getUse(), bitOffset2) and
exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 |
operandIsPropagated(operand, bitOffset1, instr) and
resultReturned(instr, bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
@@ -214,13 +281,6 @@ private predicate isArgumentForParameter(
)
}
private predicate isAlwaysReturnedArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
@@ -270,12 +330,15 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
/**
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
*/
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
operandIsPropagated(operand, bitOffset)
private predicate operandIsPropagatedIncludingByCall(
Operand operand, IntValue bitOffset, Instruction instr
) {
operandIsPropagated(operand, bitOffset, instr)
or
exists(CallInstruction call, Instruction init |
isArgumentForParameter(call, operand, init) and
resultReturned(init, bitOffset)
resultReturned(init, bitOffset) and
instr = call
)
}
@@ -292,8 +355,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base,
// We already have an offset from `middle`.
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
// `middle` is propagated from `base`.
middleOperand = middle.getAnOperand() and
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and
base = middleOperand.getDef() and
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
)

View File

@@ -105,7 +105,21 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
DynamicAllocation() { this = TDynamicAllocation(call) }
final override string toString() {
result = call.toString() + " at " + call.getLocation() // This isn't performant, but it's only used in test/dump code right now.
// This isn't performant, but it's only used in test/dump code right now.
// Dynamic allocations within a function are numbered in the order by start
// line number. This keeps them stable when the function moves within the
// file, or when non-allocating lines are added and removed within the
// function.
exists(int i |
result = "dynamic{" + i.toString() + "}" and
call =
rank[i](CallInstruction rangeCall |
exists(TDynamicAllocation(rangeCall)) and
rangeCall.getEnclosingIRFunction() = call.getEnclosingIRFunction()
|
rangeCall order by rangeCall.getLocation().getStartLine()
)
)
}
final override CallInstruction getABaseInstruction() { result = call }

View File

@@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction {
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
/**
* Holds if the result is a side effect for the argument at the specified index, or `this` if
* `index` is `-1`.
*
* This helper predicate makes it easy to join on both of these columns at once, avoiding
* pathological join orders in case the argument index should get joined first.
*/
pragma[noinline]
final SideEffectInstruction getAParameterSideEffect(int index) {
this = result.getPrimaryInstruction() and
index = result.(IndexedInstruction).getIndex()
}
}
/**

View File

@@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction {
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
/**
* Holds if the result is a side effect for the argument at the specified index, or `this` if
* `index` is `-1`.
*
* This helper predicate makes it easy to join on both of these columns at once, avoiding
* pathological join orders in case the argument index should get joined first.
*/
pragma[noinline]
final SideEffectInstruction getAParameterSideEffect(int index) {
this = result.getPrimaryInstruction() and
index = result.(IndexedInstruction).getIndex()
}
}
/**

View File

@@ -4,6 +4,71 @@ private import AliasAnalysisImports
private class IntValue = Ints::IntValue;
/**
* If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side
* effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`.
*/
private CallInstruction getPrimaryCall(Instruction instr) {
result = instr
or
result = instr.(SideEffectInstruction).getPrimaryInstruction()
}
/**
* Holds if `operand` serves as an input argument (or indirection) to `call`, in the position
* specified by `input`.
*/
private predicate isCallInput(
CallInstruction call, Operand operand, AliasModels::FunctionInput input
) {
call = getPrimaryCall(operand.getUse()) and
(
exists(int index |
input.isParameterOrQualifierAddress(index) and
operand = call.getArgumentOperand(index)
)
or
exists(int index, ReadSideEffectInstruction read |
input.isParameterDerefOrQualifierObject(index) and
read = call.getAParameterSideEffect(index) and
operand = read.getSideEffectOperand()
)
)
}
/**
* Holds if `instr` serves as a return value or output argument indirection for `call`, in the
* position specified by `output`.
*/
private predicate isCallOutput(
CallInstruction call, Instruction instr, AliasModels::FunctionOutput output
) {
call = getPrimaryCall(instr) and
(
output.isReturnValue() and instr = call
or
exists(int index, WriteSideEffectInstruction write |
output.isParameterDerefOrQualifierObject(index) and
write = call.getAParameterSideEffect(index) and
instr = write
)
)
}
/**
* Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled
* address flow through a function call.
*/
private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) {
exists(
CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output
|
call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and
isCallInput(call, operand, input) and
isCallOutput(call, resultInstr, output)
)
}
/**
* Holds if the operand `tag` of instruction `instr` is used in a way that does
* not result in any address held in that operand from escaping beyond the
@@ -34,7 +99,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
private predicate operandEscapesDomain(Operand operand) {
not operandIsConsumedWithoutEscaping(operand) and
not operandIsPropagated(operand, _) and
not operandIsPropagated(operand, _, _) and
not isArgumentForParameter(_, operand, _) and
not isOnlyEscapesViaReturnArgument(operand) and
not operand.getUse() instanceof ReturnValueInstruction and
@@ -69,67 +134,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
}
/**
* Holds if any address held in operand `tag` of instruction `instr` is
* propagated to the result of `instr`, offset by the number of bits in
* `bitOffset`. If the address is propagated, but the offset is not known to be
* a constant, then `bitOffset` is unknown.
* Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by
* the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to
* be a constant, then `bitOffset` is `unknown()`.
*/
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
exists(Instruction instr |
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)
or
// Conversion using dynamic_cast results in an unknown offset
instr instanceof CheckedConvertOrNullInstruction and
bitOffset = Ints::unknown()
or
// Converting to a derived class subtracts the offset of the base class.
exists(ConvertToDerivedInstruction convert |
convert = instr and
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
)
or
// Converting to a virtual base class adds an unknown offset.
instr instanceof ConvertToVirtualBaseInstruction and
bitOffset = Ints::unknown()
or
// Conversion to another pointer type propagates the source address.
exists(ConvertInstruction convert, IRType resultType |
convert = instr and
resultType = convert.getResultIRType() and
resultType instanceof IRAddressType and
bitOffset = 0
)
or
// Adding an integer to or subtracting an integer from a pointer propagates
// the address with an offset.
exists(PointerOffsetInstruction ptrOffset |
ptrOffset = instr and
operand = ptrOffset.getLeftOperand() and
bitOffset = getPointerBitOffset(ptrOffset)
)
or
// Computing a field address from a pointer propagates the address plus the
// offset of the field.
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
or
// A copy propagates the source value.
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
or
// Some functions are known to propagate an argument
isAlwaysReturnedArgument(operand) and bitOffset = 0
private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) {
// Some functions are known to propagate an argument
hasAddressFlowThroughCall(operand, instr) and
bitOffset = 0
or
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)
or
// Conversion using dynamic_cast results in an unknown offset
instr instanceof CheckedConvertOrNullInstruction and
bitOffset = Ints::unknown()
or
// Converting to a derived class subtracts the offset of the base class.
exists(ConvertToDerivedInstruction convert |
convert = instr and
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
)
or
// Converting to a virtual base class adds an unknown offset.
instr instanceof ConvertToVirtualBaseInstruction and
bitOffset = Ints::unknown()
or
// Conversion to another pointer type propagates the source address.
exists(ConvertInstruction convert, IRType resultType |
convert = instr and
resultType = convert.getResultIRType() and
resultType instanceof IRAddressType and
bitOffset = 0
)
or
// Adding an integer to or subtracting an integer from a pointer propagates
// the address with an offset.
exists(PointerOffsetInstruction ptrOffset |
ptrOffset = instr and
operand = ptrOffset.getLeftOperand() and
bitOffset = getPointerBitOffset(ptrOffset)
)
or
// Computing a field address from a pointer propagates the address plus the
// offset of the field.
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
or
// A copy propagates the source value.
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
)
}
private predicate operandEscapesNonReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse())
exists(Instruction instr |
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr)
)
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -151,9 +216,11 @@ private predicate operandEscapesNonReturn(Operand operand) {
}
private predicate operandMayReachReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and
resultMayReachReturn(operand.getUse())
exists(Instruction instr |
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _, instr) and
resultMayReachReturn(instr)
)
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -173,9 +240,9 @@ private predicate operandMayReachReturn(Operand operand) {
private predicate operandReturned(Operand operand, IntValue bitOffset) {
// The address is propagated to the result of the instruction, and that result itself is returned
exists(IntValue bitOffset1, IntValue bitOffset2 |
operandIsPropagated(operand, bitOffset1) and
resultReturned(operand.getUse(), bitOffset2) and
exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 |
operandIsPropagated(operand, bitOffset1, instr) and
resultReturned(instr, bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
@@ -214,13 +281,6 @@ private predicate isArgumentForParameter(
)
}
private predicate isAlwaysReturnedArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
@@ -270,12 +330,15 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
/**
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
*/
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
operandIsPropagated(operand, bitOffset)
private predicate operandIsPropagatedIncludingByCall(
Operand operand, IntValue bitOffset, Instruction instr
) {
operandIsPropagated(operand, bitOffset, instr)
or
exists(CallInstruction call, Instruction init |
isArgumentForParameter(call, operand, init) and
resultReturned(init, bitOffset)
resultReturned(init, bitOffset) and
instr = call
)
}
@@ -292,8 +355,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base,
// We already have an offset from `middle`.
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
// `middle` is propagated from `base`.
middleOperand = middle.getAnOperand() and
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and
base = middleOperand.getDef() and
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
)

View File

@@ -1,12 +1,14 @@
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.PointerWrapper
/**
* The `std::shared_ptr` and `std::unique_ptr` template classes.
* The `std::shared_ptr`, `std::weak_ptr`, and `std::unique_ptr` template classes.
*/
private class UniqueOrSharedPtr extends Class, PointerWrapper {
UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) }
private class SmartPtr extends Class, PointerWrapper {
SmartPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "weak_ptr", "unique_ptr"]) }
override MemberFunction getAnUnwrapperFunction() {
result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this
@@ -17,11 +19,19 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
override predicate pointsToConst() { this.getTemplateArgument(0).(Type).isConst() }
}
/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */
private class PointerWrapperFlow extends TaintFunction, DataFlowFunction {
PointerWrapperFlow() {
this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
not this.getUnspecifiedType() instanceof ReferenceType
/**
* Any function that returns the address wrapped by a `PointerWrapper`, whether as a pointer or a
* reference.
*
* Examples:
* - `std::unique_ptr<T>::get()`
* - `std::shared_ptr<T>::operator->()`
* - `std::weak_ptr<T>::operator*()`
*/
private class PointerUnwrapperFunction extends MemberFunction, TaintFunction, DataFlowFunction,
SideEffectFunction, AliasFunction {
PointerUnwrapperFunction() {
exists(PointerWrapper wrapper | wrapper.getAnUnwrapperFunction() = this)
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -32,6 +42,23 @@ private class PointerWrapperFlow extends TaintFunction, DataFlowFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and output.isReturnValue()
}
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
// Only reads from `*this`.
i = -1 and buffer = false
}
override predicate parameterNeverEscapes(int index) { index = -1 }
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and output.isReturnValue()
}
}
/**
@@ -62,31 +89,80 @@ private class MakeUniqueOrShared extends TaintFunction {
}
/**
* A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type.
* A function that sets the value of a smart pointer.
*
* This could be a constructor, an assignment operator, or a named member function like `reset()`.
*/
private class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction {
UniqueOrSharedDereferenceMemberOperator() {
this.hasName("operator*") and
this.getDeclaringType() instanceof UniqueOrSharedPtr
private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, SideEffectFunction {
SmartPtrSetterFunction() {
this.getDeclaringType() instanceof SmartPtr and
not this.isStatic() and
(
this instanceof Constructor
or
this.hasName("operator=")
or
this.hasName("reset")
)
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
override predicate hasOnlySpecificReadSideEffects() { none() }
/**
* The `std::shared_ptr` or `std::unique_ptr` function `get`.
*/
private class UniqueOrSharedGet extends TaintFunction {
UniqueOrSharedGet() {
this.hasName("get") and
this.getDeclaringType() instanceof UniqueOrSharedPtr
override predicate hasOnlySpecificWriteSideEffects() { none() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
// Always write to the destination smart pointer itself.
i = -1 and buffer = false and mustWrite = true
or
// When taking ownership of a smart pointer via an rvalue reference, always overwrite the input
// smart pointer.
getPointerInput().isParameterDeref(i) and
this.getParameter(i).getUnspecifiedType() instanceof RValueReferenceType and
buffer = false and
mustWrite = true
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
getPointerInput().isParameterDeref(i) and
buffer = false
or
not this instanceof Constructor and
i = -1 and
buffer = false
}
override predicate parameterNeverEscapes(int index) { index = -1 }
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) {
input = getPointerInput() and
output.isQualifierObject()
or
// Assignment operator always returns a reference to `*this`.
this.hasName("operator=") and
input.isQualifierAddress() and
output.isReturnValue()
}
private FunctionInput getPointerInput() {
exists(Parameter param0 |
param0 = this.getParameter(0) and
(
param0.getUnspecifiedType().(ReferenceType).getBaseType() instanceof SmartPtr and
if this.getParameter(1).getUnspecifiedType() instanceof PointerType
then
// This is one of the constructors of `std::shared_ptr<T>` that creates a smart pointer that
// wraps a raw pointer with ownership controlled by an unrelated smart pointer. We propagate
// the raw pointer in the second parameter, rather than the smart pointer in the first
// parameter.
result.isParameter(1)
else result.isParameterDeref(0)
)
or
// One of the functions that takes ownership of a raw pointer.
param0.getUnspecifiedType() instanceof PointerType and
result.isParameter(0)
)
}
}

View File

@@ -50,5 +50,16 @@ abstract class AliasFunction extends Function {
/**
* Holds if the function always returns the value of the parameter at the specified index.
*/
abstract predicate parameterIsAlwaysReturned(int index);
predicate parameterIsAlwaysReturned(int index) { none() }
/**
* Holds if the address passed in via `input` is always propagated to `output`.
*/
predicate hasAddressFlow(FunctionInput input, FunctionOutput output) {
exists(int index |
// By default, just use the old `parameterIsAlwaysReturned` predicate to detect flow from the
// parameter to the return value.
input.isParameter(index) and output.isReturnValue() and this.parameterIsAlwaysReturned(index)
)
}
}

View File

@@ -0,0 +1,139 @@
#if !defined(CODEQL_MEMORY_H)
#define CODEQL_MEMORY_H
namespace std {
namespace detail {
template<typename T>
class compressed_pair_element {
T element;
public:
compressed_pair_element() = default;
compressed_pair_element(const T& t) : element(t) {}
T& get() { return element; }
const T& get() const { return element; }
};
template<typename T, typename U>
struct compressed_pair : private compressed_pair_element<T>, private compressed_pair_element<U> {
compressed_pair() = default;
compressed_pair(T& t) : compressed_pair_element<T>(t), compressed_pair_element<U>() {}
compressed_pair(const compressed_pair&) = delete;
compressed_pair(compressed_pair<T, U>&&) noexcept = default;
T& first() { return static_cast<compressed_pair_element<T>&>(*this).get(); }
U& second() { return static_cast<compressed_pair_element<U>&>(*this).get(); }
const T& first() const { return static_cast<const compressed_pair_element<T>&>(*this).get(); }
const U& second() const { return static_cast<const compressed_pair_element<U>&>(*this).get(); }
};
}
template<class T>
struct default_delete {
void operator()(T* ptr) const { delete ptr; }
};
template<class T>
struct default_delete<T[]> {
template<class U>
void operator()(U* ptr) const { delete[] ptr; }
};
template<class T, class Deleter = default_delete<T> >
class unique_ptr {
private:
detail::compressed_pair<T*, Deleter> data;
public:
constexpr unique_ptr() noexcept {}
explicit unique_ptr(T* ptr) noexcept : data(ptr) {}
unique_ptr(const unique_ptr& ptr) = delete;
unique_ptr(unique_ptr&& ptr) noexcept = default;
unique_ptr& operator=(unique_ptr&& ptr) noexcept = default;
T& operator*() const { return *get(); }
T* operator->() const noexcept { return get(); }
T* get() const noexcept { return data.first(); }
T* release() noexcept {
Deleter& d = data.second();
d(data.first());
data.first() = nullptr;
}
~unique_ptr() {
Deleter& d = data.second();
d(data.first());
}
};
template<typename T, class... Args> unique_ptr<T> make_unique(Args&&... args) {
return unique_ptr<T>(new T(args...)); // std::forward calls elided for simplicity.
}
class ctrl_block {
unsigned uses;
public:
ctrl_block() : uses(1) {}
void inc() { ++uses; }
bool dec() { return --uses == 0; }
virtual void destroy() = 0;
virtual ~ctrl_block() {}
};
template<typename T, class Deleter = default_delete<T> >
struct ctrl_block_impl: public ctrl_block {
T* ptr;
Deleter d;
ctrl_block_impl(T* ptr, Deleter d) : ptr(ptr), d(d) {}
virtual void destroy() override { d(ptr); }
};
template<class T>
class shared_ptr {
private:
ctrl_block* ctrl;
T* ptr;
void dec() {
if(ctrl->dec()) {
ctrl->destroy();
delete ctrl;
}
}
void inc() {
ctrl->inc();
}
public:
constexpr shared_ptr() noexcept = default;
shared_ptr(T* ptr) : ctrl(new ctrl_block_impl<T>(ptr, default_delete<T>())) {}
shared_ptr(const shared_ptr& s) noexcept : ptr(s.ptr), ctrl(s.ctrl) {
inc();
}
shared_ptr(shared_ptr&& s) noexcept = default;
shared_ptr(unique_ptr<T>&& s) : shared_ptr(s.release()) {
}
T* operator->() const { return ptr; }
T& operator*() const { return *ptr; }
T* get() const noexcept { return ptr; }
~shared_ptr() { dec(); }
};
template<typename T, class... Args> shared_ptr<T> make_shared(Args&&... args) {
return shared_ptr<T>(new T(args...)); // std::forward calls elided for simplicity.
}
}
#endif

View File

@@ -0,0 +1,21 @@
#if !defined(CODEQL_TYPE_TRAITS_H)
#define CODEQL_TYPE_TRAITS_H
namespace std {
template<typename T>
struct remove_reference {
typedef T type;
};
template<typename T>
struct remove_reference<T&> {
typedef T type;
};
template<typename T>
struct remove_reference<T&&> {
typedef T type;
};
}
#endif

View File

@@ -0,0 +1,13 @@
#if !defined(CODEQL_UTILITY_H)
#define CODEQL_UTILITY_H
#include "type_traits.h"
namespace std {
template<typename T>
typename remove_reference<T>::type&& move(T&& src) {
return static_cast<typename remove_reference<T>::type&&>(src);
}
}
#endif

View File

@@ -7,7 +7,7 @@ void test_unique_ptr_int() {
std::unique_ptr<int> p1(new int(source()));
std::unique_ptr<int> p2 = std::make_unique<int>(source());
sink(*p1); // $ MISSING: ast,ir
sink(*p1); // $ ir MISSING: ast
sink(*p2); // $ ast ir=8:50
}
@@ -21,7 +21,7 @@ void test_unique_ptr_struct() {
std::unique_ptr<A> p1(new A{source(), 0});
std::unique_ptr<A> p2 = std::make_unique<A>(source(), 0);
sink(p1->x); // $ MISSING: ast,ir
sink(p1->x); // $ ir MISSING: ast
sink(p1->y);
sink(p2->x); // $ MISSING: ast,ir
sink(p2->y);
@@ -31,7 +31,7 @@ void test_shared_ptr_int() {
std::shared_ptr<int> p1(new int(source()));
std::shared_ptr<int> p2 = std::make_shared<int>(source());
sink(*p1); // $ ast
sink(*p1); // $ ast ir
sink(*p2); // $ ast ir=32:50
}
@@ -39,7 +39,7 @@ void test_shared_ptr_struct() {
std::shared_ptr<A> p1(new A{source(), 0});
std::shared_ptr<A> p2 = std::make_shared<A>(source(), 0);
sink(p1->x); // $ MISSING: ast,ir
sink(p1->x); // $ ir MISSING: ast
sink(p1->y);
sink(p2->x); // $ MISSING: ast,ir
sink(p2->y);

View File

@@ -3228,6 +3228,7 @@
| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:18:11:18:11 | p | |
| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:19:10:19:10 | p | |
| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | p [inner post update] | |
| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | ref arg p | TAINT |
| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:19:10:19:10 | p | |
| smart_pointer.cpp:18:11:18:11 | p | smart_pointer.cpp:18:10:18:10 | call to operator* | TAINT |
| smart_pointer.cpp:18:11:18:11 | ref arg p | smart_pointer.cpp:18:11:18:11 | p [inner post update] | |
@@ -3240,6 +3241,7 @@
| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:30:11:30:11 | p | |
| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:31:10:31:10 | p | |
| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | p [inner post update] | |
| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | ref arg p | TAINT |
| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:31:10:31:10 | p | |
| smart_pointer.cpp:30:11:30:11 | p | smart_pointer.cpp:30:10:30:10 | call to operator* | TAINT |
| smart_pointer.cpp:30:11:30:11 | ref arg p | smart_pointer.cpp:30:11:30:11 | p [inner post update] | |
@@ -3248,6 +3250,7 @@
| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:38:10:38:10 | p | |
| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:39:11:39:11 | p | |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | p [inner post update] | |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | ref arg p | TAINT |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:38:10:38:10 | p | |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:39:11:39:11 | p | |
| smart_pointer.cpp:37:5:37:17 | ... = ... | smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | |
@@ -3262,6 +3265,7 @@
| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:46:10:46:10 | p | |
| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:47:11:47:11 | p | |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | p [inner post update] | |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | ref arg p | TAINT |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:46:10:46:10 | p | |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:47:11:47:11 | p | |
| smart_pointer.cpp:45:5:45:17 | ... = ... | smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | |
@@ -3291,6 +3295,7 @@
| smart_pointer.cpp:70:37:70:39 | ptr | smart_pointer.cpp:71:4:71:6 | ptr | |
| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:70:37:70:39 | ptr | |
| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ptr [inner post update] | |
| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ref arg ptr | TAINT |
| smart_pointer.cpp:71:3:71:17 | ... = ... | smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | |
| smart_pointer.cpp:71:4:71:6 | ptr | smart_pointer.cpp:71:3:71:3 | call to operator* | |
| smart_pointer.cpp:71:4:71:6 | ref arg ptr | smart_pointer.cpp:70:37:70:39 | ptr | |
@@ -3371,6 +3376,7 @@
| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:121:4:121:6 | ptr | |
| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:120:48:120:50 | ptr | |
| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ptr [inner post update] | |
| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ref arg ptr | TAINT |
| smart_pointer.cpp:121:3:121:17 | ... = ... | smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | |
| smart_pointer.cpp:121:4:121:6 | ptr | smart_pointer.cpp:121:3:121:3 | call to operator* | |
| smart_pointer.cpp:121:4:121:6 | ref arg ptr | smart_pointer.cpp:120:48:120:50 | ptr | |
@@ -3394,9 +3400,11 @@
| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT |
| smart_pointer.cpp:126:12:126:12 | q | smart_pointer.cpp:126:13:126:13 | call to operator-> | |
| smart_pointer.cpp:128:13:128:13 | call to operator* | smart_pointer.cpp:128:13:128:15 | call to shared_ptr | TAINT |
| smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | smart_pointer.cpp:128:14:128:15 | ref arg p2 | TAINT |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | ref arg p2 | TAINT |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 | |
| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:124:90:124:91 | p2 | |
| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | |
@@ -3409,6 +3417,7 @@
| smart_pointer.cpp:129:9:129:9 | call to operator* | smart_pointer.cpp:129:8:129:8 | call to operator* | TAINT |
| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | |
| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 [inner post update] | |
| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | ref arg p2 | TAINT |
| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:8:129:8 | call to operator* | |
| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:9:129:9 | call to operator* | TAINT |
| smart_pointer.cpp:129:10:129:11 | ref arg p2 | smart_pointer.cpp:124:90:124:91 | p2 | |
@@ -3429,6 +3438,7 @@
| smart_pointer.cpp:134:12:134:12 | q | smart_pointer.cpp:134:13:134:13 | call to operator-> | |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | ref arg p2 | TAINT |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 | |
| smart_pointer.cpp:136:18:136:19 | p2 | smart_pointer.cpp:136:17:136:17 | call to operator* | TAINT |
| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | |
@@ -3437,6 +3447,7 @@
| smart_pointer.cpp:137:9:137:9 | call to operator* | smart_pointer.cpp:137:8:137:8 | call to operator* | TAINT |
| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | |
| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | |
| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | ref arg p2 | TAINT |
| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:8:137:8 | call to operator* | |
| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:9:137:9 | call to operator* | TAINT |
| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | |
@@ -3532,13 +3543,13 @@
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:120:2:120:3 | it | |
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:117:7:117:8 | it [post update] | standalone_iterators.cpp:122:7:122:8 | c1 | |
| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | |
| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | TAINT |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:119:7:119:8 | it | |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:120:2:120:3 | it | |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:122:7:122:8 | c1 | |
| standalone_iterators.cpp:118:8:118:8 | 1 | standalone_iterators.cpp:118:2:118:3 | ref arg it | TAINT |
| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | |
| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | TAINT |
| standalone_iterators.cpp:120:2:120:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:120:8:120:13 | call to source | standalone_iterators.cpp:120:2:120:3 | ref arg it | TAINT |
| stl.h:75:8:75:8 | Unknown literal | stl.h:75:8:75:8 | constructor init of field container | TAINT |
@@ -3655,12 +3666,12 @@
| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | |
| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | |
| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT |
@@ -3673,12 +3684,12 @@
| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| string.cpp:25:12:25:17 | call to source | string.cpp:29:7:29:7 | a | |
| string.cpp:26:16:26:20 | 123 | string.cpp:26:16:26:21 | call to basic_string | TAINT |
| string.cpp:26:16:26:21 | call to basic_string | string.cpp:30:7:30:7 | b | |
@@ -4231,13 +4242,13 @@
| string.cpp:407:9:407:10 | i6 | string.cpp:407:8:407:8 | call to operator* | TAINT |
| string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | |
| string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i7 | |
| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | |
| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | TAINT |
| string.cpp:409:12:409:12 | call to operator+= | string.cpp:409:8:409:8 | call to operator* | TAINT |
| string.cpp:409:12:409:12 | ref arg call to operator+= | string.cpp:409:10:409:11 | ref arg i7 | TAINT |
| string.cpp:409:14:409:14 | 1 | string.cpp:409:10:409:11 | ref arg i7 | TAINT |
| string.cpp:410:8:410:9 | i2 | string.cpp:410:3:410:9 | ... = ... | |
| string.cpp:410:8:410:9 | i2 | string.cpp:411:10:411:11 | i8 | |
| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | |
| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | TAINT |
| string.cpp:411:12:411:12 | call to operator-= | string.cpp:411:8:411:8 | call to operator* | TAINT |
| string.cpp:411:12:411:12 | ref arg call to operator-= | string.cpp:411:10:411:11 | ref arg i8 | TAINT |
| string.cpp:411:14:411:14 | 1 | string.cpp:411:10:411:11 | ref arg i8 | TAINT |
@@ -4652,7 +4663,7 @@
| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:39:7:39:9 | ss3 | |
| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:44:7:44:9 | ss3 | |
| stringstream.cpp:33:7:33:9 | ss3 | stringstream.cpp:33:11:33:11 | call to operator<< | |
| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | |
| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | TAINT |
| stringstream.cpp:33:11:33:11 | ref arg call to operator<< | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT |
| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT |
| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:11:33:11 | call to operator<< | TAINT |
@@ -4661,7 +4672,7 @@
| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:40:7:40:9 | ss4 | |
| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:45:7:45:9 | ss4 | |
| stringstream.cpp:34:7:34:9 | ss4 | stringstream.cpp:34:11:34:11 | call to operator<< | |
| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | |
| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | TAINT |
| stringstream.cpp:34:11:34:11 | ref arg call to operator<< | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT |
| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT |
| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:11:34:11 | call to operator<< | TAINT |
@@ -4942,7 +4953,7 @@
| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:11:147:11 | call to operator>> | |
| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:14:147:15 | ref arg s3 | TAINT |
| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | |
| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | TAINT |
| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:20:147:21 | ref arg s4 | TAINT |
| stringstream.cpp:147:11:147:11 | ref arg call to operator>> | stringstream.cpp:147:7:147:9 | ref arg ss2 | TAINT |
| stringstream.cpp:147:14:147:15 | ref arg s3 | stringstream.cpp:150:7:150:8 | s3 | |
@@ -4974,7 +4985,7 @@
| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:11:155:11 | call to operator>> | |
| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:14:155:15 | ref arg b3 | TAINT |
| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | |
| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | TAINT |
| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:20:155:21 | ref arg b4 | TAINT |
| stringstream.cpp:155:11:155:11 | ref arg call to operator>> | stringstream.cpp:155:7:155:9 | ref arg ss2 | TAINT |
| stringstream.cpp:155:14:155:15 | ref arg b3 | stringstream.cpp:158:7:158:8 | b3 | |
@@ -5296,7 +5307,7 @@
| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:7:245:13 | call to getline | |
| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:20:245:21 | ref arg s6 | TAINT |
| stringstream.cpp:245:20:245:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | |
| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | |
| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | TAINT |
| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:33:250:34 | ref arg s8 | TAINT |
| stringstream.cpp:250:15:250:21 | ref arg call to getline | stringstream.cpp:250:23:250:25 | ref arg ss2 | TAINT |
| stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:15:250:21 | call to getline | |
@@ -5500,7 +5511,7 @@
| swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:103:10:103:10 | x | |
| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:5:100:5 | ref arg y | TAINT |
| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:7:100:7 | call to operator= | TAINT |
| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | |
| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | TAINT |
| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:109:5:109:13 | move_from | |
| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:111:10:111:18 | move_from | |
| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:113:41:113:49 | move_from | |
@@ -5513,7 +5524,7 @@
| swap1.cpp:113:31:113:39 | call to move | swap1.cpp:113:31:113:51 | call to Class | TAINT |
| swap1.cpp:113:31:113:39 | ref arg call to move | swap1.cpp:113:41:113:49 | move_from [inner post update] | |
| swap1.cpp:113:31:113:51 | call to Class | swap1.cpp:115:10:115:16 | move_to | |
| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | |
| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | TAINT |
| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:51 | call to Class | |
| swap1.cpp:120:23:120:23 | x | swap1.cpp:122:5:122:5 | x | |
| swap1.cpp:120:23:120:23 | x | swap1.cpp:124:10:124:10 | x | |
@@ -5547,7 +5558,7 @@
| swap1.cpp:142:5:142:5 | ref arg y | swap1.cpp:144:10:144:10 | y | |
| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:142:29:142:29 | x [inner post update] | |
| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:145:10:145:10 | x | |
| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | |
| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | TAINT |
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
@@ -5679,7 +5690,7 @@
| swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:103:10:103:10 | x | |
| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:5:100:5 | ref arg y | TAINT |
| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:7:100:7 | call to operator= | TAINT |
| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | |
| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | TAINT |
| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:109:5:109:13 | move_from | |
| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:111:10:111:18 | move_from | |
| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:113:41:113:49 | move_from | |
@@ -5692,7 +5703,7 @@
| swap2.cpp:113:31:113:39 | call to move | swap2.cpp:113:31:113:51 | call to Class | TAINT |
| swap2.cpp:113:31:113:39 | ref arg call to move | swap2.cpp:113:41:113:49 | move_from [inner post update] | |
| swap2.cpp:113:31:113:51 | call to Class | swap2.cpp:115:10:115:16 | move_to | |
| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | |
| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | TAINT |
| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:51 | call to Class | |
| swap2.cpp:120:23:120:23 | x | swap2.cpp:122:5:122:5 | x | |
| swap2.cpp:120:23:120:23 | x | swap2.cpp:124:10:124:10 | x | |
@@ -5726,7 +5737,7 @@
| swap2.cpp:142:5:142:5 | ref arg y | swap2.cpp:144:10:144:10 | y | |
| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:142:29:142:29 | x [inner post update] | |
| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:145:10:145:10 | x | |
| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | |
| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | TAINT |
| taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | |
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | |
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | |
@@ -7950,7 +7961,7 @@
| vector.cpp:527:9:527:10 | ref arg it | vector.cpp:529:9:529:10 | it | |
| vector.cpp:527:9:527:10 | ref arg it | vector.cpp:530:3:530:4 | it | |
| vector.cpp:527:9:527:10 | ref arg it | vector.cpp:531:9:531:10 | it | |
| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | |
| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | TAINT |
| vector.cpp:528:3:528:4 | ref arg it | vector.cpp:529:9:529:10 | it | |
| vector.cpp:528:3:528:4 | ref arg it | vector.cpp:530:3:530:4 | it | |
| vector.cpp:528:3:528:4 | ref arg it | vector.cpp:531:9:531:10 | it | |
@@ -7958,7 +7969,7 @@
| vector.cpp:529:9:529:10 | it | vector.cpp:529:8:529:8 | call to operator* | TAINT |
| vector.cpp:529:9:529:10 | ref arg it | vector.cpp:530:3:530:4 | it | |
| vector.cpp:529:9:529:10 | ref arg it | vector.cpp:531:9:531:10 | it | |
| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | |
| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | TAINT |
| vector.cpp:530:3:530:4 | ref arg it | vector.cpp:531:9:531:10 | it | |
| vector.cpp:530:9:530:14 | call to source | vector.cpp:530:3:530:4 | ref arg it | TAINT |
| vector.cpp:531:9:531:10 | it | vector.cpp:531:8:531:8 | call to operator* | TAINT |

View File

@@ -9,7 +9,7 @@ template<typename T> void sink(std::unique_ptr<T>&);
void test_make_shared() {
std::shared_ptr<int> p = std::make_shared<int>(source());
sink(*p); // $ ast MISSING: ir
sink(*p); // $ ast,ir
sink(p); // $ ast,ir
}
@@ -21,7 +21,7 @@ void test_make_shared_array() {
void test_make_unique() {
std::unique_ptr<int> p = std::make_unique<int>(source());
sink(*p); // $ ast MISSING: ir
sink(*p); // $ ast,ir
sink(p); // $ ast,ir
}
@@ -101,7 +101,7 @@ void taint_x(A* pa) {
void reverse_taint_smart_pointer() {
std::unique_ptr<A> p = std::unique_ptr<A>(new A);
taint_x(p.get());
sink(p->x); // $ ast MISSING: ir
sink(p->x); // $ ast,ir
}
struct C {

View File

@@ -5,7 +5,16 @@ private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
private predicate ignoreAllocation(string name) {
name = "i" or
name = "p" or
name = "q"
name = "q" or
name = "s" or
name = "t" or
name = "?{AllAliased}"
}
private predicate ignoreFile(File file) {
// Ignore standard headers.
file.getBaseName() = ["memory.h", "type_traits.h", "utility.h"] or
not file.fromSource()
}
module Raw {
@@ -29,7 +38,8 @@ module Raw {
not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and
value = memLocation.toString() and
element = instr.toString() and
location = instr.getLocation()
location = instr.getLocation() and
not ignoreFile(location.getFile())
)
}
}
@@ -52,13 +62,14 @@ module UnaliasedSSA {
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Instruction instr, MemoryLocation memLocation |
memLocation = getAMemoryAccess(instr) and
not memLocation instanceof AliasedVirtualVariable and
not memLocation.getVirtualVariable() instanceof AliasedVirtualVariable and
not memLocation instanceof AllNonLocalMemory and
tag = "ussa" and
not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and
value = memLocation.toString() and
element = instr.toString() and
location = instr.getLocation()
location = instr.getLocation() and
not ignoreFile(location.getFile())
)
}
}

View File

@@ -0,0 +1,33 @@
#include "../../../include/memory.h"
#include "../../../include/utility.h"
using std::shared_ptr;
using std::unique_ptr;
struct S {
int x;
};
void unique_ptr_init(S s) {
unique_ptr<S> p(new S); //$ussa=dynamic{1}
int i = (*p).x; //$ussa=dynamic{1}[0..4)<int>
*p = s; //$ussa=dynamic{1}[0..4)<S>
unique_ptr<S> q = std::move(p);
*(q.get()) = s; //$ussa=dynamic{1}[0..4)<S>
shared_ptr<S> t(std::move(q));
t->x = 5; //$ussa=dynamic{1}[0..4)<int>
*t = s; //$ussa=dynamic{1}[0..4)<S>
*(t.get()) = s; //$ussa=dynamic{1}[0..4)<S>
}
void shared_ptr_init(S s) {
shared_ptr<S> p(new S); //$ussa=dynamic{1}
int i = (*p).x; //$ussa=dynamic{1}[0..4)<int>
*p = s; //$ussa=dynamic{1}[0..4)<S>
shared_ptr<S> q = std::move(p);
*(q.get()) = s; //$ussa=dynamic{1}[0..4)<S>
shared_ptr<S> t(q);
t->x = 5; //$ussa=dynamic{1}[0..4)<int>
*t = s; //$ussa=dynamic{1}[0..4)<S>
*(t.get()) = s; //$ussa=dynamic{1}[0..4)<S>
}