smatch.h: introduce sm_pedantic()
[smatch.git] / check_missing_error_code.c
blob7f102f6267708aaa6c5ea3a6fab0a7658496b91b
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;
41 * Instead of having a bunch of different states declared here,
42 * what this does is it has "goto == &yup" and "cleanup == &yup"
45 STATE(yup);
47 static bool set_label;
49 static void match_goto(struct statement *stmt)
51 if (stmt->type != STMT_GOTO)
52 return;
53 set_state(my_id, "goto", NULL, &yup);
56 static void match_label(struct statement *stmt)
58 sval_t sval;
60 if (stmt->type != STMT_LABEL)
61 return;
62 if (get_state(my_id, "cleanup", NULL) == &yup) {
63 /* The second label in a cleanup block is still cleanup. */
64 set_label = true;
65 return;
69 * If the cleanup block isn't preceded by a "return 0;" then it could
70 * be a success path.
73 if (__get_cur_stree())
74 return;
75 if (!__prev_stmt || __prev_stmt->type != STMT_RETURN)
76 return;
77 if (!get_implied_value(__prev_stmt->ret_value, &sval) || sval.value != 0)
78 return;
79 /* We can't set the state until after we've merged the gotos. */
80 set_label = true;
83 static void match_label_after(struct statement *stmt)
85 if (stmt->type != STMT_LABEL)
86 return;
87 if (set_label) {
88 set_state(my_id, "cleanup", NULL, &yup);
89 set_label = false;
93 static bool find_pool(struct sm_state *extra_sm, struct stree *pool)
95 if (!extra_sm || !pool)
96 return false;
98 if (extra_sm->pool == pool)
99 return true;
100 if (find_pool(extra_sm->left, pool))
101 return true;
102 if (find_pool(extra_sm->right, pool))
103 return true;
105 return false;
108 static int check_pool(struct sm_state *goto_sm, struct sm_state *extra_sm)
110 struct sm_state *old;
111 struct stree *orig;
112 int line = 0;
113 sval_t sval;
115 if (!goto_sm->pool)
116 return 0;
118 orig = __swap_cur_stree(goto_sm->pool);
120 old = get_sm_state(SMATCH_EXTRA, extra_sm->name, extra_sm->sym);
121 if (!old)
122 goto swap;
123 if (goto_sm->line < old->line)
124 goto swap; // hopefully this is impossible
125 if (goto_sm->line < old->line + 3)
126 goto swap; // the ret value is deliberately set to zero
127 if (!estate_get_single_value(old->state, &sval) || sval.value != 0)
128 goto swap; // the ret variable is not zero
129 if (!find_pool(extra_sm, old->pool))
130 goto swap; // the ret variable was modified after the goto
132 line = goto_sm->line;
133 swap:
134 __swap_cur_stree(orig);
135 return line;
138 static int recursive_search_for_zero(struct sm_state *goto_sm, struct sm_state *extra_sm)
140 int line;
142 if (!goto_sm)
143 return 0;
145 line = check_pool(goto_sm, extra_sm);
146 if (line)
147 return line;
149 line = recursive_search_for_zero(goto_sm->left, extra_sm);
150 if (line)
151 return line;
152 line = recursive_search_for_zero(goto_sm->right, extra_sm);
153 if (line)
154 return line;
156 return 0;
159 static void match_return(struct statement *stmt)
161 struct sm_state *goto_sm, *extra_sm;
162 char *name;
163 int line;
165 if (stmt->type != STMT_RETURN)
166 return;
167 if (!stmt->ret_value || stmt->ret_value->type != EXPR_SYMBOL)
168 return;
169 if (cur_func_return_type() != &int_ctype)
170 return;
171 if (get_state(my_id, "cleanup", NULL) != &yup)
172 return;
173 goto_sm = get_sm_state(my_id, "goto", NULL);
174 if (!goto_sm || goto_sm->state != &yup)
175 return;
176 extra_sm = get_extra_sm_state(stmt->ret_value);
177 if (!extra_sm)
178 return;
179 if (!estate_rl(extra_sm->state) ||
180 !sval_is_negative(rl_min(estate_rl(extra_sm->state))))
181 return;
182 line = recursive_search_for_zero(goto_sm, extra_sm);
183 if (!line)
184 return;
186 name = expr_to_str(stmt->ret_value);
187 sm_printf("%s:%d %s() warn: missing error code '%s'\n",
188 get_filename(), line, get_function(), name);
189 free_string(name);
192 void check_missing_error_code(int id)
194 if (option_project != PROJ_KERNEL)
195 return;
197 my_id = id;
199 add_hook(&match_goto, STMT_HOOK);
200 add_hook(&match_label, STMT_HOOK);
201 add_hook(&match_label_after, STMT_HOOK_AFTER);
202 add_hook(&match_return, STMT_HOOK);