Merge branch 'main' into mathiasvp/replace-ast-with-ir-use-usedataflow

This commit is contained in:
Mathias Vorreiter Pedersen
2023-01-31 14:12:23 +00:00
40 changed files with 9204 additions and 176 deletions

View File

@@ -0,0 +1,263 @@
private import cpp
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
float evaluateConstantExpr(Expr e) {
result = e.getValue().toFloat()
or
// This handles when a constant value is put into a variable
// and the variable is used later
exists(SsaDefinition defn, StackVariable sv |
defn.getAUse(sv) = e and
result = defn.getDefiningValue(sv).getValue().toFloat()
)
}
// If the constant right operand is negative or is greater than or equal to the number of
// bits in the left operands type, then the result is undefined (except on the IA-32
// architecture where the shift value is masked with 0b00011111, but we can't
// assume the architecture).
bindingset[val]
private predicate isValidShiftExprShift(float val, Expr l) {
val >= 0 and
// We use getFullyConverted because the spec says to use the *promoted* left operand
val < (l.getFullyConverted().getUnderlyingType().getSize() * 8)
}
bindingset[val, shift, max_val]
private predicate canLShiftOverflow(int val, int shift, int max_val) {
// val << shift = val * 2^shift > max_val => val > max_val/2^shift = max_val >> b
val > max_val.bitShiftRight(shift)
}
/**
* A range analysis expression consisting of the `>>` or `>>=` operator when at least
* one operand is a constant (and if the right operand is a constant, it must be "valid"
* (see `isValidShiftExprShift`)). When handling any undefined behavior, it leaves the
* values unconstrained. From the C++ standard: "The behavior is undefined if the right
* operand is negative, or greater than or equal to the length in bits of the promoted
* left operand. The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an
* unsigned type or if E1 has a signed type and a non-negative value, the value of the
* result is the integral part of the quotient of E1/2^E2. If E1 has a signed type and a
* negative value, the resulting value is implementation-defined."
*/
class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
/**
* Holds for `a >> b` or `a >>= b` in one of the following two cases:
* 1. `a` is a constant and `b` is not
* 2. `b` is constant
*
* We don't handle the case where `a` and `b` are both non-constant values.
*/
ConstantRShiftExprRange() {
getUnspecifiedType() instanceof IntegralType and
exists(Expr l, Expr r |
l = this.(RShiftExpr).getLeftOperand() and
r = this.(RShiftExpr).getRightOperand()
or
l = this.(AssignRShiftExpr).getLValue() and
r = this.(AssignRShiftExpr).getRValue()
|
l.getUnspecifiedType() instanceof IntegralType and
r.getUnspecifiedType() instanceof IntegralType and
(
// If the left operand is a constant, verify that the right operand is not a constant
exists(evaluateConstantExpr(l)) and not exists(evaluateConstantExpr(r))
or
// If the right operand is a constant, check if it is a valid shift expression
exists(float constROp |
constROp = evaluateConstantExpr(r) and isValidShiftExprShift(constROp, l)
)
)
)
}
Expr getLeftOperand() {
result = this.(RShiftExpr).getLeftOperand() or
result = this.(AssignRShiftExpr).getLValue()
}
Expr getRightOperand() {
result = this.(RShiftExpr).getRightOperand() or
result = this.(AssignRShiftExpr).getRValue()
}
override float getLowerBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
if
lLower < 0
or
not (
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
// and a negative shift is implementation defined, so we set the result
// to the minimum value
result = exprMinVal(this)
else
// We can get the smallest value by shifting the smallest bound by the largest bound
result = lLower.bitShiftRight(rUpper)
)
}
override float getUpperBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
if
lLower < 0
or
not (
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
// and a negative shift is implementation defined, so we set the result
// to the maximum value
result = exprMaxVal(this)
else
// We can get the largest value by shifting the largest bound by the smallest bound
result = lUpper.bitShiftRight(rLower)
)
}
override predicate dependsOnChild(Expr child) {
child = getLeftOperand() or child = getRightOperand()
}
}
/**
* A range analysis expression consisting of the `<<` or `<<=` operator when at least
* one operand is a constant (and if the right operand is a constant, it must be "valid"
* (see `isValidShiftExprShift`)). When handling any undefined behavior, it leaves the
* values unconstrained. From the C++ standard: "The behavior is undefined if the right
* operand is negative, or greater than or equal to the length in bits of the promoted left operand.
* The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1
* has an unsigned type, the value of the result is E1 x 2 E2, reduced modulo one more than the
* maximum value representable in the result type. Otherwise, if E1 has a signed type and
* non-negative value, and E1 x 2 E2 is representable in the corresponding unsigned type of the
* result type, then that value, converted to the result type, is the resulting value; otherwise,
* the behavior is undefined."
*/
class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
/**
* Holds for `a << b` or `a <<= b` in one of the following two cases:
* 1. `a` is a constant and `b` is not
* 2. `b` is constant
*
* We don't handle the case where `a` and `b` are both non-constant values.
*/
ConstantLShiftExprRange() {
getUnspecifiedType() instanceof IntegralType and
exists(Expr l, Expr r |
l = this.(LShiftExpr).getLeftOperand() and
r = this.(LShiftExpr).getRightOperand()
or
l = this.(AssignLShiftExpr).getLValue() and
r = this.(AssignLShiftExpr).getRValue()
|
l.getUnspecifiedType() instanceof IntegralType and
r.getUnspecifiedType() instanceof IntegralType and
(
// If the left operand is a constant, verify that the right operand is not a constant
exists(evaluateConstantExpr(l)) and not exists(evaluateConstantExpr(r))
or
// If the right operand is a constant, check if it is a valid shift expression
exists(float constROp |
constROp = evaluateConstantExpr(r) and isValidShiftExprShift(constROp, l)
)
)
)
}
Expr getLeftOperand() {
result = this.(LShiftExpr).getLeftOperand() or
result = this.(AssignLShiftExpr).getLValue()
}
Expr getRightOperand() {
result = this.(LShiftExpr).getRightOperand() or
result = this.(AssignLShiftExpr).getRValue()
}
override float getLowerBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
if
lLower < 0
or
not (
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
// and a negative shift is undefined, so we set to the minimum value
result = exprMinVal(this)
else
// If we have `0b01010000 << [0, 2]`, the max value for 8 bits is 0b10100000
// (a shift of 1) but doing a shift by the upper bound would give 0b01000000.
// So if the left shift operation causes an overflow, we just assume the max value
// If necessary, we may be able to improve this bound in the future
if canLShiftOverflow(lUpper, rUpper, exprMaxVal(this))
then result = exprMinVal(this)
else result = lLower.bitShiftLeft(rLower)
)
}
override float getUpperBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
if
lLower < 0
or
not (
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
// and a negative shift is undefined, so we set it to the maximum value
result = exprMaxVal(this)
else
// If we have `0b01010000 << [0, 2]`, the max value for 8 bits is 0b10100000
// (a shift of 1) but doing a shift by the upper bound would give 0b01000000.
// So if the left shift operation causes an overflow, we just assume the max value
// If necessary, we may be able to improve this bound in the future
if canLShiftOverflow(lUpper, rUpper, exprMaxVal(this))
then result = exprMaxVal(this)
else result = lUpper.bitShiftLeft(rUpper)
)
}
override predicate dependsOnChild(Expr child) {
child = getLeftOperand() or child = getRightOperand()
}
}

