db/fixup_kernel.sh: fix clear_user() handling
[smatch.git] / check_missing_error_code.c
blob1e2a2ac6fee71fbd4b318ae4dee409c02f65529e
1 /*
2 * Copyright (C) 2020 Oracle.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
19 * This check looks for code like:
21 * return 0;
23 * err_foo:
24 * free(foo);
25 * err_bar:
26 * free(bar);
28 * return ret;
30 * And the bug is that some error paths forgot to set "ret" to an error code.
34 #include "smatch.h"
35 #include "smatch_slist.h"
36 #include "smatch_extra.h"
38 static int my_id;
40 static unsigned long set_label;
42 static void match_goto(struct statement *stmt)
44 if (stmt->type != STMT_GOTO)
45 return;
46 set_state(my_id, "goto", NULL, &true_state);
49 static void match_label(struct statement *stmt)
51 sval_t sval;
53 if (stmt->type != STMT_LABEL)
54 return;
55 if (get_state(my_id, "cleanup", NULL) == &true_state) {
56 /* The second label in a cleanup block is still cleanup. */
57 set_label = true;
58 return;
62 * If the cleanup block isn't preceded by a "return 0;" then it could
63 * be a success path.
66 if (__get_cur_stree())
67 return;
68 if (!__prev_stmt || __prev_stmt->type != STMT_RETURN)
69 return;
70 if (!get_implied_value(__prev_stmt->ret_value, &sval) || sval.value != 0)
71 return;
72 /* We can't set the state until after we've merged the gotos. */
73 set_label = true;
76 static void match_label_after(struct statement *stmt)
78 if (stmt->type != STMT_LABEL)
79 return;
80 if (set_label) {
81 set_state(my_id, "cleanup", NULL, &true_state);
82 set_label = false;
86 static bool find_pool(struct sm_state *extra_sm, struct stree *pool)
88 if (!extra_sm || !pool)
89 return false;
91 if (extra_sm->pool == pool)
92 return true;
93 if (find_pool(extra_sm->left, pool))
94 return true;
95 if (find_pool(extra_sm->right, pool))
96 return true;
98 return false;
101 static int check_pool(struct sm_state *goto_sm, struct sm_state *extra_sm)
103 struct sm_state *old;
104 struct stree *orig;
105 int line = 0;
106 sval_t sval;
108 if (goto_sm->merged || !goto_sm->pool)
109 return 0;
111 orig = __swap_cur_stree(goto_sm->pool);
113 old = get_sm_state(SMATCH_EXTRA, extra_sm->name, extra_sm->sym);
114 if (!old)
115 goto swap;
116 if (goto_sm->line < old->line)
117 goto swap; // hopefully this is impossible
118 if (goto_sm->line < old->line + 3)
119 goto swap; // the ret value is deliberately set to zero
120 if (!estate_get_single_value(old->state, &sval) || sval.value != 0)
121 goto swap; // the ret variable is not zero
122 if (!find_pool(extra_sm, old->pool))
123 goto swap; // the ret variable was modified after the goto
125 line = goto_sm->line;
126 swap:
127 __swap_cur_stree(orig);
128 return line;
131 static int recursive_search_for_zero(struct sm_state *goto_sm, struct sm_state *extra_sm)
133 int line;
135 if (!goto_sm)
136 return 0;
138 line = check_pool(goto_sm, extra_sm);
139 if (line)
140 return line;
142 line = recursive_search_for_zero(goto_sm->left, extra_sm);
143 if (line)
144 return line;
145 line = recursive_search_for_zero(goto_sm->right, extra_sm);
146 if (line)
147 return line;
149 return 0;
152 static void match_return(struct statement *stmt)
154 struct sm_state *goto_sm, *extra_sm;
155 char *name;
156 int line;
158 if (stmt->type != STMT_RETURN)
159 return;
160 if (!stmt->ret_value || stmt->ret_value->type != EXPR_SYMBOL)
161 return;
162 if (cur_func_return_type() != &int_ctype)
163 return;
164 if (get_state(my_id, "cleanup", NULL) != &true_state)
165 return;
166 goto_sm = get_goto_sm_state();
167 if (!goto_sm || goto_sm->state != &true_state)
168 return;
169 extra_sm = get_extra_sm_state(stmt->ret_value);
170 if (!extra_sm)
171 return;
172 if (!estate_rl(extra_sm->state) ||
173 !sval_is_negative(rl_min(estate_rl(extra_sm->state))))
174 return;
175 line = recursive_search_for_zero(goto_sm, extra_sm);
176 if (!line)
177 return;
179 name = expr_to_str(stmt->ret_value);
180 sm_printf("%s:%d %s() warn: missing error code '%s'\n",
181 get_filename(), line, get_function(), name);
182 free_string(name);
185 void check_missing_error_code(int id)
187 if (option_project != PROJ_KERNEL)
188 return;
190 my_id = id;
192 add_function_data(&set_label);
194 add_hook(&match_goto, STMT_HOOK);
195 add_hook(&match_label, STMT_HOOK);
196 add_hook(&match_label_after, STMT_HOOK_AFTER);
197 add_hook(&match_return, STMT_HOOK);