From 3abf5d1bd25c44593b2fd1119faf6c2918d81a55 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 4 May 2023 16:28:05 -0400 Subject: [PATCH 01/11] C++: stitch paths in array off-by-one query --- .../CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 41 ++++++--- .../ConstantSizeArrayOffByOne.expected | 87 ++++++++++++++++--- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index bc68a7f14d53..af41bb7222a9 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -14,7 +14,7 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysi import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.DataFlow -import PointerArithmeticToDerefFlow::PathGraph +import StitchedPathGraph pragma[nomagic] Instruction getABoundIn(SemBound b, IRFunction func) { @@ -93,11 +93,11 @@ predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string ) } -predicate isConstantSizeOverflowSource(Field f, PointerAddInstruction pai, int delta) { - exists(int size, int bound, DataFlow::Node source, DataFlow::InstructionNode sink | - FieldAddressToPointerArithmeticFlow::flow(source, sink) and - isFieldAddressSource(f, source) and - pai.getLeft() = sink.asInstruction() and +predicate isConstantSizeOverflowSource(Field f, FieldAddressToPointerArithmeticFlow::PathNode fieldSource, PointerAddInstruction pai, int delta) { + exists(int size, int bound, FieldAddressToPointerArithmeticFlow::PathNode sink | + FieldAddressToPointerArithmeticFlow::flowPath(fieldSource, sink) and + isFieldAddressSource(f, fieldSource.getNode()) and + pai.getLeft() = sink.getNode().(DataFlow::InstructionNode).asInstruction() and f.getUnspecifiedType().(ArrayType).getArraySize() = size and semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and delta = bound - size and @@ -109,22 +109,39 @@ predicate isConstantSizeOverflowSource(Field f, PointerAddInstruction pai, int d module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - isConstantSizeOverflowSource(_, source.asInstruction(), _) + isConstantSizeOverflowSource(_, _, source.asInstruction(), _) } pragma[inline] predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) } } +module MergedPathGraph = DataFlow::MergePathGraph; +class PathNode = MergedPathGraph::PathNode; +module StitchedPathGraph implements DataFlow::PathGraphSig{ + query predicate edges(PathNode a, PathNode b) { + MergedPathGraph::PathGraph::edges(a, b) + or + a.asPathNode2().getNode().(DataFlow::InstructionNode).asInstruction() = b.asPathNode1().getNode().(DataFlow::InstructionNode).asInstruction().(PointerAddInstruction).getLeft() + } + + query predicate nodes(PathNode n, string key, string val) { + MergedPathGraph::PathGraph::nodes(n, key, val) + } + + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + MergedPathGraph::PathGraph::subpaths(arg, par, ret, out) + } +} module PointerArithmeticToDerefFlow = DataFlow::Global; from - Field f, PointerArithmeticToDerefFlow::PathNode source, - PointerArithmeticToDerefFlow::PathNode sink, Instruction deref, string operation, int delta + Field f, PathNode fieldSource, PathNode paiNode, + PathNode sink, Instruction deref, string operation, int delta where - PointerArithmeticToDerefFlow::flowPath(source, sink) and + PointerArithmeticToDerefFlow::flowPath(paiNode.asPathNode1(), sink.asPathNode1()) and isInvalidPointerDerefSink2(sink.getNode(), deref, operation) and - isConstantSizeOverflowSource(f, source.getNode().asInstruction(), delta) -select source, source, sink, + isConstantSizeOverflowSource(f, fieldSource.asPathNode2(), paiNode.getNode().asInstruction(), delta) +select paiNode, fieldSource, sink, "This pointer arithmetic may have an off-by-" + (delta + 1) + " error allowing it to overrun $@ at this $@.", f, f.getName(), deref, operation diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected index e201ef15af94..777fa1d55642 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected @@ -1,37 +1,100 @@ edges +| test.cpp:26:10:26:12 | buf | test.cpp:26:5:26:12 | buf | +| test.cpp:30:10:30:12 | buf | test.cpp:30:5:30:12 | buf | +| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:12 | buf | +| test.cpp:35:5:35:12 | buf | test.cpp:35:5:35:22 | access to array | +| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:12 | buf | +| test.cpp:36:5:36:12 | buf | test.cpp:36:5:36:24 | access to array | +| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:12 | buf | +| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:16 | buf | +| test.cpp:43:9:43:16 | buf | test.cpp:43:9:43:19 | access to array | +| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:16 | buf | +| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:12 | buf | +| test.cpp:49:5:49:12 | buf | test.cpp:49:5:49:22 | access to array | +| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:12 | buf | +| test.cpp:50:5:50:12 | buf | test.cpp:50:5:50:24 | access to array | +| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:12 | buf | +| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:16 | buf | +| test.cpp:57:9:57:16 | buf | test.cpp:57:9:57:19 | access to array | +| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:16 | buf | +| test.cpp:61:9:61:16 | buf | test.cpp:61:9:61:19 | access to array | +| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:16 | buf | | test.cpp:66:32:66:32 | p | test.cpp:66:32:66:32 | p | | test.cpp:66:32:66:32 | p | test.cpp:67:5:67:6 | * ... | | test.cpp:66:32:66:32 | p | test.cpp:67:6:67:6 | p | +| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:5 | p | +| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:5 | p | +| test.cpp:72:5:72:5 | p | test.cpp:72:5:72:15 | access to array | +| test.cpp:76:32:76:34 | buf | test.cpp:76:27:76:34 | buf | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | +| test.cpp:77:27:77:34 | buf | test.cpp:77:27:77:44 | access to array | | test.cpp:77:27:77:44 | access to array | test.cpp:77:26:77:44 | & ... | +| test.cpp:77:32:77:34 | buf | test.cpp:77:27:77:34 | buf | +| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | +| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | nodes +| test.cpp:26:5:26:12 | buf | semmle.label | buf | +| test.cpp:26:10:26:12 | buf | semmle.label | buf | +| test.cpp:30:5:30:12 | buf | semmle.label | buf | +| test.cpp:30:10:30:12 | buf | semmle.label | buf | +| test.cpp:34:5:34:12 | buf | semmle.label | buf | +| test.cpp:34:10:34:12 | buf | semmle.label | buf | +| test.cpp:35:5:35:12 | buf | semmle.label | buf | | test.cpp:35:5:35:22 | access to array | semmle.label | access to array | +| test.cpp:35:10:35:12 | buf | semmle.label | buf | +| test.cpp:36:5:36:12 | buf | semmle.label | buf | | test.cpp:36:5:36:24 | access to array | semmle.label | access to array | +| test.cpp:36:10:36:12 | buf | semmle.label | buf | +| test.cpp:39:9:39:16 | buf | semmle.label | buf | +| test.cpp:39:14:39:16 | buf | semmle.label | buf | +| test.cpp:43:9:43:16 | buf | semmle.label | buf | | test.cpp:43:9:43:19 | access to array | semmle.label | access to array | +| test.cpp:43:14:43:16 | buf | semmle.label | buf | +| test.cpp:48:5:48:12 | buf | semmle.label | buf | +| test.cpp:48:10:48:12 | buf | semmle.label | buf | +| test.cpp:49:5:49:12 | buf | semmle.label | buf | | test.cpp:49:5:49:22 | access to array | semmle.label | access to array | +| test.cpp:49:10:49:12 | buf | semmle.label | buf | +| test.cpp:50:5:50:12 | buf | semmle.label | buf | | test.cpp:50:5:50:24 | access to array | semmle.label | access to array | +| test.cpp:50:10:50:12 | buf | semmle.label | buf | +| test.cpp:53:9:53:16 | buf | semmle.label | buf | +| test.cpp:53:14:53:16 | buf | semmle.label | buf | +| test.cpp:57:9:57:16 | buf | semmle.label | buf | | test.cpp:57:9:57:19 | access to array | semmle.label | access to array | +| test.cpp:57:14:57:16 | buf | semmle.label | buf | +| test.cpp:61:9:61:16 | buf | semmle.label | buf | | test.cpp:61:9:61:19 | access to array | semmle.label | access to array | +| test.cpp:61:14:61:16 | buf | semmle.label | buf | | test.cpp:66:32:66:32 | p | semmle.label | p | | test.cpp:66:32:66:32 | p | semmle.label | p | | test.cpp:66:32:66:32 | p | semmle.label | p | | test.cpp:67:5:67:6 | * ... | semmle.label | * ... | | test.cpp:67:6:67:6 | p | semmle.label | p | +| test.cpp:70:33:70:33 | p | semmle.label | p | +| test.cpp:71:5:71:5 | p | semmle.label | p | +| test.cpp:72:5:72:5 | p | semmle.label | p | | test.cpp:72:5:72:15 | access to array | semmle.label | access to array | +| test.cpp:76:27:76:34 | buf | semmle.label | buf | +| test.cpp:76:32:76:34 | buf | semmle.label | buf | | test.cpp:77:26:77:44 | & ... | semmle.label | & ... | +| test.cpp:77:27:77:34 | buf | semmle.label | buf | | test.cpp:77:27:77:44 | access to array | semmle.label | access to array | +| test.cpp:77:32:77:34 | buf | semmle.label | buf | +| test.cpp:79:27:79:34 | buf | semmle.label | buf | +| test.cpp:79:32:79:34 | buf | semmle.label | buf | subpaths #select -| test.cpp:35:5:35:22 | access to array | test.cpp:35:5:35:22 | access to array | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | -| test.cpp:36:5:36:24 | access to array | test.cpp:36:5:36:24 | access to array | test.cpp:36:5:36:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:36:5:36:28 | Store: ... = ... | write | -| test.cpp:43:9:43:19 | access to array | test.cpp:43:9:43:19 | access to array | test.cpp:43:9:43:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:43:9:43:23 | Store: ... = ... | write | -| test.cpp:49:5:49:22 | access to array | test.cpp:49:5:49:22 | access to array | test.cpp:49:5:49:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:49:5:49:26 | Store: ... = ... | write | -| test.cpp:50:5:50:24 | access to array | test.cpp:50:5:50:24 | access to array | test.cpp:50:5:50:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:50:5:50:28 | Store: ... = ... | write | -| test.cpp:57:9:57:19 | access to array | test.cpp:57:9:57:19 | access to array | test.cpp:57:9:57:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:57:9:57:23 | Store: ... = ... | write | -| test.cpp:61:9:61:19 | access to array | test.cpp:61:9:61:19 | access to array | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | -| test.cpp:72:5:72:15 | access to array | test.cpp:72:5:72:15 | access to array | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:27:77:44 | access to array | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:27:77:44 | access to array | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:27:77:44 | access to array | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:27:77:44 | access to array | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:35:5:35:22 | access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | +| test.cpp:36:5:36:24 | access to array | test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:36:5:36:28 | Store: ... = ... | write | +| test.cpp:43:9:43:19 | access to array | test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:43:9:43:23 | Store: ... = ... | write | +| test.cpp:49:5:49:22 | access to array | test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:49:5:49:26 | Store: ... = ... | write | +| test.cpp:50:5:50:24 | access to array | test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:50:5:50:28 | Store: ... = ... | write | +| test.cpp:57:9:57:19 | access to array | test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:57:9:57:23 | Store: ... = ... | write | +| test.cpp:61:9:61:19 | access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | +| test.cpp:72:5:72:15 | access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | From d9665e16784b8d147b60646b2bec1bc4de5a6b72 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 4 May 2023 16:34:29 -0400 Subject: [PATCH 02/11] C++: add case test for constant off-by-one query --- .../constant-size/ConstantSizeArrayOffByOne.expected | 11 +++++++++++ .../Security/CWE/CWE-193/constant-size/test.cpp | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected index 777fa1d55642..294ddb0e46d8 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected @@ -33,6 +33,10 @@ edges | test.cpp:77:32:77:34 | buf | test.cpp:77:27:77:34 | buf | | test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | | test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | +| test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:11 | charBuf | +| test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:11 | charBuf | +| test.cpp:87:5:87:11 | charBuf | test.cpp:87:5:87:31 | access to array | +| test.cpp:88:5:88:11 | charBuf | test.cpp:88:5:88:27 | access to array | nodes | test.cpp:26:5:26:12 | buf | semmle.label | buf | | test.cpp:26:10:26:12 | buf | semmle.label | buf | @@ -84,6 +88,11 @@ nodes | test.cpp:77:32:77:34 | buf | semmle.label | buf | | test.cpp:79:27:79:34 | buf | semmle.label | buf | | test.cpp:79:32:79:34 | buf | semmle.label | buf | +| test.cpp:85:34:85:36 | buf | semmle.label | buf | +| test.cpp:87:5:87:11 | charBuf | semmle.label | charBuf | +| test.cpp:87:5:87:31 | access to array | semmle.label | access to array | +| test.cpp:88:5:88:11 | charBuf | semmle.label | charBuf | +| test.cpp:88:5:88:27 | access to array | semmle.label | access to array | subpaths #select | test.cpp:35:5:35:22 | access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | @@ -98,3 +107,5 @@ subpaths | test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | | test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | | test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:87:5:87:31 | access to array | test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:31 | access to array | This pointer arithmetic may have an off-by-3072 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:87:5:87:35 | Store: ... = ... | write | +| test.cpp:88:5:88:27 | access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | This pointer arithmetic may have an off-by-3073 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:88:5:88:31 | Store: ... = ... | write | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp index df4cd7b44911..c2ca24011272 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp @@ -78,3 +78,12 @@ void testInterproc(BigArray *arr) { addToPointerAndAssign(arr->buf); } + +#define MAX_SIZE_BYTES 4096 + +void testCharIndex(BigArray *arr) { + char *charBuf = (char*) arr->buf; + + charBuf[MAX_SIZE_BYTES - 1] = 0; // GOOD [FALSE POSITIVE] + charBuf[MAX_SIZE_BYTES] = 0; // BAD +} \ No newline at end of file From b7653ec92ddd617bf8745643cb7e6d32cd27a1aa Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 4 May 2023 16:39:02 -0400 Subject: [PATCH 03/11] C++: ignore cast arrays in constant off-by-one query --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 1 + .../constant-size/ConstantSizeArrayOffByOne.expected | 6 ------ .../query-tests/Security/CWE/CWE-193/constant-size/test.cpp | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index af41bb7222a9..ce604510d70f 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -98,6 +98,7 @@ predicate isConstantSizeOverflowSource(Field f, FieldAddressToPointerArithmeticF FieldAddressToPointerArithmeticFlow::flowPath(fieldSource, sink) and isFieldAddressSource(f, fieldSource.getNode()) and pai.getLeft() = sink.getNode().(DataFlow::InstructionNode).asInstruction() and + pai.getElementSize() = f.getUnspecifiedType().(ArrayType).getBaseType().getSize() and f.getUnspecifiedType().(ArrayType).getArraySize() = size and semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and delta = bound - size and diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected index 294ddb0e46d8..0b6888102629 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected @@ -35,8 +35,6 @@ edges | test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | | test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:11 | charBuf | | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:11 | charBuf | -| test.cpp:87:5:87:11 | charBuf | test.cpp:87:5:87:31 | access to array | -| test.cpp:88:5:88:11 | charBuf | test.cpp:88:5:88:27 | access to array | nodes | test.cpp:26:5:26:12 | buf | semmle.label | buf | | test.cpp:26:10:26:12 | buf | semmle.label | buf | @@ -90,9 +88,7 @@ nodes | test.cpp:79:32:79:34 | buf | semmle.label | buf | | test.cpp:85:34:85:36 | buf | semmle.label | buf | | test.cpp:87:5:87:11 | charBuf | semmle.label | charBuf | -| test.cpp:87:5:87:31 | access to array | semmle.label | access to array | | test.cpp:88:5:88:11 | charBuf | semmle.label | charBuf | -| test.cpp:88:5:88:27 | access to array | semmle.label | access to array | subpaths #select | test.cpp:35:5:35:22 | access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | @@ -107,5 +103,3 @@ subpaths | test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | | test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | | test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:87:5:87:31 | access to array | test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:31 | access to array | This pointer arithmetic may have an off-by-3072 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:87:5:87:35 | Store: ... = ... | write | -| test.cpp:88:5:88:27 | access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | This pointer arithmetic may have an off-by-3073 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:88:5:88:31 | Store: ... = ... | write | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp index c2ca24011272..5749331b7d54 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp @@ -84,6 +84,6 @@ void testInterproc(BigArray *arr) { void testCharIndex(BigArray *arr) { char *charBuf = (char*) arr->buf; - charBuf[MAX_SIZE_BYTES - 1] = 0; // GOOD [FALSE POSITIVE] - charBuf[MAX_SIZE_BYTES] = 0; // BAD + charBuf[MAX_SIZE_BYTES - 1] = 0; // GOOD + charBuf[MAX_SIZE_BYTES] = 0; // BAD [FALSE NEGATIVE] } \ No newline at end of file From f77c77fdf9baaf83b50f97ad2c30cbe11371632f Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 10 May 2023 14:58:11 -0400 Subject: [PATCH 04/11] C++: refactor off-by-one query to use flowstate --- .../CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 98 +++++++++---------- 1 file changed, 44 insertions(+), 54 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index ce604510d70f..8ad251a9fc28 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -14,7 +14,7 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysi import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.DataFlow -import StitchedPathGraph +import FieldAddressToDerefFlow::PathGraph pragma[nomagic] Instruction getABoundIn(SemBound b, IRFunction func) { @@ -42,21 +42,6 @@ bindingset[b] pragma[inline_late] predicate bounded2(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) } -module FieldAddressToPointerArithmeticConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { isFieldAddressSource(_, source) } - - predicate isSink(DataFlow::Node sink) { - exists(PointerAddInstruction pai | pai.getLeft() = sink.asInstruction()) - } -} - -module FieldAddressToPointerArithmeticFlow = - DataFlow::Global; - -predicate isFieldAddressSource(Field f, DataFlow::Node source) { - source.asInstruction().(FieldAddressInstruction).getField() = f -} - bindingset[delta] predicate isInvalidPointerDerefSinkImpl( int delta, Instruction i, AddressOperand addr, string operation @@ -93,56 +78,61 @@ predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string ) } -predicate isConstantSizeOverflowSource(Field f, FieldAddressToPointerArithmeticFlow::PathNode fieldSource, PointerAddInstruction pai, int delta) { - exists(int size, int bound, FieldAddressToPointerArithmeticFlow::PathNode sink | - FieldAddressToPointerArithmeticFlow::flowPath(fieldSource, sink) and - isFieldAddressSource(f, fieldSource.getNode()) and - pai.getLeft() = sink.getNode().(DataFlow::InstructionNode).asInstruction() and - pai.getElementSize() = f.getUnspecifiedType().(ArrayType).getBaseType().getSize() and - f.getUnspecifiedType().(ArrayType).getArraySize() = size and - semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and - delta = bound - size and - delta >= 0 and - size != 0 and - size != 1 - ) +predicate pointerArithOverflow( + PointerArithmeticInstruction pai, Field f, int size, int bound, int delta +) { + pai.getElementSize() = f.getUnspecifiedType().(ArrayType).getBaseType().getSize() and + f.getUnspecifiedType().(ArrayType).getArraySize() = size and + semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and + delta = bound - size } -module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - isConstantSizeOverflowSource(_, _, source.asInstruction(), _) - } +module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { + newtype FlowState = + additional TArray(Field f) or + additional TOverflowArithmetic(PointerArithmeticInstruction pai) - pragma[inline] - predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) } -} - -module MergedPathGraph = DataFlow::MergePathGraph; -class PathNode = MergedPathGraph::PathNode; -module StitchedPathGraph implements DataFlow::PathGraphSig{ - query predicate edges(PathNode a, PathNode b) { - MergedPathGraph::PathGraph::edges(a, b) - or - a.asPathNode2().getNode().(DataFlow::InstructionNode).asInstruction() = b.asPathNode1().getNode().(DataFlow::InstructionNode).asInstruction().(PointerAddInstruction).getLeft() + predicate isSource(DataFlow::Node source, FlowState state) { + exists(Field f | + source.asInstruction().(FieldAddressInstruction).getField() = f and + state = TArray(f) + ) } - query predicate nodes(PathNode n, string key, string val) { - MergedPathGraph::PathGraph::nodes(n, key, val) + predicate isSink(DataFlow::Node sink, FlowState state) { + isInvalidPointerDerefSink1(sink, _, _) and + state instanceof TOverflowArithmetic } - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - MergedPathGraph::PathGraph::subpaths(arg, par, ret, out) + predicate isBarrier(DataFlow::Node node, FlowState state) { none() } + + predicate isAdditionalFlowStep( + DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 + ) { + exists(PointerArithmeticInstruction pai, Field f, int size, int delta | + state1 = TArray(f) and + state2 = TOverflowArithmetic(pai) and + pai.getLeft() = node1.asInstruction() and + node2.asInstruction() = pai and + pointerArithOverflow(pai, f, size, _, delta) and + delta >= 0 and + size != 0 and + size != 1 + ) } } -module PointerArithmeticToDerefFlow = DataFlow::Global; + +module FieldAddressToDerefFlow = DataFlow::GlobalWithState; from - Field f, PathNode fieldSource, PathNode paiNode, - PathNode sink, Instruction deref, string operation, int delta + Field f, FieldAddressToDerefFlow::PathNode source, PointerArithmeticInstruction pai, + FieldAddressToDerefFlow::PathNode sink, Instruction deref, string operation, int delta where - PointerArithmeticToDerefFlow::flowPath(paiNode.asPathNode1(), sink.asPathNode1()) and + FieldAddressToDerefFlow::flowPath(source, sink) and isInvalidPointerDerefSink2(sink.getNode(), deref, operation) and - isConstantSizeOverflowSource(f, fieldSource.asPathNode2(), paiNode.getNode().asInstruction(), delta) -select paiNode, fieldSource, sink, + source.getState() = FieldAddressToDerefConfig::TArray(f) and + sink.getState() = FieldAddressToDerefConfig::TOverflowArithmetic(pai) and + pointerArithOverflow(pai, f, _, _, delta) +select pai, source, sink, "This pointer arithmetic may have an off-by-" + (delta + 1) + " error allowing it to overrun $@ at this $@.", f, f.getName(), deref, operation From 584adf843a5ab7aa0aa5ad997100876d161928f9 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 12 May 2023 12:43:10 -0400 Subject: [PATCH 05/11] C++: restrict flowstates in constant off-by-one query --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index 8ad251a9fc28..943faf6d75cf 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -89,8 +89,10 @@ predicate pointerArithOverflow( module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { newtype FlowState = - additional TArray(Field f) or - additional TOverflowArithmetic(PointerArithmeticInstruction pai) + additional TArray(Field f) { pointerArithOverflow(_, f, _, _, _) } or + additional TOverflowArithmetic(PointerArithmeticInstruction pai) { + pointerArithOverflow(pai, _, _, _, _) + } predicate isSource(DataFlow::Node source, FlowState state) { exists(Field f | From bf07b0f97b196c635629b749191949657246b4c0 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 19 May 2023 18:32:09 -0400 Subject: [PATCH 06/11] C++: fix cxartesian product in constant off-by-one query --- .../CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 19 +++- .../ConstantSizeArrayOffByOne.expected | 90 +++++-------------- 2 files changed, 38 insertions(+), 71 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index 943faf6d75cf..da227e65f924 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -87,6 +87,18 @@ predicate pointerArithOverflow( delta = bound - size } +module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + pointerArithOverflow(source.asInstruction(), _, _, _, _) + } + + predicate isSink(DataFlow::Node sink) { + isInvalidPointerDerefSink1(sink, _, _) + } +} + +module PointerArithmeticToDerefFlow = DataFlow::Global; + module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { newtype FlowState = additional TArray(Field f) { pointerArithOverflow(_, f, _, _, _) } or @@ -101,9 +113,12 @@ module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { ) } + pragma[inline] predicate isSink(DataFlow::Node sink, FlowState state) { - isInvalidPointerDerefSink1(sink, _, _) and - state instanceof TOverflowArithmetic + exists(DataFlow::Node pai | + state = TOverflowArithmetic(pai.asInstruction()) and + PointerArithmeticToDerefFlow::flow(pai, sink) + ) } predicate isBarrier(DataFlow::Node node, FlowState state) { none() } diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected index 0b6888102629..6ff343ea369a 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected @@ -1,72 +1,33 @@ edges -| test.cpp:26:10:26:12 | buf | test.cpp:26:5:26:12 | buf | -| test.cpp:30:10:30:12 | buf | test.cpp:30:5:30:12 | buf | -| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:12 | buf | -| test.cpp:35:5:35:12 | buf | test.cpp:35:5:35:22 | access to array | -| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:12 | buf | -| test.cpp:36:5:36:12 | buf | test.cpp:36:5:36:24 | access to array | -| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:12 | buf | -| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:16 | buf | -| test.cpp:43:9:43:16 | buf | test.cpp:43:9:43:19 | access to array | -| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:16 | buf | -| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:12 | buf | -| test.cpp:49:5:49:12 | buf | test.cpp:49:5:49:22 | access to array | -| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:12 | buf | -| test.cpp:50:5:50:12 | buf | test.cpp:50:5:50:24 | access to array | -| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:12 | buf | -| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:16 | buf | -| test.cpp:57:9:57:16 | buf | test.cpp:57:9:57:19 | access to array | -| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:16 | buf | -| test.cpp:61:9:61:16 | buf | test.cpp:61:9:61:19 | access to array | -| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:16 | buf | +| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | +| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | +| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | +| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | +| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | +| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | +| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | | test.cpp:66:32:66:32 | p | test.cpp:66:32:66:32 | p | | test.cpp:66:32:66:32 | p | test.cpp:67:5:67:6 | * ... | | test.cpp:66:32:66:32 | p | test.cpp:67:6:67:6 | p | -| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:5 | p | -| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:5 | p | -| test.cpp:72:5:72:5 | p | test.cpp:72:5:72:15 | access to array | -| test.cpp:76:32:76:34 | buf | test.cpp:76:27:76:34 | buf | +| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | -| test.cpp:77:27:77:34 | buf | test.cpp:77:27:77:44 | access to array | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:26:77:44 | & ... | -| test.cpp:77:32:77:34 | buf | test.cpp:77:27:77:34 | buf | +| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | | test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | | test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | -| test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:11 | charBuf | -| test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:11 | charBuf | nodes -| test.cpp:26:5:26:12 | buf | semmle.label | buf | -| test.cpp:26:10:26:12 | buf | semmle.label | buf | -| test.cpp:30:5:30:12 | buf | semmle.label | buf | -| test.cpp:30:10:30:12 | buf | semmle.label | buf | -| test.cpp:34:5:34:12 | buf | semmle.label | buf | -| test.cpp:34:10:34:12 | buf | semmle.label | buf | -| test.cpp:35:5:35:12 | buf | semmle.label | buf | | test.cpp:35:5:35:22 | access to array | semmle.label | access to array | | test.cpp:35:10:35:12 | buf | semmle.label | buf | -| test.cpp:36:5:36:12 | buf | semmle.label | buf | | test.cpp:36:5:36:24 | access to array | semmle.label | access to array | | test.cpp:36:10:36:12 | buf | semmle.label | buf | -| test.cpp:39:9:39:16 | buf | semmle.label | buf | -| test.cpp:39:14:39:16 | buf | semmle.label | buf | -| test.cpp:43:9:43:16 | buf | semmle.label | buf | | test.cpp:43:9:43:19 | access to array | semmle.label | access to array | | test.cpp:43:14:43:16 | buf | semmle.label | buf | -| test.cpp:48:5:48:12 | buf | semmle.label | buf | -| test.cpp:48:10:48:12 | buf | semmle.label | buf | -| test.cpp:49:5:49:12 | buf | semmle.label | buf | | test.cpp:49:5:49:22 | access to array | semmle.label | access to array | | test.cpp:49:10:49:12 | buf | semmle.label | buf | -| test.cpp:50:5:50:12 | buf | semmle.label | buf | | test.cpp:50:5:50:24 | access to array | semmle.label | access to array | | test.cpp:50:10:50:12 | buf | semmle.label | buf | -| test.cpp:53:9:53:16 | buf | semmle.label | buf | -| test.cpp:53:14:53:16 | buf | semmle.label | buf | -| test.cpp:57:9:57:16 | buf | semmle.label | buf | | test.cpp:57:9:57:19 | access to array | semmle.label | access to array | | test.cpp:57:14:57:16 | buf | semmle.label | buf | -| test.cpp:61:9:61:16 | buf | semmle.label | buf | | test.cpp:61:9:61:19 | access to array | semmle.label | access to array | | test.cpp:61:14:61:16 | buf | semmle.label | buf | | test.cpp:66:32:66:32 | p | semmle.label | p | @@ -75,31 +36,22 @@ nodes | test.cpp:67:5:67:6 | * ... | semmle.label | * ... | | test.cpp:67:6:67:6 | p | semmle.label | p | | test.cpp:70:33:70:33 | p | semmle.label | p | -| test.cpp:71:5:71:5 | p | semmle.label | p | -| test.cpp:72:5:72:5 | p | semmle.label | p | | test.cpp:72:5:72:15 | access to array | semmle.label | access to array | -| test.cpp:76:27:76:34 | buf | semmle.label | buf | -| test.cpp:76:32:76:34 | buf | semmle.label | buf | | test.cpp:77:26:77:44 | & ... | semmle.label | & ... | -| test.cpp:77:27:77:34 | buf | semmle.label | buf | -| test.cpp:77:27:77:44 | access to array | semmle.label | access to array | | test.cpp:77:32:77:34 | buf | semmle.label | buf | | test.cpp:79:27:79:34 | buf | semmle.label | buf | | test.cpp:79:32:79:34 | buf | semmle.label | buf | -| test.cpp:85:34:85:36 | buf | semmle.label | buf | -| test.cpp:87:5:87:11 | charBuf | semmle.label | charBuf | -| test.cpp:88:5:88:11 | charBuf | semmle.label | charBuf | subpaths #select -| test.cpp:35:5:35:22 | access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | -| test.cpp:36:5:36:24 | access to array | test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:36:5:36:28 | Store: ... = ... | write | -| test.cpp:43:9:43:19 | access to array | test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:43:9:43:23 | Store: ... = ... | write | -| test.cpp:49:5:49:22 | access to array | test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:49:5:49:26 | Store: ... = ... | write | -| test.cpp:50:5:50:24 | access to array | test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:50:5:50:28 | Store: ... = ... | write | -| test.cpp:57:9:57:19 | access to array | test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:57:9:57:23 | Store: ... = ... | write | -| test.cpp:61:9:61:19 | access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | -| test.cpp:72:5:72:15 | access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | +| test.cpp:36:5:36:24 | PointerAdd: access to array | test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:36:5:36:28 | Store: ... = ... | write | +| test.cpp:43:9:43:19 | PointerAdd: access to array | test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:43:9:43:23 | Store: ... = ... | write | +| test.cpp:49:5:49:22 | PointerAdd: access to array | test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:49:5:49:26 | Store: ... = ... | write | +| test.cpp:50:5:50:24 | PointerAdd: access to array | test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:50:5:50:28 | Store: ... = ... | write | +| test.cpp:57:9:57:19 | PointerAdd: access to array | test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:57:9:57:23 | Store: ... = ... | write | +| test.cpp:61:9:61:19 | PointerAdd: access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | +| test.cpp:72:5:72:15 | PointerAdd: access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | From 604affdeb06c13611b470803479381078562ae4b Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 22 May 2023 09:31:39 -0400 Subject: [PATCH 07/11] C++: autoformat --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index da227e65f924..f7bbbbb75d9a 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -92,9 +92,7 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { pointerArithOverflow(source.asInstruction(), _, _, _, _) } - predicate isSink(DataFlow::Node sink) { - isInvalidPointerDerefSink1(sink, _, _) - } + predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) } } module PointerArithmeticToDerefFlow = DataFlow::Global; From 4ed7450689b1f9d355d2041fdf243438bea59849 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 22 May 2023 11:09:44 -0400 Subject: [PATCH 08/11] C++: remove unneeded pragma --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index f7bbbbb75d9a..fb664a0c364a 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -111,7 +111,6 @@ module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { ) } - pragma[inline] predicate isSink(DataFlow::Node sink, FlowState state) { exists(DataFlow::Node pai | state = TOverflowArithmetic(pai.asInstruction()) and From 64d7b4923da98d086318c0e77ce048803d1d202f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 24 May 2023 15:16:34 -0700 Subject: [PATCH 09/11] C++: Prune flow states based on 'PointerArithmeticToDerefConfig'. --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index fb664a0c364a..684b93c231b6 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -78,7 +78,7 @@ predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string ) } -predicate pointerArithOverflow( +predicate pointerArithOverflow0( PointerArithmeticInstruction pai, Field f, int size, int bound, int delta ) { pai.getElementSize() = f.getUnspecifiedType().(ArrayType).getBaseType().getSize() and @@ -89,7 +89,7 @@ predicate pointerArithOverflow( module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - pointerArithOverflow(source.asInstruction(), _, _, _, _) + pointerArithOverflow0(source.asInstruction(), _, _, _, _) } predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) } @@ -97,6 +97,13 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { module PointerArithmeticToDerefFlow = DataFlow::Global; +predicate pointerArithOverflow( + PointerArithmeticInstruction pai, Field f, int size, int bound, int delta +) { + pointerArithOverflow0(pai, f, size, bound, delta) and + PointerArithmeticToDerefFlow::flow(DataFlow::instructionNode(pai), _) +} + module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { newtype FlowState = additional TArray(Field f) { pointerArithOverflow(_, f, _, _, _) } or From 298013a57e806ba42fa16ed8f52865f49f9a3009 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 24 May 2023 15:50:00 -0700 Subject: [PATCH 10/11] C++: Add in-barrier on sources to reduce duplication. --- .../CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index 684b93c231b6..82bdb8c5d420 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -84,7 +84,10 @@ predicate pointerArithOverflow0( pai.getElementSize() = f.getUnspecifiedType().(ArrayType).getBaseType().getSize() and f.getUnspecifiedType().(ArrayType).getArraySize() = size and semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and - delta = bound - size + delta = bound - size and + delta >= 0 and + size != 0 and + size != 1 } module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { @@ -92,6 +95,8 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { pointerArithOverflow0(source.asInstruction(), _, _, _, _) } + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } + predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) } } @@ -127,18 +132,17 @@ module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { predicate isBarrier(DataFlow::Node node, FlowState state) { none() } + predicate isBarrierIn(DataFlow::Node node) { isSource(node, _) } + predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { - exists(PointerArithmeticInstruction pai, Field f, int size, int delta | + exists(PointerArithmeticInstruction pai, Field f | state1 = TArray(f) and state2 = TOverflowArithmetic(pai) and pai.getLeft() = node1.asInstruction() and node2.asInstruction() = pai and - pointerArithOverflow(pai, f, size, _, delta) and - delta >= 0 and - size != 0 and - size != 1 + pointerArithOverflow(pai, f, _, _, _) ) } } From c3fdc83af67165a01ace101f576d692e2c689bfa Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 25 May 2023 12:23:50 -0700 Subject: [PATCH 11/11] C++: Also add an out barrier on all sinks. --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 4 ++++ .../constant-size/ConstantSizeArrayOffByOne.expected | 11 ----------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index 82bdb8c5d420..88db396f2cf2 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -97,6 +97,8 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { predicate isBarrierIn(DataFlow::Node node) { isSource(node) } + predicate isBarrierOut(DataFlow::Node node) { isSink(node) } + predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) } } @@ -134,6 +136,8 @@ module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { predicate isBarrierIn(DataFlow::Node node) { isSource(node, _) } + predicate isBarrierOut(DataFlow::Node node) { isSink(node, _) } + predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected index 6ff343ea369a..7d3df8cb7cb6 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected @@ -6,12 +6,8 @@ edges | test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | | test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | -| test.cpp:66:32:66:32 | p | test.cpp:66:32:66:32 | p | -| test.cpp:66:32:66:32 | p | test.cpp:67:5:67:6 | * ... | -| test.cpp:66:32:66:32 | p | test.cpp:67:6:67:6 | p | | test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | -| test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | | test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | | test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | | test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | @@ -31,10 +27,6 @@ nodes | test.cpp:61:9:61:19 | access to array | semmle.label | access to array | | test.cpp:61:14:61:16 | buf | semmle.label | buf | | test.cpp:66:32:66:32 | p | semmle.label | p | -| test.cpp:66:32:66:32 | p | semmle.label | p | -| test.cpp:66:32:66:32 | p | semmle.label | p | -| test.cpp:67:5:67:6 | * ... | semmle.label | * ... | -| test.cpp:67:6:67:6 | p | semmle.label | p | | test.cpp:70:33:70:33 | p | semmle.label | p | | test.cpp:72:5:72:15 | access to array | semmle.label | access to array | | test.cpp:77:26:77:44 | & ... | semmle.label | & ... | @@ -52,6 +44,3 @@ subpaths | test.cpp:61:9:61:19 | PointerAdd: access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | | test.cpp:72:5:72:15 | PointerAdd: access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write | | test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:5:67:6 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | -| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:67:6:67:6 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write |