Skip to content

Commit ae8ecc9

Browse files
committed
C++: Add a final configuration to preserve call contexts between configuration transitions.
1 parent 055aea6 commit ae8ecc9

File tree

2 files changed

+259
-903
lines changed

2 files changed

+259
-903
lines changed

cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql

Lines changed: 83 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ module InvalidPointerToDerefBarrier {
318318
private module BarrierConfig implements DataFlow::ConfigSig {
319319
predicate isSource(DataFlow::Node source) {
320320
// The sources is the same as in the sources for `InvalidPointerToDerefConfig`.
321-
invalidPointerToDerefSource(_, source, _)
321+
invalidPointerToDerefSource(_, _, source, _)
322322
}
323323

324324
additional predicate isSink(
@@ -336,7 +336,7 @@ module InvalidPointerToDerefBarrier {
336336
private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) {
337337
exists(DataFlow::Node source |
338338
flow(source, node) and
339-
invalidPointerToDerefSource(_, source, result)
339+
invalidPointerToDerefSource(_, _, source, result)
340340
)
341341
}
342342

@@ -373,7 +373,7 @@ module InvalidPointerToDerefBarrier {
373373
* by `AllocToInvalidPointerConfig` to a dereference of the pointer.
374374
*/
375375
module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
376-
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, source, _) }
376+
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, _, source, _) }
377377

378378
pragma[inline]
379379
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _) }
@@ -388,23 +388,26 @@ module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
388388
module InvalidPointerToDerefFlow = DataFlow::Global<InvalidPointerToDerefConfig>;
389389

