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 @@ -36,6 +36,7 @@ import Lambdas
import Literals
import Loops
import Macros
import Memory1
import MoveForward
import Naming
import Null
Expand Down Expand Up @@ -95,6 +96,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 @@ -154,6 +156,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,141 @@
/**
* @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() }
}

newtype TArrayAllocation =
TStackAllocation(ArrayDeclaration arrayDecl) or
TDynamicAllocation(AllocationFunction alloc)

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

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

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

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

int getLength() {
result = this.asStackAllocation().getLength() or
none() // TODO: 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
result = this.asPointerArithmetic()
}

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

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 arrayAccessNode
) {
TrackArray::flow(arrayDeclarationNode, arrayAccessNode)
}

private predicate arrayIndexExceedsOutOfBounds(
DataFlow::Node arrayDeclarationNode, DataFlow::Node arrayAccessNode
) {
/* 1. Ensure the array access is reachable from the array declaration. */
arrayDeclarationAndAccess(arrayDeclarationNode, arrayAccessNode) and
exists(ArrayAllocation arrayAllocation, PointerFormation pointerFormation |
arrayDeclarationNode.asExpr() = arrayAllocation.asStackAllocation().getInitExpr() and
arrayAccessNode = 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 Expr expr
where
not isExcluded(expr, Memory1Package::pointerArithmeticFormsAnInvalidPointerQuery()) and
none() // TODO
select "TODO", "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
80 changes: 80 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,80 @@
#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() {
/* 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 = 3;

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

int new_num_of_elements = 2;

int* array_realloc = (int*)std::realloc(array_malloc, new_num_of_elements * 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."
}
}
}
24 changes: 24 additions & 0 deletions rule_packages/cpp/Memory3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"MISRA-C++-2023": {
"RULE-8-9-1": {
"properties": {
"enforcement": "undecidable",
"obligation": "required"
},
"queries": [
{
"description": "Pointer comparison should be done between ones that belong to a same array.",
"kind": "problem",
"name": "The built-in relational operators >, >=, < and <= shall not be applied to objects of pointer type.",
"precision": "very-high",
"severity": "error",
"short_name": "PointerComparedBetweenDifferentArrays",
"tags": [
"scope/system"
]
}
],
"title": "Pointer comparison should be done between ones that belong to a same array."
}
}
}
24 changes: 24 additions & 0 deletions rule_packages/cpp/Memory4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"MISRA-C++-2023": {
"RULE-8-18-1": {
"properties": {
"enforcement": "undecidable",
"obligation": "mandatory"
},
"queries": [
{
"description": "Copying a member of a union to another, and copying a slice of an array to an overlapping one causes undefined behavior.",
"kind": "problem",
"name": "An object or subobject must not be copied to an overlapping object.",
"precision": "high",
"severity": "error",
"short_name": "ObjectMustNotBeCopiedToAnOverlappingObject",
"tags": [
"scope/system"
]
}
],
"title": "Copying a member of a union to another, and copying a slice of an array to an overlapping one causes undefined behavior."
}
}
}
24 changes: 24 additions & 0 deletions rule_packages/cpp/Memory5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"MISRA-C++-2023": {
"RULE-21-6-2": {
"properties": {
"enforcement": "decidable",
"obligation": "required"
},
"queries": [
{
"description": "Dynamically allocated memory must not be managed manually.",
"kind": "problem",
"name": "Dynamic memory shall be managed automatically.",
"precision": "very-high",
"severity": "error",
"short_name": "DynamicMemoryManagedManually",
"tags": [
"scope/single-translation-unit"
]
}
],
"title": "Dynamically allocated memory must not be managed manually."
}
}
}
Loading
Loading