Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b2231c9
Number `Memory` packages
jeongsoolee09 Nov 18, 2025
9b5d8b2
Add rule description files
jeongsoolee09 Nov 18, 2025
a5d4127
Add Memory1 package files
jeongsoolee09 Jan 12, 2026
1a2cde8
Expose malloc, calloc and realloc
jeongsoolee09 Jan 12, 2026
c21e862
Minor comments
jeongsoolee09 Jan 12, 2026
c0b1e55
Checkpoint
jeongsoolee09 Jan 13, 2026
9d3bab0
Split out source and sinks into their cases
jeongsoolee09 Jan 13, 2026
a8a6db7
Checkpoint
jeongsoolee09 Jan 14, 2026
4464702
Merge branch 'main' into jeongsoolee09/MISRA-C++-2023-Memory
jeongsoolee09 Jan 15, 2026
e2c5870
Checkpoint: Add `DynamicAllocation` case
jeongsoolee09 Jan 16, 2026
fe2a3c4
First working draft
jeongsoolee09 Jan 20, 2026
5ea652b
Refine into path-problem
jeongsoolee09 Jan 21, 2026
7b860d9
Change `TaintTracking` to `DataFlow`
jeongsoolee09 Jan 21, 2026
08b8bf7
Finalize first working draft for stack / heap arrays
jeongsoolee09 Jan 22, 2026
062c62f
Document code copy and clean up imports
jeongsoolee09 Jan 23, 2026
e264dfd
Add multidimensional arrays alloc'ed on stack
jeongsoolee09 Jan 23, 2026
4d2bc8b
Add test.cpp and expected test results
jeongsoolee09 Jan 23, 2026
21500b8
Add exclusion for Memory1.qll
jeongsoolee09 Jan 23, 2026
e9f39a2
Adjust precision of existing rule and add a supplementary rule
jeongsoolee09 Jan 23, 2026
a62e2e1
Add supplementary query files
jeongsoolee09 Jan 23, 2026
ca62995
Fix @kind from problem to path-problem
jeongsoolee09 Jan 26, 2026
8abf097
Copy OutOfBounds.qll to cpp/common/src/codingstandards/cpp/
jeongsoolee09 Jan 26, 2026
f5454de
Remove unused import codingstandards.cpp.Variable in OutOfBounds.qll
jeongsoolee09 Jan 26, 2026
d82ed6e
Add PointerArgumentToCstringFunctionIsInvalid.ql and create testref f…
jeongsoolee09 Jan 26, 2026
356bbf2
Copy test.c from ARR38-C and add strncpy
jeongsoolee09 Jan 27, 2026
4c4cf49
Add headers, Add defininitions to headers, remove cases without null …
jeongsoolee09 Jan 27, 2026
9ced913
Remove testref and add qlref and expected
jeongsoolee09 Jan 29, 2026
1c5dc84
Address case of `strncat`
jeongsoolee09 Jan 29, 2026
2f80208
Remove unused predicate and update .expected
jeongsoolee09 Jan 29, 2026
0b7f024
Fix formatting error on the name property of the package
jeongsoolee09 Jan 29, 2026
8585568
Fix formatting of test.cpp
jeongsoolee09 Jan 29, 2026
610af04
Merge branch 'main' into jeongsoolee09/MISRA-C++-2023-Memory
jeongsoolee09 Jan 30, 2026
fe1bb85
Fix name formatting
jeongsoolee09 Jan 30, 2026
4989ff2
Merge branch 'jeongsoolee09/MISRA-C++-2023-Memory' of github.com:gith…
jeongsoolee09 Jan 30, 2026
ccd7993
Commit these later
jeongsoolee09 Jan 30, 2026
4780155
Remove alerts for negative offsets and fix @name property
jeongsoolee09 Jan 30, 2026
fd2713f
Fix precision in description file
jeongsoolee09 Jan 30, 2026
8ecc213
Fix test case formatting
jeongsoolee09 Jan 30, 2026
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,358 changes: 1,358 additions & 0 deletions cpp/common/src/codingstandards/cpp/OutOfBounds.qll

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory1.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype Memory1Query =
TPointerArithmeticFormsAnInvalidPointerQuery() or
TPointerArgumentToCstringFunctionIsInvalidQuery()

