1 /*
   2  * Copyright (C) 2015 Oracle.
   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  * The point here is to store that a buffer has x bytes even if we don't know
  20  * the value of x.
  21  *
  22  */
  23 
  24 #include "smatch.h"
  25 #include "smatch_slist.h"
  26 #include "smatch_extra.h"
  27 
  28 static int my_id;
  29 
  30 static void array_check(struct expression *expr)
  31 {
  32         struct expression *array;
  33         struct expression *size;
  34         struct expression *offset;
  35         char *array_str, *offset_str;
  36 
  37         expr = strip_expr(expr);
  38         if (!is_array(expr))
  39                 return;
  40 
  41         array = get_array_base(expr);
  42         size = get_size_variable(array);
  43         if (!size)
  44                 return;
  45         offset = get_array_offset(expr);
  46         if (!possible_comparison(size, SPECIAL_EQUAL, offset))
  47                 return;
  48 
  49         array_str = expr_to_str(array);
  50         offset_str = expr_to_str(offset);
  51         sm_warning("potentially one past the end of array '%s[%s]'", array_str, offset_str);
  52         free_string(array_str);
  53         free_string(offset_str);
  54 }
  55 
  56 static int known_access_ok_comparison(struct expression *expr)
  57 {
  58         struct expression *array;
  59         struct expression *size;
  60         struct expression *offset;
  61         int comparison;
  62 
  63         array = get_array_base(expr);
  64         size = get_size_variable(array);
  65         if (!size)
  66                 return 0;
  67         offset = get_array_offset(expr);
  68         comparison = get_comparison(size, offset);
  69         if (comparison == '>' || comparison == SPECIAL_UNSIGNED_GT)
  70                 return 1;
  71 
  72         return 0;
  73 }
  74 
  75 static int known_access_ok_numbers(struct expression *expr)
  76 {
  77         struct expression *array;
  78         struct expression *offset;
  79         sval_t max;
  80         int size;
  81 
  82         array = get_array_base(expr);
  83         offset = get_array_offset(expr);
  84 
  85         size = get_array_size(array);
  86         if (size <= 0)
  87                 return 0;
  88 
  89         get_absolute_max(offset, &max);
  90         if (max.uvalue < size)
  91                 return 1;
  92         return 0;
  93 }
  94 
  95 static void array_check_data_info(struct expression *expr)
  96 {
  97         struct expression *array;
  98         struct expression *offset;
  99         struct state_list *slist;
 100         struct sm_state *sm;
 101         struct compare_data *comp;
 102         char *offset_name;
 103         const char *equal_name = NULL;
 104 
 105         expr = strip_expr(expr);
 106         if (!is_array(expr))
 107                 return;
 108 
 109         if (known_access_ok_numbers(expr))
 110                 return;
 111         if (known_access_ok_comparison(expr))
 112                 return;
 113 
 114         array = get_array_base(expr);
 115         offset = get_array_offset(expr);
 116         offset_name = expr_to_var(offset);
 117         if (!offset_name)
 118                 return;
 119         slist = get_all_possible_equal_comparisons(offset);
 120         if (!slist)
 121                 goto free;
 122 
 123         FOR_EACH_PTR(slist, sm) {
 124                 comp = sm->state->data;
 125                 if (strcmp(comp->left_var, offset_name) == 0) {
 126                         if (db_var_is_array_limit(array, comp->right_var, comp->right_vsl)) {
 127                                 equal_name = comp->right_var;
 128                                 break;
 129                         }
 130                 } else if (strcmp(comp->right_var, offset_name) == 0) {
 131                         if (db_var_is_array_limit(array, comp->left_var, comp->left_vsl)) {
 132                                 equal_name = comp->left_var;
 133                                 break;
 134                         }
 135                 }
 136         } END_FOR_EACH_PTR(sm);
 137 
 138         if (equal_name) {
 139                 char *array_name = expr_to_str(array);
 140 
 141                 sm_warning("potential off by one '%s[]' limit '%s'", array_name, equal_name);
 142                 free_string(array_name);
 143         }
 144 
 145 free:
 146         free_slist(&slist);
 147         free_string(offset_name);
 148 }
 149 
 150 void check_off_by_one_relative(int id)
 151 {
 152         my_id = id;
 153 
 154         add_hook(&array_check, OP_HOOK);
 155         add_hook(&array_check_data_info, OP_HOOK);
 156 }
 157