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