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,187 @@
/**
* @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 path-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.TaintTracking
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()
}

DataFlow::Node getNode() {
result.asExpr() = this.asStackAllocation().getInitExpr() or
result.asConvertedExpr() = this.asDynamicAllocation()
}
}

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 // This needs to be array base, as we are only dealing with pointers here.
result = this.asPointerArithmetic()
}

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

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

module TrackArrayConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
exists(ArrayAllocation arrayAllocation | node = arrayAllocation.getNode())
}

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

module TrackArray = TaintTracking::Global<TrackArrayConfig>;

private predicate arrayIndexExceedsOutOfBounds(
DataFlow::Node arrayDeclarationNode, DataFlow::Node pointerFormationNode
) {
/* 1. Ensure the array access is reachable from the array declaration. */
TrackArray::flow(arrayDeclarationNode, pointerFormationNode) and
/* 2. Cases where a pointer formation becomes illegal. */
exists(ArrayAllocation arrayAllocation, PointerFormation pointerFormation |
arrayDeclarationNode = arrayAllocation.getNode() and
pointerFormationNode = pointerFormation.getNode()
|
/* 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()
)
}

import TrackArray::PathGraph

from TrackArray::PathNode source, TrackArray::PathNode sink
where
not isExcluded(sink.getNode().asExpr(),
Memory1Package::pointerArithmeticFormsAnInvalidPointerQuery()) and
arrayIndexExceedsOutOfBounds(source.getNode(), sink.getNode())
select sink, source, sink, "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
157 changes: 157 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,157 @@
#include <cstdlib>

void stack_allocation_pointer_arithmetic(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 stack_allocation_array_access(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 malloc_pointer_arithmetic(int *array) { // [1, 4]
/* 1. Pointer formed from performing arithmetic */
int *valid1 = array; // COMPLIANT: pointer is within boundary (lower bound: 1)
int *valid2 = array + 1; // COMPLIANT: pointer points more than one beyond the
// last element (lower bound: 1)
int *valid3 = array + 2; // NON_COMPLIANT: pointer points more than one beyond
// the last element (lower bound: 1)
int *valid4 = array + 3; // NON_COMPLIANT: pointer points more than one beyond
// the last element (lower bound: 1)
int *invalid1 = array + 4; // NON_COMPLIANT: pointer points more than one
// beyond the last element (lower bound: 1)
int *invalid2 = array + 5; // NON_COMPLIANT: pointer points more than one
// beyond the last element (lower bound: 1)
int *invalid3 = array - 1; // NON_COMPLIANT: pointer is outside boundary
}

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

void calloc_pointer_arithmetic(int *array) { // [2, 5]
/* 1. Pointer formed from performing arithmetic */
int *valid1 = array; // COMPLIANT: pointer is within boundary (lower bound: 2)
int *valid2 =
array + 1; // COMPLIANT: pointer is within boundary (lower bound: 2)
int *valid3 = array + 2; // COMPLIANT: pointer points more than one beyond the
// last element, but non-compliant to Rule 4.1.3
// (lower bound: 2)
int *valid4 = array + 3; // NON_COMPLIANT: pointer points more than one beyond
// the last element (lower bound: 2)
int *invalid1 = array + 4; // NON_COMPLIANT: pointer points more than one
// beyond the last element (lower bound: 2)
int *invalid2 = array - 1; // NON_COMPLIANT: pointer is outside boundary
}

void calloc_array_access(int *array) { // [2, 5]
/* 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 more than one beyond the
// last element, but non-compliant to Rule 4.1.3
// (lower bound: 2)
int valid4 = array[3]; // NON_COMPLIANT: pointer points more than one beyond
// the last element (lower bound: 2)
int invalid1 = array[4]; // NON_COMPLIANT: pointer points more than one
// beyond the last element (lower bound: 2)
int invalid2 = array[-1]; // NON_COMPLIANT: pointer is outside boundary
}

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

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

stack_allocation_pointer_arithmetic(array);
stack_allocation_array_access(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 *)malloc(num_of_elements_malloc * sizeof(int));
int *array_calloc = (int *)calloc(num_of_elements_calloc, sizeof(int));

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

malloc_pointer_arithmetic(array_malloc);
malloc_array_access(array_malloc);

calloc_pointer_arithmetic(array_calloc);
calloc_array_access(array_calloc);

realloc_pointer_arithmetic(array_realloc);
realloc_array_access(array_realloc);

return 0;
}
Loading
Loading