Skip to content

Commit aa7c677

Browse files
committed
Merge branch '51-2cppnon-constant-format-alter-not-const-source' into cpp-non-constant-format-as-path-query
# Conflicts: # cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
2 parents d6b0746 + c38376a commit aa7c677

File tree

4 files changed

+34
-38
lines changed

4 files changed

+34
-38
lines changed

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

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,10 @@
44
* to a mismatch between the number of arguments defined by the 'format' and the number
55
* of arguments actually passed to the function. If the format string ultimately stems
66
* from an untrusted source, this can be used for exploits.
7-
* This query finds all sources leading to a format string that cannot be verified to be literal.
8-
* Even if the format string type is `const char*` it is still considered non-constant if the
9-
* value is not a string literal. For example, a parameter to a function that is never observed to be called
10-
* that takes in a `const char*` and uses it as a format string, there is no way to verify the originating
11-
* value was a string literal. This is especially problematic with conversion of c strings to char *,
12-
* via `c_str()`, which returns a `const char*`, regardless if the original string was a string literal or not.
13-
* The query does not consider uninitialized variables as non-constant sources. Uninitialized
14-
* variables are a separate vulnerability concern and should be addressed by a separate query.
7+
* This query finds format strings coming from non-literal sources. Note that format strings of
8+
* type `const char*` it is still considered non-constant if the value is not coming from a string
9+
* literal. For example, for a parameter with type `const char*` of an exported function that is
10+
* used as a format string, there is no way to ensure the originating value was a string literal.
1511
* @kind path-problem
1612
* @problem.severity recommendation
1713
* @security-severity 9.3
@@ -30,7 +26,6 @@ import semmle.code.cpp.ir.dataflow.internal.ModelUtil
3026
import semmle.code.cpp.models.interfaces.DataFlow
3127
import semmle.code.cpp.models.interfaces.Taint
3228
import semmle.code.cpp.ir.IR
33-
import NonConstFlow::PathGraph
3429

3530
class UncalledFunction extends Function {
3631
UncalledFunction() {
@@ -73,11 +68,7 @@ predicate isNonConst(DataFlow::Node node) {
7368
// Parameters of uncalled functions that aren't const
7469
exists(UncalledFunction f, Parameter p |
7570
f.getAParameter() = p and
76-
p = node.asParameter() and
77-
// Exclude main in this instance since that should have its own defined FlowSource
78-
// and including main would likely result in redundancy.
79-
// Note, argc is not a flow source, so only filter out argv
80-
(p.getFunction().getName() = "main" implies not p.getName() = "argv")
71+
p = node.asParameter()
8172
)
8273
or
8374
// Consider as an input any out arg of a function or a function's return where the function is not:
@@ -86,21 +77,25 @@ predicate isNonConst(DataFlow::Node node) {
8677
// i.e., functions that with unknown bodies and are not known to define the output through its input
8778
// are considered as possible non-const sources
8879
// The function's output must also not be const to be considered a non-const source
89-
exists(Call c |
90-
exists(Expr arg | c.getAnArgument() = arg | arg = node.asDefiningArgument())
80+
exists(Function func, CallInstruction call |
81+
// NOTE: could use `Call` getAnArgument() instead of `CallInstruction` but requires two
82+
// variables representing the same call in ordoer to use `callOutput` below.
83+
exists(Expr arg |
84+
call.getPositionalArgumentOperand(_).getDef().getUnconvertedResultExpression() = arg and
85+
arg = node.asDefiningArgument()
86+
)
9187
or
92-
c = node.asIndirectExpr()
93-
) and
94-
not exists(FunctionInput input, FunctionOutput output, CallInstruction call |
95-
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
96-
// variant function's output are now possible non-const sources
97-
(
98-
pragma[only_bind_out](call.getStaticCallTarget())
99-
.(DataFlowFunction)
100-
.hasDataFlow(input, output) or
101-
pragma[only_bind_out](call.getStaticCallTarget()).(TaintFunction).hasTaintFlow(input, output)
102-
) and
103-
node = callOutput(call, output)
88+
call.getUnconvertedResultExpression() = node.asIndirectExpr()
89+
|
90+
func = call.getStaticCallTarget() and
91+
not exists(FunctionOutput output |
92+
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
93+
// variant function's output are now possible non-const sources
94+
pragma[only_bind_out](func).(DataFlowFunction).hasDataFlow(_, output) or
95+
pragma[only_bind_out](func).(TaintFunction).hasTaintFlow(_, output)
96+
|
97+
node = callOutput(call, output)
98+
)
10499
) and
105100
not exists(Call c |
106101
c.getTarget().hasDefinition() and
@@ -132,13 +127,11 @@ module NonConstFlowConfig implements DataFlow::ConfigSig {
132127

133128
module NonConstFlow = TaintTracking::Global<NonConstFlowConfig>;
134129

135-
from
136-
FormattingFunctionCall call, Expr formatString, NonConstFlow::PathNode sink,
137-
NonConstFlow::PathNode source
130+
from FormattingFunctionCall call, Expr formatString, DataFlow::Node sink
138131
where
139-
isSinkImpl(sink.getNode(), formatString) and
140132
call.getArgument(call.getFormatParameterIndex()) = formatString and
141-
NonConstFlow::flowPath(source, sink)
142-
select sink.getNode(), source, sink,
143-
"The format string argument to $@ has a source which cannot be " +
144-
"verified to originate from a string literal.", call, call.getTarget().getName()
133+
NonConstFlow::flowTo(sink) and
134+
isSinkImpl(sink, formatString)
135+
select formatString,
136+
"The format string argument to " + call.getTarget().getName() +
137+
" should be constant to prevent security issues and other potential errors."
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The "non-constant format string" query (`cpp/non-constant-format`) has been updated to produce fewer false positives.

cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/consts/NonConstantFormat.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
| consts.cpp:81:9:81:10 | c8 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
21
| consts.cpp:86:9:86:10 | v1 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
32
| consts.cpp:91:9:91:10 | v2 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
43
| consts.cpp:95:9:95:10 | v3 | The format string argument to printf should be constant to prevent security issues and other potential errors. |

cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/consts/consts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void a() {
7575
// GOOD: constFuncToArray() always returns a value from gc1, which is always constant
7676
printf(constFuncToArray(0));
7777

78-
// BAD: format string is not constant
78+
// BAD: format string is not constant [NOT DETECTED]
7979
char c8[10];
8080
sprintf(c8, "%d", 1);
8181
printf(c8);

0 commit comments

Comments
 (0)