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         int limit_type;
  37 
  38         expr = strip_expr(expr);
  39         if (!is_array(expr))
  40                 return;
  41 
  42         array = get_array_base(expr);
  43         size = get_size_variable(array, &limit_type);
  44         if (!size || limit_type != ELEM_COUNT)
  45                 return;
  46         offset = get_array_offset(expr);
  47         if (!possible_comparison(size, SPECIAL_EQUAL, offset))
  48                 return;
  49 
  50         if (buf_comparison_index_ok(expr))
  51                 return;
  52 
  53         array_str = expr_to_str(array);
  54         offset_str = expr_to_str(offset);
  55         sm_warning("potentially one past the end of array '%s[%s]'", array_str, offset_str);
  56         free_string(array_str);
  57         free_string(offset_str);
  58 }
  59 
  60 static int known_access_ok_numbers(struct expression *expr)
  61 {
  62         struct expression *array;
  63         struct expression *offset;
  64         sval_t max;
  65         int size;
  66 
  67         array = get_array_base(expr);
  68         offset = get_array_offset(expr);
  69 
  70         size = get_array_size(array);
  71         if (size <= 0)
  72                 return 0;
  73 
  74         get_absolute_max(offset, &max);
  75         if (max.uvalue < size)
  76                 return 1;
  77         return 0;
  78 }
  79 
  80 static void array_check_data_info(struct expression *expr)
  81 {
  82         struct expression *array;
  83         struct expression *offset;
  84         struct state_list *slist;
  85         struct sm_state *sm;
  86         struct compare_data *comp;
  87         char *offset_name;
  88         const char *equal_name = NULL;
  89 
  90         expr = strip_expr(expr);
  91         if (!is_array(expr))
  92                 return;
  93 
  94         if (known_access_ok_numbers(expr))
  95                 return;
  96         if (buf_comparison_index_ok(expr))
  97                 return;
  98 
  99         array = get_array_base(expr);
 100         offset = get_array_offset(expr);
 101         offset_name = expr_to_var(offset);
 102         if (!offset_name)
 103                 return;
 104         slist = get_all_possible_equal_comparisons(offset);
 105         if (!slist)
 106                 goto free;
 107 
 108         FOR_EACH_PTR(slist, sm) {
 109                 comp = sm->state->data;
 110                 if (strcmp(comp->left_var, offset_name) == 0) {
 111                         if (db_var_is_array_limit(array, comp->right_var, comp->right_vsl)) {
 112                                 equal_name = comp->right_var;
 113                                 break;
 114                         }
 115                 } else if (strcmp(comp->right_var, offset_name) == 0) {
 116                         if (db_var_is_array_limit(array, comp->left_var, comp->left_vsl)) {
 117                                 equal_name = comp->left_var;
 118                                 break;
 119                         }
 120                 }
 121         } END_FOR_EACH_PTR(sm);
 122 
 123         if (equal_name) {
 124                 char *array_name = expr_to_str(array);
 125 
 126                 sm_warning("potential off by one '%s[]' limit '%s'", array_name, equal_name);
 127                 free_string(array_name);
 128         }
 129 
 130 free:
 131         free_slist(&slist);
 132         free_string(offset_name);
 133 }
 134 
 135 void check_off_by_one_relative(int id)
 136 {
 137         my_id = id;
 138 
 139         add_hook(&array_check, OP_HOOK);
 140         add_hook(&array_check_data_info, OP_HOOK);
 141 }
 142