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 #include "smatch.h"
  19 
  20 static int my_id;
  21 
  22 static int can_overflow(struct expression *expr)
  23 {
  24         sval_t max;
  25         int uncapped = 0;
  26 
  27         expr = strip_expr(expr);
  28 
  29         if (expr->type == EXPR_BINOP) {
  30                 uncapped += can_overflow(expr->left);
  31                 uncapped += can_overflow(expr->right);
  32 
  33                 if (uncapped &&
  34                         (expr->op == '+' || expr->op == '*' || expr->op == SPECIAL_LEFTSHIFT))
  35                         return 1;
  36 
  37                 return 0;
  38         }
  39 
  40         if (get_implied_max(expr, &max))
  41                 return 0;
  42         if (get_absolute_max(expr, &max) && sval_cmp_val(max, 4096) <= 0)
  43                 return 0;
  44         return 1;
  45 }
  46 
  47 static void match_size(struct expression *size_expr)
  48 {
  49         char *name;
  50 
  51         size_expr = strip_expr(size_expr);
  52         if (!size_expr)
  53                 return;
  54         if (size_expr->type != EXPR_BINOP) {
  55                 size_expr = get_assigned_expr(size_expr);
  56                 if (!size_expr || size_expr->type != EXPR_BINOP)
  57                         return;
  58         }
  59         if (!can_overflow(size_expr))
  60                 return;
  61 
  62         name = expr_to_str(size_expr);
  63         sm_warning("math in access_ok() is dangerous '%s'", name);
  64 
  65         free_string(name);
  66 }
  67 
  68 static void match_access_ok(const char *fn, struct expression *expr, void *data)
  69 {
  70         struct expression *size_expr;
  71 
  72         size_expr = get_argument_from_call_expr(expr->args, 1);
  73         match_size(size_expr);
  74 }
  75 
  76 static void split_asm_constraints(struct expression_list *expr_list)
  77 {
  78         struct expression *expr;
  79         int state = 0;
  80         int i;
  81 
  82         i = 0;
  83         FOR_EACH_PTR(expr_list, expr) {
  84 
  85                 switch (state) {
  86                 case 0: /* identifier */
  87                 case 1: /* constraint */
  88                         state++;
  89                         continue;
  90                 case 2: /* expression */
  91                         state = 0;
  92                         if (i == 1)
  93                                 match_size(expr);
  94                         i++;
  95                         continue;
  96                 }
  97         } END_FOR_EACH_PTR(expr);
  98 }
  99 
 100 static void match_asm_stmt(struct statement *stmt)
 101 {
 102         char *name;
 103 
 104         name = get_macro_name(stmt->pos);
 105         if (!name || strcmp(name, "access_ok") != 0)
 106                 return;
 107         split_asm_constraints(stmt->asm_inputs);
 108 }
 109 
 110 void check_access_ok_math(int id)
 111 {
 112         my_id = id;
 113         if (option_project != PROJ_KERNEL)
 114                 return;
 115         if (!option_spammy)
 116                 return;
 117         add_function_hook("__access_ok", &match_access_ok, NULL);
 118         add_hook(&match_asm_stmt, ASM_HOOK);
 119 }