Skip to content

Pointers3: Add test cases #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@
"Pointers",
"Pointers1",
"Pointers2",
"Pointers3",
"Scope",
"SideEffects1",
"SideEffects2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# EXP32-C: Do not access a volatile object through a nonvolatile reference

This query implements the CERT-C rule EXP32-C:

> Do not access a volatile object through a nonvolatile reference


## CERT

** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **

## Implementation notes

None

## References

* CERT-C: [EXP32-C: Do not access a volatile object through a nonvolatile reference](https://wiki.sei.cmu.edu/confluence/display/c)
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* @id c/cert/do-not-access-volatile-object-with-non-volatile-reference
* @name EXP32-C: Do not access a volatile object through a nonvolatile reference
* @description If an an object defined with a volatile-qualified type is referred to with an lvalue
* of a non-volatile-qualified type, the behavior is undefined.
* @kind problem
* @precision high
* @problem.severity error
* @tags external/cert/id/exp32-c
* correctness
* external/cert/obligation/rule
*/

import cpp
import codingstandards.c.cert

/**
* A `Cast` which converts from a pointer to a volatile-qualified type
* to a pointer to a non-volatile-qualified type.
*/
class CastFromVolatileToNonVolatileBaseType extends Cast {
CastFromVolatileToNonVolatileBaseType() {
this.getExpr().getType().(PointerType).getBaseType*().isVolatile() and
this.getActualType() instanceof PointerType and
not this.getActualType().(PointerType).getBaseType*().isVolatile()
}
}

/**
* An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and
* and *rvalue* that is not also a pointer to a volatile base type.
*/
class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr {
NonVolatileObjectAssignedToVolatilePointer() {
this.getLValue().getType().(DerivedType).getBaseType*().isVolatile() and
not this.getRValue().getUnconverted().getType().(DerivedType).getBaseType*().isVolatile()
}

/**
* All `VariableAccess` expressions which are transitive successors of
* this `Expr` and which access the variable accessed in the *rvalue* of this `Expr`
*/
Expr getASubsequentAccessOfAssignedObject() {
result =
any(VariableAccess va |
va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and
this.getASuccessor+() = va
|
va
)
}
}

from Expr e, string message
where
not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery()) and
(
e instanceof CastFromVolatileToNonVolatileBaseType and
message = "Cast of object with a volatile-qualified type to a non-volatile-qualified type."
or
exists(e.(NonVolatileObjectAssignedToVolatilePointer).getASubsequentAccessOfAssignedObject()) and
message =
"Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type."
)
select e, message
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# EXP36-C: Do not cast pointers into more strictly aligned pointer types

This query implements the CERT-C rule EXP36-C:

> Do not cast pointers into more strictly aligned pointer types


## CERT

** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **

## Implementation notes

None

## References

* CERT-C: [EXP36-C: Do not cast pointers into more strictly aligned pointer types](https://wiki.sei.cmu.edu/confluence/display/c)
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* @id c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type
* @name EXP36-C: Do not cast pointers into more strictly aligned pointer types
* @description Converting a pointer to a different type results in undefined behavior if the
* pointer is not correctly aligned for the new type.
* @kind path-problem
* @precision high
* @problem.severity error
* @tags external/cert/id/exp36-c
* correctness
* external/cert/obligation/rule
*/

import cpp
import codingstandards.c.cert
import codingstandards.cpp.Alignment
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import DataFlow::PathGraph

/**
* An expression with a type that has defined alignment requirements
*/
abstract class ExprWithAlignment extends Expr {
/**
* Gets the alignment requirements in bytes for the underlying `Expr`
*/
abstract int getAlignment();

/**
* Gets a descriptive string describing the type of expression
*/
abstract string getKind();
}

/**
* A class extending `AddressOfExpr` and `ExprWithAlignment` to reason about the
* alignment of base types addressed with C address-of expressions
*/
class AddressOfAlignedVariableExpr extends AddressOfExpr, ExprWithAlignment {
AddressOfAlignedVariableExpr() { this.getAddressable() instanceof Variable }

AlignAs alignAsAttribute() { result = this.getAddressable().(Variable).getAnAttribute() }

override int getAlignment() {
result = alignAsAttribute().getArgument(0).getValueInt()
or
result = alignAsAttribute().getArgument(0).getValueType().getSize()
or
not exists(alignAsAttribute()) and
result = this.getAddressable().(Variable).getType().getAlignment()
}

override string getKind() { result = "address-of expression" }
}

/**
* A class extending `FunctionCall` and `ExprWithAlignment` to reason about the
* alignment of pointers allocated with calls to C standard library allocation functions
*/
class DefinedAlignmentAllocationExpr extends FunctionCall, ExprWithAlignment {
int alignment;

DefinedAlignmentAllocationExpr() {
this.getTarget().getName() = "aligned_alloc" and
lowerBound(this.getArgument(0)) = upperBound(this.getArgument(0)) and
alignment = upperBound(this.getArgument(0))
or
this.getTarget().getName() = ["malloc", "calloc", "realloc"] and
alignment = getGlobalMaxAlignT()
}

override int getAlignment() { result = alignment }

override string getKind() { result = "call to " + this.getTarget().getName() }
}

/**
* A class extending `VariableAccess` and `ExprWithAlignment` to reason about the
* alignment of pointers accessed based solely on the pointers' base types.
*/
class DefaultAlignedPointerAccessExpr extends VariableAccess, ExprWithAlignment {
DefaultAlignedPointerAccessExpr() {
this.getTarget().getUnspecifiedType() instanceof PointerType and
not this.getTarget().getUnspecifiedType() instanceof VoidPointerType
}

override int getAlignment() {
result = this.getTarget().getType().(PointerType).getBaseType().getAlignment()
}

override string getKind() { result = "pointer base type" }
}

/**
* A data-flow configuration for analysing the flow of `ExprWithAlignment` pointer expressions
* to casts which perform pointer type conversions and potentially create pointer alignment issues.
*/
class ExprWithAlignmentToCStyleCastConfiguration extends DataFlow::Configuration {
ExprWithAlignmentToCStyleCastConfiguration() {
this = "ExprWithAlignmentToCStyleCastConfiguration"
}

override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof ExprWithAlignment
}

override predicate isSink(DataFlow::Node sink) {
exists(CStyleCast cast |
cast.getUnderlyingType() instanceof PointerType and
cast.getUnconverted() = sink.asExpr()
)
}
}

