11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2010 Dan Carpenter.
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 /*
191f5207b7SJohn Levon  * Looks for integers that we get from the user which can be attacked
201f5207b7SJohn Levon  * with an integer overflow.
211f5207b7SJohn Levon  *
221f5207b7SJohn Levon  */
231f5207b7SJohn Levon 
241f5207b7SJohn Levon #include "smatch.h"
251f5207b7SJohn Levon #include "smatch_slist.h"
261f5207b7SJohn Levon 
271f5207b7SJohn Levon static int my_max_id;
281f5207b7SJohn Levon static int my_min_id;
291f5207b7SJohn Levon 
301f5207b7SJohn Levon STATE(capped);
311f5207b7SJohn Levon STATE(user_data);
321f5207b7SJohn Levon 
match_condition(struct expression * expr)331f5207b7SJohn Levon static void match_condition(struct expression *expr)
341f5207b7SJohn Levon {
351f5207b7SJohn Levon 	struct smatch_state *left_max_true = NULL;
361f5207b7SJohn Levon 	struct smatch_state *left_max_false = NULL;
371f5207b7SJohn Levon 	struct smatch_state *right_max_true = NULL;
381f5207b7SJohn Levon 	struct smatch_state *right_max_false = NULL;
391f5207b7SJohn Levon 
401f5207b7SJohn Levon 	struct smatch_state *left_min_true = NULL;
411f5207b7SJohn Levon 	struct smatch_state *left_min_false = NULL;
421f5207b7SJohn Levon 	struct smatch_state *right_min_true = NULL;
431f5207b7SJohn Levon 	struct smatch_state *right_min_false = NULL;
441f5207b7SJohn Levon 
451f5207b7SJohn Levon 	if (expr->type != EXPR_COMPARE)
461f5207b7SJohn Levon 		return;
471f5207b7SJohn Levon 
481f5207b7SJohn Levon 	switch (expr->op) {
491f5207b7SJohn Levon 	case '<':
501f5207b7SJohn Levon 	case SPECIAL_LTE:
511f5207b7SJohn Levon 	case SPECIAL_UNSIGNED_LT:
521f5207b7SJohn Levon 	case SPECIAL_UNSIGNED_LTE:
531f5207b7SJohn Levon 		left_max_true = &capped;
541f5207b7SJohn Levon 		right_max_false = &capped;
551f5207b7SJohn Levon 		right_min_true = &capped;
561f5207b7SJohn Levon 		left_min_false = &capped;
571f5207b7SJohn Levon 		break;
581f5207b7SJohn Levon 	case '>':
591f5207b7SJohn Levon 	case SPECIAL_GTE:
601f5207b7SJohn Levon 	case SPECIAL_UNSIGNED_GT:
611f5207b7SJohn Levon 	case SPECIAL_UNSIGNED_GTE:
621f5207b7SJohn Levon 		left_max_false = &capped;
631f5207b7SJohn Levon 		right_max_true = &capped;
641f5207b7SJohn Levon 		left_min_true = &capped;
651f5207b7SJohn Levon 		right_min_false = &capped;
661f5207b7SJohn Levon 		break;
671f5207b7SJohn Levon 	case SPECIAL_EQUAL:
681f5207b7SJohn Levon 		left_max_true = &capped;
691f5207b7SJohn Levon 		right_max_true = &capped;
701f5207b7SJohn Levon 		left_min_true = &capped;
711f5207b7SJohn Levon 		right_min_true = &capped;
721f5207b7SJohn Levon 		break;
731f5207b7SJohn Levon 	case SPECIAL_NOTEQUAL:
741f5207b7SJohn Levon 		left_max_false = &capped;
751f5207b7SJohn Levon 		right_max_false = &capped;
761f5207b7SJohn Levon 		left_min_false = &capped;
771f5207b7SJohn Levon 		right_min_false = &capped;
781f5207b7SJohn Levon 		break;
791f5207b7SJohn Levon 	default:
801f5207b7SJohn Levon 		return;
811f5207b7SJohn Levon 	}
821f5207b7SJohn Levon 
831f5207b7SJohn Levon 	if (get_state_expr(my_max_id, expr->left)) {
841f5207b7SJohn Levon 		set_true_false_states_expr(my_max_id, expr->left, left_max_true, left_max_false);
851f5207b7SJohn Levon 		set_true_false_states_expr(my_min_id, expr->left, left_min_true, left_min_false);
861f5207b7SJohn Levon 	}
871f5207b7SJohn Levon 	if (get_state_expr(my_max_id, expr->right)) {
881f5207b7SJohn Levon 		set_true_false_states_expr(my_max_id, expr->right, right_max_true, right_max_false);
891f5207b7SJohn Levon 		set_true_false_states_expr(my_min_id, expr->right, right_min_true, right_min_false);
901f5207b7SJohn Levon 	}
911f5207b7SJohn Levon }
921f5207b7SJohn Levon 
match_normal_assign(struct expression * expr)931f5207b7SJohn Levon static void match_normal_assign(struct expression *expr)
941f5207b7SJohn Levon {
951f5207b7SJohn Levon 	if (get_state_expr(my_max_id, expr->left)) {
961f5207b7SJohn Levon 		set_state_expr(my_max_id, expr->left, &capped);
971f5207b7SJohn Levon 		set_state_expr(my_min_id, expr->left, &capped);
981f5207b7SJohn Levon 	}
991f5207b7SJohn Levon }
1001f5207b7SJohn Levon 
match_assign(struct expression * expr)1011f5207b7SJohn Levon static void match_assign(struct expression *expr)
1021f5207b7SJohn Levon {
1031f5207b7SJohn Levon 	char *name;
1041f5207b7SJohn Levon 
1051f5207b7SJohn Levon 	name = get_macro_name(expr->pos);
1061f5207b7SJohn Levon 	if (!name || strcmp(name, "get_user") != 0) {
1071f5207b7SJohn Levon 		match_normal_assign(expr);
1081f5207b7SJohn Levon 		return;
1091f5207b7SJohn Levon 	}
1101f5207b7SJohn Levon 	name = expr_to_var(expr->right);
111*c85f09ccSJohn Levon 	if (!name || (strcmp(name, "__val_gu") != 0 && strcmp(name, "__gu_val")))
1121f5207b7SJohn Levon 		goto free;
1131f5207b7SJohn Levon 	set_state_expr(my_max_id, expr->left, &user_data);
1141f5207b7SJohn Levon 	set_state_expr(my_min_id, expr->left, &user_data);
1151f5207b7SJohn Levon free:
1161f5207b7SJohn Levon 	free_string(name);
1171f5207b7SJohn Levon }
1181f5207b7SJohn Levon 
check_expr(struct expression * expr)1191f5207b7SJohn Levon static void check_expr(struct expression *expr)
1201f5207b7SJohn Levon {
1211f5207b7SJohn Levon 	struct sm_state *sm;
1221f5207b7SJohn Levon 	sval_t max;
1231f5207b7SJohn Levon 	sval_t sval;
1241f5207b7SJohn Levon 	char *name;
1251f5207b7SJohn Levon 	int overflow = 0;
1261f5207b7SJohn Levon 	int underflow = 0;
1271f5207b7SJohn Levon 
1281f5207b7SJohn Levon 	sm = get_sm_state_expr(my_max_id, expr);
1291f5207b7SJohn Levon 	if (sm && slist_has_state(sm->possible, &user_data)) {
130*c85f09ccSJohn Levon 		get_absolute_max(expr, &max);
131*c85f09ccSJohn Levon 		if (sval_cmp_val(max, 20000) > 0)
1321f5207b7SJohn Levon 			overflow = 1;
1331f5207b7SJohn Levon 	}
1341f5207b7SJohn Levon 
1351f5207b7SJohn Levon 	sm = get_sm_state_expr(my_min_id, expr);
1361f5207b7SJohn Levon 	if (sm && slist_has_state(sm->possible, &user_data)) {
137*c85f09ccSJohn Levon 		get_absolute_min(expr, &sval);
138*c85f09ccSJohn Levon 		if (sval_is_negative(sval) && sval_cmp_val(sval, -20000) < 0)
1391f5207b7SJohn Levon 			underflow = 1;
1401f5207b7SJohn Levon 	}
1411f5207b7SJohn Levon 
1421f5207b7SJohn Levon 	if (!overflow && !underflow)
1431f5207b7SJohn Levon 		return;
1441f5207b7SJohn Levon 
1451f5207b7SJohn Levon 	name = expr_to_var_sym(expr, NULL);
1461f5207b7SJohn Levon 	if (overflow && underflow)
1471f5207b7SJohn Levon 		sm_warning("check for integer over/underflow '%s'", name);
1481f5207b7SJohn Levon 	else if (underflow)
1491f5207b7SJohn Levon 		sm_warning("check for integer underflow '%s'", name);
1501f5207b7SJohn Levon 	else
1511f5207b7SJohn Levon 		sm_warning("check for integer overflow '%s'", name);
1521f5207b7SJohn Levon 	free_string(name);
1531f5207b7SJohn Levon 
1541f5207b7SJohn Levon 	set_state_expr(my_max_id, expr, &capped);
1551f5207b7SJohn Levon 	set_state_expr(my_min_id, expr, &capped);
1561f5207b7SJohn Levon }
1571f5207b7SJohn Levon 
match_binop(struct expression * expr)1581f5207b7SJohn Levon static void match_binop(struct expression *expr)
1591f5207b7SJohn Levon {
1601f5207b7SJohn Levon 	if (expr->op == '^')
1611f5207b7SJohn Levon 		return;
1621f5207b7SJohn Levon 	if (expr->op == '&')
1631f5207b7SJohn Levon 		return;
1641f5207b7SJohn Levon 	if (expr->op == '|')
1651f5207b7SJohn Levon 		return;
1661f5207b7SJohn Levon 	if (expr->op == SPECIAL_RIGHTSHIFT)
1671f5207b7SJohn Levon 		return;
1681f5207b7SJohn Levon 	if (expr->op == SPECIAL_LEFTSHIFT)
1691f5207b7SJohn Levon 		return;
1701f5207b7SJohn Levon 
1711f5207b7SJohn Levon 	check_expr(expr->left);
1721f5207b7SJohn Levon 	check_expr(expr->right);
1731f5207b7SJohn Levon }
1741f5207b7SJohn Levon 
check_get_user_overflow(int id)1751f5207b7SJohn Levon void check_get_user_overflow(int id)
1761f5207b7SJohn Levon {
1771f5207b7SJohn Levon 	if (option_project != PROJ_KERNEL)
1781f5207b7SJohn Levon 		return;
1791f5207b7SJohn Levon 	my_max_id = id;
1801f5207b7SJohn Levon 	add_hook(&match_condition, CONDITION_HOOK);
1811f5207b7SJohn Levon 	add_hook(&match_assign, ASSIGNMENT_HOOK);
1821f5207b7SJohn Levon 	add_hook(&match_binop, BINOP_HOOK);
1831f5207b7SJohn Levon }
1841f5207b7SJohn Levon 
check_get_user_overflow2(int id)1851f5207b7SJohn Levon void check_get_user_overflow2(int id)
1861f5207b7SJohn Levon {
1871f5207b7SJohn Levon 	my_min_id = id;
1881f5207b7SJohn Levon }
189