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 143