Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import Lambdas
import Literals
import Loops
import Macros
import Memory1
import MoveForward
import Naming
import Null
Expand Down Expand Up @@ -99,6 +100,7 @@ newtype TCPPQuery =
TLiteralsPackageQuery(LiteralsQuery q) or
TLoopsPackageQuery(LoopsQuery q) or
TMacrosPackageQuery(MacrosQuery q) or
TMemory1PackageQuery(Memory1Query q) or
TMoveForwardPackageQuery(MoveForwardQuery q) or
TNamingPackageQuery(NamingQuery q) or
TNullPackageQuery(NullQuery q) or
Expand Down Expand Up @@ -161,6 +163,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isLiteralsQueryMetadata(query, queryId, ruleId, category) or
isLoopsQueryMetadata(query, queryId, ruleId, category) or
isMacrosQueryMetadata(query, queryId, ruleId, category) or
isMemory1QueryMetadata(query, queryId, ruleId, category) or
isMoveForwardQueryMetadata(query, queryId, ruleId, category) or
isNamingQueryMetadata(query, queryId, ruleId, category) or
isNullQueryMetadata(query, queryId, ruleId, category) or
Expand Down
3 changes: 3 additions & 0 deletions cpp/common/test/includes/standard-library/cstdlib
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ using ::strtoll;
using ::strtoul;
using ::strtoull;
using ::system;
using ::malloc;
using ::calloc;
using ::realloc;
} // namespace std
#endif // _GHLIBCPP_CSTDLIB
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/**
* @id cpp/misra/pointer-arithmetic-forms-an-invalid-pointer
* @name RULE-8-7-1: Pointer arithmetic shall not form an invalid pointer.
* @description Pointers obtained as result of performing arithmetic should point to an initialized
* object, or an element right next to the last element of an array.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-8-7-1
* scope/system
* external/misra/enforcement/undecidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import semmle.code.cpp.dataflow.new.DataFlow
import semmle.code.cpp.security.BufferAccess

class ArrayDeclaration extends VariableDeclarationEntry {
int length;

ArrayDeclaration() { this.getType().getUnderlyingType().(ArrayType).getArraySize() = length }

/**
* Gets the declared length of this array.
*/
int getLength() { result = length }

/**
* Gets the expression that the variable declared is initialized to, given there is such one.
*/
Expr getInitExpr() { result = this.getVariable().getInitializer().getExpr() }
}

class HeapAllocationFunctionCall extends FunctionCall {
AllocationFunction heapAllocFunction;

HeapAllocationFunctionCall() { this.getTarget() = heapAllocFunction }

predicate isMallocCall() { heapAllocFunction.getName() = "malloc" }

predicate isCallocCall() { heapAllocFunction.getName() = "calloc" }

predicate isReallocCall() { heapAllocFunction.getName() = "realloc" }

abstract Expr getByteArgument();

int getByteLowerBound() { result = lowerBound(this.getByteArgument()) }
}

class MallocFunctionCall extends HeapAllocationFunctionCall {
MallocFunctionCall() { this.isMallocCall() }

override Expr getByteArgument() { result = this.getArgument(0) }
}

class CallocReallocFunctionCall extends HeapAllocationFunctionCall {
CallocReallocFunctionCall() { this.isCallocCall() or this.isReallocCall() }

override Expr getByteArgument() { result = this.getArgument(1) }
}

class NarrowedHeapAllocationFunctionCall extends Cast {
HeapAllocationFunctionCall alloc;

NarrowedHeapAllocationFunctionCall() { alloc = this.getExpr() }

int getMinNumElements() {
result =
alloc.getByteLowerBound() / this.getUnderlyingType().(PointerType).getBaseType().getSize()
}

HeapAllocationFunctionCall getAllocFunctionCall() { result = alloc }
}

newtype TArrayAllocation =
TStackAllocation(ArrayDeclaration arrayDecl) or
TDynamicAllocation(NarrowedHeapAllocationFunctionCall narrowedAlloc)

newtype TPointerFormation =
TArrayExpr(ArrayExprBA arrayExpr) or
TPointerArithmetic(PointerArithmeticOperation pointerArithmetic)

