# GCC 12 static analyzer
靜態分析器檢測流程,分析一下gcc12 目前檢測流程
# Analyzer Internals
https://gcc.gnu.org/onlinedocs/gccint/Analyzer-Internals.html
# debug gcc compiler
![](https://i.imgur.com/p6GHnq1.png)
```bash=
sudo apt install gcc-multilib
sudo make -j 16 STAGE1_CXXFLAGS="-g3"
```
需要編譯 cc1 記得-g
# gdb command
```
gdb --args cc1 test.c -fanalyzer -Wanalyzer-double-free
```
# gcc/cgraphunit.c
```c=
void
symbol_table::finalize_compilation_unit (void)
{
timevar_push (TV_CGRAPH);
/* If we're here there's no current function anymore. Some frontends
are lazy in clearing these. */
current_function_decl = NULL;
set_cfun (NULL);
/* Do not skip analyzing the functions if there were errors, we
miss diagnostics for following functions otherwise. */
/* Emit size functions we didn't inline. */
finalize_size_functions ();
/* Mark alias targets necessary and emit diagnostics. */
handle_alias_pairs ();
if (!quiet_flag)
{
fprintf (stderr, "\nAnalyzing compilation unit\n");
fflush (stderr);
}
if (flag_dump_passes)
dump_passes ();
/* Gimplify and lower all functions, compute reachability and
remove unreachable nodes. */
analyze_functions (/*first_time=*/true);
/* Mark alias targets necessary and emit diagnostics. */
handle_alias_pairs ();
/* Gimplify and lower thunks. */
analyze_functions (/*first_time=*/false);
/* All nested functions should be lowered now. */
nested_function_info::release ();
/* Offloading requires LTO infrastructure. */
if (!in_lto_p && g->have_offload)
flag_generate_offload = 1;
if (!seen_error ())
{
/* Give the frontends the chance to emit early debug based on
what is still reachable in the TU. */
(*lang_hooks.finalize_early_debug) ();
/* Clean up anything that needs cleaning up after initial debug
generation. */
debuginfo_early_start ();
(*debug_hooks->early_finish) (main_input_filename);
debuginfo_early_stop ();
}
/* Finally drive the pass manager. */
compile ();
timevar_pop (TV_CGRAPH);
}
```
/mnt/c/Users/hpclab/gcc/gcc/cgraphunit.c
```c=
/* Don't run the IPA passes if there was any error or sorry messages. */
if (!seen_error ())
{
timevar_start (TV_CGRAPH_IPA_PASSES);
ipa_passes ();
timevar_stop (TV_CGRAPH_IPA_PASSES);
}
```
定位至double free
# main
```bash=
b /gcc/main.c:36
```
# do_compile (no_backend)
到這邊屬於前置處理器前端檢測規則部分,這行程式碼後
開始進行gimple pass流程檢查
```bash=
/gcc/toplev.c:2308
```
![](https://i.imgur.com/EGqLWug.png)
# ipa_passes ()
開始處理ipa_passes
```bash=
/gcc/cgraphunit.c:2529
/gcc/cgraphunit.c:2267
```
![](https://i.imgur.com/KXt7DV2.png)
![](https://i.imgur.com/az4Y7rG.png)
# execute_ipa_pass_list (opt_pass *pass)
走 Lto流程,開始遍歷所有Pass,發現 pass_analyzer 分析器
![](https://i.imgur.com/KoE4XI2.png)
# (anonymous namespace)::pass_analyzer::gate
```bash=
b /gcc/analyzer/analyzer-pass.cc:78
```
![](https://i.imgur.com/DzGRWsJ.png)
# (anonymous namespace)::pass_analyzer::execute
```bash=
/gcc/analyzer/analyzer-pass.cc:87
```
![](https://i.imgur.com/gjZPzKG.png)
# ana::run_checkers ();
可以看到這邊在建立supergraph
We can identify problems directly when processing a <point, state> instance. For example, if we’re finding the successors of
<point: before-stmt: "free (ptr);",
state: {"ptr": freed}>
```bash=
/gcc/analyzer/engine.cc:5436
```
![](https://i.imgur.com/qfKa79F.png)
# ana::make_malloc_state_machine
狀態機,
state: {"ptr": freed}>
ptr可能是 malloc
假設更新兩次就是double-free
```bash=
/gcc/analyzer/sm-malloc.cc:2067
```
# make_checkers
```bash=
/gcc/analyzer/sm.cc:169
```
![](https://i.imgur.com/xnlDTBN.png)
# process_worklist
/* The main loop of the analysis.
Take freshly-created exploded_nodes from the worklist, calling
process_node on them to explore the <point, state> graph.
Add edges to their successors, potentially creating new successors
(which are also added to the worklist). */
```bash=
/gcc/analyzer/engine.cc:2825
```
![](https://i.imgur.com/KfUfz2N.png)
# malloc_state_machine::on_stmt
從process_worklist 觀察callstack
可以發現這邊是每個stmt都會觸發這個on_stmt fucntion
```bash=
(gdb) bt
#0 ana::(anonymous namespace)::malloc_state_machine::on_stmt (this=0x2589950, sm_ctxt=0x7ffffffeceb0, node=0x2567d30,
stmt=0x7ffffed51090) at ../../gcc/analyzer/sm-malloc.cc:1574
#1 0x0000000000ef26b0 in ana::exploded_node::on_stmt (this=<optimized out>, eg=..., snode=0x2567d30,
stmt=0x7ffffed51090, state=0x7ffffffecfb0, uncertainty=<optimized out>, path_ctxt=0x7ffffffed030)
at ../../gcc/analyzer/engine.cc:1310
#2 0x0000000000ef4e2c in ana::exploded_graph::process_node (this=0x7ffffffed370, node=0x25d6530)
at ../../gcc/analyzer/engine.cc:3366
#3 0x0000000000ef5be3 in ana::exploded_graph::process_worklist (this=0x7ffffffed370)
```
![](https://i.imgur.com/dYw6iLm.png)
# dump gimple stmt
```bahs
p debug(stmt)
```
![](https://i.imgur.com/YSsqmJT.png)
c
可以看到on_stmt這個fucntion
有看到已經開始整合c++的部分
根據 fucntion name
# c++
```c=
if (is_named_call_p (callee_fndecl, "operator new", call, 1))
on_allocator_call (sm_ctxt, call, &m_scalar_delete);
else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
on_allocator_call (sm_ctxt, call, &m_vector_delete);
else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
|| is_named_call_p (callee_fndecl, "operator delete", call, 2))
```
# c
```c=
if (is_named_call_p (callee_fndecl, "free", call, 1)
|| is_std_named_call_p (callee_fndecl, "free", call, 1)
|| is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
{
on_deallocator_call (sm_ctxt, node, call,
&m_free.m_deallocator, 0);
return true;
}
if (is_named_call_p (callee_fndecl, "realloc", call, 2)
|| is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
{
on_realloc_call (sm_ctxt, node, call);
return true;
}
```
![](https://i.imgur.com/J1xOugW.png)
![](https://i.imgur.com/yBDz4jz.png)
# malloc_state_machine::on_deallocator_call
```c=
b malloc_state_machine::on_deallocator_call
```
可以看到每次on_stmt 後就會更新一次節點,假設是free funciton 就on_deallocator_call 檢查一次看
state: {"ptr": freed}> 是否已經 m_freed 有則觸發 double-free檢測規則
```c=
else if (state == d->m_freed)
{
/* freed -> stop, with warning. */
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
sm_ctxt->warn (node, call, arg,
new double_free (*this, diag_arg, d->m_name));
sm_ctxt->set_next_state (call, arg, m_stop);
}
```
# double_free
```c=
b double_free
```
到這邊已經可以看到假設在on_deallocator_call 檢查發生有double-free問題則新增cwe漏洞 415 double free
```c=
bool emit (rich_location *rich_loc) FINAL OVERRIDE
{
auto_diagnostic_group d;
diagnostic_metadata m;
m.add_cwe (415); /* CWE-415: Double Free. */
return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
"double-%qs of %qE", m_funcname, m_arg);
}
```
顯示報錯
warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
"double-%qs of %qE", m_funcname, m_arg);
![](https://i.imgur.com/Vw3YChn.png)
![](https://i.imgur.com/36YeRpA.png)
![](https://i.imgur.com/V9qeARi.png)
![](https://i.imgur.com/BU36IZz.png)