C++ field flow: Add test.

This is a fairly direct translation of the Java field flow test to
C++. Not all the `// flow` annotations are currently accurate.
This commit is contained in:
Pavel Avgustinov
2019-07-11 11:14:22 +01:00
committed by Jonas Jensen
parent dccc0f4db1
commit 6d4d131ad4
5 changed files with 417 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
class A
{
class C
{
public:
virtual void f(void *) {}
};
class C1 : public C
{
public:
A *a;
};
class C2 : public C
{
};
class B
{
public:
C *c;
B() {}
B(C *c)
{
this->c = c;
}
void set(C *c) { this->c = c; }
C *get() { return this->c; }
static B *make(C *c)
{
return new B(c);
}
};
public:
void f0()
{
C cc;
C ct;
cc.f(nullptr);
ct.f(new C());
sink(&cc); // no flow
sink(&ct); // flow
}
void f1()
{
C *c = new C();
B *b = B::make(c);
sink(b->c); // flow
}
void f2()
{
B *b = new B();
b->set(new C1());
sink(b->get()); // flow
sink((new B(new C()))->get()); // flow
}
void f3()
{
B *b1 = new B();
B *b2;
b2 = setOnB(b1, new C2());
sink(b1->c); // no flow
sink(b2->c); // flow
}
void f4()
{
B *b1 = new B();
B *b2;
b2 = setOnBWrap(b1, new C2());
sink(b1->c); // no flow
sink(b2->c); // flow
}
B *setOnBWrap(B *b1, C *c)
{
B *b2;
b2 = setOnB(b1, c);
return r() ? b1 : b2;
}
A::B *setOnB(B *b1, C *c)
{
if (r())
{
B *b2 = new B();
b2->set(c);
return b2;
}
return b1;
}
void f5()
{
A *a = new A();
C1 *c1 = new C1();
c1->a = a;
f6(c1);
}
void f6(C *c)
{
if (C1 *c1 = dynamic_cast<C1 *>(c))
{
sink(c1->a); // flow
}
C *cc;
if (C2 *c2 = dynamic_cast<C2 *>(c))
{
cc = c2;
}
else
{
cc = new C1();
}
if (C1 *c1 = dynamic_cast<C1 *>(cc))
{
sink(c1->a); // no flow, stopped by cast to C2
}
}
void f7(B *b)
{
b->set(new C());
}
void f8()
{
B *b = new B();
f7(b);
sink(b->c); // flow
}
class D
{
public:
A::B *b;
D(A::B *b, bool x)
{
b->c = new C();
this->b = x ? b : new B();
}
};
void
f9()
{
B *b = new B();
D *d = new D(b, r());
sink(d->b); // flow x2
sink(d->b->c); // flow
sink(b->c); // flow
}
void f10()
{
B *b = new B();
MyList *l1 = new MyList(b, new MyList(nullptr, nullptr));
MyList *l2 = new MyList(nullptr, l1);
MyList *l3 = new MyList(nullptr, l2);
sink(l3->head); // no flow, b is nested beneath at least one ->next
sink(l3->next->head); // flow, the precise nesting depth isn't tracked
sink(l3->next->next->head); // flow
sink(l3->next->next->next->head); // flow
for (MyList *l = l3; l != nullptr; l = l->next)
{
sink(l->head); // flow
}
}
static void sink(void *o) {}
bool r() { return reinterpret_cast<long long>(this) % 10 > 5; }
class MyList
{
public:
B *head;
MyList *next;
MyList(B *newHead, MyList *next)
{
head = newHead;
this->next = next;
}
};
};

View File

@@ -0,0 +1,49 @@
class B
{
void f1()
{
Elem *e = new Elem();
Box1 *b1 = new Box1(e, nullptr);
Box2 *b2 = new Box2(b1);
sink(b2->box1->elem1); // flow
sink(b2->box1->elem2); // FP due to flow in f2 below
}
void f2()
{
Elem *e = new B::Elem();
Box1 *b1 = new B::Box1(nullptr, e);
Box2 *b2 = new Box2(b1);
sink(b2->box1->elem1); // FP due to flow in f1 above
sink(b2->box1->elem2); // flow
}
static void sink(void *o) {}
class Elem
{
};
class Box1
{
public:
Elem *elem1;
Elem *elem2;
Box1(Elem *e1, Elem *e2)
{
this->elem1 = e1;
this->elem2 = e2;
}
};
class Box2
{
public:
Box1 *box1;
Box2(Box1 *b1)
{
this->box1 = b1;
}
};
};

