11f5207b7SJohn Levon /*
21f5207b7SJohn Levon * Copyright (C) 2013 Oracle.
31f5207b7SJohn Levon *
41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon * of the License, or (at your option) any later version.
81f5207b7SJohn Levon *
91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121f5207b7SJohn Levon * GNU General Public License for more details.
131f5207b7SJohn Levon *
141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon */
171f5207b7SJohn Levon
181f5207b7SJohn Levon #include "smatch.h"
191f5207b7SJohn Levon #include "smatch_extra.h"
201f5207b7SJohn Levon #include "smatch_slist.h"
211f5207b7SJohn Levon
221f5207b7SJohn Levon static int my_id;
231f5207b7SJohn Levon
is_comparison_call(struct expression * expr)241f5207b7SJohn Levon static int is_comparison_call(struct expression *expr)
251f5207b7SJohn Levon {
261f5207b7SJohn Levon expr = expr_get_parent_expr(expr);
271f5207b7SJohn Levon if (!expr || expr->type != EXPR_COMPARE)
281f5207b7SJohn Levon return 0;
291f5207b7SJohn Levon if (expr->op != SPECIAL_EQUAL && expr->op != SPECIAL_NOTEQUAL)
301f5207b7SJohn Levon return 0;
311f5207b7SJohn Levon return 1;
321f5207b7SJohn Levon }
331f5207b7SJohn Levon
next_line_is_if(struct expression * expr)341f5207b7SJohn Levon static int next_line_is_if(struct expression *expr)
351f5207b7SJohn Levon {
361f5207b7SJohn Levon struct expression *next;
371f5207b7SJohn Levon
381f5207b7SJohn Levon if (!__next_stmt || __next_stmt->type != STMT_IF)
391f5207b7SJohn Levon return 0;
401f5207b7SJohn Levon
411f5207b7SJohn Levon next = strip_expr(__next_stmt->if_conditional);
421f5207b7SJohn Levon while (next->type == EXPR_PREOP && next->op == '!')
431f5207b7SJohn Levon next = strip_expr(next->unop);
441f5207b7SJohn Levon if (expr_equiv(expr, next))
451f5207b7SJohn Levon return 1;
461f5207b7SJohn Levon return 0;
471f5207b7SJohn Levon }
481f5207b7SJohn Levon
next_line_checks_IS_ERR(struct expression * call,struct expression * arg)491f5207b7SJohn Levon static int next_line_checks_IS_ERR(struct expression *call, struct expression *arg)
501f5207b7SJohn Levon {
511f5207b7SJohn Levon struct expression *next;
521f5207b7SJohn Levon struct expression *tmp;
531f5207b7SJohn Levon
541f5207b7SJohn Levon tmp = expr_get_parent_expr(call);
551f5207b7SJohn Levon if (tmp && tmp->type == EXPR_ASSIGNMENT) {
561f5207b7SJohn Levon if (next_line_checks_IS_ERR(NULL, tmp->left))
571f5207b7SJohn Levon return 1;
581f5207b7SJohn Levon }
591f5207b7SJohn Levon
601f5207b7SJohn Levon if (!__next_stmt || __next_stmt->type != STMT_IF)
611f5207b7SJohn Levon return 0;
621f5207b7SJohn Levon
631f5207b7SJohn Levon next = strip_expr(__next_stmt->if_conditional);
641f5207b7SJohn Levon while (next->type == EXPR_PREOP && next->op == '!')
651f5207b7SJohn Levon next = strip_expr(next->unop);
661f5207b7SJohn Levon if (!next || next->type != EXPR_CALL)
671f5207b7SJohn Levon return 0;
681f5207b7SJohn Levon if (next->fn->type != EXPR_SYMBOL || !next->fn->symbol ||
691f5207b7SJohn Levon !next->fn->symbol->ident ||
701f5207b7SJohn Levon (strcmp(next->fn->symbol->ident->name, "IS_ERR") != 0 &&
711f5207b7SJohn Levon strcmp(next->fn->symbol->ident->name, "IS_ERR_OR_NULL") != 0))
721f5207b7SJohn Levon return 0;
731f5207b7SJohn Levon next = get_argument_from_call_expr(next->args, 0);
741f5207b7SJohn Levon return expr_equiv(next, arg);
751f5207b7SJohn Levon }
761f5207b7SJohn Levon
is_non_zero_int(struct range_list * rl)77*efe51d0cSJohn Levon static int is_non_zero_int(struct range_list *rl)
78*efe51d0cSJohn Levon {
79*efe51d0cSJohn Levon struct data_range *tmp;
80*efe51d0cSJohn Levon int cnt = -1;
81*efe51d0cSJohn Levon
82*efe51d0cSJohn Levon FOR_EACH_PTR(rl, tmp) {
83*efe51d0cSJohn Levon cnt++;
84*efe51d0cSJohn Levon
85*efe51d0cSJohn Levon if (cnt == 0) {
86*efe51d0cSJohn Levon if (tmp->min.value == INT_MIN &&
87*efe51d0cSJohn Levon tmp->max.value == -1)
88*efe51d0cSJohn Levon continue;
89*efe51d0cSJohn Levon } else if (cnt == 1) {
90*efe51d0cSJohn Levon if (tmp->min.value == 1 &&
91*efe51d0cSJohn Levon tmp->max.value == INT_MAX)
92*efe51d0cSJohn Levon return 1;
93*efe51d0cSJohn Levon }
94*efe51d0cSJohn Levon return 0;
95*efe51d0cSJohn Levon } END_FOR_EACH_PTR(tmp);
96*efe51d0cSJohn Levon return 0;
97*efe51d0cSJohn Levon }
98*efe51d0cSJohn Levon
is_valid_ptr(sval_t sval)991f5207b7SJohn Levon static int is_valid_ptr(sval_t sval)
1001f5207b7SJohn Levon {
101*efe51d0cSJohn Levon if (sval.value == INT_MIN || sval.value == INT_MAX)
1021f5207b7SJohn Levon return 0;
1031f5207b7SJohn Levon
1041f5207b7SJohn Levon if (sval_cmp(valid_ptr_min_sval, sval) <= 0 &&
105*efe51d0cSJohn Levon sval_cmp(valid_ptr_max_sval, sval) >= 0) {
1061f5207b7SJohn Levon return 1;
107*efe51d0cSJohn Levon }
108*efe51d0cSJohn Levon return 0;
109*efe51d0cSJohn Levon }
110*efe51d0cSJohn Levon
has_distinct_zero(struct range_list * rl)111*efe51d0cSJohn Levon static int has_distinct_zero(struct range_list *rl)
112*efe51d0cSJohn Levon {
113*efe51d0cSJohn Levon struct data_range *tmp;
114*efe51d0cSJohn Levon
115*efe51d0cSJohn Levon FOR_EACH_PTR(rl, tmp) {
116*efe51d0cSJohn Levon if (tmp->min.value == 0 || tmp->max.value == 0)
117*efe51d0cSJohn Levon return 1;
118*efe51d0cSJohn Levon } END_FOR_EACH_PTR(tmp);
1191f5207b7SJohn Levon return 0;
1201f5207b7SJohn Levon }
1211f5207b7SJohn Levon
match_err_ptr(const char * fn,struct expression * expr,void * data)1221f5207b7SJohn Levon static void match_err_ptr(const char *fn, struct expression *expr, void *data)
1231f5207b7SJohn Levon {
1241f5207b7SJohn Levon struct expression *arg_expr;
1251f5207b7SJohn Levon struct sm_state *sm, *tmp;
126*efe51d0cSJohn Levon
127*efe51d0cSJohn Levon if (is_impossible_path())
128*efe51d0cSJohn Levon return;
1291f5207b7SJohn Levon
1301f5207b7SJohn Levon arg_expr = get_argument_from_call_expr(expr->args, 0);
1311f5207b7SJohn Levon sm = get_sm_state_expr(SMATCH_EXTRA, arg_expr);
1321f5207b7SJohn Levon if (!sm)
1331f5207b7SJohn Levon return;
1341f5207b7SJohn Levon
1351f5207b7SJohn Levon if (is_comparison_call(expr))
1361f5207b7SJohn Levon return;
1371f5207b7SJohn Levon
1381f5207b7SJohn Levon if (next_line_checks_IS_ERR(expr, arg_expr))
1391f5207b7SJohn Levon return;
1401f5207b7SJohn Levon if (strcmp(fn, "ERR_PTR") == 0 &&
1411f5207b7SJohn Levon next_line_is_if(arg_expr))
1421f5207b7SJohn Levon return;
1431f5207b7SJohn Levon
1441f5207b7SJohn Levon FOR_EACH_PTR(sm->possible, tmp) {
1451f5207b7SJohn Levon if (!estate_rl(tmp->state))
1461f5207b7SJohn Levon continue;
147*efe51d0cSJohn Levon if (is_non_zero_int(estate_rl(tmp->state)))
148*efe51d0cSJohn Levon continue;
149*efe51d0cSJohn Levon if (has_distinct_zero(estate_rl(tmp->state))) {
150*efe51d0cSJohn Levon sm_warning("passing zero to '%s'", fn);
151*efe51d0cSJohn Levon return;
152*efe51d0cSJohn Levon }
153*efe51d0cSJohn Levon if (strcmp(fn, "PTR_ERR") != 0)
154*efe51d0cSJohn Levon continue;
1551f5207b7SJohn Levon if (is_valid_ptr(estate_min(tmp->state)) &&
1561f5207b7SJohn Levon is_valid_ptr(estate_max(tmp->state))) {
1571f5207b7SJohn Levon sm_warning("passing a valid pointer to '%s'", fn);
1581f5207b7SJohn Levon return;
1591f5207b7SJohn Levon }
1601f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp);
1611f5207b7SJohn Levon }
1621f5207b7SJohn Levon
check_zero_to_err_ptr(int id)1631f5207b7SJohn Levon void check_zero_to_err_ptr(int id)
1641f5207b7SJohn Levon {
1651f5207b7SJohn Levon if (option_project != PROJ_KERNEL)
1661f5207b7SJohn Levon return;
1671f5207b7SJohn Levon
1681f5207b7SJohn Levon my_id = id;
1691f5207b7SJohn Levon add_function_hook("ERR_PTR", &match_err_ptr, NULL);
1701f5207b7SJohn Levon add_function_hook("ERR_CAST", &match_err_ptr, NULL);
1711f5207b7SJohn Levon add_function_hook("PTR_ERR", &match_err_ptr, NULL);
1721f5207b7SJohn Levon }
173