class ArrayAllocation extends TArrayAllocation {
ArrayDeclaration asStackAllocation() { this = TStackAllocation(result) }

NarrowedHeapAllocationFunctionCall asDynamicAllocation() { this = TDynamicAllocation(result) }

string toString() {
result = this.asStackAllocation().toString() or
result = this.asDynamicAllocation().toString()
}

/**
* Gets the number of the object that the array holds. This number is exact for a stack-allocated
* array, and the minimum estimated value for a heap-allocated one.
*/
int getLength() {
result = this.asStackAllocation().getLength() or
result = this.asDynamicAllocation().getMinNumElements()
}

Location getLocation() {
result = this.asStackAllocation().getLocation() or
result = this.asDynamicAllocation().getLocation()
}
}

class PointerFormation extends TPointerFormation {
ArrayExprBA asArrayExpr() { this = TArrayExpr(result) }

PointerArithmeticOperation asPointerArithmetic() { this = TPointerArithmetic(result) }

string toString() {
result = this.asArrayExpr().toString() or
result = this.asPointerArithmetic().toString()
}

int getOffset() {
result = this.asArrayExpr().getArrayOffset().getValue().toInt()
or
exists(PointerAddExpr pointerAddition | pointerAddition = this.asPointerArithmetic() |
result = pointerAddition.getAnOperand().getValue().toInt() // TODO: only get the number being added
)
or
exists(PointerSubExpr pointerSubtraction | pointerSubtraction = this.asPointerArithmetic() |
result = -pointerSubtraction.getAnOperand().getValue().toInt()
)
}

Expr asExpr() {
result = this.asArrayExpr() or
result = this.asPointerArithmetic()
}

DataFlow::Node getNode() {
result.asExpr() = this.asExpr() or
result.asIndirectExpr() = this.asExpr()
}

Location getLocation() {
result = this.asArrayExpr().getLocation() or
result = this.asPointerArithmetic().getLocation()
}
}

module TrackArrayConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
/* 1. Declaring / Initializing an array-type variable */
exists(ArrayAllocation arrayAllocation |
node.asExpr() = arrayAllocation.asStackAllocation().getInitExpr()
)
or
/* 2. Allocating dynamic memory as an array */
none() // TODO
}

predicate isSink(DataFlow::Node node) {
exists(PointerFormation pointerFormation | node = pointerFormation.getNode())
}
}

module TrackArray = DataFlow::Global<TrackArrayConfig>;

private predicate arrayDeclarationAndAccess(
DataFlow::Node arrayDeclarationNode, DataFlow::Node pointerFormationNode
) {
TrackArray::flow(arrayDeclarationNode, pointerFormationNode)
}

private predicate arrayIndexExceedsOutOfBounds(
DataFlow::Node arrayDeclarationNode, DataFlow::Node pointerFormationNode
) {
/* 1. Ensure the array access is reachable from the array declaration. */
arrayDeclarationAndAccess(arrayDeclarationNode, pointerFormationNode) and
exists(ArrayAllocation arrayAllocation, PointerFormation pointerFormation |
arrayDeclarationNode.asExpr() = arrayAllocation.asStackAllocation().getInitExpr() and
pointerFormationNode = pointerFormation.getNode()
|
/* 2. Cases where a pointer formation becomes illegal. */
(
/* 2-1. An offset cannot be negative. */
pointerFormation.getOffset() < 0
or
/* 2-2. The offset should be at most (number of elements) + 1 = (the declared length). */
arrayAllocation.getLength() < pointerFormation.getOffset()
)
)
}

from PointerFormation pointerFormation
where
not isExcluded(pointerFormation.asExpr(),
Memory1Package::pointerArithmeticFormsAnInvalidPointerQuery()) and
exists(DataFlow::Node pointerFormationNode | pointerFormationNode = pointerFormation.getNode() |
arrayIndexExceedsOutOfBounds(_, pointerFormationNode)
)
select pointerFormation, "TODO"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No expected results have yet been specified
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-8-7-1/PointerArithmeticFormsAnInvalidPointer.ql
91 changes: 91 additions & 0 deletions cpp/misra/test/rules/RULE-8-7-1/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include <cstdlib>

void f1(int *array) {
/* 1. Pointer formed from performing arithmetic */
int *valid1 = array; // COMPLIANT: pointer is within boundary
int *valid2 = array + 1; // COMPLIANT: pointer is within boundary
int *valid3 = array + 2; // COMPLIANT: pointer is within boundary
int *valid4 =
array + 3; // COMPLIANT: pointer points one beyond the last element
int *invalid1 =
array +
4; // NON_COMPLIANT: pointer points more than one beyond the last element
int *invalid2 = array - 1; // NON_COMPLIANT: pointer is outside boundary
}

