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