predicate isMemory1QueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `pointerArithmeticFormsAnInvalidPointer` query
Memory1Package::pointerArithmeticFormsAnInvalidPointerQuery() and
queryId =
// `@id` for the `pointerArithmeticFormsAnInvalidPointer` query
"cpp/misra/pointer-arithmetic-forms-an-invalid-pointer" and
ruleId = "RULE-8-7-1" and
category = "required"
or
query =
// `Query` instance for the `pointerArgumentToCstringFunctionIsInvalid` query
Memory1Package::pointerArgumentToCstringFunctionIsInvalidQuery() and
queryId =
// `@id` for the `pointerArgumentToCstringFunctionIsInvalid` query
"cpp/misra/pointer-argument-to-cstring-function-is-invalid" and
ruleId = "RULE-8-7-1" and
category = "required"
}

module Memory1Package {
Query pointerArithmeticFormsAnInvalidPointerQuery() {
//autogenerate `Query` type
result =
// `Query` type for `pointerArithmeticFormsAnInvalidPointer` query
TQueryCPP(TMemory1PackageQuery(TPointerArithmeticFormsAnInvalidPointerQuery()))
}

Query pointerArgumentToCstringFunctionIsInvalidQuery() {
//autogenerate `Query` type
result =
// `Query` type for `pointerArgumentToCstringFunctionIsInvalid` query
TQueryCPP(TMemory1PackageQuery(TPointerArgumentToCstringFunctionIsInvalidQuery()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import Linkage2
import Literals
import Loops
import Macros
import Memory1
import MoveForward
import Naming
import Null
Expand Down Expand Up @@ -105,6 +106,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 @@ -170,6 +172,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
8 changes: 7 additions & 1 deletion cpp/common/test/includes/standard-library/stdlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ long double strtold(const char *str, char **endptr);

int rand(void);

#endif // _GHLIBCPP_STDLIB
int mblen (const char *, size_t);
int mbtowc (wchar_t *__restrict, const char *__restrict, size_t);
int wctomb (char *, wchar_t);
size_t mbstowcs (wchar_t *__restrict, const char *__restrict, size_t);
size_t wcstombs (char *__restrict, const wchar_t *__restrict, size_t);

#endif // _GHLIBCPP_STDLIB
3 changes: 2 additions & 1 deletion cpp/common/test/includes/standard-library/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ void *memcpy(void *dest, const void *src, size_t count);
void *memset(void *dest, int ch, size_t count);
void *memmove(void *dest, const void *src, size_t count);
int memcmp(const void *lhs, const void *rhs, size_t count);
void *memchr (const void *, int, size_t);

#endif // _GHLIBCPP_STRINGH
#endif // _GHLIBCPP_STRINGH
5 changes: 4 additions & 1 deletion cpp/common/test/includes/standard-library/wchar.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ double wcstod(const wchar_t *str, wchar_t **endptr);
float wcstof(const wchar_t *str, wchar_t **endptr);
long double wcstold(const wchar_t *str, wchar_t **endptr);

size_t wcsftime (wchar_t *__restrict, size_t, const wchar_t *__restrict, const struct tm *__restrict);
size_t wcsxfrm (wchar_t *__restrict, const wchar_t *__restrict, size_t);

// Character classification and conversion types
typedef struct {
int __count;
Expand All @@ -26,4 +29,4 @@ typedef struct {
} __value;
} mbstate_t;

#endif // _GHLIBCPP_WCHAR
#endif // _GHLIBCPP_WCHAR
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @id cpp/misra/pointer-argument-to-cstring-function-is-invalid
* @name RULE-8-7-1: Pointer and index arguments passed to functions in <cstring> shall not be invalid
* @description Pointer and index arguments passed to functions in <cstring> should result in valid
* reads and/or writes.
* @kind problem
* @precision 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.OutOfBounds // for OOB::problems
import codingstandards.cpp.Exclusions // for isExcluded(Element, Query)
import codingstandards.cpp.exclusions.c.RuleMetadata

from
OOB::BufferAccessLibraryFunctionCall fc, string message, Expr bufferArg, string bufferArgStr,
Expr sizeOrOtherBufferArg, string otherStr
where
not isExcluded(fc, OutOfBoundsPackage::libraryFunctionArgumentOutOfBoundsQuery()) and
OOB::problems(fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr)
select fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/**
* @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 medium
* @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 }
}

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 int getMinNumBytes();
}

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

override int getMinNumBytes() { result = lowerBound(this.getArgument(0)) }
}

class CallocFunctionCall extends HeapAllocationFunctionCall {
CallocFunctionCall() { this.isCallocCall() }

override int getMinNumBytes() {
result = lowerBound(this.getArgument(0)) * lowerBound(this.getArgument(1))
}
}

class ReallocFunctionCall extends HeapAllocationFunctionCall {
ReallocFunctionCall() { this.isReallocCall() }

override int getMinNumBytes() { result = lowerBound(this.getArgument(1)) }
}

class NarrowedHeapAllocationFunctionCall extends Cast {
HeapAllocationFunctionCall alloc;

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

int getMinNumElements() {
exists(int rawResult |
rawResult =
alloc.getMinNumBytes() / this.getUnderlyingType().(PointerType).getBaseType().getSize()
|
result = rawResult.maximum(1)
)
}
}

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.asUninitialized() = this.asStackAllocation().getVariable() 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
/*.getArrayBase()*/ result = this.asPointerArithmetic()
}

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

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

/**
* NOTE The code in the below module is copied from
* `cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll` in `github/codeql`, commit hash
* `960e990`. This commit hash is the latest of the ones with tag `codeql-cli-2.21.4` which is the CLI version
* compatible with `codeql/cpp-all: 5.0.0` that this query depends on.
*/
module Copied {
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.internal.SsaInternals as Ssa

predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) {
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
// pointer indirections.
instrTo.getAnOperand() = opFrom and
(
instrTo instanceof ArithmeticInstruction
or
instrTo instanceof BitwiseInstruction
or
instrTo instanceof PointerArithmeticInstruction
)
or
// Taint flow from an address to its dereference.
Ssa::isDereference(instrTo, opFrom, _)
or
// Unary instructions tend to preserve enough information in practice that we
// want taint to flow through.
// The exception is `FieldAddressInstruction`. Together with the rules below for
// `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
// could cause flow into one field to come out an unrelated field.
// This would happen across function boundaries, where the IR would not be able to
// match loads to stores.
instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and
(
not instrTo instanceof FieldAddressInstruction
or
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
)
or
// Taint from int to boolean casts. This ensures that we have flow to `!x` in:
// ```cpp
// x = integer_source();
// if(!x) { ... }
// ```
exists(Operand zero |
zero.getDef().(ConstantValueInstruction).getValue() = "0" and
instrTo.(CompareNEInstruction).hasOperands(opFrom, zero)
)
}
}

import Copied

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())
}

predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
nodeTo.asInstruction() instanceof PointerArithmeticInstruction
}
}

module TrackArray = DataFlow::Global<TrackArrayConfig>;

predicate arrayIndexExceedsBounds(
DataFlow::Node arrayDeclarationNode, DataFlow::Node pointerFormationNode, int pointerOffset,
int arrayLength
) {
/* 1. Ensure the array access is reachable from the array declaration. */
TrackArray::flow(arrayDeclarationNode, pointerFormationNode) and
/* 2. The offset must be at most (number of elements) + 1 = (the declared length). */
exists(ArrayAllocation arrayAllocation, PointerFormation pointerFormation |
arrayDeclarationNode = arrayAllocation.getNode() and
pointerFormationNode = pointerFormation.getNode() and
pointerOffset = pointerFormation.getOffset() and
arrayLength = arrayAllocation.getLength()
|
arrayLength < pointerOffset
)
}

import TrackArray::PathGraph

from TrackArray::PathNode source, TrackArray::PathNode sink, string message
where
not isExcluded(sink.getNode().asExpr(),
Memory1Package::pointerArithmeticFormsAnInvalidPointerQuery()) and
exists(int pointerOffset, int arrayLength |
arrayIndexExceedsBounds(source.getNode(), sink.getNode(), pointerOffset, arrayLength) and
message =
"This pointer has offset " + pointerOffset +
" when the minimum possible length of the object might be " + arrayLength + "."
)
select sink, source, sink, message
Loading
Loading