void f2(int *array) {
/* 2. Array Access (entails pointer arithmetic) */
int valid1 = array[0]; // COMPLIANT: pointer is within boundary
int valid2 = array[1]; // COMPLIANT: pointer is within boundary
int valid3 = array[2]; // COMPLIANT: pointer is within boundary
int valid4 = array[3]; // COMPLIANT: pointer points one beyond the last
// element, but non-compliant to Rule 4.1.3
int invalid1 = array[4]; // NON_COMPLIANT: pointer points more than one beyond
// the last element
int invalid2 = array[-1]; // NON_COMPLIANT: pointer is outside boundary
}

void f1_realloc(int *array) {
/* 1. Pointer formed from performing arithmetic */
int *valid1 = array; // COMPLIANT: pointer is within boundary
int *valid2 = array + 1; // COMPLIANT: pointer is within boundary
int *valid3 = array + 2; // COMPLIANT: pointer is within boundary
int *valid4 =
array + 3; // COMPLIANT: pointer points one beyond the last element
int *invalid1 =
array +
4; // NON_COMPLIANT: pointer points more than one beyond the last element
int *invalid2 = array - 1; // NON_COMPLIANT: pointer is outside boundary
}

void f2_realloc(int *array) {
/* 2. Array Access (entails pointer arithmetic) */
int valid1 = array[0]; // COMPLIANT: pointer is within boundary
int valid2 = array[1]; // COMPLIANT: pointer is within boundary
int valid3 = array[2]; // COMPLIANT: pointer points one beyond the last
int invalid1 = array[3]; // NON_COMPLIANT: pointer points one beyond the last
// element, but non-compliant to Rule 4.1.3
int invalid2 = array[4]; // NON_COMPLIANT: pointer points more than one beyond
// the last element
int invalid3 = array[-1]; // NON_COMPLIANT: pointer is outside boundary
}

int main(int argc, char *argv[]) {
/* 1. Array initialized on the stack */
int array[3] = {0, 1, 2};

f1(array);
f2(array);

/* 2. Array initialized on the heap */
int num_of_elements_malloc;
int num_of_elements_calloc;
int num_of_elements_realloc;

if (argc) {
num_of_elements_malloc = 1;
num_of_elements_calloc = 2;
num_of_elements_realloc = 3;
} else {
num_of_elements_malloc = 4;
num_of_elements_calloc = 5;
num_of_elements_realloc = 6;
}

int *array_malloc = (int *)std::malloc(num_of_elements_malloc * sizeof(int));
int *array_calloc = (int *)std::calloc(num_of_elements_calloc, sizeof(int));

int *array_realloc =
(int *)std::realloc(array_malloc, num_of_elements_realloc * sizeof(int));

f1(array_malloc);
f2(array_malloc);

f1(array_calloc);
f2(array_calloc);

f1_realloc(array_realloc);
f2_realloc(array_realloc);

return 0;
}
24 changes: 24 additions & 0 deletions rule_packages/cpp/Memory1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"MISRA-C++-2023": {
"RULE-8-7-1": {
"properties": {
"enforcement": "undecidable",
"obligation": "required"
},
"queries": [
{
"description": "Pointers obtained as result of performing arithmetic should point to an initialized object, or an element right next to the last element of an array.",
"kind": "problem",
"name": "Pointer arithmetic shall not form an invalid pointer.",
"precision": "very-high",
"severity": "error",
"short_name": "PointerArithmeticFormsAnInvalidPointer",
"tags": [
"scope/system"
]
}
],
"title": "Pointers obtained as result of performing arithmetic should point to an initialized object, or an element right next to the last element of an array."
}
}
}
24 changes: 24 additions & 0 deletions rule_packages/cpp/Memory2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"MISRA-C++-2023": {
"RULE-8-7-2": {
"properties": {
"enforcement": "undecidable",
"obligation": "required"
},
"queries": [
{
"description": "Pointer difference should be taken from pointers that belong to a same array.",
"kind": "problem",
"name": "Subtraction between pointers shall only be applied to ones that address elements of the same array.",
"precision": "very-high",
"severity": "error",
"short_name": "PointerDifferenceTakenBetweenDifferentArrays",
"tags": [
"scope/system"
]
}
],
"title": "Pointer difference should be taken from pointers that belong to a same array."
}
}
}
Loading
Loading