|
5 | 5 | import cpp
|
6 | 6 | import codingstandards.cpp.Customizations
|
7 | 7 | import codingstandards.cpp.Exclusions
|
| 8 | +import codingstandards.cpp.Pointers |
| 9 | +import codingstandards.cpp.Variable |
| 10 | +import codingstandards.cpp.dataflow.DataFlow |
| 11 | +import semmle.code.cpp.pointsto.PointsTo |
| 12 | +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis |
8 | 13 |
|
9 |
| -abstract class DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery extends Query { } |
| 14 | +/** |
| 15 | + * A function that has a parameter with a restrict-qualified pointer type. |
| 16 | + */ |
| 17 | +class FunctionWithRestrictParameters extends Function { |
| 18 | + Parameter restrictPtrParam; |
| 19 | + |
| 20 | + FunctionWithRestrictParameters() { |
| 21 | + restrictPtrParam.getUnspecifiedType() instanceof PointerOrArrayType and |
| 22 | + ( |
| 23 | + restrictPtrParam.getType().hasSpecifier(["restrict"]) and |
| 24 | + restrictPtrParam = this.getAParameter() |
| 25 | + or |
| 26 | + this.hasGlobalName(["strcpy", "strncpy", "strcat", "strncat", "memcpy"]) and |
| 27 | + restrictPtrParam = this.getParameter([0, 1]) |
| 28 | + or |
| 29 | + this.hasGlobalName(["strcpy_s", "strncpy_s", "strcat_s", "strncat_s", "memcpy_s"]) and |
| 30 | + restrictPtrParam = this.getParameter([0, 2]) |
| 31 | + or |
| 32 | + this.hasGlobalName(["strtok_s"]) and |
| 33 | + restrictPtrParam = this.getAParameter() |
| 34 | + or |
| 35 | + this.hasGlobalName(["printf", "printf_s", "scanf", "scanf_s"]) and |
| 36 | + restrictPtrParam = this.getParameter(0) |
| 37 | + or |
| 38 | + this.hasGlobalName(["sprintf", "sprintf_s", "snprintf", "snprintf_s"]) and |
| 39 | + restrictPtrParam = this.getParameter(3) |
| 40 | + ) |
| 41 | + } |
| 42 | + |
| 43 | + Parameter getARestrictPtrParam() { result = restrictPtrParam } |
| 44 | +} |
| 45 | + |
| 46 | +/** |
| 47 | + * A call to a function that has a parameter with a restrict-qualified pointer type. |
| 48 | + */ |
| 49 | +class CallToFunctionWithRestrictParameters extends FunctionCall { |
| 50 | + CallToFunctionWithRestrictParameters() { |
| 51 | + this.getTarget() instanceof FunctionWithRestrictParameters |
| 52 | + } |
| 53 | + |
| 54 | + Expr getARestrictPtrArg() { |
| 55 | + result = |
| 56 | + this.getArgument(this.getTarget() |
| 57 | + .(FunctionWithRestrictParameters) |
| 58 | + .getARestrictPtrParam() |
| 59 | + .getIndex()) |
| 60 | + } |
| 61 | + |
| 62 | + Expr getAPtrArg(int index) { |
| 63 | + result = this.getArgument(index) and |
| 64 | + pointerValue(result) |
| 65 | + } |
| 66 | + |
| 67 | + Expr getAPossibleSizeArg() { |
| 68 | + exists(Parameter param | |
| 69 | + param = this.getTarget().(FunctionWithRestrictParameters).getAParameter() and |
| 70 | + param.getUnderlyingType() instanceof IntegralType and |
| 71 | + // exclude __builtin_object_size |
| 72 | + not result.(FunctionCall).getTarget() instanceof BuiltInFunction and |
| 73 | + result = this.getArgument(param.getIndex()) |
| 74 | + ) |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * A `PointsToExpr` that is an argument of a pointer-type in a `CallToFunctionWithRestrictParameters` |
| 80 | + */ |
| 81 | +class CallToFunctionWithRestrictParametersArgExpr extends Expr { |
| 82 | + int paramIndex; |
| 83 | + |
| 84 | + CallToFunctionWithRestrictParametersArgExpr() { |
| 85 | + this = any(CallToFunctionWithRestrictParameters call).getAPtrArg(paramIndex) |
| 86 | + } |
| 87 | + |
| 88 | + int getParamIndex() { result = paramIndex } |
| 89 | +} |
| 90 | + |
| 91 | +int getStatedValue(Expr e) { |
| 92 | + // `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful |
| 93 | + // result in this case we pick the minimum value obtainable from dataflow and range analysis. |
| 94 | + result = |
| 95 | + upperBound(e) |
| 96 | + .minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())) |
| 97 | +} |
| 98 | + |
| 99 | +int getPointerArithmeticOperandStatedValue(CallToFunctionWithRestrictParametersArgExpr expr) { |
| 100 | + result = getStatedValue(expr.(PointerArithmeticExpr).getOperand()) |
| 101 | + or |
| 102 | + // edge-case: &(array[index]) expressions |
| 103 | + result = getStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand()) |
| 104 | + or |
| 105 | + // fall-back if `expr` is not a pointer arithmetic expression |
| 106 | + not expr instanceof PointerArithmeticExpr and |
| 107 | + not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and |
| 108 | + result = 0 |
| 109 | +} |
| 110 | + |
| 111 | +module PointerValueToRestrictArgConfig implements DataFlow::ConfigSig { |
| 112 | + predicate isSource(DataFlow::Node source) { pointerValue(source.asExpr()) } |
| 113 | + |
| 114 | + predicate isSink(DataFlow::Node sink) { |
| 115 | + exists(CallToFunctionWithRestrictParameters call | |
| 116 | + sink.asExpr() = call.getAPtrArg(_).getAChild*() |
| 117 | + ) |
| 118 | + } |
| 119 | + |
| 120 | + predicate isBarrierIn(DataFlow::Node node) { |
| 121 | + exists(AddressOfExpr a | node.asExpr() = a.getOperand().getAChild*()) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +module PointerValueToRestrictArgFlow = DataFlow::Global<PointerValueToRestrictArgConfig>; |
| 126 | + |
| 127 | +abstract class DoNotPassAliasedPointerToRestrictQualifiedParam_SharedSharedQuery extends Query { } |
10 | 128 |
|
11 | 129 | Query getQuery() {
|
12 |
| - result instanceof DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery |
| 130 | + result instanceof DoNotPassAliasedPointerToRestrictQualifiedParam_SharedSharedQuery |
13 | 131 | }
|
14 | 132 |
|
15 |
| -query predicate problems(Element e, string message) { |
16 |
| - not isExcluded(e, getQuery()) and message = "<replace with problem alert message for >" |
| 133 | +query predicate problems( |
| 134 | + CallToFunctionWithRestrictParameters call, string message, |
| 135 | + CallToFunctionWithRestrictParametersArgExpr arg2, string arg2message, |
| 136 | + CallToFunctionWithRestrictParametersArgExpr arg1, string arg1message, Expr source1, |
| 137 | + string sourceMessage2, Expr source2, string lastMessage2 |
| 138 | +) { |
| 139 | + not isExcluded(call, getQuery()) and |
| 140 | + exists(int argOffset1, int argOffset2, string sourceMessage1 | |
| 141 | + arg1 = call.getARestrictPtrArg() and |
| 142 | + arg2 = call.getAPtrArg(_) and |
| 143 | + // enforce ordering to remove permutations if multiple restrict-qualified args exist |
| 144 | + (not arg2 = call.getARestrictPtrArg() or arg2.getParamIndex() > arg1.getParamIndex()) and |
| 145 | + ( |
| 146 | + // check if two pointers address the same object |
| 147 | + PointerValueToRestrictArgFlow::flow(DataFlow::exprNode(source1), |
| 148 | + DataFlow::exprNode(arg1.getAChild*())) and |
| 149 | + ( |
| 150 | + // one pointer value flows to both args |
| 151 | + PointerValueToRestrictArgFlow::flow(DataFlow::exprNode(source1), |
| 152 | + DataFlow::exprNode(arg2.getAChild*())) and |
| 153 | + sourceMessage1 = "$@" and |
| 154 | + sourceMessage2 = "source" and |
| 155 | + source1 = source2 |
| 156 | + or |
| 157 | + // there are two separate values that flow from an AddressOfExpr of the same target |
| 158 | + getAddressOfExprTargetBase(source1) = getAddressOfExprTargetBase(source2) and |
| 159 | + PointerValueToRestrictArgFlow::flow(DataFlow::exprNode(source2), |
| 160 | + DataFlow::exprNode(arg2.getAChild*())) and |
| 161 | + sourceMessage1 = "a pair of address-of expressions ($@, $@)" and |
| 162 | + sourceMessage2 = "addressof1" and |
| 163 | + not source1 = source2 |
| 164 | + ) |
| 165 | + ) and |
| 166 | + // get the offset of the pointer arithmetic operand (or '0' if there is none) |
| 167 | + argOffset1 = getPointerArithmeticOperandStatedValue(arg1) and |
| 168 | + argOffset2 = getPointerArithmeticOperandStatedValue(arg2) and |
| 169 | + ( |
| 170 | + // case 1: the pointer args are the same. |
| 171 | + // (definite aliasing) |
| 172 | + argOffset1 = argOffset2 |
| 173 | + or |
| 174 | + // case 2: the pointer args are different, a size arg exists, |
| 175 | + // and the size arg is greater than the difference between the offsets. |
| 176 | + // (potential aliasing) |
| 177 | + exists(Expr sizeArg | |
| 178 | + sizeArg = call.getAPossibleSizeArg() and |
| 179 | + getStatedValue(sizeArg) > (argOffset1 - argOffset2).abs() |
| 180 | + ) |
| 181 | + or |
| 182 | + // case 3: the pointer args are different, and a size arg does not exist |
| 183 | + // (potential aliasing) |
| 184 | + not exists(call.getAPossibleSizeArg()) |
| 185 | + ) and |
| 186 | + lastMessage2 = "addressof2" and |
| 187 | + arg2message = "aliased pointer" and |
| 188 | + arg1message = "restrict-qualified parameter" and |
| 189 | + message = |
| 190 | + "Call to '" + call.getTarget().getName() + |
| 191 | + "' passes an $@ to a $@ (pointer value derived from " + sourceMessage1 + "." |
| 192 | + ) |
17 | 193 | }
|
0 commit comments