11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2018 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 #include "smatch.h"
191f5207b7SJohn Levon #include "smatch_slist.h"
201f5207b7SJohn Levon 
211f5207b7SJohn Levon static int my_id;
221f5207b7SJohn Levon static int param_set_id;
231f5207b7SJohn Levon 
241f5207b7SJohn Levon STATE(terminated);
251f5207b7SJohn Levon STATE(unterminated);
261f5207b7SJohn Levon STATE(set);
271f5207b7SJohn Levon 
set_terminated_var_sym(const char * name,struct symbol * sym,struct smatch_state * state)281f5207b7SJohn Levon static void set_terminated_var_sym(const char *name, struct symbol *sym, struct smatch_state *state)
291f5207b7SJohn Levon {
301f5207b7SJohn Levon 	if (get_param_num_from_sym(sym) >= 0)
311f5207b7SJohn Levon 		set_state(param_set_id, name, sym, &set);
321f5207b7SJohn Levon 	set_state(my_id, name, sym, state);
331f5207b7SJohn Levon }
341f5207b7SJohn Levon 
set_terminated(struct expression * expr,struct smatch_state * state)351f5207b7SJohn Levon static void set_terminated(struct expression *expr, struct smatch_state *state)
361f5207b7SJohn Levon {
371f5207b7SJohn Levon 	struct symbol *sym;
381f5207b7SJohn Levon 	char *name;
391f5207b7SJohn Levon 
401f5207b7SJohn Levon 	name = expr_to_var_sym(expr, &sym);
411f5207b7SJohn Levon 	if (!name || !sym)
421f5207b7SJohn Levon 		return;
431f5207b7SJohn Levon 	set_terminated_var_sym(name, sym, state);
441f5207b7SJohn Levon 	free_string(name);
451f5207b7SJohn Levon }
461f5207b7SJohn Levon 
match_nul_assign(struct expression * expr)471f5207b7SJohn Levon static void match_nul_assign(struct expression *expr)
481f5207b7SJohn Levon {
491f5207b7SJohn Levon 	struct expression *array;
501f5207b7SJohn Levon 	struct symbol *type;
511f5207b7SJohn Levon 	sval_t sval;
521f5207b7SJohn Levon 
531f5207b7SJohn Levon 	if (expr->op != '=')
541f5207b7SJohn Levon 		return;
551f5207b7SJohn Levon 
561f5207b7SJohn Levon 	if (!get_value(expr->right, &sval) || sval.value != 0)
571f5207b7SJohn Levon 		return;
581f5207b7SJohn Levon 
591f5207b7SJohn Levon 	array = get_array_base(expr->left);
601f5207b7SJohn Levon 	if (!array)
611f5207b7SJohn Levon 		return;
621f5207b7SJohn Levon 
631f5207b7SJohn Levon 	type = get_type(array);
641f5207b7SJohn Levon 	if (!type)
651f5207b7SJohn Levon 		return;
661f5207b7SJohn Levon 	type = get_real_base_type(type);
671f5207b7SJohn Levon 	if (type != &char_ctype)
681f5207b7SJohn Levon 		return;
691f5207b7SJohn Levon 	set_terminated(array, &terminated);
701f5207b7SJohn Levon }
711f5207b7SJohn Levon 
get_terminated_state_var_sym(const char * name,struct symbol * sym)72*c85f09ccSJohn Levon static struct smatch_state *get_terminated_state_var_sym(const char *name, struct symbol *sym)
73*c85f09ccSJohn Levon {
74*c85f09ccSJohn Levon 	struct sm_state *sm, *tmp;
75*c85f09ccSJohn Levon 
76*c85f09ccSJohn Levon 	sm = get_sm_state(my_id, name, sym);
77*c85f09ccSJohn Levon 	if (!sm)
78*c85f09ccSJohn Levon 		return NULL;
79*c85f09ccSJohn Levon 	if (sm->state == &terminated || sm->state == &unterminated)
80*c85f09ccSJohn Levon 		return sm->state;
81*c85f09ccSJohn Levon 
82*c85f09ccSJohn Levon 	FOR_EACH_PTR(sm->possible, tmp) {
83*c85f09ccSJohn Levon 		if (tmp->state == &unterminated)
84*c85f09ccSJohn Levon 			return &unterminated;
85*c85f09ccSJohn Levon 	} END_FOR_EACH_PTR(tmp);
86*c85f09ccSJohn Levon 
87*c85f09ccSJohn Levon 	return NULL;
88*c85f09ccSJohn Levon }
89*c85f09ccSJohn Levon 
get_terminated_state(struct expression * expr)901f5207b7SJohn Levon static struct smatch_state *get_terminated_state(struct expression *expr)
911f5207b7SJohn Levon {
921f5207b7SJohn Levon 	struct sm_state *sm, *tmp;
931f5207b7SJohn Levon 
941f5207b7SJohn Levon 	if (!expr)
951f5207b7SJohn Levon 		return NULL;
961f5207b7SJohn Levon 	if (expr->type == EXPR_STRING)
971f5207b7SJohn Levon 		return &terminated;
981f5207b7SJohn Levon 	sm = get_sm_state_expr(my_id, expr);
991f5207b7SJohn Levon 	if (!sm)
1001f5207b7SJohn Levon 		return NULL;
1011f5207b7SJohn Levon 	if (sm->state == &terminated || sm->state == &unterminated)
1021f5207b7SJohn Levon 		return sm->state;
1031f5207b7SJohn Levon 
1041f5207b7SJohn Levon 	FOR_EACH_PTR(sm->possible, tmp) {
1051f5207b7SJohn Levon 		if (tmp->state == &unterminated)
1061f5207b7SJohn Levon 			return &unterminated;
1071f5207b7SJohn Levon 	} END_FOR_EACH_PTR(tmp);
1081f5207b7SJohn Levon 
1091f5207b7SJohn Levon 	return NULL;
1101f5207b7SJohn Levon }
1111f5207b7SJohn Levon 
match_string_assign(struct expression * expr)1121f5207b7SJohn Levon static void match_string_assign(struct expression *expr)
1131f5207b7SJohn Levon {
1141f5207b7SJohn Levon 	struct smatch_state *state;
1151f5207b7SJohn Levon 
1161f5207b7SJohn Levon 	if (expr->op != '=')
1171f5207b7SJohn Levon 		return;
1181f5207b7SJohn Levon 	state = get_terminated_state(expr->right);
1191f5207b7SJohn Levon 	if (!state)
1201f5207b7SJohn Levon 		return;
1211f5207b7SJohn Levon 	set_terminated(expr->left, state);
1221f5207b7SJohn Levon }
1231f5207b7SJohn Levon 
sm_to_term(struct sm_state * sm)1241f5207b7SJohn Levon static int sm_to_term(struct sm_state *sm)
1251f5207b7SJohn Levon {
1261f5207b7SJohn Levon 	struct sm_state *tmp;
1271f5207b7SJohn Levon 
1281f5207b7SJohn Levon 	if (!sm)
1291f5207b7SJohn Levon 		return -1;
1301f5207b7SJohn Levon 	if (sm->state == &terminated)
1311f5207b7SJohn Levon 		return 1;
1321f5207b7SJohn Levon 
1331f5207b7SJohn Levon 	FOR_EACH_PTR(sm->possible, tmp) {
1341f5207b7SJohn Levon 		if (tmp->state == &unterminated)
1351f5207b7SJohn Levon 			return 0;
1361f5207b7SJohn Levon 	} END_FOR_EACH_PTR(tmp);
1371f5207b7SJohn Levon 
1381f5207b7SJohn Levon 	return -1;
1391f5207b7SJohn Levon }
1401f5207b7SJohn Levon 
struct_member_callback(struct expression * call,int param,char * printed_name,struct sm_state * sm)1411f5207b7SJohn Levon static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
1421f5207b7SJohn Levon {
1431f5207b7SJohn Levon 	int term;
1441f5207b7SJohn Levon 
1451f5207b7SJohn Levon 	term = sm_to_term(sm);
1461f5207b7SJohn Levon 	if (term < 0)
1471f5207b7SJohn Levon 		return;
1481f5207b7SJohn Levon 
1491f5207b7SJohn Levon 	sql_insert_caller_info(call, TERMINATED, param, printed_name, term ? "1" : "0");
1501f5207b7SJohn Levon }
1511f5207b7SJohn Levon 
match_call_info(struct expression * expr)1521f5207b7SJohn Levon static void match_call_info(struct expression *expr)
1531f5207b7SJohn Levon {
1541f5207b7SJohn Levon 	struct smatch_state *state;
1551f5207b7SJohn Levon 	struct expression *arg;
1561f5207b7SJohn Levon 	int i;
1571f5207b7SJohn Levon 
1581f5207b7SJohn Levon 	i = -1;
1591f5207b7SJohn Levon 	FOR_EACH_PTR(expr->args, arg) {
1601f5207b7SJohn Levon 		i++;
1611f5207b7SJohn Levon 
1621f5207b7SJohn Levon 		state = get_terminated_state(arg);
1631f5207b7SJohn Levon 		if (!state)
1641f5207b7SJohn Levon 			continue;
1651f5207b7SJohn Levon 		sql_insert_caller_info(expr, TERMINATED, i, "$",
1661f5207b7SJohn Levon 				       (state == &terminated) ? "1" : "0");
1671f5207b7SJohn Levon 	} END_FOR_EACH_PTR(arg);
1681f5207b7SJohn Levon }
1691f5207b7SJohn Levon 
caller_info_terminated(const char * name,struct symbol * sym,char * key,char * value)1701f5207b7SJohn Levon static void caller_info_terminated(const char *name, struct symbol *sym, char *key, char *value)
1711f5207b7SJohn Levon {
1721f5207b7SJohn Levon 	char fullname[256];
1731f5207b7SJohn Levon 
1741f5207b7SJohn Levon 	if (strcmp(key, "*$") == 0)
1751f5207b7SJohn Levon 		snprintf(fullname, sizeof(fullname), "*%s", name);
1761f5207b7SJohn Levon 	else if (strncmp(key, "$", 1) == 0)
1771f5207b7SJohn Levon 		snprintf(fullname, 256, "%s%s", name, key + 1);
1781f5207b7SJohn Levon 	else
1791f5207b7SJohn Levon 		return;
1801f5207b7SJohn Levon 
1811f5207b7SJohn Levon 	set_state(my_id, fullname, sym, (*value == '1') ? &terminated : &unterminated);
1821f5207b7SJohn Levon }
1831f5207b7SJohn Levon 
split_return_info(int return_id,char * return_ranges,struct expression * expr)1841f5207b7SJohn Levon static void split_return_info(int return_id, char *return_ranges, struct expression *expr)
1851f5207b7SJohn Levon {
1861f5207b7SJohn Levon 	struct symbol *returned_sym;
1871f5207b7SJohn Levon 	struct sm_state *tmp, *sm;
1881f5207b7SJohn Levon 	const char *param_name;
1891f5207b7SJohn Levon 	int param;
1901f5207b7SJohn Levon 	int term;
1911f5207b7SJohn Levon 
1921f5207b7SJohn Levon 	FOR_EACH_MY_SM(param_set_id, __get_cur_stree(), tmp) {
1931f5207b7SJohn Levon 		sm = get_sm_state(my_id, tmp->name, tmp->sym);
1941f5207b7SJohn Levon 		if (!sm)
1951f5207b7SJohn Levon 			continue;
1961f5207b7SJohn Levon 		term = sm_to_term(sm);
1971f5207b7SJohn Levon 		if (term < 0)
1981f5207b7SJohn Levon 			continue;
1991f5207b7SJohn Levon 		param = get_param_num_from_sym(tmp->sym);
2001f5207b7SJohn Levon 		if (param < 0)
2011f5207b7SJohn Levon 			continue;
2021f5207b7SJohn Levon 
2031f5207b7SJohn Levon 		param_name = get_param_name(sm);
2041f5207b7SJohn Levon 		if (!param_name)
2051f5207b7SJohn Levon 			continue;
2061f5207b7SJohn Levon 		if (strcmp(param_name, "$") == 0)
2071f5207b7SJohn Levon 			continue;
2081f5207b7SJohn Levon 
2091f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges, TERMINATED,
2101f5207b7SJohn Levon 					 param, param_name, term ? "1" : "0");
2111f5207b7SJohn Levon 	} END_FOR_EACH_SM(tmp);
2121f5207b7SJohn Levon 
2131f5207b7SJohn Levon 	returned_sym = expr_to_sym(expr);
2141f5207b7SJohn Levon 	if (!returned_sym)
2151f5207b7SJohn Levon 		return;
2161f5207b7SJohn Levon 
2171f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
2181f5207b7SJohn Levon 		if (sm->sym != returned_sym)
2191f5207b7SJohn Levon 			continue;
2201f5207b7SJohn Levon 		term = sm_to_term(sm);
2211f5207b7SJohn Levon 		if (term < 0)
2221f5207b7SJohn Levon 			continue;
2231f5207b7SJohn Levon 		param_name = get_param_name(sm);
2241f5207b7SJohn Levon 		if (!param_name)
2251f5207b7SJohn Levon 			continue;
2261f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges, TERMINATED,
2271f5207b7SJohn Levon 					 -1, param_name, term ? "1" : "0");
2281f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
2291f5207b7SJohn Levon }
2301f5207b7SJohn Levon 
return_info_terminated(struct expression * expr,int param,char * key,char * value)2311f5207b7SJohn Levon static void return_info_terminated(struct expression *expr, int param, char *key, char *value)
2321f5207b7SJohn Levon {
2331f5207b7SJohn Levon 	struct expression *arg;
2341f5207b7SJohn Levon 	char *name;
2351f5207b7SJohn Levon 	struct symbol *sym;
2361f5207b7SJohn Levon 
2371f5207b7SJohn Levon 	if (param == -1) {
2381f5207b7SJohn Levon 		arg = expr->left;
2391f5207b7SJohn Levon 	} else {
2401f5207b7SJohn Levon 		struct expression *call = expr;
2411f5207b7SJohn Levon 
2421f5207b7SJohn Levon 		while (call->type == EXPR_ASSIGNMENT)
2431f5207b7SJohn Levon 			call = strip_expr(call->right);
2441f5207b7SJohn Levon 		if (call->type != EXPR_CALL)
2451f5207b7SJohn Levon 			return;
2461f5207b7SJohn Levon 
2471f5207b7SJohn Levon 		arg = get_argument_from_call_expr(call->args, param);
2481f5207b7SJohn Levon 		if (!arg)
2491f5207b7SJohn Levon 			return;
2501f5207b7SJohn Levon 	}
2511f5207b7SJohn Levon 
2521f5207b7SJohn Levon 	name = get_variable_from_key(arg, key, &sym);
2531f5207b7SJohn Levon 	if (!name || !sym)
2541f5207b7SJohn Levon 		goto free;
2551f5207b7SJohn Levon 
2561f5207b7SJohn Levon 	set_terminated_var_sym(name, sym, (*value == '1') ? &terminated : &unterminated);
2571f5207b7SJohn Levon free:
2581f5207b7SJohn Levon 	free_string(name);
2591f5207b7SJohn Levon }
2601f5207b7SJohn Levon 
is_nul_terminated_var_sym(const char * name,struct symbol * sym)261*c85f09ccSJohn Levon bool is_nul_terminated_var_sym(const char *name, struct symbol *sym)
262*c85f09ccSJohn Levon {
263*c85f09ccSJohn Levon 	if (get_terminated_state_var_sym(name, sym) == &terminated)
264*c85f09ccSJohn Levon 		return 1;
265*c85f09ccSJohn Levon 	return 0;
266*c85f09ccSJohn Levon }
267*c85f09ccSJohn Levon 
is_nul_terminated(struct expression * expr)2681f5207b7SJohn Levon bool is_nul_terminated(struct expression *expr)
2691f5207b7SJohn Levon {
2701f5207b7SJohn Levon 	if (get_terminated_state(expr) == &terminated)
2711f5207b7SJohn Levon 		return 1;
2721f5207b7SJohn Levon 	return 0;
2731f5207b7SJohn Levon }
2741f5207b7SJohn Levon 
match_strnlen_test(struct expression * expr)275efe51d0cSJohn Levon static void match_strnlen_test(struct expression *expr)
276efe51d0cSJohn Levon {
277efe51d0cSJohn Levon 	struct expression *left, *tmp, *arg;
278efe51d0cSJohn Levon 	int cnt;
279efe51d0cSJohn Levon 
280efe51d0cSJohn Levon 	if (expr->type != EXPR_COMPARE)
281efe51d0cSJohn Levon 		return;
282efe51d0cSJohn Levon 	if (expr->op != SPECIAL_EQUAL && expr->op != SPECIAL_NOTEQUAL)
283efe51d0cSJohn Levon 		return;
284efe51d0cSJohn Levon 
285efe51d0cSJohn Levon 	left = strip_expr(expr->left);
286efe51d0cSJohn Levon 	cnt = 0;
287efe51d0cSJohn Levon 	while ((tmp = get_assigned_expr(left))) {
288efe51d0cSJohn Levon 		if (cnt++ > 3)
289efe51d0cSJohn Levon 			break;
290efe51d0cSJohn Levon 		left = tmp;
291efe51d0cSJohn Levon 	}
292efe51d0cSJohn Levon 
293efe51d0cSJohn Levon 	if (left->type != EXPR_CALL)
294efe51d0cSJohn Levon 		return;
295efe51d0cSJohn Levon 	if (!sym_name_is("strnlen", left->fn))
296efe51d0cSJohn Levon 		return;
297efe51d0cSJohn Levon 	arg = get_argument_from_call_expr(left->args, 0);
298efe51d0cSJohn Levon 	set_true_false_states_expr(my_id, arg,
299efe51d0cSJohn Levon 			(expr->op == SPECIAL_EQUAL) ? &terminated : NULL,
300efe51d0cSJohn Levon 			(expr->op == SPECIAL_NOTEQUAL) ? &terminated : NULL);
301efe51d0cSJohn Levon 	if (get_param_num(arg) >= 0)
302efe51d0cSJohn Levon 		set_true_false_states_expr(param_set_id, arg,
303efe51d0cSJohn Levon 				(expr->op == SPECIAL_EQUAL) ? &terminated : NULL,
304efe51d0cSJohn Levon 				(expr->op == SPECIAL_NOTEQUAL) ? &terminated : NULL);
305efe51d0cSJohn Levon }
306efe51d0cSJohn Levon 
register_nul_terminator(int id)3071f5207b7SJohn Levon void register_nul_terminator(int id)
3081f5207b7SJohn Levon {
3091f5207b7SJohn Levon 	my_id = id;
3101f5207b7SJohn Levon 
3111f5207b7SJohn Levon 	add_hook(&match_nul_assign, ASSIGNMENT_HOOK);
3121f5207b7SJohn Levon 	add_hook(&match_string_assign, ASSIGNMENT_HOOK);
3131f5207b7SJohn Levon 
3141f5207b7SJohn Levon 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
3151f5207b7SJohn Levon 	add_member_info_callback(my_id, struct_member_callback);
3161f5207b7SJohn Levon 	add_split_return_callback(&split_return_info);
3171f5207b7SJohn Levon 
3181f5207b7SJohn Levon 	select_caller_info_hook(caller_info_terminated, TERMINATED);
3191f5207b7SJohn Levon 	select_return_states_hook(TERMINATED, return_info_terminated);
320efe51d0cSJohn Levon 
321efe51d0cSJohn Levon 	add_hook(&match_strnlen_test, CONDITION_HOOK);
3221f5207b7SJohn Levon }
3231f5207b7SJohn Levon 
register_nul_terminator_param_set(int id)3241f5207b7SJohn Levon void register_nul_terminator_param_set(int id)
3251f5207b7SJohn Levon {
3261f5207b7SJohn Levon 	param_set_id = id;
3271f5207b7SJohn Levon }
328