memcpy_overflow: improve struct member boundary calculation for anon unions
[smatch.git] / check_off_by_one_relative.c
blobf8e29a43207f5c93fbb9050f643ddf90e96b0c37
1 /*
2 * Copyright (C) 2015 Oracle.
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.
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.
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
19 * The point here is to store that a buffer has x bytes even if we don't know
20 * the value of x.
24 #include "smatch.h"
25 #include "smatch_slist.h"
26 #include "smatch_extra.h"
28 static int my_id;
30 static void array_check(struct expression *expr)
32 struct expression *array;
33 struct expression *size;
34 struct expression *offset;
35 char *array_str, *offset_str;
37 expr = strip_expr(expr);
38 if (!is_array(expr))
39 return;
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;
49 array_str = expr_to_str(array);
50 offset_str = expr_to_str(offset);
51 sm_msg("warn: potentially one past the end of array '%s[%s]'", array_str, offset_str);
52 free_string(array_str);
53 free_string(offset_str);
56 static int known_access_ok_comparison(struct expression *expr)
58 struct expression *array;
59 struct expression *size;
60 struct expression *offset;
61 int comparison;
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;
72 return 0;
75 static int known_access_ok_numbers(struct expression *expr)
77 struct expression *array;
78 struct expression *offset;
79 sval_t max;
80 int size;
82 array = get_array_base(expr);
83 offset = get_array_offset(expr);
85 size = get_array_size(array);
86 if (size <= 0)
87 return 0;
89 get_absolute_max(offset, &max);
90 if (max.uvalue < size)
91 return 1;
92 return 0;
95 static void array_check_data_info(struct expression *expr)
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;
105 expr = strip_expr(expr);
106 if (!is_array(expr))
107 return;
109 if (known_access_ok_numbers(expr))
110 return;
111 if (known_access_ok_comparison(expr))
112 return;
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;
123 FOR_EACH_PTR(slist, sm) {
124 comp = sm->state->data;
125 if (strcmp(comp->var1, offset_name) == 0) {
126 if (db_var_is_array_limit(array, comp->var2, comp->vsl2)) {
127 equal_name = comp->var2;
128 break;
130 } else if (strcmp(comp->var2, offset_name) == 0) {
131 if (db_var_is_array_limit(array, comp->var1, comp->vsl1)) {
132 equal_name = comp->var1;
133 break;
136 } END_FOR_EACH_PTR(sm);
138 if (equal_name) {
139 char *array_name = expr_to_str(array);
141 sm_msg("warn: potential off by one '%s[]' limit '%s'", array_name, equal_name);
142 free_string(array_name);
145 free:
146 free_slist(&slist);
147 free_string(offset_name);
150 void check_off_by_one_relative(int id)
152 my_id = id;
154 add_hook(&array_check, OP_HOOK);
155 add_hook(&array_check_data_info, OP_HOOK);