/**
* A data-flow configuration for tracking flow from `AddressOfExpr` which provide
* most reliable or explicitly defined alignment information to the less reliable
* `DefaultAlignedPointerAccessExpr` expressions.
*
* This data-flow configuration is used
* to exclude an `DefaultAlignedPointerAccessExpr` as a source if a preceding source
* defined by this configuration provides more accurate alignment information.
*/
class AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig extends DataFlow::Configuration {
AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig() {
this = "AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig"
}

override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof AddressOfAlignedVariableExpr or
source.asExpr() instanceof DefinedAlignmentAllocationExpr
}

override predicate isSink(DataFlow::Node sink) {
sink.asExpr() instanceof DefaultAlignedPointerAccessExpr
}
}

from
DataFlow::PathNode source, DataFlow::PathNode sink, ExprWithAlignment expr, CStyleCast cast,
Type toBaseType, int alignmentFrom, int alignmentTo
where
not isExcluded(cast, Pointers3Package::doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery()) and
any(ExprWithAlignmentToCStyleCastConfiguration config).hasFlowPath(source, sink) and
source.getNode().asExpr() = expr and
sink.getNode().asExpr() = cast.getUnconverted() and
(
// possibility 1: the source node (ExprWithAlignment) is NOT a DefaultAlignedPointerAccessExpr
// meaning that its alignment info is accurate regardless of any preceding ExprWithAlignment nodes
expr instanceof DefaultAlignedPointerAccessExpr
implies
(
// possibility 2: the source node (ExprWithAlignment) IS a DefaultAlignedPointerAccessExpr
// meaning that its alignment info is only accurate if no preceding ExprWithAlignment nodes exist
not any(AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig config)
.hasFlowTo(source.getNode()) and
expr instanceof DefaultAlignedPointerAccessExpr and
cast.getUnconverted() instanceof VariableAccess
)
) and
toBaseType = cast.getActualType().(PointerType).getBaseType() and
alignmentTo = toBaseType.getAlignment() and
alignmentFrom = expr.getAlignment() and
// only flag cases where the cast's target type has stricter alignment requirements than the source
alignmentFrom < alignmentTo
select sink, source, sink,
"Cast from pointer with " + alignmentFrom +
"-byte alignment (defined by $@) to pointer with base type " + toBaseType.getUnderlyingType() +
" with " + alignmentTo + "-byte alignment.", expr.getUnconverted(), expr.getKind()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| test.c:5:13:5:21 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. |
| test.c:6:13:6:31 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. |
| test.c:14:3:14:55 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. |
| test.c:24:3:25:36 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql
38 changes: 38 additions & 0 deletions c/cert/test/rules/EXP32-C/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
volatile int *volatile_f();

void test_cast_away_volatile() {
volatile int *l1 = volatile_f(); // COMPLIANT
int *l2 = (int *)l1; // NON_COMPLIANT
int *l3 = (int *)volatile_f(); // NON_COMPLIANT
*l2; // Volatile object is accessed through a non-volatile pointer
}

void test_volatile_lost_by_assignment() {
static volatile int val = 0;
static int *non_compliant_pointer;
static volatile int **compliant_pointer_to_pointer;
compliant_pointer_to_pointer = &non_compliant_pointer; // NON_COMPLIANT
*compliant_pointer_to_pointer = &val;
*non_compliant_pointer; // Volatile object is accessed through a non-volatile
// pointer
}

void test_volatile_lost_by_assignment_and_cast() {
static volatile int val = 0;
static int *non_compliant_pointer;
static volatile int **compliant_pointer_to_pointer;
compliant_pointer_to_pointer =
(int **)&non_compliant_pointer; // NON_COMPLIANT
*compliant_pointer_to_pointer = &val;
*non_compliant_pointer; // Volatile object is accessed through a non-volatile
// pointer
}

void test_volatile_not_lost_by_assignment_and_cast() {
static volatile int val = 0;
static volatile int *compliant_pointer;
static volatile int **compliant_pointer_to_pointer;
compliant_pointer_to_pointer = &compliant_pointer; // COMPLIANT
*compliant_pointer_to_pointer = &val;
*compliant_pointer; // Volatile object is accessed through a volatile pointer
}
Loading