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  * This tries to find places which should probably return -EFAULT
  20  * but return the number of bytes to copy instead.
  21  */
  22 
  23 #include <string.h>
  24 #include "smatch.h"
  25 #include "smatch_slist.h"
  26 #include "smatch_extra.h"
  27 
  28 static int my_id;
  29 
  30 STATE(remaining);
  31 STATE(ok);
  32 
  33 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
  34 {
  35         if (sm->state != &ok)
  36                 set_state(my_id, sm->name, sm->sym, &ok);
  37 }
  38 
  39 static void match_copy(const char *fn, struct expression *expr, void *unused)
  40 {
  41         if (expr->op == SPECIAL_SUB_ASSIGN)
  42                 return;
  43         set_state_expr(my_id, expr->left, &remaining);
  44 }
  45 
  46 static void match_condition(struct expression *expr)
  47 {
  48         if (!get_state_expr(my_id, expr))
  49                 return;
  50         /* If the variable is zero that's ok */
  51         set_true_false_states_expr(my_id, expr, NULL, &ok);
  52 }
  53 
  54 /*
  55  * This function is biased in favour of print out errors.
  56  * The heuristic to print is:
  57  *    If we have a potentially positive return from copy_to_user
  58  *    and there is a possibility that we return negative as well
  59  *    then complain.
  60  */
  61 static void match_return_var(struct expression *ret_value)
  62 {
  63         struct smatch_state *state;
  64         struct sm_state *sm;
  65         sval_t min;
  66 
  67         sm = get_sm_state_expr(my_id, ret_value);
  68         if (!sm)
  69                 return;
  70         if (!slist_has_state(sm->possible, &remaining))
  71                 return;
  72         state = get_state_expr(SMATCH_EXTRA, ret_value);
  73         if (!state)
  74                 return;
  75         if (!get_absolute_min(ret_value, &min))
  76                 return;
  77         if (min.value == 0)
  78                 return;
  79         sm_warning("maybe return -EFAULT instead of the bytes remaining?");
  80 }
  81 
  82 static void match_return_call(struct expression *ret_value)
  83 {
  84         struct expression *fn;
  85         struct range_list *rl;
  86         const char *fn_name;
  87         char *cur_func;
  88 
  89         if (!ret_value || ret_value->type != EXPR_CALL)
  90                 return;
  91         cur_func = get_function();
  92         if (!cur_func)
  93                 return;
  94         if (strstr(cur_func, "_to_user") ||
  95             strstr(cur_func, "_from_user"))
  96                 return;
  97 
  98         fn = strip_expr(ret_value->fn);
  99         if (fn->type != EXPR_SYMBOL)
 100                 return;
 101         fn_name = fn->symbol_name->name;
 102         if (strcmp(fn_name, "copy_to_user") != 0 &&
 103             strcmp(fn_name, "__copy_to_user") != 0 &&
 104             strcmp(fn_name, "copy_from_user") != 0 &&
 105             strcmp(fn_name, "__copy_from_user"))
 106                 return;
 107 
 108         rl = db_return_vals_from_str(get_function());
 109         if (!rl)
 110                 return;
 111 
 112         if (!sval_is_negative(rl_min(rl)))
 113                 return;
 114         sm_warning("maybe return -EFAULT instead of the bytes remaining?");
 115 }
 116 
 117 void check_return_efault(int id)
 118 {
 119         if (option_project != PROJ_KERNEL)
 120                 return;
 121 
 122         my_id = id;
 123         add_function_assign_hook("copy_to_user", &match_copy, NULL);
 124         add_function_assign_hook("__copy_to_user", &match_copy, NULL);
 125         add_function_assign_hook("copy_from_user", &match_copy, NULL);
 126         add_function_assign_hook("__copy_from_user", &match_copy, NULL);
 127         add_function_assign_hook("clear_user", &match_copy, NULL);
 128         add_hook(&match_condition, CONDITION_HOOK);
 129         add_hook(&match_return_var, RETURN_HOOK);
 130         add_hook(&match_return_call, RETURN_HOOK);
 131         add_modification_hook(my_id, &ok_to_use);
 132 }