Hey, I was trying to understand why my query didn't find specific alerts.
Basically, I wanted to find uses of &foo which are being checked for whether it is (un)equal to NULL, because &foo should never be NULL in UB-free code (as far as I understand the standard).
However, it failed for if (&global_var == NULL) while it works for if (&global_var) and local variables and parameter variables.
(I thought that maybe CodeQL does some similar optimizations where it knows that &foo is always non-NULL (but only for global variables) and that's why it's not being detected, but that doesn't really make sense, because equivalent formulations were also not detected)
So I looked at the IR graph using this query:
/**
* @name ff
* @description aa
* @kind graph
* @id aaaaa
* @tags security
*/
import cpp
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.PrintIR
import semmle.code.cpp.Declaration
private class Foo extends PrintIRConfiguration {
override predicate shouldPrintDeclaration(Declaration decl) {
decl.(Function).getName() = "test_global_variables"
}
}
and then running codeql database analyze --format=dot --output=lol.dot PATH_TO_TEST_DB IR_Print.ql which gave me the dot file in this issue.
And importantly, there is a big difference between the same patterns and global_var, local_var (and param_var).
For global_var there is some constant folding being done, but also not for all cases.
I don't know whether the folding is intentional and local and parameter vars just not yet perform this folding?
Anyway, is there a way to "fix" this problem and if so, how?
Version: 2.24.0
Testfile
#define NULL ((void*)0)
int global_var;
// Test global variables
void test_global_variables(int param_var) {
int local_var;
if (&global_var == NULL) {} // $ Missing: Alert
if (&global_var != NULL) {} // $ Missing: Alert
if (&global_var) {} // $ Alert
if (!&global_var) {} // $ Missing: Alert
if (&local_var == NULL) {} // $ Alert
if (&local_var != NULL) {} // $ Alert
if (&local_var) {} // $ Alert
if (!&local_var) {} // $ Alert
if (¶m_var == NULL) {} // $ Alert
if (¶m_var != NULL) {} // $ Alert
if (¶m_var) {} // $ Alert
if (!¶m_var) {} // $ Alert
}
IR Graph dot
digraph {
compound=true;
subgraph cluster_0 {
0[shape=point, width=0];
"label"="void test_global_variables(int)";
subgraph cluster_1 {
1[shape=point, width=0];
"label"="Block 0";
2["label"="v6_1(void) = EnterFunction : "; ];
3["label"="m6_2(unknown) = AliasedDefinition : "; ];
4["label"="m6_3(unknown) = InitializeNonLocal : "; ];
5["label"="m6_4(unknown) = Chi : total:m6_2, partial:m6_3"; ];
6["label"="r6_5(glval<int>) = VariableAddress[param_var] : "; ];
7["label"="m6_6(int) = InitializeParameter[param_var] : &:r6_5"; ];
8["label"="r7_1(glval<int>) = VariableAddress[local_var] : "; ];
9["label"="m7_2(int) = Uninitialized[local_var] : &:r7_1"; ];
10["label"="r9_1(int) = Constant[0] : "; ];
11["label"="r9_2(int) = Constant[0] : "; ];
12["label"="r9_3(bool) = CompareNE : r9_1, r9_2"; ];
13["label"="v9_4(void) = ConditionalBranch : r9_3"; ];
}
subgraph cluster_14 {
14[shape=point, width=0];
"label"="Block 1";
15["label"="r10_1(int) = Constant[1] : "; ];
16["label"="r10_2(int) = Constant[0] : "; ];
17["label"="r10_3(bool) = CompareNE : r10_1, r10_2"; ];
18["label"="v10_4(void) = ConditionalBranch : r10_3"; ];
}
subgraph cluster_19 {
19[shape=point, width=0];
"label"="Block 2";
20["label"="v10_5(void) = NoOp : "; ];
21["label"="r11_1(glval<int>) = VariableAddress[global_var] : "; ];
22["label"="r11_2(int *) = CopyValue : r11_1"; ];
23["label"="r11_3(int *) = Constant[0] : "; ];
24["label"="r11_4(bool) = CompareNE : r11_2, r11_3"; ];
25["label"="v11_5(void) = ConditionalBranch : r11_4"; ];
}
subgraph cluster_26 {
26[shape=point, width=0];
"label"="Block 3";
27["label"="v11_6(void) = NoOp : "; ];
}
subgraph cluster_28 {
28[shape=point, width=0];
"label"="Block 4";
29["label"="r12_1(int) = Constant[0] : "; ];
30["label"="r12_2(int) = Constant[0] : "; ];
31["label"="r12_3(bool) = CompareNE : r12_1, r12_2"; ];
32["label"="v12_4(void) = ConditionalBranch : r12_3"; ];
}
subgraph cluster_33 {
33[shape=point, width=0];
"label"="Block 5";
34["label"="r14_1(glval<int>) = VariableAddress[local_var] : "; ];
35["label"="r14_2(int *) = CopyValue : r14_1"; ];
36["label"="r14_3(int *) = Constant[0] : "; ];
37["label"="r14_4(bool) = CompareEQ : r14_2, r14_3"; ];
38["label"="v14_5(void) = ConditionalBranch : r14_4"; ];
}
subgraph cluster_39 {
39[shape=point, width=0];
"label"="Block 6";
40["label"="v14_6(void) = NoOp : "; ];
}
subgraph cluster_41 {
41[shape=point, width=0];
"label"="Block 7";
42["label"="r15_1(glval<int>) = VariableAddress[local_var] : "; ];
43["label"="r15_2(int *) = CopyValue : r15_1"; ];
44["label"="r15_3(int *) = Constant[0] : "; ];
45["label"="r15_4(bool) = CompareNE : r15_2, r15_3"; ];
46["label"="v15_5(void) = ConditionalBranch : r15_4"; ];
}
subgraph cluster_47 {
47[shape=point, width=0];
"label"="Block 8";
48["label"="v15_6(void) = NoOp : "; ];
}
subgraph cluster_49 {
49[shape=point, width=0];
"label"="Block 9";
50["label"="r16_1(glval<int>) = VariableAddress[local_var] : "; ];
51["label"="r16_2(int *) = CopyValue : r16_1"; ];
52["label"="r16_3(int *) = Constant[0] : "; ];
53["label"="r16_4(bool) = CompareNE : r16_2, r16_3"; ];
54["label"="v16_5(void) = ConditionalBranch : r16_4"; ];
}
subgraph cluster_55 {
55[shape=point, width=0];
"label"="Block 10";
56["label"="v16_6(void) = NoOp : "; ];
}
subgraph cluster_57 {
57[shape=point, width=0];
"label"="Block 11";
58["label"="r17_1(glval<int>) = VariableAddress[local_var] : "; ];
59["label"="r17_2(int *) = CopyValue : r17_1"; ];
60["label"="r17_3(int *) = Constant[0] : "; ];
61["label"="r17_4(bool) = CompareEQ : r17_2, r17_3"; ];
62["label"="v17_5(void) = ConditionalBranch : r17_4"; ];
}
subgraph cluster_63 {
63[shape=point, width=0];
"label"="Block 12";
64["label"="v17_6(void) = NoOp : "; ];
}
subgraph cluster_65 {
65[shape=point, width=0];
"label"="Block 13";
66["label"="r19_1(glval<int>) = VariableAddress[param_var] : "; ];
67["label"="r19_2(int *) = CopyValue : r19_1"; ];
68["label"="r19_3(int *) = Constant[0] : "; ];
69["label"="r19_4(bool) = CompareEQ : r19_2, r19_3"; ];
70["label"="v19_5(void) = ConditionalBranch : r19_4"; ];
}
subgraph cluster_71 {
71[shape=point, width=0];
"label"="Block 14";
72["label"="v19_6(void) = NoOp : "; ];
}
subgraph cluster_73 {
73[shape=point, width=0];
"label"="Block 15";
74["label"="r20_1(glval<int>) = VariableAddress[param_var] : "; ];
75["label"="r20_2(int *) = CopyValue : r20_1"; ];
76["label"="r20_3(int *) = Constant[0] : "; ];
77["label"="r20_4(bool) = CompareNE : r20_2, r20_3"; ];
78["label"="v20_5(void) = ConditionalBranch : r20_4"; ];
}
subgraph cluster_79 {
79[shape=point, width=0];
"label"="Block 16";
80["label"="v20_6(void) = NoOp : "; ];
}
subgraph cluster_81 {
81[shape=point, width=0];
"label"="Block 17";
82["label"="r21_1(glval<int>) = VariableAddress[param_var] : "; ];
83["label"="r21_2(int *) = CopyValue : r21_1"; ];
84["label"="r21_3(int *) = Constant[0] : "; ];
85["label"="r21_4(bool) = CompareNE : r21_2, r21_3"; ];
86["label"="v21_5(void) = ConditionalBranch : r21_4"; ];
}
subgraph cluster_87 {
87[shape=point, width=0];
"label"="Block 18";
88["label"="v21_6(void) = NoOp : "; ];
}
subgraph cluster_89 {
89[shape=point, width=0];
"label"="Block 19";
90["label"="r22_1(glval<int>) = VariableAddress[param_var] : "; ];
91["label"="r22_2(int *) = CopyValue : r22_1"; ];
92["label"="r22_3(int *) = Constant[0] : "; ];
93["label"="r22_4(bool) = CompareEQ : r22_2, r22_3"; ];
94["label"="v22_5(void) = ConditionalBranch : r22_4"; ];
}
subgraph cluster_95 {
95[shape=point, width=0];
"label"="Block 20";
96["label"="v22_6(void) = NoOp : "; ];
}
subgraph cluster_97 {
97[shape=point, width=0];
"label"="Block 21";
98["label"="v23_1(void) = NoOp : "; ];
99["label"="v6_7(void) = ReturnVoid : "; ];
100["label"="v6_8(void) = AliasedUse : m6_3"; ];
101["label"="v6_9(void) = ExitFunction : "; ];
}
subgraph cluster_102 {
102[shape=point, width=0];
"label"="Block 22";
103["label"="v6_10(void) = Unreached : "; ];
}
}
1 -> 14["label"="False"; "ltail"="cluster_1"; "lhead"="cluster_14"; ];
19 -> 28["label"="False"; "ltail"="cluster_19"; "lhead"="cluster_28"; ];
26 -> 28["label"="Goto"; "ltail"="cluster_26"; "lhead"="cluster_28"; ];
39 -> 41["label"="Goto"; "ltail"="cluster_39"; "lhead"="cluster_41"; ];
47 -> 49["label"="Goto"; "ltail"="cluster_47"; "lhead"="cluster_49"; ];
55 -> 57["label"="Goto"; "ltail"="cluster_55"; "lhead"="cluster_57"; ];
63 -> 65["label"="Goto"; "ltail"="cluster_63"; "lhead"="cluster_65"; ];
71 -> 73["label"="Goto"; "ltail"="cluster_71"; "lhead"="cluster_73"; ];
79 -> 81["label"="Goto"; "ltail"="cluster_79"; "lhead"="cluster_81"; ];
87 -> 89["label"="Goto"; "ltail"="cluster_87"; "lhead"="cluster_89"; ];
95 -> 97["label"="Goto"; "ltail"="cluster_95"; "lhead"="cluster_97"; ];
14 -> 102["label"="False"; "ltail"="cluster_14"; "lhead"="cluster_102"; ];
28 -> 33["label"="False"; "ltail"="cluster_28"; "lhead"="cluster_33"; ];
33 -> 41["label"="False"; "ltail"="cluster_33"; "lhead"="cluster_41"; ];
41 -> 49["label"="False"; "ltail"="cluster_41"; "lhead"="cluster_49"; ];
49 -> 57["label"="False"; "ltail"="cluster_49"; "lhead"="cluster_57"; ];
57 -> 65["label"="False"; "ltail"="cluster_57"; "lhead"="cluster_65"; ];
65 -> 73["label"="False"; "ltail"="cluster_65"; "lhead"="cluster_73"; ];
73 -> 81["label"="False"; "ltail"="cluster_73"; "lhead"="cluster_81"; ];
81 -> 89["label"="False"; "ltail"="cluster_81"; "lhead"="cluster_89"; ];
89 -> 97["label"="False"; "ltail"="cluster_89"; "lhead"="cluster_97"; ];
1 -> 102["label"="True"; "ltail"="cluster_1"; "lhead"="cluster_102"; ];
19 -> 26["label"="True"; "ltail"="cluster_19"; "lhead"="cluster_26"; ];
14 -> 19["label"="True"; "ltail"="cluster_14"; "lhead"="cluster_19"; ];
28 -> 102["label"="True"; "ltail"="cluster_28"; "lhead"="cluster_102"; ];
33 -> 39["label"="True"; "ltail"="cluster_33"; "lhead"="cluster_39"; ];
41 -> 47["label"="True"; "ltail"="cluster_41"; "lhead"="cluster_47"; ];
49 -> 55["label"="True"; "ltail"="cluster_49"; "lhead"="cluster_55"; ];
57 -> 63["label"="True"; "ltail"="cluster_57"; "lhead"="cluster_63"; ];
65 -> 71["label"="True"; "ltail"="cluster_65"; "lhead"="cluster_71"; ];
73 -> 79["label"="True"; "ltail"="cluster_73"; "lhead"="cluster_79"; ];
81 -> 87["label"="True"; "ltail"="cluster_81"; "lhead"="cluster_87"; ];
89 -> 95["label"="True"; "ltail"="cluster_89"; "lhead"="cluster_95"; ];
}
Impacted query
/**
* @name Suspicious check on address-of expression
* @description Taking the address of a valid object always produces a non-NULL
* pointer, so checking the result against NULL is either redundant
* or indicates a bug where a different check was intended.
* @kind problem
* @problem.severity warning
* @precision high
* @id asymmetric-research/suspicious-address-of-null-check
* @tags reliability
* correctness
* readability
*/
import cpp
import semmle.code.cpp.ir.dataflow.MustFlow
import semmle.code.cpp.controlflow.Guards
private class MustFlowConfig extends MustFlowConfiguration {
MustFlowConfig() { this = "SuspiciousAddressOfNullCheckMustFlow" }
override predicate isSource(Instruction source) {
source.getUnconvertedResultExpression() instanceof ValidAddressOfExpr
}
override predicate isSink(Operand sink) { isNullOrNonNullCheck(sink) }
override predicate allowInterproceduralFlow() { none() }
override predicate isBarrier(Instruction instr) {
instr instanceof InitializeParameterInstruction
}
}
predicate debug(Instruction instr, string instrType/*, Expr expr, string exprType*/, int startline) {
instr.getLocation().getStartLine() = startline and
instr.getLocation().getStartLine() = [28,82, 83] and
// instr.getLocation().getEndLine() <= 86 and
// instr.getUnconvertedResultExpression() = expr and
instrType = instr.getAQlClass() and
any()
// exprType = expr.getAQlClass()
}
/**
* Technically, we'd ignore cases where we do something like &foo->first_field (where foo is a pointer)
* this is because if foo is NULL, the expression could actually evaluate to NULL.
* However, it doesn't seem likely that anyone would write code like that intentionally
* and it's also undefined behavior to dereference a NULL pointer anyway.
*/
class ValidAddressOfExpr extends AddressOfExpr { }
predicate isNullOrNonNullCheck(Operand operand) {
any(IRGuardCondition gc).comparesEq(operand, 0, _, _)
}
from MustFlowPathNode source, MustFlowPathNode sink, MustFlowConfig cfg
where
cfg.hasFlowPath(source, sink) and
not source.getInstruction().getUnconvertedResultExpression().isInMacroExpansion() and
source.getInstruction().getEnclosingFunction() = sink.getInstruction().getEnclosingFunction()
select sink, "Taking the address of $@ always produces a non-NULL pointer, but it is checked $@.",
source.getInstruction().getUnconvertedResultExpression().(ValidAddressOfExpr).getOperand(),
"this operand", sink, "here"
Hey, I was trying to understand why my query didn't find specific alerts.
Basically, I wanted to find uses of
&foowhich are being checked for whether it is (un)equal toNULL, because&fooshould never beNULLin UB-free code (as far as I understand the standard).However, it failed for
if (&global_var == NULL)while it works forif (&global_var)and local variables and parameter variables.(I thought that maybe CodeQL does some similar optimizations where it knows that
&foois always non-NULL (but only for global variables) and that's why it's not being detected, but that doesn't really make sense, because equivalent formulations were also not detected)So I looked at the IR graph using this query:
and then running
codeql database analyze --format=dot --output=lol.dot PATH_TO_TEST_DB IR_Print.qlwhich gave me the dot file in this issue.And importantly, there is a big difference between the same patterns and
global_var,local_var(andparam_var).For
global_varthere is some constant folding being done, but also not for all cases.I don't know whether the folding is intentional and local and parameter vars just not yet perform this folding?
Anyway, is there a way to "fix" this problem and if so, how?
Version: 2.24.0
Testfile
IR Graph dot
Impacted query