Skip to content

Commit e84f98f

Browse files
authored
Merge branch 'main' into lcartey/essential-types
2 parents b38552c + 1821034 commit e84f98f

File tree

38 files changed

+3120
-75
lines changed

38 files changed

+3120
-75
lines changed

.vscode/tasks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@
244244
"Pointers",
245245
"Pointers1",
246246
"Pointers2",
247+
"Pointers3",
247248
"Scope",
248249
"SideEffects1",
249250
"SideEffects2",

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

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,10 @@
1313

1414
import cpp
1515
import codingstandards.c.cert
16+
import codingstandards.c.Pointers
1617
import semmle.code.cpp.dataflow.TaintTracking
1718
import DataFlow::PathGraph
1819

19-
/**
20-
* An expression which performs pointer arithmetic
21-
*/
22-
abstract class PointerArithmeticExpr extends Expr {
23-
abstract Expr getPointer();
24-
25-
abstract Expr getOperand();
26-
}
27-
28-
/**
29-
* A pointer arithmetic binary operation expression.
30-
*/
31-
class SimplePointerArithmeticExpr extends PointerArithmeticExpr, PointerArithmeticOperation {
32-
override Expr getPointer() { result = this.getLeftOperand() }
33-
34-
override Expr getOperand() { result = this.getRightOperand() }
35-
}
36-
37-
/**
38-
* A pointer arithmetic assignment expression.
39-
*/
40-
class AssignPointerArithmeticExpr extends PointerArithmeticExpr, AssignOperation {
41-
AssignPointerArithmeticExpr() {
42-
this instanceof AssignPointerAddExpr or
43-
this instanceof AssignPointerSubExpr
44-
}
45-
46-
override Expr getPointer() { result = this.getLValue() }
47-
48-
override Expr getOperand() { result = this.getRValue() }
49-
}
50-
51-
/**
52-
* A pointer arithmetic array access expression.
53-
*/
54-
class ArrayPointerArithmeticExpr extends PointerArithmeticExpr, ArrayExpr {
55-
override Expr getPointer() { result = this.getArrayBase() }
56-
57-
override Expr getOperand() { result = this.getArrayOffset() }
58-
}
59-
6020
/**
6121
* An expression which invokes the `offsetof` macro or `__builtin_offsetof` operation.
6222
*/
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# EXP32-C: Do not access a volatile object through a nonvolatile reference
2+
3+
This query implements the CERT-C rule EXP32-C:
4+
5+
> Do not access a volatile object through a nonvolatile reference
6+
7+
8+
## Description
9+
10+
An object that has volatile-qualified type may be modified in ways unknown to the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) or have other unknown [side effects](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-sideeffect). Referencing a volatile object by using a non-volatile lvalue is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The C Standard, 6.7.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
11+
12+
> If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
13+
14+
15+
See [undefined behavior 65](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_65).
16+
17+
## Noncompliant Code Example
18+
19+
In this noncompliant code example, a volatile object is accessed through a non-volatile-qualified reference, resulting in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior):
20+
21+
```cpp
22+
#include <stdio.h>
23+
24+
void func(void) {
25+
static volatile int **ipp;
26+
static int *ip;
27+
static volatile int i = 0;
28+
29+
printf("i = %d.\n", i);
30+
31+
ipp = &ip; /* May produce a warning diagnostic */
32+
ipp = (int**) &ip; /* Constraint violation; may produce a warning diagnostic */
33+
*ipp = &i; /* Valid */
34+
if (*ip != 0) { /* Valid */
35+
/* ... */
36+
}
37+
}
38+
```
39+
The assignment `ipp = &ip` is not safe because it allows the valid code that follows to reference the value of the volatile object `i` through the non-volatile-qualified reference `ip`. In this example, the compiler may optimize out the entire `if` block because `*ip != 0` must be false if the object to which `ip` points is not volatile.
40+
41+
**Implementation Details**
42+
43+
This example compiles without warning on Microsoft Visual Studio 2013 when compiled in C mode (`/TC`) but causes errors when compiled in C++ mode (`/TP`).
44+
45+
GCC 4.8.1 generates a warning but compiles successfully.
46+
47+
## Compliant Solution
48+
49+
In this compliant solution, `ip` is declared `volatile`:
50+
51+
```cpp
52+
#include <stdio.h>
53+
54+
void func(void) {
55+
static volatile int **ipp;
56+
static volatile int *ip;
57+
static volatile int i = 0;
58+
59+
printf("i = %d.\n", i);
60+
61+
ipp = &ip;
62+
*ipp = &i;
63+
if (*ip != 0) {
64+
/* ... */
65+
}
66+
67+
}
68+
```
69+
70+
## Risk Assessment
71+
72+
Accessing an object with a volatile-qualified type through a reference with a non-volatile-qualified type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
73+
74+
<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> EXP32-C </td> <td> Low </td> <td> Likely </td> <td> Medium </td> <td> <strong>P6</strong> </td> <td> <strong>L2</strong> </td> </tr> </tbody> </table>
75+
76+
77+
## Automated Detection
78+
79+
<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> <strong>pointer-qualifier-cast-volatile</strong> <strong>pointer-qualifier-cast-volatile-implicit</strong> </td> <td> Supported indirectly via MISRA C 2012 Rule 11.8 </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-EXP32</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Clang </a> </td> <td> 3.9 </td> <td> <code>-Wincompatible-pointer-types-discards-qualifiers</code> </td> <td> </td> </tr> <tr> <td> <a> Compass/ROSE </a> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td> <a> Coverity </a> </td> <td> 2017.07 </td> <td> <strong>MISRA C 2012 Rule 11.8</strong> </td> <td> Implemented </td> </tr> <tr> <td> <a> GCC </a> </td> <td> 4.3.5 </td> <td> </td> <td> Can detect violations of this rule when the <code>-Wcast-qual</code> flag is used </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.4 </td> <td> <strong>C0312, C0562, C0563, C0673, C0674</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.4 </td> <td> <strong>CERT.EXPR.VOLATILE.ADDR</strong> <strong>CERT.EXPR.VOLATILE.ADDR.PARAM</strong> <strong>CERT.EXPR.VOLATILE.PTRPTR</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>344 S</strong> </td> <td> Partially implemented </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.2 </td> <td> <strong>CERT_C-EXP32-a</strong> </td> <td> A cast shall not remove any 'const' or 'volatile' qualification from the type of a pointer or reference </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> </td> <td> <a> CERT C: Rule EXP32-C </a> </td> <td> Checks for cast to pointer that removes const or volatile qualification (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>0312,562,563,673,674</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>pointer-qualifier-cast-volatile</strong> <strong>pointer-qualifier-cast-volatile-implicit</strong> </td> <td> Supported indirectly via MISRA C 2012 Rule 11.8 </td> </tr> </tbody> </table>
80+
81+
82+
## Related Vulnerabilities
83+
84+
Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP32-C).
85+
86+
## Related Guidelines
87+
88+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
89+
90+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Pointer Casting and Pointer Type Changes \[HFC\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Type System \[IHN\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> MISRA C:2012 </a> </td> <td> Rule 11.8 (required) </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> CERT C </a> </td> <td> <a> EXP55-CPP. Do not access a cv-qualified object through a cv-unqualified type </a> </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> </tbody> </table>
91+
92+
93+
## Bibliography
94+
95+
<table> <tbody> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> 6.7.3, "Type Qualifiers" </td> </tr> </tbody> </table>
96+
97+
98+
## Implementation notes
99+
100+
In limited cases, this query can raise false-positives for assignment of volatile objects and subsequent accesses of those objects via non-volatile pointers.
101+
102+
## References
103+
104+
* CERT-C: [EXP32-C: Do not access a volatile object through a nonvolatile reference](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @id c/cert/do-not-access-volatile-object-with-non-volatile-reference
3+
* @name EXP32-C: Do not access a volatile object through a nonvolatile reference
4+
* @description If an an object defined with a volatile-qualified type is referred to with an lvalue
5+
* of a non-volatile-qualified type, the behavior is undefined.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp32-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import semmle.code.cpp.controlflow.Dereferenced
17+
18+
/**
19+
* An expression involving volatile-qualified types that results in undefined behavior.
20+
*/
21+
abstract class UndefinedVolatilePointerExpr extends Expr {
22+
/**
23+
* Gets a descriptive string describing the type of expression and undefined behavior.
24+
*/
25+
abstract string getMessage();
26+
}
27+
28+
/**
29+
* Gets the depth of a pointer's base type's volatile qualifier
30+
*/
31+
int getAVolatileDepth(Type type) {
32+
type.isVolatile() and result = 1
33+
or
34+
result = getAVolatileDepth(type.(DerivedType).getBaseType()) + 1
35+
}
36+
37+
/**
38+
* A `Cast` which converts from a pointer to a volatile-qualified type
39+
* to a pointer to a non-volatile-qualified type.
40+
*/
41+
class CastFromVolatileToNonVolatileBaseType extends Cast, UndefinedVolatilePointerExpr {
42+
CastFromVolatileToNonVolatileBaseType() {
43+
exists(int i |
44+
i = getAVolatileDepth(this.getExpr().getType()) and
45+
not i = getAVolatileDepth(this.getActualType())
46+
)
47+
}
48+
49+
override string getMessage() {
50+
result = "Cast of object with a volatile-qualified type to a non-volatile-qualified type."
51+
}
52+
}
53+
54+
/**
55+
* Holds if `va` has a subsequent `VariableAccess` which is dereferenced after access
56+
*/
57+
bindingset[va]
58+
predicate hasSubsequentDereference(VariableAccess va) {
59+
dereferenced(pragma[only_bind_out](va).getASuccessor+())
60+
}
61+
62+
/**
63+
* An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and
64+
* and *rvalue* that is not also a pointer to a volatile base type.
65+
*/
66+
class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr, UndefinedVolatilePointerExpr {
67+
NonVolatileObjectAssignedToVolatilePointer() {
68+
exists(int i |
69+
not i = getAVolatileDepth(this.getRValue().getType()) and
70+
i = getAVolatileDepth(this.getLValue().(VariableAccess).getTarget().getType())
71+
) and
72+
// Checks for subsequent accesses to the underlying object via the original non-volatile
73+
// pointer assigned to the volatile pointer. This heuristic can cause false-positives
74+
// in certain instances which require more advanced reachability analysis, e.g. loops and scope
75+
// considerations that this simple forward traversal of the control-flow graph does not account for.
76+
exists(VariableAccess va |
77+
va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and
78+
hasSubsequentDereference(va)
79+
)
80+
}
81+
82+
override string getMessage() {
83+
result =
84+
"Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer."
85+
}
86+
}
87+
88+
from UndefinedVolatilePointerExpr e
89+
where not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery())
90+
select e, e.getMessage()

0 commit comments

Comments
 (0)