Skip to content

Commit 9fc2405

Browse files
committed
Updating non-const source logic and associated tests and expected files.
1 parent 80bf38d commit 9fc2405

File tree

4 files changed

+82
-35
lines changed

4 files changed

+82
-35
lines changed

cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,51 +26,94 @@ import semmle.code.cpp.ir.implementation.raw.Instruction
2626
class UncalledFunction extends Function {
2727
UncalledFunction() {
2828
not exists(Call c | c.getTarget() = this) and
29-
not this.(MemberFunction).overrides(_)
29+
// TODO: Need rationale here, added based on suggestion
30+
//but unclear of the scenario being avoided
31+
not this.(MemberFunction).overrides(_) and
32+
// Ignore functions that appear to be function pointers
33+
not exists(FunctionAccess fa | fa.getTarget() = this)
3034
}
3135
}
3236

3337
/**
3438
* Holds if `node` is a non-constant source of data flow.
3539
* This is defined as either:
3640
* 1) a `FlowSource`
37-
* 2) a parameter of an 'uncalled' function (i.e., a function that is not called in the program)
41+
* 2) a parameter of an 'uncalled' function
3842
* 3) an argument to a function with no definition that is not known to define the output through its input
3943
* 4) an out arg of a function with no definition that is not known to define the output through its input
4044
*
45+
* With exception to `FlowSource` all non-const values have a type that is not const
46+
* (declared without a `const` specifier)
47+
* ASSUMPTION: any const values are assumed to be static if their assignment is not seen (
48+
* i.e., assuming users did not get non-const data and cast into a const
49+
*
4150
* The latter two cases address identifying standard string manipulation libraries as input sources
4251
* e.g., strcpy, but it will identify unknown function calls as possible non-constant source
4352
* since it cannot be determined if the out argument or return is constant.
4453
*/
4554
predicate isNonConst(DataFlow::Node node) {
4655
node instanceof FlowSource
4756
or
48-
exists(UncalledFunction f | f.getAParameter() = node.asParameter())
57+
// Parameters of uncalled functions that aren't const
58+
exists(UncalledFunction f, Parameter p, Type t |
59+
not t.isConst() and
60+
f.getAParameter() = p and
61+
p = node.asParameter() and
62+
not f.getType().getUnderlyingType().isConst() and
63+
(
64+
// const char* means (const char)*, so the pointer is not const, the pointed to value is
65+
// Grab the base type if a pointer, as this is the type we will check for const-ness
66+
if p.getType().getUnderlyingType() instanceof PointerType
67+
then t = p.getType().getUnderlyingType().(PointerType).getBaseType()
68+
else t = p.getType().getUnderlyingType()
69+
)
70+
)
4971
or
5072
// Consider as an input any out arg of a function or a function's return where the function is not:
5173
// 1. a function with a known dataflow or taintflow from input to output and the `node` is the output
5274
// 2. a function where there is a known definition
5375
// i.e., functions that with unknown bodies and are not known to define the output through its input
5476
// are considered as possible non-const sources
55-
(
56-
node instanceof DataFlow::DefinitionByReferenceNode or
57-
node.asIndirectExpr() instanceof Call
58-
) and
59-
not exists(Function func, FunctionInput input, FunctionOutput output, CallInstruction call |
60-
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
61-
// variant function's output are now possible non-const sources
62-
(
63-
func.(DataFlowFunction).hasDataFlow(input, output) or
64-
func.(TaintFunction).hasTaintFlow(input, output)
77+
// The function's output must also not be const to be considered a non-const source
78+
exists(Type t |
79+
not t.isConst() and
80+
exists(Call c |
81+
exists(Expr arg | c.getAnArgument() = arg |
82+
arg = node.asDefiningArgument() and
83+
(
84+
// const char* means (const char)*, so the pointer is not const, the pointed to value is
85+
// Grab the base type if a pointer, as this is the type we will check for const-ness
86+
if arg.getType().getUnderlyingType() instanceof PointerType
87+
then t = arg.getType().getUnderlyingType().(PointerType).getBaseType()
88+
else t = arg.getType().getUnderlyingType()
89+
)
90+
)
91+
or
92+
c = node.asIndirectExpr() and
93+
(
94+
// const char* means (const char)*, so the pointer is not const, the pointed to value is
95+
// Grab the base type if a pointer, as this is the type we will check for const-ness
96+
if c.getType().getUnderlyingType() instanceof PointerType
97+
then t = c.getType().getUnderlyingType().(PointerType).getBaseType()
98+
else t = c.getType().getUnderlyingType()
99+
)
100+
) and
101+
not exists(Function func, FunctionInput input, FunctionOutput output, CallInstruction call |
102+
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
103+
// variant function's output are now possible non-const sources
104+
(
105+
func.(DataFlowFunction).hasDataFlow(input, output) or
106+
func.(TaintFunction).hasTaintFlow(input, output)
107+
) and
108+
node = callOutput(call, output) and
109+
call.getStaticCallTarget() = func
65110
) and
66-
node = callOutput(call, output) and
67-
call.getStaticCallTarget() = func
68-
) and
69-
not exists(Call c |
70-
c.getTarget().hasDefinition() and
71-
if node instanceof DataFlow::DefinitionByReferenceNode
72-
then c.getAnArgument() = node.asDefiningArgument()
73-
else c = [node.asExpr(), node.asIndirectExpr()]
111+
not exists(Call c |
112+
c.getTarget().hasDefinition() and
113+
if node instanceof DataFlow::DefinitionByReferenceNode
114+
then c.getAnArgument() = node.asDefiningArgument()
115+
else c = [node.asExpr(), node.asIndirectExpr()]
116+
)
74117
)
75118
}
76119

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
| NonConstantFormat.c:30:10:30:16 | access to array | The format string argument to printf should be constant to prevent security issues and other potential errors. |
22
| NonConstantFormat.c:41:9:41:27 | call to any_random_function | The format string argument to printf should be constant to prevent security issues and other potential errors. |
3-
| nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. |
4-
| 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. |
3+
| NonConstantFormat.c:45:9:45:48 | call to gettext | The format string argument to printf should be constant to prevent security issues and other potential errors. |
54
| nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
65
| 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. |
7-
| test.cpp:57:12:57:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
8-
| 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. |
9-
| test.cpp:61:12:61:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
10-
| test.cpp:62:12:62:17 | + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
11-
| test.cpp:63:12:63:18 | * ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
12-
| test.cpp:64:12:64:18 | & ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
13-
| test.cpp:65:12:65:39 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
146
| test.cpp:130:20:130:26 | access to array | The format string argument to sprintf should be constant to prevent security issues and other potential errors. |
157
| test.cpp:157:12:157:15 | data | The format string argument to printf should be constant to prevent security issues and other potential errors. |
8+
| test.cpp:170:12:170:14 | res | The format string argument to printf should be constant to prevent security issues and other potential errors. |
9+
| test.cpp:195:31:195:33 | str | The format string argument to StringCchPrintfW should be constant to prevent security issues and other potential errors. |
10+
| test.cpp:197:11:197:14 | wstr | The format string argument to wprintf should be constant to prevent security issues and other potential errors. |
11+
| test.cpp:205:12:205:20 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
12+
| test.cpp:206:12:206:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
13+
| test.cpp:211:12:211:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
14+
| test.cpp:217:12:217:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
15+
| test.cpp:223:12:223:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
16+
| test.cpp:228:12:228:18 | ++ ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
17+
| test.cpp:235:12:235:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
18+
| test.cpp:242:12:242:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
19+
| test.cpp:247:12:247:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |

cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/nested.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extern "C" int snprintf ( char * s, int n, const char * format, ... );
1818
struct A {
1919
void do_print(const char *fmt0) {
2020
char buf[32];
21-
snprintf(buf, 32, fmt0); // BAD through call from c.do_some_printing(c.ext_fmt_str())
21+
snprintf(buf, 32, fmt0); // GOOD, all paths to year use const char*
2222
}
2323
};
2424

@@ -76,12 +76,12 @@ void diagnostic(const char *fmt, ...)
7676
}
7777

7878
void bar(void) {
79-
diagnostic (some_instance->get_fmt()); // BAD
79+
diagnostic (some_instance->get_fmt()); // GOOD get_fmt is const char* assumed static
8080
}
8181

8282
namespace ns {
8383

84-
class blab {
84+
class blab {
8585
void out1(void) {
8686
char *fmt = (char *)__builtin_alloca(10);
8787
diagnostic(fmt); // BAD

cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ int main(int argc, char **argv) {
107107
printf(hello); // GOOD
108108
}
109109
if (gettext_debug) {
110-
printf(new char[100]); // BAD [FALSE NEGATIVE]
110+
printf(new char[100]); // BAD [FALSE NEGATIVE: uninitialized value not considered a source]
111111
}
112112
{
113113
const char *hello = "Hello, World\n";
@@ -197,7 +197,7 @@ void wchar_t_test_bad(wchar_t* str){
197197
wprintf(wstr); // BAD
198198
}
199199

200-
const char* get_string();
200+
char* get_string();
201201

202202
void pointer_arithmetic_test_on_bad_string(){
203203
{

0 commit comments

Comments
 (0)