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 i;
  80 
  81         i = 0;
  82         FOR_EACH_PTR(expr_list, expr) {
  83                 i++;
  84                 if (expr->type != EXPR_ASM_OPERAND)
  85                         continue;
  86                 if (i == 1)
  87                         match_size(expr->expr);
  88         } END_FOR_EACH_PTR(expr);
  89 }
  90 
  91 static void match_asm_stmt(struct statement *stmt)
  92 {
  93         char *name;
  94 
  95         name = get_macro_name(stmt->pos);
  96         if (!name || strcmp(name, "access_ok") != 0)
  97                 return;
  98         split_asm_constraints(stmt->asm_inputs);
  99 }
 100 
 101 void check_access_ok_math(int id)
 102 {
 103         my_id = id;
 104         if (option_project != PROJ_KERNEL)
 105                 return;
 106         if (!option_spammy)
 107                 return;
 108         add_function_hook("__access_ok", &match_access_ok, NULL);
 109         add_hook(&match_asm_stmt, ASM_HOOK);
 110 }