Skip to content

Commit 28c0fed

Browse files
authoredAug 26, 2024··
Merge pull request #637 from knewbury01/knewbury01/fix-119
M0-2-1: make into split and shared query
·
v2.48.0v2
2 parents a1a5cab + a1de784 commit 28c0fed

File tree

33 files changed

+408
-200
lines changed

33 files changed

+408
-200
lines changed
 

‎c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.cert
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717
import codingstandards.cpp.dataflow.TaintTracking
1818
import ScaledIntegerPointerArithmeticFlow::PathGraph
1919

‎c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql

Lines changed: 5 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -12,177 +12,11 @@
1212

1313
import cpp
1414
import codingstandards.c.cert
15-
import codingstandards.c.Pointers
16-
import codingstandards.c.Variable
17-
import codingstandards.cpp.dataflow.DataFlow
18-
import semmle.code.cpp.pointsto.PointsTo
19-
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
15+
import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared
2016

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

‎c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import cpp
1414
import codingstandards.cpp.dataflow.DataFlow
1515
import semmle.code.cpp.controlflow.Dominance
1616
import codingstandards.c.cert
17-
import codingstandards.c.Variable
17+
import codingstandards.cpp.Variable
1818

1919
/**
2020
* An `Expr` that is an assignment or initialization to a restrict-qualified pointer-type variable.

‎c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.qlref

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql

‎c/common/src/codingstandards/c/OutOfBounds.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import cpp
8-
import codingstandards.c.Pointers
8+
import codingstandards.cpp.Pointers
99
import codingstandards.c.Variable
1010
import codingstandards.cpp.Allocations
1111
import codingstandards.cpp.Overflow

‎c/common/src/codingstandards/c/Variable.qll

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,6 @@ class FlexibleArrayMemberCandidate extends MemberVariable {
3939
}
4040
}
4141

42-
/**
43-
* Returns the target variable of a `VariableAccess`.
44-
* If the access is a field access, then the target is the `Variable` of the qualifier.
45-
* If the access is an array access, then the target is the array base.
46-
*/
47-
Variable getAddressOfExprTargetBase(AddressOfExpr expr) {
48-
result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget()
49-
or
50-
not expr.getOperand() instanceof ValueFieldAccess and
51-
result = expr.getOperand().(VariableAccess).getTarget()
52-
or
53-
result = expr.getOperand().(ArrayExpr).getArrayBase().(VariableAccess).getTarget()
54-
}
55-
5642
/**
5743
* A struct that contains a flexible array member
5844
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared
3+
4+
class TestFileQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery,
5+
TestQuery
6+
{ }
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <stddef.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
5+
int *restrict g1;
6+
int *restrict g2;
7+
int *restrict g1_1;
8+
int *g2_1;
9+
10+
struct s1 {
11+
int x, y, z;
12+
};
13+
struct s1 v1;
14+
15+
void test_global_local() {
16+
int *restrict i1 = g1; // COMPLIANT
17+
int *restrict i2 = g2; // COMPLIANT
18+
int *restrict i3 = i2; // NON_COMPLIANT
19+
g1 = g2; // NON_COMPLIANT
20+
i1 = i2; // NON_COMPLIANT
21+
{
22+
int *restrict i4;
23+
int *restrict i5;
24+
int *restrict i6;
25+
i4 = g1; // COMPLIANT
26+
i4 = (void *)0; // COMPLIANT
27+
i5 = g1; // NON_COMPLIANT - block rather than statement scope matters
28+
i4 = g1; // NON_COMPLIANT
29+
i6 = g2; // COMPLIANT
30+
}
31+
}
32+
33+
void test_global_local_1() {
34+
g1_1 = g2_1; // COMPLIANT
35+
}
36+
37+
void test_structs() {
38+
struct s1 *restrict p1 = &v1;
39+
int *restrict px = &v1.x; // NON_COMPLIANT
40+
{
41+
int *restrict py;
42+
int *restrict pz;
43+
py = &v1.y; // COMPLIANT
44+
py = (int *)0;
45+
pz = &v1.z; // NON_COMPLIANT - block rather than statement scope matters
46+
py = &v1.y; // NON_COMPLIANT
47+
}
48+
}
49+
50+
void copy(int *restrict p1, int *restrict p2, size_t s) {
51+
for (size_t i = 0; i < s; ++i) {
52+
p2[i] = p1[i];
53+
}
54+
}
55+
56+
void test_restrict_params() {
57+
int i1 = 1;
58+
int i2 = 2;
59+
copy(&i1, &i1, 1); // NON_COMPLIANT
60+
copy(&i1, &i2, 1); // COMPLIANT
61+
62+
int x[10];
63+
int *px = &x[0];
64+
copy(&x[0], &x[1], 1); // COMPLIANT - non overlapping
65+
copy(&x[0], &x[1], 2); // NON_COMPLIANT - overlapping
66+
copy(&x[0], (int *)x[0], 1); // COMPLIANT - non overlapping
67+
copy(&x[0], px, 1); // NON_COMPLIANT - overlapping
68+
}
69+
70+
void test_strcpy() {
71+
char s1[] = "my test string";
72+
char s2[] = "my other string";
73+
strcpy(&s1, &s1 + 3); // NON_COMPLIANT
74+
strcpy(&s2, &s1); // COMPLIANT
75+
}
76+
77+
void test_memcpy() {
78+
char s1[] = "my test string";
79+
char s2[] = "my other string";
80+
memcpy(&s1, &s1 + 3, 5); // NON_COMPLIANT
81+
memcpy(&s2, &s1 + 3, 5); // COMPLIANT
82+
}
83+
84+
void test_memmove() {
85+
char s1[] = "my test string";
86+
char s2[] = "my other string";
87+
memmove(&s1, &s1 + 3, 5); // COMPLIANT - memmove is allowed to overlap
88+
memmove(&s2, &s1 + 3, 5); // COMPLIANT
89+
}
90+
91+
void test_scanf() {
92+
char s1[200] = "%10s";
93+
scanf(&s1, &s1 + 4); // NON_COMPLIANT
94+
}
95+
96+
// TODO also consider the following:
97+
// strncpy(), strncpy_s()
98+
// strcat(), strcat_s()
99+
// strncat(), strncat_s()
100+
// strtok_s()

‎c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.misra
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717

1818
from CStyleCast cast, Type type, Type newType
1919
where

‎c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.misra
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717
import codingstandards.cpp.Type
1818

1919
from Cast cast, Type type, Type newType

‎c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import cpp
1616
import codingstandards.c.misra
17-
import codingstandards.c.Pointers
17+
import codingstandards.cpp.Pointers
1818

1919
from CStyleCast cast, Type baseTypeFrom, Type baseTypeTo
2020
where

‎c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.misra
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717

1818
from CStyleCast cast, Type typeFrom, Type typeTo
1919
where

‎c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.misra
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717

1818
from Cast cast, VoidPointerType type, PointerToObjectType newType
1919
where

‎c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.misra
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717

1818
from CStyleCast cast, Type typeFrom, Type typeTo
1919
where

‎c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import cpp
1515
import codingstandards.c.misra
16-
import codingstandards.c.Pointers
16+
import codingstandards.cpp.Pointers
1717

1818
class MisraNonIntegerArithmeticType extends Type {
1919
MisraNonIntegerArithmeticType() {

‎c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import cpp
1414
import codingstandards.c.misra
15-
import codingstandards.c.Pointers
15+
import codingstandards.cpp.Pointers
1616
import codingstandards.cpp.Type
1717

1818
from Zero zero, Expr e, string type

‎c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import cpp
1414
import codingstandards.c.misra
15-
import codingstandards.c.Pointers
15+
import codingstandards.cpp.Pointers
1616

1717
class MemCmpMoveCpy extends Function {
1818
// Couldn't extend BuiltInFunction because it misses `memcmp`

‎c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import cpp
1717
import codingstandards.c.misra
18-
import codingstandards.c.Pointers
18+
import codingstandards.cpp.Pointers
1919
import codingstandards.cpp.SideEffect
2020

2121
from Variable ptr, PointerOrArrayType type
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- `M0-2-1` - `DoNotPassAliasedPointerToRestrictQualifiedParam.ql`:
2+
- Fixes #119. Adds shared query to cover missing detection of overlapping arrays or pointers in specific list of functions that list undefined behaviour when their parameters overlap.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @id cpp/autosar/do-not-pass-aliased-pointer-to-param
3+
* @name M0-2-1: Do not pass aliased pointers as parameters of functions where it is undefined behaviour for those pointers to overlap
4+
* @description Passing a aliased pointers as parameters of certain functions is undefined behavior.
5+
* @kind problem
6+
* @precision medium
7+
* @problem.severity error
8+
* @tags external/autosar/id/m0-2-1
9+
* correctness
10+
* external/autosar/allocated-target/implementation
11+
* external/autosar/enforcement/automated
12+
* external/autosar/obligation/required
13+
*/
14+
15+
import cpp
16+
import codingstandards.cpp.autosar
17+
import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared
18+
19+
class DoNotPassAliasedPointerToRestrictQualifiedParamQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery
20+
{
21+
DoNotPassAliasedPointerToRestrictQualifiedParamQuery() {
22+
this = RepresentationPackage::doNotPassAliasedPointerToParamQuery()
23+
}
24+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql

‎cpp/autosar/test/rules/M0-2-1/test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ void internal_shift() {
5151
void separate_access() {
5252
UnionSecret_t hash1, hash2;
5353
hash2.diff.suffix = hash1.fnv.suffix; // COMPLIANT, different union.
54-
}
54+
}

‎cpp/common/src/codingstandards/cpp/Variable.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,17 @@ import semmle.code.cpp.PODType03
55
class ScalarVariable extends Variable {
66
ScalarVariable() { isScalarType03(this.getType()) }
77
}
8+
9+
/**
10+
* Returns the target variable of a `VariableAccess`.
11+
* If the access is a field access, then the target is the `Variable` of the qualifier.
12+
* If the access is an array access, then the target is the array base.
13+
*/
14+
Variable getAddressOfExprTargetBase(AddressOfExpr expr) {
15+
result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget()
16+
or
17+
not expr.getOperand() instanceof ValueFieldAccess and
18+
result = expr.getOperand().(VariableAccess).getTarget()
19+
or
20+
result = expr.getOperand().(ArrayExpr).getArrayBase().(VariableAccess).getTarget()
21+
}

‎cpp/common/src/codingstandards/cpp/exclusions/cpp/Representation.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ newtype RepresentationQuery =
77
TBitFieldsShallBeUsedOnlyWhenInterfacingToHardwareOrConformingToCommunicationProtocolsQuery() or
88
TAuditPossibleHardwareInterfaceDueToBitFieldUsageInDataTypeDefinitionQuery() or
99
TObjectAssignedToAnOverlappingObjectQuery() or
10+
TDoNotPassAliasedPointerToParamQuery() or
1011
TUnderlyingBitRepresentationsOfFloatingPointValuesUsedQuery() or
1112
TNamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBitQuery() or
1213
TMemsetUsedToAccessObjectRepresentationQuery() or
@@ -41,6 +42,15 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule
4142
ruleId = "M0-2-1" and
4243
category = "required"
4344
or
45+
query =
46+
// `Query` instance for the `doNotPassAliasedPointerToParam` query
47+
RepresentationPackage::doNotPassAliasedPointerToParamQuery() and
48+
queryId =
49+
// `@id` for the `doNotPassAliasedPointerToParam` query
50+
"cpp/autosar/do-not-pass-aliased-pointer-to-param" and
51+
ruleId = "M0-2-1" and
52+
category = "required"
53+
or
4454
query =
4555
// `Query` instance for the `underlyingBitRepresentationsOfFloatingPointValuesUsed` query
4656
RepresentationPackage::underlyingBitRepresentationsOfFloatingPointValuesUsedQuery() and
@@ -109,6 +119,13 @@ module RepresentationPackage {
109119
TQueryCPP(TRepresentationPackageQuery(TObjectAssignedToAnOverlappingObjectQuery()))
110120
}
111121

122+
Query doNotPassAliasedPointerToParamQuery() {
123+
//autogenerate `Query` type
124+
result =
125+
// `Query` type for `doNotPassAliasedPointerToParam` query
126+
TQueryCPP(TRepresentationPackageQuery(TDoNotPassAliasedPointerToParamQuery()))
127+
}
128+
112129
Query underlyingBitRepresentationsOfFloatingPointValuesUsedQuery() {
113130
//autogenerate `Query` type
114131
result =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* Provides a library which includes a `problems` predicate for reporting....
3+
*/
4+
5+
import cpp
6+
import codingstandards.cpp.Customizations
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
13+
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 DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery extends Query { }
128+
129+
Query getQuery() {
130+
result instanceof DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery
131+
}
132+
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+
)
193+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.cpp:6:3:6:13 | call to memcpy | Call to 'memcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.cpp:6:22:6:26 | & ... | aliased pointer | test.cpp:6:15:6:19 | & ... | restrict-qualified parameter | test.cpp:6:15:6:19 | & ... | addressof1 | test.cpp:6:22:6:26 | & ... | addressof2 |
2+
| test.cpp:8:3:8:13 | call to memcpy | Call to 'memcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.cpp:8:22:8:26 | & ... | aliased pointer | test.cpp:8:15:8:19 | & ... | restrict-qualified parameter | test.cpp:8:15:8:19 | & ... | addressof1 | test.cpp:8:22:8:26 | & ... | addressof2 |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared
3+
4+
class TestFileQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery,
5+
TestQuery
6+
{ }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <cstring>
2+
3+
int a[20];
4+
5+
void undefined_behaviour_fn_119(void) {
6+
std::memcpy(&a[0], &a[1], 10u * sizeof(a[0])); // NON_COMPLIANT
7+
std::memmove(&a[0], &a[1], 10u * sizeof(a[0])); // COMPLIANT
8+
std::memcpy(&a[1], &a[0], 10u * sizeof(a[0])); // NON_COMPLIANT
9+
std::memmove(&a[1], &a[0], 10u * sizeof(a[0])); // COMPLIANT
10+
}

‎rule_packages/c/Pointers3.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"precision": "medium",
7373
"severity": "error",
7474
"short_name": "DoNotPassAliasedPointerToRestrictQualifiedParam",
75+
"shared_implementation_short_name": "DoNotPassAliasedPointerToRestrictQualifiedParamShared",
7576
"tags": [
7677
"correctness"
7778
]

‎rule_packages/cpp/Representation.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@
5353
"tags": [
5454
"correctness"
5555
]
56+
},
57+
{
58+
"description": "Passing a aliased pointers as parameters of certain functions is undefined behavior.",
59+
"kind": "problem",
60+
"name": "Do not pass aliased pointers as parameters of functions where it is undefined behaviour for those pointers to overlap",
61+
"precision": "medium",
62+
"severity": "error",
63+
"short_name": "DoNotPassAliasedPointerToParam",
64+
"shared_implementation_short_name": "DoNotPassAliasedPointerToRestrictQualifiedParamShared",
65+
"tags": [
66+
"correctness"
67+
]
5668
}
5769
],
5870
"title": "An object shall not be assigned to an overlapping object."

0 commit comments

Comments
 (0)
Please sign in to comment.