1 /*
   2  * Copyright (C) 2010 Dan Carpenter.
   3  *
   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.
   8  *
   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.
  13  *
  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
  16  */
  17 
  18 /*
  19  * Looks for integers that we get from the user which can be attacked
  20  * with an integer overflow.
  21  *
  22  */
  23 
  24 #include "smatch.h"
  25 #include "smatch_slist.h"
  26 
  27 static int my_max_id;
  28 static int my_min_id;
  29 
  30 STATE(capped);
  31 STATE(user_data);
  32 
  33 static void match_condition(struct expression *expr)
  34 {
  35         struct smatch_state *left_max_true = NULL;
  36         struct smatch_state *left_max_false = NULL;
  37         struct smatch_state *right_max_true = NULL;
  38         struct smatch_state *right_max_false = NULL;
  39 
  40         struct smatch_state *left_min_true = NULL;
  41         struct smatch_state *left_min_false = NULL;
  42         struct smatch_state *right_min_true = NULL;
  43         struct smatch_state *right_min_false = NULL;
  44 
  45         if (expr->type != EXPR_COMPARE)
  46                 return;
  47 
  48         switch (expr->op) {
  49         case '<':
  50         case SPECIAL_LTE:
  51         case SPECIAL_UNSIGNED_LT:
  52         case SPECIAL_UNSIGNED_LTE:
  53                 left_max_true = &capped;
  54                 right_max_false = &capped;
  55                 right_min_true = &capped;
  56                 left_min_false = &capped;
  57                 break;
  58         case '>':
  59         case SPECIAL_GTE:
  60         case SPECIAL_UNSIGNED_GT:
  61         case SPECIAL_UNSIGNED_GTE:
  62                 left_max_false = &capped;
  63                 right_max_true = &capped;
  64                 left_min_true = &capped;
  65                 right_min_false = &capped;
  66                 break;
  67         case SPECIAL_EQUAL:
  68                 left_max_true = &capped;
  69                 right_max_true = &capped;
  70                 left_min_true = &capped;
  71                 right_min_true = &capped;
  72                 break;
  73         case SPECIAL_NOTEQUAL:
  74                 left_max_false = &capped;
  75                 right_max_false = &capped;
  76                 left_min_false = &capped;
  77                 right_min_false = &capped;
  78                 break;
  79         default:
  80                 return;
  81         }
  82 
  83         if (get_state_expr(my_max_id, expr->left)) {
  84                 set_true_false_states_expr(my_max_id, expr->left, left_max_true, left_max_false);
  85                 set_true_false_states_expr(my_min_id, expr->left, left_min_true, left_min_false);
  86         }
  87         if (get_state_expr(my_max_id, expr->right)) {
  88                 set_true_false_states_expr(my_max_id, expr->right, right_max_true, right_max_false);
  89                 set_true_false_states_expr(my_min_id, expr->right, right_min_true, right_min_false);
  90         }
  91 }
  92 
  93 static void match_normal_assign(struct expression *expr)
  94 {
  95         if (get_state_expr(my_max_id, expr->left)) {
  96                 set_state_expr(my_max_id, expr->left, &capped);
  97                 set_state_expr(my_min_id, expr->left, &capped);
  98         }
  99 }
 100 
 101 static void match_assign(struct expression *expr)
 102 {
 103         char *name;
 104 
 105         name = get_macro_name(expr->pos);
 106         if (!name || strcmp(name, "get_user") != 0) {
 107                 match_normal_assign(expr);
 108                 return;
 109         }
 110         name = expr_to_var(expr->right);
 111         if (!name || strcmp(name, "__val_gu") != 0)
 112                 goto free;
 113         set_state_expr(my_max_id, expr->left, &user_data);
 114         set_state_expr(my_min_id, expr->left, &user_data);
 115 free:
 116         free_string(name);
 117 }
 118 
 119 static void check_expr(struct expression *expr)
 120 {
 121         struct sm_state *sm;
 122         sval_t max;
 123         sval_t sval;
 124         char *name;
 125         int overflow = 0;
 126         int underflow = 0;
 127 
 128         sm = get_sm_state_expr(my_max_id, expr);
 129         if (sm && slist_has_state(sm->possible, &user_data)) {
 130                 if (!get_absolute_max(expr, &max) || sval_cmp_val(max, 20000) > 0)
 131                         overflow = 1;
 132         }
 133 
 134         sm = get_sm_state_expr(my_min_id, expr);
 135         if (sm && slist_has_state(sm->possible, &user_data)) {
 136                 if (!get_absolute_min(expr, &sval) ||
 137                     (sval_is_negative(sval) && sval_cmp_val(sval, -20000) < 0))
 138                         underflow = 1;
 139         }
 140 
 141         if (!overflow && !underflow)
 142                 return;
 143 
 144         name = expr_to_var_sym(expr, NULL);
 145         if (overflow && underflow)
 146                 sm_warning("check for integer over/underflow '%s'", name);
 147         else if (underflow)
 148                 sm_warning("check for integer underflow '%s'", name);
 149         else
 150                 sm_warning("check for integer overflow '%s'", name);
 151         free_string(name);
 152 
 153         set_state_expr(my_max_id, expr, &capped);
 154         set_state_expr(my_min_id, expr, &capped);
 155 }
 156 
 157 static void match_binop(struct expression *expr)
 158 {
 159         if (expr->op == '^')
 160                 return;
 161         if (expr->op == '&')
 162                 return;
 163         if (expr->op == '|')
 164                 return;
 165         if (expr->op == SPECIAL_RIGHTSHIFT)
 166                 return;
 167         if (expr->op == SPECIAL_LEFTSHIFT)
 168                 return;
 169 
 170         check_expr(expr->left);
 171         check_expr(expr->right);
 172 }
 173 
 174 void check_get_user_overflow(int id)
 175 {
 176         if (option_project != PROJ_KERNEL)
 177                 return;
 178         my_max_id = id;
 179         add_hook(&match_condition, CONDITION_HOOK);
 180         add_hook(&match_assign, ASSIGNMENT_HOOK);
 181         add_hook(&match_binop, BINOP_HOOK);
 182 }
 183 
 184 void check_get_user_overflow2(int id)
 185 {
 186         my_min_id = id;
 187 }