View File

@@ -0,0 +1,37 @@
class C
{
class Elem
{
};
private:
Elem *s1 = new Elem();
const Elem *s2 = new Elem();
Elem *s3;
public:
const static Elem *s4;
void main(void)
{
C *c = new C();
c->func();
}
C() : s1(new Elem())
{
this->s3 = new Elem();
}
void func()
{
sink(s1);
sink(s2);
sink(s3);
sink(s4);
}
static void sink(const void *o) {}
};
const C::Elem *C::s4 = new Elem();

View File

@@ -0,0 +1,112 @@
edges
| A.cpp:41:10:41:16 | new [void] | A.cpp:43:10:43:12 | & ... |
| A.cpp:47:12:47:18 | new [void] | A.cpp:48:20:48:20 | c [void] |
| A.cpp:48:12:48:18 | call to make [c, ... (1)] | A.cpp:49:10:49:10 | b [c, ... (1)] |
| A.cpp:48:20:48:20 | c [void] | A.cpp:48:12:48:18 | call to make [c, ... (1)] |
| A.cpp:49:10:49:10 | b [c, ... (1)] | A.cpp:49:13:49:13 | c |
| A.cpp:55:5:55:5 | b [post update] [c, ... (1)] | A.cpp:56:10:56:10 | b [c, ... (1)] |
| A.cpp:55:12:55:19 | new [void] | A.cpp:55:5:55:5 | b [post update] [c, ... (1)] |
| A.cpp:56:10:56:10 | b [c, ... (1)] | A.cpp:56:13:56:15 | call to get |
| A.cpp:57:11:57:24 | call to B [post update] [c, ... (1)] | A.cpp:57:11:57:24 | new [c, ... (1)] |
| A.cpp:57:11:57:24 | new [c, ... (1)] | A.cpp:57:28:57:30 | call to get |
| A.cpp:57:17:57:23 | new [void] | A.cpp:57:11:57:24 | call to B [post update] [c, ... (1)] |
| A.cpp:64:10:64:15 | call to setOnB [c, ... (1)] | A.cpp:66:10:66:11 | b2 [c, ... (1)] |
| A.cpp:64:21:64:28 | new [void] | A.cpp:64:10:64:15 | call to setOnB [c, ... (1)] |
| A.cpp:66:10:66:11 | b2 [c, ... (1)] | A.cpp:66:14:66:14 | c |
| A.cpp:73:10:73:19 | call to setOnBWrap [c, ... (1)] | A.cpp:75:10:75:11 | b2 [c, ... (1)] |
| A.cpp:73:25:73:32 | new [void] | A.cpp:73:10:73:19 | call to setOnBWrap [c, ... (1)] |
| A.cpp:75:10:75:11 | b2 [c, ... (1)] | A.cpp:75:14:75:14 | c |
| A.cpp:98:12:98:18 | new [void] | A.cpp:100:5:100:13 | ... = ... [void] |
| A.cpp:100:5:100:6 | c1 [post update] [a, ... (1)] | A.cpp:101:8:101:9 | c1 [a, ... (1)] |
| A.cpp:100:5:100:13 | ... = ... [void] | A.cpp:100:5:100:6 | c1 [post update] [a, ... (1)] |
| A.cpp:101:8:101:9 | c1 [a, ... (1)] | A.cpp:103:14:103:14 | c [a, ... (1)] |
| A.cpp:103:14:103:14 | c [a, ... (1)] | A.cpp:107:12:107:13 | c1 [a, ... (1)] |
| A.cpp:103:14:103:14 | c [a, ... (1)] | A.cpp:120:12:120:13 | c1 [a, ... (1)] |
| A.cpp:107:12:107:13 | c1 [a, ... (1)] | A.cpp:107:16:107:16 | a |
| A.cpp:120:12:120:13 | c1 [a, ... (1)] | A.cpp:120:16:120:16 | a |
| A.cpp:126:5:126:5 | b [post update] [c, ... (1)] | A.cpp:131:8:131:8 | b [post update] [c, ... (1)] |
| A.cpp:126:12:126:18 | new [void] | A.cpp:126:5:126:5 | b [post update] [c, ... (1)] |
| A.cpp:131:8:131:8 | b [post update] [c, ... (1)] | A.cpp:132:10:132:10 | b [c, ... (1)] |
| A.cpp:132:10:132:10 | b [c, ... (1)] | A.cpp:132:13:132:13 | c |
| A.cpp:142:7:142:7 | b [post update] [c, ... (1)] | A.cpp:143:7:143:31 | ... = ... [c, ... (1)] |
| A.cpp:142:7:142:7 | b [post update] [c, ... (1)] | A.cpp:151:18:151:18 | b [post update] [c, ... (1)] |
| A.cpp:142:7:142:20 | ... = ... [void] | A.cpp:142:7:142:7 | b [post update] [c, ... (1)] |
| A.cpp:142:14:142:20 | new [void] | A.cpp:142:7:142:20 | ... = ... [void] |
| A.cpp:143:7:143:10 | this [post update] [b, ... (1)] | A.cpp:151:12:151:24 | call to D [post update] [b, ... (1)] |
| A.cpp:143:7:143:10 | this [post update] [b, ... (2)] | A.cpp:151:12:151:24 | call to D [post update] [b, ... (2)] |
| A.cpp:143:7:143:31 | ... = ... [c, ... (1)] | A.cpp:143:7:143:10 | this [post update] [b, ... (2)] |
| A.cpp:143:7:143:31 | ... = ... [void] | A.cpp:143:7:143:10 | this [post update] [b, ... (1)] |
| A.cpp:143:25:143:31 | new [void] | A.cpp:143:7:143:31 | ... = ... [void] |
| A.cpp:150:12:150:18 | new [void] | A.cpp:151:18:151:18 | b [void] |
| A.cpp:151:12:151:24 | call to D [post update] [b, ... (1)] | A.cpp:152:10:152:10 | d [b, ... (1)] |
| A.cpp:151:12:151:24 | call to D [post update] [b, ... (2)] | A.cpp:153:10:153:10 | d [b, ... (2)] |
| A.cpp:151:18:151:18 | b [post update] [c, ... (1)] | A.cpp:154:10:154:10 | b [c, ... (1)] |
| A.cpp:151:18:151:18 | b [void] | A.cpp:151:12:151:24 | call to D [post update] [b, ... (1)] |
| A.cpp:152:10:152:10 | d [b, ... (1)] | A.cpp:152:13:152:13 | b |
| A.cpp:153:10:153:10 | d [b, ... (2)] | A.cpp:153:13:153:13 | b [c, ... (1)] |
| A.cpp:153:13:153:13 | b [c, ... (1)] | A.cpp:153:16:153:16 | c |
| A.cpp:154:10:154:10 | b [c, ... (1)] | A.cpp:154:13:154:13 | c |
| A.cpp:159:12:159:18 | new [void] | A.cpp:160:29:160:29 | b [void] |
| A.cpp:160:18:160:60 | call to MyList [post update] [head, ... (1)] | A.cpp:161:38:161:39 | l1 [head, ... (1)] |
| A.cpp:160:29:160:29 | b [void] | A.cpp:160:18:160:60 | call to MyList [post update] [head, ... (1)] |
| A.cpp:161:18:161:40 | call to MyList [post update] [next, ... (2)] | A.cpp:162:38:162:39 | l2 [next, ... (2)] |
| A.cpp:161:38:161:39 | l1 [head, ... (1)] | A.cpp:161:18:161:40 | call to MyList [post update] [next, ... (2)] |
| A.cpp:162:18:162:40 | call to MyList [post update] [next, ... (3)] | A.cpp:165:10:165:11 | l3 [next, ... (3)] |
| A.cpp:162:18:162:40 | call to MyList [post update] [next, ... (3)] | A.cpp:167:44:167:44 | l [next, ... (3)] |
| A.cpp:162:38:162:39 | l2 [next, ... (2)] | A.cpp:162:18:162:40 | call to MyList [post update] [next, ... (3)] |
| A.cpp:165:10:165:11 | l3 [next, ... (3)] | A.cpp:165:14:165:17 | next [next, ... (2)] |
| A.cpp:165:14:165:17 | next [next, ... (2)] | A.cpp:165:20:165:23 | next [head, ... (1)] |
| A.cpp:165:20:165:23 | next [head, ... (1)] | A.cpp:165:26:165:29 | head |
| A.cpp:167:44:167:44 | l [next, ... (2)] | A.cpp:167:47:167:50 | next [head, ... (1)] |
| A.cpp:167:44:167:44 | l [next, ... (3)] | A.cpp:167:47:167:50 | next [next, ... (2)] |
| A.cpp:167:47:167:50 | next [head, ... (1)] | A.cpp:169:12:169:12 | l [head, ... (1)] |
| A.cpp:167:47:167:50 | next [next, ... (2)] | A.cpp:167:44:167:44 | l [next, ... (2)] |
| A.cpp:169:12:169:12 | l [head, ... (1)] | A.cpp:169:15:169:18 | head |
| B.cpp:6:15:6:24 | new [void] | B.cpp:7:25:7:25 | e [void] |
| B.cpp:7:16:7:35 | call to Box1 [post update] [elem1, ... (1)] | B.cpp:8:25:8:26 | b1 [elem1, ... (1)] |
| B.cpp:7:25:7:25 | e [void] | B.cpp:7:16:7:35 | call to Box1 [post update] [elem1, ... (1)] |
| B.cpp:8:16:8:27 | call to Box2 [post update] [box1, ... (2)] | B.cpp:9:10:9:11 | b2 [box1, ... (2)] |
| B.cpp:8:16:8:27 | call to Box2 [post update] [box1, ... (2)] | B.cpp:10:10:10:11 | b2 [box1, ... (2)] |
| B.cpp:8:25:8:26 | b1 [elem1, ... (1)] | B.cpp:8:16:8:27 | call to Box2 [post update] [box1, ... (2)] |
| B.cpp:9:10:9:11 | b2 [box1, ... (2)] | B.cpp:9:14:9:17 | box1 [elem1, ... (1)] |
| B.cpp:9:14:9:17 | box1 [elem1, ... (1)] | B.cpp:9:20:9:24 | elem1 |
| B.cpp:10:10:10:11 | b2 [box1, ... (2)] | B.cpp:10:14:10:17 | box1 [elem2, ... (1)] |
| B.cpp:10:14:10:17 | box1 [elem2, ... (1)] | B.cpp:10:20:10:24 | elem2 |
| B.cpp:15:15:15:27 | new [void] | B.cpp:16:37:16:37 | e [void] |
| B.cpp:16:16:16:38 | call to Box1 [post update] [elem2, ... (1)] | B.cpp:17:25:17:26 | b1 [elem2, ... (1)] |
| B.cpp:16:37:16:37 | e [void] | B.cpp:16:16:16:38 | call to Box1 [post update] [elem2, ... (1)] |
| B.cpp:17:16:17:27 | call to Box2 [post update] [box1, ... (2)] | B.cpp:18:10:18:11 | b2 [box1, ... (2)] |
| B.cpp:17:16:17:27 | call to Box2 [post update] [box1, ... (2)] | B.cpp:19:10:19:11 | b2 [box1, ... (2)] |
| B.cpp:17:25:17:26 | b1 [elem2, ... (1)] | B.cpp:17:16:17:27 | call to Box2 [post update] [box1, ... (2)] |
| B.cpp:18:10:18:11 | b2 [box1, ... (2)] | B.cpp:18:14:18:17 | box1 [elem1, ... (1)] |
| B.cpp:18:14:18:17 | box1 [elem1, ... (1)] | B.cpp:18:20:18:24 | elem1 |
| B.cpp:19:10:19:11 | b2 [box1, ... (2)] | B.cpp:19:14:19:17 | box1 [elem2, ... (1)] |
| B.cpp:19:14:19:17 | box1 [elem2, ... (1)] | B.cpp:19:20:19:24 | elem2 |
| C.cpp:18:12:18:18 | call to C [post update] [s3, ... (1)] | C.cpp:19:5:19:5 | c [s3, ... (1)] |
| C.cpp:19:5:19:5 | c [s3, ... (1)] | C.cpp:27:8:27:11 | `this` parameter in func [s3, ... (1)] |
| C.cpp:24:5:24:8 | this [post update] [s3, ... (1)] | C.cpp:18:12:18:18 | call to C [post update] [s3, ... (1)] |
| C.cpp:24:5:24:25 | ... = ... [void] | C.cpp:24:5:24:8 | this [post update] [s3, ... (1)] |
| C.cpp:24:16:24:25 | new [void] | C.cpp:24:5:24:25 | ... = ... [void] |
| C.cpp:27:8:27:11 | `this` parameter in func [s3, ... (1)] | file://:0:0:0:0 | this [s3, ... (1)] |
| file://:0:0:0:0 | this [s3, ... (1)] | C.cpp:31:10:31:11 | s3 |
#select
| A.cpp:43:10:43:12 | & ... | A.cpp:41:10:41:16 | new [void] | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:10:41:16 | new [void] | new [void] |
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new [void] | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new [void] | new [void] |
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new [void] | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new [void] | new [void] |
| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new [void] | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new [void] | new [void] |
| A.cpp:66:14:66:14 | c | A.cpp:64:21:64:28 | new [void] | A.cpp:66:14:66:14 | c | c flows from $@ | A.cpp:64:21:64:28 | new [void] | new [void] |
| A.cpp:75:14:75:14 | c | A.cpp:73:25:73:32 | new [void] | A.cpp:75:14:75:14 | c | c flows from $@ | A.cpp:73:25:73:32 | new [void] | new [void] |
| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new [void] | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new [void] | new [void] |
| A.cpp:120:16:120:16 | a | A.cpp:98:12:98:18 | new [void] | A.cpp:120:16:120:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new [void] | new [void] |
| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new [void] | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new [void] | new [void] |
| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new [void] | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new [void] | new [void] |
| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new [void] | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new [void] | new [void] |
| A.cpp:153:16:153:16 | c | A.cpp:142:14:142:20 | new [void] | A.cpp:153:16:153:16 | c | c flows from $@ | A.cpp:142:14:142:20 | new [void] | new [void] |
| A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new [void] | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new [void] | new [void] |
| A.cpp:165:26:165:29 | head | A.cpp:159:12:159:18 | new [void] | A.cpp:165:26:165:29 | head | head flows from $@ | A.cpp:159:12:159:18 | new [void] | new [void] |
| A.cpp:169:15:169:18 | head | A.cpp:159:12:159:18 | new [void] | A.cpp:169:15:169:18 | head | head flows from $@ | A.cpp:159:12:159:18 | new [void] | new [void] |
| B.cpp:9:20:9:24 | elem1 | B.cpp:6:15:6:24 | new [void] | B.cpp:9:20:9:24 | elem1 | elem1 flows from $@ | B.cpp:6:15:6:24 | new [void] | new [void] |
| B.cpp:10:20:10:24 | elem2 | B.cpp:6:15:6:24 | new [void] | B.cpp:10:20:10:24 | elem2 | elem2 flows from $@ | B.cpp:6:15:6:24 | new [void] | new [void] |
| B.cpp:18:20:18:24 | elem1 | B.cpp:15:15:15:27 | new [void] | B.cpp:18:20:18:24 | elem1 | elem1 flows from $@ | B.cpp:15:15:15:27 | new [void] | new [void] |
| B.cpp:19:20:19:24 | elem2 | B.cpp:15:15:15:27 | new [void] | B.cpp:19:20:19:24 | elem2 | elem2 flows from $@ | B.cpp:15:15:15:27 | new [void] | new [void] |
| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new [void] | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new [void] | new [void] |

View File

@@ -0,0 +1,32 @@
/**
* @kind path-problem
*/
import semmle.code.cpp.dataflow.DataFlow
import DataFlow::PathGraph
import DataFlow
import cpp
class Conf extends Configuration {
Conf() { this = "FieldFlowConf" }
override predicate isSource(Node src) { src.asExpr() instanceof NewExpr }
override predicate isSink(Node sink) {
exists(Call c |
c.getTarget().hasName("sink") and
c.getAnArgument() = sink.asExpr()
)
}
override predicate isAdditionalFlowStep(Node a, Node b) {
b.asPartialDefinition() = any(Call c |
c.getTarget().hasName("f") and c.getAnArgument() = a.asExpr()
).getQualifier()
or
b.asExpr().(AddressOfExpr).getOperand() = a.asExpr()
}
}
from DataFlow::PathNode src, DataFlow::PathNode sink, Conf conf
where conf.hasFlowPath(src, sink)
select sink, src, sink, sink + " flows from $@", src, src.toString()