db/fixup_kernel.sh: fix clear_user() handling
[smatch.git] / check_zero_to_err_ptr.c
blob5d9afa7637dbf4d8aef8d80fe3f22881924c8b58
1 /*
2 * Copyright (C) 2013 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
18 #include "smatch.h"
19 #include "smatch_extra.h"
20 #include "smatch_slist.h"
22 static int my_id;
24 static bool is_select_assign(struct expression *expr)
26 /* select assignments are faked in smatch_conditions.c */
27 expr = expr_get_parent_expr(expr);
28 if (!expr || expr->type != EXPR_ASSIGNMENT)
29 return false;
30 expr = expr_get_parent_expr(expr);
31 if (!expr)
32 return false;
33 if (expr->type == EXPR_CONDITIONAL ||
34 expr->type == EXPR_SELECT)
35 return true;
36 return false;
39 static int is_comparison_call(struct expression *expr)
41 expr = expr_get_parent_expr(expr);
42 if (!expr || expr->type != EXPR_COMPARE)
43 return 0;
44 if (expr->op != SPECIAL_EQUAL && expr->op != SPECIAL_NOTEQUAL)
45 return 0;
46 return 1;
49 static bool is_switch_condition(struct expression *expr)
51 struct statement *stmt;
53 stmt = expr_get_parent_stmt(expr);
54 if (stmt && stmt->type == STMT_SWITCH)
55 return true;
56 return false;
59 static bool is_condition_expr(struct expression *expr)
61 if (is_comparison_call(expr) ||
62 is_select_assign(expr) ||
63 is_switch_condition(expr))
64 return true;
65 return false;
68 static int next_line_is_if(struct expression *expr)
70 struct expression *next;
72 if (!__next_stmt || __next_stmt->type != STMT_IF)
73 return 0;
75 next = strip_expr(__next_stmt->if_conditional);
76 while (next->type == EXPR_PREOP && next->op == '!')
77 next = strip_expr(next->unop);
78 if (expr_equiv(expr, next))
79 return 1;
80 return 0;
83 static int next_line_checks_IS_ERR(struct expression *call, struct expression *arg)
85 struct expression *next;
86 struct expression *tmp;
88 tmp = expr_get_parent_expr(call);
89 if (tmp && tmp->type == EXPR_ASSIGNMENT) {
90 if (next_line_checks_IS_ERR(NULL, tmp->left))
91 return 1;
94 if (!__next_stmt || __next_stmt->type != STMT_IF)
95 return 0;
97 next = strip_expr(__next_stmt->if_conditional);
98 while (next->type == EXPR_PREOP && next->op == '!')
99 next = strip_expr(next->unop);
100 if (!next || next->type != EXPR_CALL)
101 return 0;
102 if (next->fn->type != EXPR_SYMBOL || !next->fn->symbol ||
103 !next->fn->symbol->ident ||
104 (strcmp(next->fn->symbol->ident->name, "IS_ERR") != 0 &&
105 strcmp(next->fn->symbol->ident->name, "IS_ERR_OR_NULL") != 0))
106 return 0;
107 next = get_argument_from_call_expr(next->args, 0);
108 return expr_equiv(next, arg);
111 static int is_non_zero_int(struct range_list *rl)
113 struct data_range *tmp;
114 int cnt = -1;
116 FOR_EACH_PTR(rl, tmp) {
117 cnt++;
119 if (cnt == 0) {
120 if (tmp->min.value == INT_MIN &&
121 tmp->max.value == -1)
122 continue;
123 } else if (cnt == 1) {
124 if (tmp->min.value == 1 &&
125 tmp->max.value == INT_MAX)
126 return 1;
128 return 0;
129 } END_FOR_EACH_PTR(tmp);
130 return 0;
133 static int is_valid_ptr(sval_t sval)
135 if (sval.value == INT_MIN || sval.value == INT_MAX)
136 return 0;
138 if (sval_cmp(valid_ptr_min_sval, sval) <= 0 &&
139 sval_cmp(valid_ptr_max_sval, sval) >= 0) {
140 return 1;
142 return 0;
145 static int has_distinct_zero(struct range_list *rl)
147 struct data_range *tmp;
149 FOR_EACH_PTR(rl, tmp) {
150 if (tmp->min.value == 0 || tmp->max.value == 0)
151 return 1;
152 } END_FOR_EACH_PTR(tmp);
153 return 0;
156 static void match_err_ptr(const char *fn, struct expression *expr, void *data)
158 struct expression *arg_expr;
159 struct sm_state *sm, *tmp;
160 int arg = PTR_INT(data);
162 if (is_impossible_path())
163 return;
165 arg_expr = get_argument_from_call_expr(expr->args, arg);
166 sm = get_sm_state_expr(SMATCH_EXTRA, arg_expr);
167 if (!sm)
168 return;
170 if (is_condition_expr(expr))
171 return;
173 if (next_line_checks_IS_ERR(expr, arg_expr))
174 return;
175 if (strcmp(fn, "ERR_PTR") == 0 &&
176 next_line_is_if(arg_expr))
177 return;
179 FOR_EACH_PTR(sm->possible, tmp) {
180 if (!estate_rl(tmp->state))
181 continue;
182 if (estate_type(tmp->state) == &llong_ctype)
183 continue;
184 if (is_non_zero_int(estate_rl(tmp->state)))
185 continue;
186 if (has_distinct_zero(estate_rl(tmp->state))) {
187 sm_warning("passing zero to '%s'", fn);
188 return;
190 if (strcmp(fn, "PTR_ERR") != 0)
191 continue;
192 if (is_valid_ptr(estate_min(tmp->state)) &&
193 is_valid_ptr(estate_max(tmp->state))) {
194 sm_warning("passing a valid pointer to '%s'", fn);
195 return;
197 } END_FOR_EACH_PTR(tmp);
200 void check_zero_to_err_ptr(int id)
202 if (option_project != PROJ_KERNEL)
203 return;
205 my_id = id;
206 add_function_hook("ERR_PTR", &match_err_ptr, INT_PTR(0));
207 add_function_hook("ERR_CAST", &match_err_ptr, INT_PTR(0));
208 add_function_hook("PTR_ERR", &match_err_ptr, INT_PTR(0));
209 add_function_hook("dev_err_probe", &match_err_ptr, INT_PTR(1));