View File

@@ -107,130 +107,34 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) {
)
}
/**
* A non-member prefix `operator*` function for an iterator type.
*/
private class IteratorPointerDereferenceOperator extends Operator, TaintFunction,
IteratorReferenceFunction {
FunctionInput iteratorInput;
IteratorPointerDereferenceOperator() {
this.hasName("operator*") and
iteratorInput = getIteratorArgumentInput(this, 0)
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isParameterDeref(0)
}
}
/**
* A non-member `operator++` or `operator--` function for an iterator type.
*
* Note that this class _only_ matches non-member functions. To find both
* non-member and versions, use `IteratorCrementOperator`.
*/
private class IteratorCrementOperator extends Operator, DataFlowFunction {
FunctionInput iteratorInput;
IteratorCrementOperator() {
class IteratorCrementNonMemberOperator extends Operator {
IteratorCrementNonMemberOperator() {
this.hasName(["operator++", "operator--"]) and
iteratorInput = getIteratorArgumentInput(this, 0)
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
or
input.isParameterDeref(0) and output.isReturnValueDeref()
}
}
/**
* A non-member `operator+` function for an iterator type.
*/
private class IteratorAddOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorAddOperator() {
this.hasName("operator+") and
iteratorInput = getIteratorArgumentInput(this, [0, 1])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator-` function that takes a pointer difference type as its second argument.
*/
private class IteratorSubOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorSubOperator() {
this.hasName("operator-") and
iteratorInput = getIteratorArgumentInput(this, 0) and
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator+=` or `operator-=` function for an iterator type.
*/
class IteratorAssignArithmeticOperator extends Operator {
IteratorAssignArithmeticOperator() {
this.hasName(["operator+=", "operator-="]) and
exists(getIteratorArgumentInput(this, 0))
}
}
private class IteratorAssignArithmeticOperatorModel extends IteratorAssignArithmeticOperator,
DataFlowFunction, TaintFunction {
private class IteratorCrementNonMemberOperatorModel extends IteratorCrementNonMemberOperator,
DataFlowFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
input = getIteratorArgumentInput(this, 0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
or
input.isParameterDeref(0) and output.isReturnValueDeref()
or
// reverse flow from returned reference to the object referenced by the first parameter
input.isReturnValueDeref() and
output.isParameterDeref(0)
or
(input.isParameter(1) or input.isParameterDeref(1)) and
output.isParameterDeref(0)
}
}
/**
* A prefix `operator*` member function for an iterator type.
*/
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
IteratorReferenceFunction {
IteratorPointerDereferenceMemberOperator() {
this.getClassAndName("operator*") instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* An `operator++` or `operator--` member function for an iterator type.
*
* Note that this class _only_ matches member functions. To find both
* non-member and member versions, use `IteratorCrementOperator`.
*/
class IteratorCrementMemberOperator extends MemberFunction {
IteratorCrementMemberOperator() {
@@ -258,25 +162,49 @@ private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOp
}
/**
* A member `operator->` function for an iterator type.
* A (member or non-member) `operator++` or `operator--` function for an iterator type.
*/
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
class IteratorCrementOperator extends Function {
IteratorCrementOperator() {
this instanceof IteratorCrementNonMemberOperator or
this instanceof IteratorCrementMemberOperator
}
}
/**
* A non-member `operator+` function for an iterator type.
*
* Note that this class _only_ matches non-member functions. To find both
* non-member and member versions, use `IteratorBinaryAddOperator`.
*/
class IteratorAddNonMemberOperator extends Operator {
IteratorAddNonMemberOperator() {
this.hasName("operator+") and
exists(getIteratorArgumentInput(this, [0, 1]))
}
}
private class IteratorAddNonMemberOperatorModel extends IteratorAddNonMemberOperator, TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
input = getIteratorArgumentInput(this, [0, 1]) and
output.isReturnValue()
}
}
/**
* An `operator+` or `operator-` member function of an iterator class.
*
* Note that this class _only_ matches member functions. To find both
* non-member and member versions, use `IteratorBinaryAddOperator`.
*/
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
class IteratorBinaryArithmeticMemberOperator extends MemberFunction {
IteratorBinaryArithmeticMemberOperator() {
this.getClassAndName(["operator+", "operator-"]) instanceof Iterator
}
}
private class IteratorBinaryArithmeticMemberOperatorModel extends IteratorBinaryArithmeticMemberOperator,
TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
@@ -284,14 +212,84 @@ private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, Tai
}
/**
* An `operator+=` or `operator-=` member function of an iterator class.
* A (member or non-member) `operator+` or `operator-` function for an iterator type.
*/
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
TaintFunction {
class IteratorBinaryArithmeticOperator extends Function {
IteratorBinaryArithmeticOperator() {
this instanceof IteratorAddNonMemberOperator or
this instanceof IteratorSubNonMemberOperator or
this instanceof IteratorBinaryArithmeticMemberOperator
}
}
/**
* A non-member `operator-` function that takes a pointer difference type as its second argument.
*
* Note that this class _only_ matches non-member functions. To find both
* non-member and member versions, use `IteratorBinaryArithmeticOperator` (which also
* includes `operator+` versions).
*/
class IteratorSubNonMemberOperator extends Operator {
IteratorSubNonMemberOperator() {
this.hasName("operator-") and
exists(getIteratorArgumentInput(this, 0)) and
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
}
}
private class IteratorSubOperatorModel extends IteratorSubNonMemberOperator, TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = getIteratorArgumentInput(this, 0) and
output.isReturnValue()
}
}
/**
* A non-member `operator+=` or `operator-=` function for an iterator type.
*
* Note that this class _only_ matches non-member functions. To find both
* non-member and member versions, use `IteratorAssignArithmeticOperator`.
*/
class IteratorAssignArithmeticNonMemberOperator extends Operator {
IteratorAssignArithmeticNonMemberOperator() {
this.hasName(["operator+=", "operator-="]) and
exists(getIteratorArgumentInput(this, 0))
}
}
private class IteratorAssignArithmeticNonMemberOperatorModel extends IteratorAssignArithmeticNonMemberOperator,
DataFlowFunction, TaintFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(0) and output.isReturnValueDeref()
or
// reverse flow from returned reference to the object referenced by the first parameter
input.isReturnValueDeref() and
output.isParameterDeref(0)
or
(input.isParameter(1) or input.isParameterDeref(1)) and
output.isParameterDeref(0)
}
}
/**
* An `operator+=` or `operator-=` member function of an iterator class.
*
* Note that this class _only_ matches member functions. To find both
* non-member and member versions, use `IteratorAssignArithmeticOperator`.
*/
class IteratorAssignArithmeticMemberOperator extends MemberFunction {
IteratorAssignArithmeticMemberOperator() {
this.getClassAndName(["operator+=", "operator-="]) instanceof Iterator
}
}
private class IteratorAssignArithmeticMemberOperatorModel extends IteratorAssignArithmeticMemberOperator,
DataFlowFunction, TaintFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and
output.isReturnValue()
@@ -310,6 +308,83 @@ private class IteratorAssignArithmeticMemberOperator extends MemberFunction, Dat
}
}
/**
* A (member or non-member) `operator+=` or `operator-=` function for an iterator type.
*/
class IteratorAssignArithmeticOperator extends Function {
IteratorAssignArithmeticOperator() {
this instanceof IteratorAssignArithmeticNonMemberOperator or
this instanceof IteratorAssignArithmeticMemberOperator
}
}
/**
* A prefix `operator*` member function for an iterator type.
*
* Note that this class _only_ matches member functions. To find both
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
*/
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
IteratorReferenceFunction {
IteratorPointerDereferenceMemberOperator() {
this.getClassAndName("operator*") instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* A non-member prefix `operator*` function for an iterator type.
*
* Note that this class _only_ matches non-member functions. To find both
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
*/
class IteratorPointerDereferenceNonMemberOperator extends Operator, IteratorReferenceFunction {
IteratorPointerDereferenceNonMemberOperator() {
this.hasName("operator*") and
exists(getIteratorArgumentInput(this, 0))
}
}
private class IteratorPointerDereferenceNonMemberOperatorModel extends IteratorPointerDereferenceNonMemberOperator,
TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = getIteratorArgumentInput(this, 0) and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isParameterDeref(0)
}
}
/**
* A (member or non-member) prefix `operator*` function for an iterator type.
*/
class IteratorPointerDereferenceOperator extends Function {
IteratorPointerDereferenceOperator() {
this instanceof IteratorPointerDereferenceNonMemberOperator or
this instanceof IteratorPointerDereferenceMemberOperator
}
}
/**
* A member `operator->` function for an iterator type.
*/
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator[]` member function of an iterator class.
*/
@@ -326,17 +401,24 @@ private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
/**
* An `operator=` member function of an iterator class that is not a copy or move assignment
* operator.
*
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
* `operator*` and use their own `operator=` to assign to the container.
*/
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
class IteratorAssignmentMemberOperator extends MemberFunction {
IteratorAssignmentMemberOperator() {
this.getClassAndName("operator=") instanceof Iterator and
not this instanceof CopyAssignmentOperator and
not this instanceof MoveAssignmentOperator
}
}
/**
* An `operator=` member function of an iterator class that is not a copy or move assignment
* operator.
*
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
* `operator*` and use their own `operator=` to assign to the container.
*/
private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMemberOperator,
TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(0) and
output.isQualifierObject()