Skip to content

Commit 27c4408

Browse files
authored
Merge pull request github#12997 from MathiasVP/sync-product-flow-across-calls
C++: Synchronize product dataflow paths on function entry points
2 parents 4e31c46 + 26206a8 commit 27c4408

File tree

3 files changed

+129
-9
lines changed

3 files changed

+129
-9
lines changed

cpp/ql/lib/experimental/semmle/code/cpp/dataflow/ProductFlow.qll

Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import semmle.code.cpp.ir.dataflow.DataFlow
2+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
3+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
4+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
25
private import codeql.util.Unit
36

47
module ProductFlow {
@@ -352,32 +355,119 @@ module ProductFlow {
352355
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
353356
}
354357

358+
private newtype TKind =
359+
TInto(DataFlowCall call) {
360+
intoImpl1(_, _, call) or
361+
intoImpl2(_, _, call)
362+
} or
363+
TOutOf(DataFlowCall call) {
364+
outImpl1(_, _, call) or
365+
outImpl2(_, _, call)
366+
} or
367+
TJump()
368+
369+
private predicate intoImpl1(Flow1::PathNode pred1, Flow1::PathNode succ1, DataFlowCall call) {
370+
Flow1::PathGraph::edges(pred1, succ1) and
371+
pred1.getNode().(ArgumentNode).getCall() = call and
372+
succ1.getNode() instanceof ParameterNode
373+
}
374+
375+
private predicate into1(Flow1::PathNode pred1, Flow1::PathNode succ1, TKind kind) {
376+
exists(DataFlowCall call |
377+
kind = TInto(call) and
378+
intoImpl1(pred1, succ1, call)
379+
)
380+
}
381+
382+
private predicate outImpl1(Flow1::PathNode pred1, Flow1::PathNode succ1, DataFlowCall call) {
383+
Flow1::PathGraph::edges(pred1, succ1) and
384+
exists(ReturnKindExt returnKind |
385+
succ1.getNode() = returnKind.getAnOutNode(call) and
386+
pred1.getNode().(ReturnNodeExt).getKind() = returnKind
387+
)
388+
}
389+
390+
private predicate out1(Flow1::PathNode pred1, Flow1::PathNode succ1, TKind kind) {
391+
exists(DataFlowCall call |
392+
outImpl1(pred1, succ1, call) and
393+
kind = TOutOf(call)
394+
)
395+
}
396+
397+
private predicate intoImpl2(Flow2::PathNode pred2, Flow2::PathNode succ2, DataFlowCall call) {
398+
Flow2::PathGraph::edges(pred2, succ2) and
399+
pred2.getNode().(ArgumentNode).getCall() = call and
400+
succ2.getNode() instanceof ParameterNode
401+
}
402+
403+
private predicate into2(Flow2::PathNode pred2, Flow2::PathNode succ2, TKind kind) {
404+
exists(DataFlowCall call |
405+
kind = TInto(call) and
406+
intoImpl2(pred2, succ2, call)
407+
)
408+
}
409+
410+
private predicate outImpl2(Flow2::PathNode pred2, Flow2::PathNode succ2, DataFlowCall call) {
411+
Flow2::PathGraph::edges(pred2, succ2) and
412+
exists(ReturnKindExt returnKind |
413+
succ2.getNode() = returnKind.getAnOutNode(call) and
414+
pred2.getNode().(ReturnNodeExt).getKind() = returnKind
415+
)
416+
}
417+
418+
private predicate out2(Flow2::PathNode pred2, Flow2::PathNode succ2, TKind kind) {
419+
exists(DataFlowCall call |
420+
kind = TOutOf(call) and
421+
outImpl2(pred2, succ2, call)
422+
)
423+
}
424+
355425
pragma[nomagic]
356426
private predicate interprocEdge1(
357-
Declaration predDecl, Declaration succDecl, Flow1::PathNode pred1, Flow1::PathNode succ1
427+
Declaration predDecl, Declaration succDecl, Flow1::PathNode pred1, Flow1::PathNode succ1,
428+
TKind kind
358429
) {
359430
Flow1::PathGraph::edges(pred1, succ1) and
360431
predDecl != succDecl and
361432
pred1.getNode().getEnclosingCallable() = predDecl and
362-
succ1.getNode().getEnclosingCallable() = succDecl
433+
succ1.getNode().getEnclosingCallable() = succDecl and
434+
(
435+
into1(pred1, succ1, kind)
436+
or
437+
out1(pred1, succ1, kind)
438+
or
439+
kind = TJump() and
440+
not into1(pred1, succ1, _) and
441+
not out1(pred1, succ1, _)
442+
)
363443
}
364444

365445
pragma[nomagic]
366446
private predicate interprocEdge2(
367-
Declaration predDecl, Declaration succDecl, Flow2::PathNode pred2, Flow2::PathNode succ2
447+
Declaration predDecl, Declaration succDecl, Flow2::PathNode pred2, Flow2::PathNode succ2,
448+
TKind kind
368449
) {
369450
Flow2::PathGraph::edges(pred2, succ2) and
370451
predDecl != succDecl and
371452
pred2.getNode().getEnclosingCallable() = predDecl and
372-
succ2.getNode().getEnclosingCallable() = succDecl
453+
succ2.getNode().getEnclosingCallable() = succDecl and
454+
(
455+
into2(pred2, succ2, kind)
456+
or
457+
out2(pred2, succ2, kind)
458+
or
459+
kind = TJump() and
460+
not into2(pred2, succ2, _) and
461+
not out2(pred2, succ2, _)
462+
)
373463
}
374464

375465
private predicate interprocEdgePair(
376466
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2
377467
) {
378-
exists(Declaration predDecl, Declaration succDecl |
379-
interprocEdge1(predDecl, succDecl, pred1, succ1) and
380-
interprocEdge2(predDecl, succDecl, pred2, succ2)
468+
exists(Declaration predDecl, Declaration succDecl, TKind kind |
469+
interprocEdge1(predDecl, succDecl, pred1, succ1, kind) and
470+
interprocEdge2(predDecl, succDecl, pred2, succ2, kind)
381471
)
382472
}
383473

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-119/OverrunWriteProductFlow.expected

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,15 @@ edges
212212
| test.cpp:214:24:214:24 | p | test.cpp:216:10:216:10 | p |
213213
| test.cpp:220:43:220:48 | call to malloc | test.cpp:222:15:222:20 | buffer |
214214
| test.cpp:222:15:222:20 | buffer | test.cpp:214:24:214:24 | p |
215+
| test.cpp:225:40:225:45 | buffer | test.cpp:226:5:226:26 | ... = ... |
216+
| test.cpp:226:5:226:26 | ... = ... | test.cpp:226:12:226:17 | p_str indirection [post update] [string] |
217+
| test.cpp:231:27:231:32 | call to malloc | test.cpp:232:22:232:27 | buffer |
218+
| test.cpp:232:16:232:19 | set_string output argument [string] | test.cpp:233:12:233:14 | str indirection [string] |
219+
| test.cpp:232:22:232:27 | buffer | test.cpp:225:40:225:45 | buffer |
220+
| test.cpp:232:22:232:27 | buffer | test.cpp:232:16:232:19 | set_string output argument [string] |
221+
| test.cpp:233:12:233:14 | str indirection [string] | test.cpp:233:12:233:21 | string |
222+
| test.cpp:233:12:233:14 | str indirection [string] | test.cpp:233:16:233:21 | string indirection |
223+
| test.cpp:233:16:233:21 | string indirection | test.cpp:233:12:233:21 | string |
215224
nodes
216225
| test.cpp:16:11:16:21 | mk_string_t indirection [string] | semmle.label | mk_string_t indirection [string] |
217226
| test.cpp:18:5:18:30 | ... = ... | semmle.label | ... = ... |
@@ -381,7 +390,17 @@ nodes
381390
| test.cpp:216:10:216:10 | p | semmle.label | p |
382391
| test.cpp:220:43:220:48 | call to malloc | semmle.label | call to malloc |
383392
| test.cpp:222:15:222:20 | buffer | semmle.label | buffer |
393+
| test.cpp:225:40:225:45 | buffer | semmle.label | buffer |
394+
| test.cpp:226:5:226:26 | ... = ... | semmle.label | ... = ... |
395+
| test.cpp:226:12:226:17 | p_str indirection [post update] [string] | semmle.label | p_str indirection [post update] [string] |
396+
| test.cpp:231:27:231:32 | call to malloc | semmle.label | call to malloc |
397+
| test.cpp:232:16:232:19 | set_string output argument [string] | semmle.label | set_string output argument [string] |
398+
| test.cpp:232:22:232:27 | buffer | semmle.label | buffer |
399+
| test.cpp:233:12:233:14 | str indirection [string] | semmle.label | str indirection [string] |
400+
| test.cpp:233:12:233:21 | string | semmle.label | string |
401+
| test.cpp:233:16:233:21 | string indirection | semmle.label | string indirection |
384402
subpaths
403+
| test.cpp:232:22:232:27 | buffer | test.cpp:225:40:225:45 | buffer | test.cpp:226:12:226:17 | p_str indirection [post update] [string] | test.cpp:232:16:232:19 | set_string output argument [string] |
385404
#select
386405
| test.cpp:42:5:42:11 | call to strncpy | test.cpp:18:19:18:24 | call to malloc | test.cpp:42:18:42:23 | string | This write may overflow $@ by 1 element. | test.cpp:42:18:42:23 | string | string |
387406
| test.cpp:72:9:72:15 | call to strncpy | test.cpp:18:19:18:24 | call to malloc | test.cpp:72:22:72:27 | string | This write may overflow $@ by 1 element. | test.cpp:72:22:72:27 | string | string |
@@ -398,4 +417,4 @@ subpaths
398417
| test.cpp:199:9:199:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:199:22:199:27 | string | This write may overflow $@ by 2 elements. | test.cpp:199:22:199:27 | string | string |
399418
| test.cpp:203:9:203:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:203:22:203:27 | string | This write may overflow $@ by 2 elements. | test.cpp:203:22:203:27 | string | string |
400419
| test.cpp:207:9:207:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:207:22:207:27 | string | This write may overflow $@ by 3 elements. | test.cpp:207:22:207:27 | string | string |
401-
| test.cpp:216:3:216:8 | call to memset | test.cpp:220:43:220:48 | call to malloc | test.cpp:216:10:216:10 | p | This write may overflow $@ by 5 elements. | test.cpp:216:10:216:10 | p | p |
420+
| test.cpp:233:5:233:10 | call to memset | test.cpp:231:27:231:32 | call to malloc | test.cpp:233:12:233:21 | string | This write may overflow $@ by 1 element. | test.cpp:233:16:233:21 | string | string |

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-119/test.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,15 @@ void test_missing_call_context(unsigned char *unrelated_buffer, unsigned size) {
220220
unsigned char* buffer = (unsigned char*)malloc(size);
221221
call_memset(unrelated_buffer, size + 5);
222222
call_memset(buffer, size);
223-
}
223+
}
224+
225+
void set_string(string_t* p_str, char* buffer) {
226+
p_str->string = buffer;
227+
}
228+
229+
void test_flow_through_setter(unsigned size) {
230+
string_t str;
231+
char* buffer = (char*)malloc(size);
232+
set_string(&str, buffer);
233+
memset(str.string, 0, size + 1); // BAD
234+
}

0 commit comments

Comments
 (0)