daily report on extending static analyzer project [GSoC]
Ankur Saini
arsenic.secondary@gmail.com
Wed Jul 28 15:49:04 GMT 2021
AIM For Today:
- Make the anlalyzer evaluate the vfunc calls by creating enodes and eedges
representing the call.
- Make the analyzer not treat such calls as call to "unknown function"
---
PROGRESS :
- After the analyzer sucessfully found which funcitons to call ( thanks to
functions from ipa-devirt.c ), All that was left was it to create enodes and
eedges for analyzeing such calls for which I took the same aproach I used to
detect and analyzer calls via function pointers.
- now the analyzer was sucessfully decting and analyzing the calls via
functions pointers, but due to some reason it was still not giving out
analysis reports for such calls.
- After a bit of digging, I found out that analyzer was also treating such
calls as " call to unknown funcitons " and in case of such calls, analyzer
plays safe by resetting all of the state-machine state for the values that
are reachable by the function call, to avoid false reports. ( it was also
mentioned in a previous discussions <https://gcc.gnu.org/pipermail/gcc/2021-April/235335.html> on the list)
- I fixed the porblem by simply updating exploded_node::on_stmt () by setting
the value of "unknown_side_effects" ( a boolean which is set true when
analyzer thinks executing a gimple stmt would cause some side effects, and
acts conservatively for such cases ) to "false" when gimple call stmt is a
polymorphic call. Some thing like this, at the end of the definition of
exploded_node::on_stmt (): -
File: {src-dir}/gcc/analyzer/engine.cc
1246: /* If the statmement is a polymorphic call then assume
1247: there are no side effects. */
1248: gimple *call_stmt = const_cast<gimple *>(stmt);
1249: if (gcall *call = dyn_cast<gcall *> (call_stmt))
1250: {
1251: function *fun = this->get_function();
1252: cgraph_edge *e = cgraph_node::get (fun->decl)->get_edge (call);
1253: if ((e && e->indirect_info) && (e->indirect_info->polymorphic))
1254: unknown_side_effects = false;
1255: }
1256:
1257: on_stmt_post (stmt, state, unknown_side_effects, &ctxt);
1258:
1259: return on_stmt_flags ();
- After this the analyzer was working as expected for virtual function calls on
all of the examples I created.
- Let's take example of the following program ( the same program which was
mentioned in proposal, for which analyzer was giving out a false positive )
File: test.cpp
01: #include <cstdlib>
02:
03: struct A
04: {
05: virtual int foo (void)
06: {
07: return 42;
08: }
09: };
10:
11: struct B: public A
12: {
13: int *ptr;
14: void alloc ()
15: {
16: ptr = (int*)malloc(sizeof(int));
17: }
18: int foo (void)
19: {
20: free(ptr);
21: return 0;
22: }
23: };
24:
25: int test()
26: {
27: struct B b, *bptr=&b;
28: b.alloc();
29: bptr->foo();
30: return bptr->foo();
31: }
32:
33: int main()
34: {
35: test();
36: }
37:
( godbolt link of the above code (https://godbolt.org/z/n17WK4MxG) )
for the same porgram, the analyzer now generates the following warning message :
warning: double-‘free’ of ‘b.B::ptr’ [CWE-415] [-Wanalyzer-double-free]
20 | free(ptr);
| ~~~~^~~~~
‘int test()’: events 1-2
|
| 25 | int test()
| | ^~~~
| | |
| | (1) entry to ‘test’
|......
| 28 | b.alloc();
| | ~~~~~~~~~
| | |
| | (2) calling ‘B::alloc’ from ‘test’
|
+--> ‘void B::alloc()’: events 3-4
|
| 14 | void alloc ()
| | ^~~~~
| | |
| | (3) entry to ‘B::alloc’
| 15 | {
| 16 | ptr = (int*)malloc(sizeof(int));
| | ~~~~~~~~~~~~~~~~~~~
| | |
| | (4) allocated here
|
<------+
|
‘int test()’: events 5-6
|
| 28 | b.alloc();
| | ~~~~~~~^~
| | |
| | (5) returning to ‘test’ from ‘B::alloc’
| 29 | bptr->foo();
| | ~~~~~~~~~~~
| | |
| | (6) calling ‘B::foo’ from ‘test’
|
+--> ‘virtual int B::foo()’: events 7-8
|
| 18 | int foo (void)
| | ^~~
| | |
| | (7) entry to ‘B::foo’
| 19 | {
| 20 | free(ptr);
| | ~~~~~~~~~
| | |
| | (8) first ‘free’ here
|
<------+
|
‘int test()’: events 9-10
|
| 29 | bptr->foo();
| | ~~~~~~~~~^~
| | |
| | (9) returning to ‘test’ from ‘B::foo’
| 30 | return bptr->foo();
| | ~~~~~~~~~~~
| | |
| | (10) calling ‘B::foo’ from ‘test’
|
+--> ‘virtual int B::foo()’: events 11-12
|
| 18 | int foo (void)
| | ^~~
| | |
| | (11) entry to ‘B::foo’
| 19 | {
| 20 | free(ptr);
| | ~~~~~~~~~
| | |
| | (12) second ‘free’ here; first ‘free’ was at (8)
|
- Sorry no patch for the following changes available right now as there is
still a bit of cleaning and refining left to do in it ( will post the patch
with tomorrow's report )
- That being said, I also want to test the working of this patch with a few
more examples of vfunc calls to check for a possible loophole.
---
STATUS AT THE END OF THE DAY :-
- Make the anlalyzer evaluate the vfunc calls by creating enodes and eedges
representing the call. ( done )
- Make the analyzer not treat such calls as call to "unknown function" ( done )
Thank you
- Ankur
More information about the Gcc
mailing list