11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2015 Oracle.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
181f5207b7SJohn Levon /*
191f5207b7SJohn Levon  * The point here is to store that a buffer has x bytes even if we don't know
201f5207b7SJohn Levon  * the value of x.
211f5207b7SJohn Levon  *
221f5207b7SJohn Levon  */
231f5207b7SJohn Levon 
241f5207b7SJohn Levon #include "smatch.h"
251f5207b7SJohn Levon #include "smatch_slist.h"
261f5207b7SJohn Levon #include "smatch_extra.h"
271f5207b7SJohn Levon 
281f5207b7SJohn Levon static int my_id;
291f5207b7SJohn Levon 
array_check(struct expression * expr)301f5207b7SJohn Levon static void array_check(struct expression *expr)
311f5207b7SJohn Levon {
321f5207b7SJohn Levon 	struct expression *array;
331f5207b7SJohn Levon 	struct expression *size;
341f5207b7SJohn Levon 	struct expression *offset;
351f5207b7SJohn Levon 	char *array_str, *offset_str;
36*efe51d0cSJohn Levon 	int limit_type;
371f5207b7SJohn Levon 
381f5207b7SJohn Levon 	expr = strip_expr(expr);
391f5207b7SJohn Levon 	if (!is_array(expr))
401f5207b7SJohn Levon 		return;
411f5207b7SJohn Levon 
421f5207b7SJohn Levon 	array = get_array_base(expr);
43*efe51d0cSJohn Levon 	size = get_size_variable(array, &limit_type);
44*efe51d0cSJohn Levon 	if (!size || limit_type != ELEM_COUNT)
451f5207b7SJohn Levon 		return;
461f5207b7SJohn Levon 	offset = get_array_offset(expr);
471f5207b7SJohn Levon 	if (!possible_comparison(size, SPECIAL_EQUAL, offset))
481f5207b7SJohn Levon 		return;
491f5207b7SJohn Levon 
50*efe51d0cSJohn Levon 	if (buf_comparison_index_ok(expr))
51*efe51d0cSJohn Levon 		return;
52*efe51d0cSJohn Levon 
531f5207b7SJohn Levon 	array_str = expr_to_str(array);
541f5207b7SJohn Levon 	offset_str = expr_to_str(offset);
551f5207b7SJohn Levon 	sm_warning("potentially one past the end of array '%s[%s]'", array_str, offset_str);
561f5207b7SJohn Levon 	free_string(array_str);
571f5207b7SJohn Levon 	free_string(offset_str);
581f5207b7SJohn Levon }
591f5207b7SJohn Levon 
known_access_ok_numbers(struct expression * expr)601f5207b7SJohn Levon static int known_access_ok_numbers(struct expression *expr)
611f5207b7SJohn Levon {
621f5207b7SJohn Levon 	struct expression *array;
631f5207b7SJohn Levon 	struct expression *offset;
641f5207b7SJohn Levon 	sval_t max;
651f5207b7SJohn Levon 	int size;
661f5207b7SJohn Levon 
671f5207b7SJohn Levon 	array = get_array_base(expr);
681f5207b7SJohn Levon 	offset = get_array_offset(expr);
691f5207b7SJohn Levon 
701f5207b7SJohn Levon 	size = get_array_size(array);
711f5207b7SJohn Levon 	if (size <= 0)
721f5207b7SJohn Levon 		return 0;
731f5207b7SJohn Levon 
741f5207b7SJohn Levon 	get_absolute_max(offset, &max);
751f5207b7SJohn Levon 	if (max.uvalue < size)
761f5207b7SJohn Levon 		return 1;
771f5207b7SJohn Levon 	return 0;
781f5207b7SJohn Levon }
791f5207b7SJohn Levon 
array_check_data_info(struct expression * expr)801f5207b7SJohn Levon static void array_check_data_info(struct expression *expr)
811f5207b7SJohn Levon {
821f5207b7SJohn Levon 	struct expression *array;
831f5207b7SJohn Levon 	struct expression *offset;
841f5207b7SJohn Levon 	struct state_list *slist;
851f5207b7SJohn Levon 	struct sm_state *sm;
861f5207b7SJohn Levon 	struct compare_data *comp;
871f5207b7SJohn Levon 	char *offset_name;
881f5207b7SJohn Levon 	const char *equal_name = NULL;
891f5207b7SJohn Levon 
901f5207b7SJohn Levon 	expr = strip_expr(expr);
911f5207b7SJohn Levon 	if (!is_array(expr))
921f5207b7SJohn Levon 		return;
931f5207b7SJohn Levon 
941f5207b7SJohn Levon 	if (known_access_ok_numbers(expr))
951f5207b7SJohn Levon 		return;
96*efe51d0cSJohn Levon 	if (buf_comparison_index_ok(expr))
971f5207b7SJohn Levon 		return;
981f5207b7SJohn Levon 
991f5207b7SJohn Levon 	array = get_array_base(expr);
1001f5207b7SJohn Levon 	offset = get_array_offset(expr);
1011f5207b7SJohn Levon 	offset_name = expr_to_var(offset);
1021f5207b7SJohn Levon 	if (!offset_name)
1031f5207b7SJohn Levon 		return;
1041f5207b7SJohn Levon 	slist = get_all_possible_equal_comparisons(offset);
1051f5207b7SJohn Levon 	if (!slist)
1061f5207b7SJohn Levon 		goto free;
1071f5207b7SJohn Levon 
1081f5207b7SJohn Levon 	FOR_EACH_PTR(slist, sm) {
1091f5207b7SJohn Levon 		comp = sm->state->data;
1101f5207b7SJohn Levon 		if (strcmp(comp->left_var, offset_name) == 0) {
1111f5207b7SJohn Levon 			if (db_var_is_array_limit(array, comp->right_var, comp->right_vsl)) {
1121f5207b7SJohn Levon 				equal_name = comp->right_var;
1131f5207b7SJohn Levon 				break;
1141f5207b7SJohn Levon 			}
1151f5207b7SJohn Levon 		} else if (strcmp(comp->right_var, offset_name) == 0) {
1161f5207b7SJohn Levon 			if (db_var_is_array_limit(array, comp->left_var, comp->left_vsl)) {
1171f5207b7SJohn Levon 				equal_name = comp->left_var;
1181f5207b7SJohn Levon 				break;
1191f5207b7SJohn Levon 			}
1201f5207b7SJohn Levon 		}
1211f5207b7SJohn Levon 	} END_FOR_EACH_PTR(sm);
1221f5207b7SJohn Levon 
1231f5207b7SJohn Levon 	if (equal_name) {
1241f5207b7SJohn Levon 		char *array_name = expr_to_str(array);
1251f5207b7SJohn Levon 
1261f5207b7SJohn Levon 		sm_warning("potential off by one '%s[]' limit '%s'", array_name, equal_name);
1271f5207b7SJohn Levon 		free_string(array_name);
1281f5207b7SJohn Levon 	}
1291f5207b7SJohn Levon 
1301f5207b7SJohn Levon free:
1311f5207b7SJohn Levon 	free_slist(&slist);
1321f5207b7SJohn Levon 	free_string(offset_name);
1331f5207b7SJohn Levon }
1341f5207b7SJohn Levon 
check_off_by_one_relative(int id)1351f5207b7SJohn Levon void check_off_by_one_relative(int id)
1361f5207b7SJohn Levon {
1371f5207b7SJohn Levon 	my_id = id;
1381f5207b7SJohn Levon 
1391f5207b7SJohn Levon 	add_hook(&array_check, OP_HOOK);
1401f5207b7SJohn Levon 	add_hook(&array_check_data_info, OP_HOOK);
1411f5207b7SJohn Levon }
1421f5207b7SJohn Levon 
143