Merge pull request #12966 from MathiasVP/dataflow-for-static-vars

C++: Dataflow for static local variables
This commit is contained in:
Mathias Vorreiter Pedersen
2023-05-02 11:52:43 +01:00
committed by GitHub
8 changed files with 101 additions and 13 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -607,13 +607,21 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result.getReturnKind() = kind result.getReturnKind() = kind
} }
/** A variable that behaves like a global variable. */
class GlobalLikeVariable extends Variable {
GlobalLikeVariable() {
this instanceof Cpp::GlobalOrNamespaceVariable or
this instanceof Cpp::StaticLocalVariable
}
}
/** /**
* Holds if data can flow from `node1` to `node2` in a way that loses the * Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a * calling context. For example, this would happen with flow through a
* global or static variable. * global or static variable.
*/ */
predicate jumpStep(Node n1, Node n2) { predicate jumpStep(Node n1, Node n2) {
exists(Cpp::GlobalOrNamespaceVariable v | exists(GlobalLikeVariable v |
exists(Ssa::GlobalUse globalUse | exists(Ssa::GlobalUse globalUse |
v = globalUse.getVariable() and v = globalUse.getVariable() and
n1.(FinalGlobalValue).getGlobalUse() = globalUse n1.(FinalGlobalValue).getGlobalUse() = globalUse

View File

@@ -145,14 +145,14 @@ private newtype TDefOrUseImpl =
or or
// Since the pruning stage doesn't know about global variables we can't use the above check to // Since the pruning stage doesn't know about global variables we can't use the above check to
// rule out dead assignments to globals. // rule out dead assignments to globals.
base.(VariableAddressInstruction).getAstVariable() instanceof Cpp::GlobalOrNamespaceVariable base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
) )
} or } or
TUseImpl(Operand operand, int indirectionIndex) { TUseImpl(Operand operand, int indirectionIndex) {
isUse(_, operand, _, _, indirectionIndex) and isUse(_, operand, _, _, indirectionIndex) and
not isDef(_, _, operand, _, _, _) not isDef(_, _, operand, _, _, _)
} or } or
TGlobalUse(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) { TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents a final "use" of a global variable to ensure that // Represents a final "use" of a global variable to ensure that
// the assignment to a global variable isn't ruled out as dead. // the assignment to a global variable isn't ruled out as dead.
exists(VariableAddressInstruction vai, int defIndex | exists(VariableAddressInstruction vai, int defIndex |
@@ -162,7 +162,7 @@ private newtype TDefOrUseImpl =
indirectionIndex = [0 .. defIndex] + 1 indirectionIndex = [0 .. defIndex] + 1
) )
} or } or
TGlobalDefImpl(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) { TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents the initial "definition" of a global variable when entering // Represents the initial "definition" of a global variable when entering
// a function body. // a function body.
exists(VariableAddressInstruction vai | exists(VariableAddressInstruction vai |
@@ -458,7 +458,7 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
} }
class GlobalUse extends UseImpl, TGlobalUse { class GlobalUse extends UseImpl, TGlobalUse {
Cpp::GlobalOrNamespaceVariable global; GlobalLikeVariable global;
IRFunction f; IRFunction f;
GlobalUse() { this = TGlobalUse(global, f, ind) } GlobalUse() { this = TGlobalUse(global, f, ind) }
@@ -468,7 +468,7 @@ class GlobalUse extends UseImpl, TGlobalUse {
override int getIndirection() { result = ind + 1 } override int getIndirection() { result = ind + 1 }
/** Gets the global variable associated with this use. */ /** Gets the global variable associated with this use. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global } GlobalLikeVariable getVariable() { result = global }
/** Gets the `IRFunction` whose body is exited from after this use. */ /** Gets the `IRFunction` whose body is exited from after this use. */
IRFunction getIRFunction() { result = f } IRFunction getIRFunction() { result = f }
@@ -496,14 +496,14 @@ class GlobalUse extends UseImpl, TGlobalUse {
} }
class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl { class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
Cpp::GlobalOrNamespaceVariable global; GlobalLikeVariable global;
IRFunction f; IRFunction f;
int indirectionIndex; int indirectionIndex;
GlobalDefImpl() { this = TGlobalDefImpl(global, f, indirectionIndex) } GlobalDefImpl() { this = TGlobalDefImpl(global, f, indirectionIndex) }
/** Gets the global variable associated with this definition. */ /** Gets the global variable associated with this definition. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global } GlobalLikeVariable getVariable() { result = global }
/** Gets the `IRFunction` whose body is evaluated after this definition. */ /** Gets the `IRFunction` whose body is evaluated after this definition. */
IRFunction getIRFunction() { result = f } IRFunction getIRFunction() { result = f }
@@ -760,13 +760,14 @@ private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
} }
private predicate sourceVariableIsGlobal( private predicate sourceVariableIsGlobal(
SourceVariable sv, Cpp::GlobalOrNamespaceVariable global, IRFunction func, int indirectionIndex SourceVariable sv, GlobalLikeVariable global, IRFunction func, int indirectionIndex
) { ) {
exists(IRVariable irVar, BaseIRVariable base | exists(IRVariable irVar, BaseIRVariable base |
sourceVariableHasBaseAndIndex(sv, base, indirectionIndex) and sourceVariableHasBaseAndIndex(sv, base, indirectionIndex) and
irVar = base.getIRVariable() and irVar = base.getIRVariable() and
irVar.getEnclosingIRFunction() = func and irVar.getEnclosingIRFunction() = func and
global = irVar.getAst() global = irVar.getAst() and
not irVar instanceof IRDynamicInitializationFlag
) )
} }
@@ -919,7 +920,7 @@ class GlobalDef extends TGlobalDef, SsaDefOrUse {
IRFunction getIRFunction() { result = global.getIRFunction() } IRFunction getIRFunction() { result = global.getIRFunction() }
/** Gets the global variable associated with this definition. */ /** Gets the global variable associated with this definition. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global.getVariable() } GlobalLikeVariable getVariable() { result = global.getVariable() }
} }
class Phi extends TPhi, SsaDefOrUse { class Phi extends TPhi, SsaDefOrUse {

View File

@@ -115,6 +115,16 @@ postWithInFlow
| test.cpp:602:3:602:7 | access to array [post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:602:3:602:7 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:608:3:608:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:608:3:608:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:608:4:608:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:608:4:608:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:639:3:639:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:646:3:646:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:652:3:652:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:653:3:653:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:659:3:659:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:660:3:660:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:671:3:671:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:681:3:681:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:689:3:689:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:690:3:690:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
viableImplInCallContextTooLarge viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition uniqueParameterNodeAtPosition
uniqueParameterNodePosition uniqueParameterNodePosition

View File

@@ -628,3 +628,65 @@ void test_def_via_phi_read(bool b)
intPointerSource(buffer); intPointerSource(buffer);
sink(buffer); // $ ast,ir sink(buffer); // $ ast,ir
} }
void test_static_local_1() {
static int x = source();
sink(x); // $ ast,ir
}
void test_static_local_2() {
static int x = source();
x = 0;
sink(x); // clean
}
void test_static_local_3() {
static int x = 0;
sink(x); // $ ir MISSING: ast
x = source();
}
void test_static_local_4() {
static int x = 0;
sink(x); // clean
x = source();
x = 0;
}
void test_static_local_5() {
static int x = 0;
sink(x); // $ ir MISSING: ast
x = 0;
x = source();
}
void test_static_local_6() {
static int s = source();
static int* ptr_to_s = &s;
sink(*ptr_to_s); // $ ir MISSING: ast
}
void test_static_local_7() {
static int s = source();
s = 0;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // clean
}
void test_static_local_8() {
static int s;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // $ ir MISSING: ast
s = source();
}
void test_static_local_9() {
static int s;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // clean
s = source();
s = 0;
}

View File

@@ -4,7 +4,9 @@ uniqueType
uniqueNodeLocation uniqueNodeLocation
missingLocation missingLocation
uniqueNodeToString uniqueNodeToString
| cpp11.cpp:50:15:50:16 | (no string representation) | Node should have one toString but has 0. |
missingToString missingToString
| Nodes without toString: 1 |
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal readStepIsLocal

View File

@@ -3,6 +3,7 @@
| nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. | | nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. |
| nested.cpp:79:32:79:38 | call to get_fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. | | nested.cpp:79:32:79:38 | call to get_fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
| nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. | | nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
| test.cpp:51:10:51:21 | call to make_message | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:57:12:57:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. | | test.cpp:57:12:57:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:60:12:60:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. | | test.cpp:60:12:60:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:61:12:61:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. | | test.cpp:61:12:61:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |

View File

@@ -48,7 +48,7 @@ int main(int argc, char **argv) {
printf(choose_message(argc - 1), argc - 1); // GOOD printf(choose_message(argc - 1), argc - 1); // GOOD
printf(messages[1]); // GOOD printf(messages[1]); // GOOD
printf(message); // GOOD printf(message); // GOOD
printf(make_message(argc - 1)); // BAD [NOT DETECTED] printf(make_message(argc - 1)); // BAD
printf("Hello, World\n"); // GOOD printf("Hello, World\n"); // GOOD
printf(_("Hello, World\n")); // GOOD printf(_("Hello, World\n")); // GOOD
{ {