Skip to content

Commit f308be7

Browse files
committed
C++: Restore the missing flow. This has a couple of side-effects: First, it gives us some new good flow (yay). Second, it causes some duplication of results that uses 'argv' as a taint source. The duplication isn't very bad, though. And since it is only for paths that start at 'argv', I think we can live with it for now.
1 parent dbcd4d6 commit f308be7

File tree

2 files changed

+70
-16
lines changed

2 files changed

+70
-16
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
806806
simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
807807
or
808808
// Flow into, through, and out of store nodes
809-
StoreNodeFlow::flowInto(nodeFrom, nodeTo)
809+
StoreNodeFlow::flowInto(nodeFrom.asInstruction(), nodeTo)
810810
or
811811
StoreNodeFlow::flowThrough(nodeFrom, nodeTo)
812812
or
@@ -831,18 +831,11 @@ private predicate adjacentDefUseFlow(Node nodeFrom, Node nodeTo) {
831831
//Def-use flow
832832
Ssa::ssaFlow(nodeFrom, nodeTo)
833833
or
834-
exists(Instruction loadAddress | loadAddress = Ssa::getSourceAddressFromNode(nodeFrom) |
835-
// Use-use flow through reads
836-
exists(Node address |
837-
Ssa::addressFlowTC(address.asInstruction(), loadAddress) and
838-
Ssa::ssaFlow(address, nodeTo)
839-
)
840-
or
841-
// Use-use flow through stores.
842-
exists(Node store |
843-
Ssa::explicitWrite(_, store.asInstruction(), loadAddress) and
844-
Ssa::ssaFlow(store, nodeTo)
845-
)
834+
// Use-use flow through stores.
835+
exists(Instruction loadAddress, Node store |
836+
loadAddress = Ssa::getSourceAddressFromNode(nodeFrom) and
837+
Ssa::explicitWrite(_, store.asInstruction(), loadAddress) and
838+
Ssa::ssaFlow(store, nodeTo)
846839
)
847840
)
848841
}
@@ -906,10 +899,13 @@ private module ReadNodeFlow {
906899
}
907900
}
908901

909-
private module StoreNodeFlow {
902+
/**
903+
* INTERNAL: Do not use.
904+
*/
905+
module StoreNodeFlow {
910906
/** Holds if the store node `nodeTo` should receive flow from `nodeFrom`. */
911-
predicate flowInto(Node nodeFrom, StoreNode nodeTo) {
912-
nodeTo.flowInto(Ssa::getDestinationAddress(nodeFrom.asInstruction()))
907+
predicate flowInto(Instruction instrFrom, StoreNode nodeTo) {
908+
nodeTo.flowInto(Ssa::getDestinationAddress(instrFrom))
913909
}
914910

915911
/** Holds if the store node `nodeTo` should receive flow from `nodeFom`. */

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,64 @@ private module Cached {
494494
explicitWrite(false, storeNode.getStoreInstruction(), def)
495495
)
496496
or
497+
// The destination of a store operation has undergone lvalue-to-rvalue conversion and is now a
498+
// right-hand-side of a store operation.
499+
// Find the next use of the variable in that store operation, and recursively find the load of that
500+
// pointer. For example, consider this case:
501+
//
502+
// ```cpp
503+
// int x = source();
504+
// int* p = &x;
505+
// sink(*p);
506+
// ```
507+
//
508+
// if we want to find the load of the address of `x`, we see that the pointer is stored into `p`,
509+
// and we then need to recursively look for the load of `p`.
510+
exists(
511+
Def def, StoreInstruction store, IRBlock block1, int rnk1, Use use, IRBlock block2, int rnk2
512+
|
513+
store = def.getInstruction() and
514+
store.getSourceValueOperand() = operand and
515+
def.hasRankInBlock(block1, rnk1) and
516+
use.hasRankInBlock(block2, rnk2) and
517+
adjacentDefRead(_, block1, rnk1, block2, rnk2)
518+
|
519+
// The shared SSA library has determined that `use` is the next use of the operand
520+
// so we find the next load of that use (but only if there is no `PostUpdateNode`) we
521+
// need to flow into first.
522+
not StoreNodeFlow::flowInto(store, _) and
523+
flowOutOfAddressStep(use.getOperand(), nodeTo)
524+
or
525+
// It may also be the case that `store` gives rise to another store step. So let's make sure that
526+
// we also take those into account.
527+
StoreNodeFlow::flowInto(store, nodeTo)
528+
)
529+
or
530+
// As we find the next load of an address, we might come across another use of the same variable.
531+
// In that case, we recursively find the next use of _that_ operand, and continue searching for
532+
// the next load of that operand. For example, consider this case:
533+
//
534+
// ```cpp
535+
// int x = source();
536+
// use(&x);
537+
// int* p = &x;
538+
// sink(*p);
539+
// ```
540+
//
541+
// The next use of `x` after its definition is `use(&x)`, but there is a later load of the address
542+
// of `x` that we want to flow to. So we use the shared SSA library to find the next load.
543+
not operand = getSourceAddressOperand(_) and
544+
exists(Use use1, Use use2, IRBlock block1, int rnk1, IRBlock block2, int rnk2 |
545+
use1.getOperand() = operand and
546+
use1.hasRankInBlock(block1, rnk1) and
547+
// Don't flow to the next use if this use is part of a store operation that totally
548+
// overrides a variable.
549+
not explicitWrite(true, _, use1.getOperand().getDef()) and
550+
adjacentDefRead(_, block1, rnk1, block2, rnk2) and
551+
use2.hasRankInBlock(block2, rnk2) and
552+
flowOutOfAddressStep(use2.getOperand(), nodeTo)
553+
)
554+
or
497555
operand = getSourceAddressOperand(nodeTo.asInstruction())
498556
or
499557
exists(ReturnIndirectionInstruction ret |

0 commit comments

Comments
 (0)