390390
/**
391-
* Holds if `pai` is a pointer-arithmetic operation and `source` is a dataflow node with a
392-
* pointer-value that is non-strictly upper bounded by `pai + delta`.
391+
* Holds if `source1` is dataflow node that represents an allocation that flows to the
392+
* left-hand side of the pointer-arithmetic `pai`, and `source` is a dataflow node with
393+
* a pointer-value that is non-strictly upper bounded by `pai + delta`.
393394
*
394395
* For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such
395396
* as `(p + size) + 1` and `source` is the node representing `(p + size) + 1`. In this
396397
* case `delta` is 1.
397398
*/
398399
predicate invalidPointerToDerefSource(
399-
PointerArithmeticInstruction pai, DataFlow::Node source, int delta
400+
AllocToInvalidPointerFlow::PathNode1 source1, PointerArithmeticInstruction pai,
401+
DataFlow::Node source, int delta
400402
) {
401403
exists(
402404
AllocToInvalidPointerFlow::PathNode1 p1, AllocToInvalidPointerFlow::PathNode2 p2,
403405
DataFlow::Node sink1, DataFlow::Node sink2, int delta0
404406
|
405407
pragma[only_bind_out](p1.getNode()) = sink1 and
406408
pragma[only_bind_out](p2.getNode()) = sink2 and
407-
AllocToInvalidPointerFlow::flowPath(_, _, pragma[only_bind_into](p1), pragma[only_bind_into](p2)) and
409+
AllocToInvalidPointerFlow::flowPath(source1, _, pragma[only_bind_into](p1),
410+
pragma[only_bind_into](p2)) and
408411
// Note that `delta` is not necessarily equal to `delta0`:
409412
// `delta0` is the constant offset added to the size of the allocation, and
410413
// delta is the constant difference between the pointer-arithmetic instruction
@@ -418,9 +421,7 @@ predicate invalidPointerToDerefSource(
418421

419422
newtype TMergedPathNode =
420423
// The path nodes computed by the first projection of `AllocToInvalidPointerConfig`
421-
TPathNode1(AllocToInvalidPointerFlow::PathNode1 p) or
422-
// The path nodes computed by `InvalidPointerToDerefConfig`
423-
TPathNode3(InvalidPointerToDerefFlow::PathNode p) or
424+
TFinalPathNode(FinalFlow::PathNode p) or
424425
// The read/write that uses the invalid pointer identified by `InvalidPointerToDerefConfig`.
425426
// This one is needed because the sink identified by `InvalidPointerToDerefConfig` is the
426427
// pointer, but we want to raise an alert at the dereference.
@@ -435,9 +436,7 @@ newtype TMergedPathNode =
435436
class MergedPathNode extends TMergedPathNode {
436437
string toString() { none() }
437438

438-
final AllocToInvalidPointerFlow::PathNode1 asPathNode1() { this = TPathNode1(result) }
439-
440-
final InvalidPointerToDerefFlow::PathNode asPathNode3() { this = TPathNode3(result) }
439+
final FinalFlow::PathNode asPathNode() { this = TFinalPathNode(result) }
441440

442441
final Instruction asSinkNode() { this = TPathNodeSink(result) }
443442

@@ -448,33 +447,18 @@ class MergedPathNode extends TMergedPathNode {
448447
}
449448
}
450449

451-
class PathNode1 extends MergedPathNode, TPathNode1 {
452-
override string toString() {
453-
exists(AllocToInvalidPointerFlow::PathNode1 p |
454-
this = TPathNode1(p) and
455-
result = p.toString()
456-
)
457-
}
458-
459-
override predicate hasLocationInfo(
460-
string filepath, int startline, int startcolumn, int endline, int endcolumn
461-
) {
462-
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
463-
}
464-
}
465-
466-
class PathNode3 extends MergedPathNode, TPathNode3 {
450+
class FinalPathNode extends MergedPathNode, TFinalPathNode {
467451
override string toString() {
468-
exists(InvalidPointerToDerefFlow::PathNode p |
469-
this = TPathNode3(p) and
452+
exists(FinalFlow::PathNode p |
453+
this = TFinalPathNode(p) and
470454
result = p.toString()
471455
)
472456
}
473457

474458
override predicate hasLocationInfo(
475459
string filepath, int startline, int startcolumn, int endline, int endcolumn
476460
) {
477-
this.asPathNode3().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
461+
this.asPathNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
478462
}
479463
}
480464

@@ -495,65 +479,88 @@ class PathSinkNode extends MergedPathNode, TPathNodeSink {
495479
}
496480
}
497481

482+
/**
483+
* A configuration that represents the full dataflow path all the way from
484+
* the allocation to the dereference. We need this final dataflow traversal
485+
* to ensure that the transition from the sink in `AllocToInvalidPointerConfig`
486+
* to the source in `InvalidPointerToDerefFlow` did not make us construct an
487+
* infeasible path (which can happen since the transition from one configuration
488+
* to the next does not preserve information about call contexts).
489+
*/
490+
module FinalConfig implements DataFlow::StateConfigSig {
491+
newtype FlowState =
492+
additional TInitial() or
493+
additional TPointerArith(PointerArithmeticInstruction pai, int delta) {
494+
invalidPointerToDerefSource(_, pai, _, delta)
495+
}
496+
497+
predicate isSource(DataFlow::Node source, FlowState state) {
498+
state = TInitial() and
499+
exists(AllocToInvalidPointerFlow::PathNode1 p, DataFlow::Node derefSource |
500+
source = p.getNode() and
501+
invalidPointerToDerefSource(p, _, derefSource, _) and
502+
InvalidPointerToDerefFlow::flow(derefSource, _)
503+
)
504+
}
505+
506+
predicate isSink(DataFlow::Node sink, FlowState state) {
507+
exists(DataFlow::Node source, PointerArithmeticInstruction pai, int delta |
508+
InvalidPointerToDerefFlow::flow(source, sink) and
509+
invalidPointerToDerefSource(_, pai, source, delta) and
510+
state = TPointerArith(pai, delta)
511+
)
512+
}
513+
514+
predicate isBarrier(DataFlow::Node n, FlowState state) { none() }
515+
516+
predicate isAdditionalFlowStep(
517+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
518+
) {
519+
exists(
520+
PointerArithmeticInstruction pai, AllocToInvalidPointerFlow::PathNode1 p1,
521+
InvalidPointerToDerefFlow::PathNode p2, int delta
522+
|
523+
isSinkImpl(pai, node1, _, _) and
524+
invalidPointerToDerefSource(_, pai, node2, delta) and
525+
node1 = p1.getNode() and
526+
node2 = p2.getNode() and
527+
state1 = TInitial() and
528+
state2 = TPointerArith(pai, delta)
529+
)
530+
}
531+
}
532+
533+
module FinalFlow = DataFlow::GlobalWithState<FinalConfig>;
534+
498535
query predicate edges(MergedPathNode node1, MergedPathNode node2) {
499-
node1.asPathNode1().getASuccessor() = node2.asPathNode1()
500-
or
501-
joinOn1(_, node1.asPathNode1(), node2.asPathNode3())
536+
FinalFlow::PathGraph::edges(node1.asPathNode(), node2.asPathNode())
502537
or
503-
node1.asPathNode3().getASuccessor() = node2.asPathNode3()
504-
or
505-
joinOn2(node1.asPathNode3(), node2.asSinkNode(), _, _)
538+
isInvalidPointerDerefSink(node1.asPathNode().getNode(), node2.asSinkNode(), _, _)
506539
}
507540

508541
query predicate nodes(MergedPathNode n, string key, string val) {
509-
AllocToInvalidPointerFlow::PathGraph1::nodes(n.asPathNode1(), key, val)
510-
or
511-
InvalidPointerToDerefFlow::PathGraph::nodes(n.asPathNode3(), key, val)
542+
FinalFlow::PathGraph::nodes(n.asPathNode(), key, val)
512543
or
513544
key = "semmle.label" and val = n.asSinkNode().toString()
514545
}
515546

516547
query predicate subpaths(
517548
MergedPathNode arg, MergedPathNode par, MergedPathNode ret, MergedPathNode out
518549
) {
519-
AllocToInvalidPointerFlow::PathGraph1::subpaths(arg.asPathNode1(), par.asPathNode1(),
520-
ret.asPathNode1(), out.asPathNode1())
521-
or
522-
InvalidPointerToDerefFlow::PathGraph::subpaths(arg.asPathNode3(), par.asPathNode3(),
523-
ret.asPathNode3(), out.asPathNode3())
524-
}
525-
526-
/**
527-
* Holds if `p1` is a sink of `AllocToInvalidPointerConfig` and `p2` is a source
528-
* of `InvalidPointerToDerefConfig`, and they are connected through `pai`.
529-
*/
530-
predicate joinOn1(
531-
PointerArithmeticInstruction pai, AllocToInvalidPointerFlow::PathNode1 p1,
532-
InvalidPointerToDerefFlow::PathNode p2
533-
) {
534-
isSinkImpl(pai, p1.getNode(), _, _) and
535-
invalidPointerToDerefSource(pai, p2.getNode(), _)
536-
}
537-
538-
/**
539-
* Holds if `p1` is a sink of `InvalidPointerToDerefConfig` and `i` is the instruction
540-
* that dereferences `p1`. The string `operation` describes whether the `i` is
541-
* a `StoreInstruction` or `LoadInstruction`.
542-
*/
543-
pragma[inline]
544-
predicate joinOn2(InvalidPointerToDerefFlow::PathNode p1, Instruction i, string operation, int delta) {
545-
isInvalidPointerDerefSink(p1.getNode(), i, operation, delta)
550+
FinalFlow::PathGraph::subpaths(arg.asPathNode(), par.asPathNode(), ret.asPathNode(),
551+
out.asPathNode())
546552
}
547553

548554
predicate hasFlowPath(
549-
MergedPathNode source1, MergedPathNode sink, InvalidPointerToDerefFlow::PathNode source3,
555+
MergedPathNode source, MergedPathNode sink, InvalidPointerToDerefFlow::PathNode source3,
550556
PointerArithmeticInstruction pai, string operation, int delta
551557
) {
552-
exists(InvalidPointerToDerefFlow::PathNode sink3, AllocToInvalidPointerFlow::PathNode1 sink1 |
553-
AllocToInvalidPointerFlow::flowPath(source1.asPathNode1(), _, sink1, _) and
554-
joinOn1(pai, sink1, source3) and
555-
InvalidPointerToDerefFlow::flowPath(source3, sink3) and
556-
joinOn2(sink3, sink.asSinkNode(), operation, delta)
558+
exists(FinalFlow::PathNode sink3, int delta0, int delta1 |
559+
FinalFlow::flowPath(source.asPathNode(), sink3) and
560+
invalidPointerToDerefSource(_, pai, source3.getNode(), delta0) and
561+
sink3.getState() = FinalConfig::TPointerArith(pai, delta0) and
562+
isInvalidPointerDerefSink(sink3.getNode(), sink.asSinkNode(), operation, delta1) and
563+
delta = delta0 + delta1
557564
)
558565
}
559566

@@ -564,12 +571,12 @@ where
564571
k =
565572
min(int k2, int k3, InvalidPointerToDerefFlow::PathNode source3 |
566573
hasFlowPath(source, sink, source3, pai, operation, k3) and
567-
invalidPointerToDerefSource(pai, source3.getNode(), k2)
574+
invalidPointerToDerefSource(_, pai, source3.getNode(), k2)
568575
|
569576
k2 + k3
570577
) and
571578
offset = pai.getRight().getUnconvertedResultExpression() and
572-
n = source.asPathNode1().getNode() and
579+
n = source.asPathNode().getNode() and
573580
if k = 0 then kstr = "" else kstr = " + " + k
574581
select sink, source, sink,
575582
"This " + operation + " might be out of bounds, as the pointer might be equal to $@ + $@" + kstr +

0 commit comments

Comments
 (0)