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