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 158