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 <stdlib.h>
191f5207b7SJohn Levon #include "parse.h"
201f5207b7SJohn Levon #include "smatch.h"
211f5207b7SJohn Levon #include "smatch_slist.h"
221f5207b7SJohn Levon #include "smatch_extra.h"
231f5207b7SJohn Levon 
241f5207b7SJohn Levon static int my_id;
251f5207b7SJohn Levon static int barrier_id;
261f5207b7SJohn Levon 
271f5207b7SJohn Levon STATE(nospec);
281f5207b7SJohn Levon 
291f5207b7SJohn Levon static bool in_nospec_stmt;
301f5207b7SJohn Levon 
unmatched_state(struct sm_state * sm)311f5207b7SJohn Levon static struct smatch_state *unmatched_state(struct sm_state *sm)
321f5207b7SJohn Levon {
331f5207b7SJohn Levon 	struct range_list *rl;
341f5207b7SJohn Levon 
351f5207b7SJohn Levon 	if (__in_function_def && !get_user_rl_var_sym(sm->name, sm->sym, &rl))
361f5207b7SJohn Levon 		return &nospec;
371f5207b7SJohn Levon 	return &undefined;
381f5207b7SJohn Levon }
391f5207b7SJohn Levon 
is_nospec(struct expression * expr)401f5207b7SJohn Levon bool is_nospec(struct expression *expr)
411f5207b7SJohn Levon {
421f5207b7SJohn Levon 	char *macro;
431f5207b7SJohn Levon 
441f5207b7SJohn Levon 	if (in_nospec_stmt)
451f5207b7SJohn Levon 		return true;
461f5207b7SJohn Levon 	if (!expr)
471f5207b7SJohn Levon 		return false;
481f5207b7SJohn Levon 	if (get_state_expr(my_id, expr) == &nospec)
491f5207b7SJohn Levon 		return true;
501f5207b7SJohn Levon 	macro = get_macro_name(expr->pos);
511f5207b7SJohn Levon 	if (macro && strcmp(macro, "array_index_nospec") == 0)
521f5207b7SJohn Levon 		return true;
531f5207b7SJohn Levon 	return false;
541f5207b7SJohn Levon }
551f5207b7SJohn Levon 
nospec_assign(struct expression * expr)561f5207b7SJohn Levon static void nospec_assign(struct expression *expr)
571f5207b7SJohn Levon {
581f5207b7SJohn Levon 	if (is_nospec(expr->right))
591f5207b7SJohn Levon 		set_state_expr(my_id, expr->left, &nospec);
601f5207b7SJohn Levon }
611f5207b7SJohn Levon 
set_param_nospec(const char * name,struct symbol * sym,char * key,char * value)621f5207b7SJohn Levon static void set_param_nospec(const char *name, struct symbol *sym, char *key, char *value)
631f5207b7SJohn Levon {
641f5207b7SJohn Levon 	char fullname[256];
651f5207b7SJohn Levon 
661f5207b7SJohn Levon 	if (strcmp(key, "*$") == 0)
671f5207b7SJohn Levon 		snprintf(fullname, sizeof(fullname), "*%s", name);
681f5207b7SJohn Levon 	else if (strncmp(key, "$", 1) == 0)
691f5207b7SJohn Levon 		snprintf(fullname, 256, "%s%s", name, key + 1);
701f5207b7SJohn Levon 	else
711f5207b7SJohn Levon 		return;
721f5207b7SJohn Levon 
731f5207b7SJohn Levon 	set_state(my_id, fullname, sym, &nospec);
741f5207b7SJohn Levon }
751f5207b7SJohn Levon 
match_call_info(struct expression * expr)761f5207b7SJohn Levon static void match_call_info(struct expression *expr)
771f5207b7SJohn Levon {
781f5207b7SJohn Levon 	struct expression *arg;
791f5207b7SJohn Levon 	int i = 0;
801f5207b7SJohn Levon 
811f5207b7SJohn Levon 	FOR_EACH_PTR(expr->args, arg) {
821f5207b7SJohn Levon 		if (get_state_expr(my_id, arg) == &nospec)
831f5207b7SJohn Levon 			sql_insert_caller_info(expr, NOSPEC, i, "$", "");
841f5207b7SJohn Levon 		i++;
851f5207b7SJohn Levon 	} END_FOR_EACH_PTR(arg);
861f5207b7SJohn Levon }
871f5207b7SJohn Levon 
struct_member_callback(struct expression * call,int param,char * printed_name,struct sm_state * sm)881f5207b7SJohn Levon static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
891f5207b7SJohn Levon {
901f5207b7SJohn Levon 	struct range_list *rl;
911f5207b7SJohn Levon 
921f5207b7SJohn Levon 	if (!get_user_rl_var_sym(sm->name, sm->sym, &rl))
931f5207b7SJohn Levon 		return;
941f5207b7SJohn Levon 	sql_insert_caller_info(call, NOSPEC, param, printed_name, "");
951f5207b7SJohn Levon }
961f5207b7SJohn Levon 
returned_struct_members(int return_id,char * return_ranges,struct expression * expr)971f5207b7SJohn Levon static void returned_struct_members(int return_id, char *return_ranges, struct expression *expr)
981f5207b7SJohn Levon {
99efe51d0cSJohn Levon 	struct stree *start_states = get_start_states();
1001f5207b7SJohn Levon 	struct symbol *returned_sym;
1011f5207b7SJohn Levon 	struct sm_state *sm;
1021f5207b7SJohn Levon 	const char *param_name;
1031f5207b7SJohn Levon 	struct range_list *rl;
1041f5207b7SJohn Levon 	int param;
1051f5207b7SJohn Levon 
1061f5207b7SJohn Levon 	returned_sym = expr_to_sym(expr);
1071f5207b7SJohn Levon 
1081f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
109efe51d0cSJohn Levon 		if (get_state_stree(start_states, my_id, sm->name, sm->sym) == sm->state)
110efe51d0cSJohn Levon 			continue;
1111f5207b7SJohn Levon 		param = get_param_num_from_sym(sm->sym);
1121f5207b7SJohn Levon 		if (param < 0) {
1131f5207b7SJohn Levon 			if (!returned_sym || returned_sym != sm->sym)
1141f5207b7SJohn Levon 				continue;
1151f5207b7SJohn Levon 			param = -1;
1161f5207b7SJohn Levon 		}
1171f5207b7SJohn Levon 
1181f5207b7SJohn Levon 		param_name = get_param_name(sm);
1191f5207b7SJohn Levon 		if (!param_name)
1201f5207b7SJohn Levon 			continue;
1211f5207b7SJohn Levon 		if (param != -1 && strcmp(param_name, "$") == 0)
1221f5207b7SJohn Levon 			continue;
1231f5207b7SJohn Levon 
1241f5207b7SJohn Levon 		if (!get_user_rl_var_sym(sm->name, sm->sym, &rl))
1251f5207b7SJohn Levon 			continue;
1261f5207b7SJohn Levon 
1271f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges, NOSPEC, param, param_name, "");
1281f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
1291f5207b7SJohn Levon 
1301f5207b7SJohn Levon 	if (is_nospec(expr) && get_user_rl(expr, &rl))
1311f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges, NOSPEC, -1, "$", "");
1321f5207b7SJohn Levon 
1331f5207b7SJohn Levon 	if (get_state(barrier_id, "barrier", NULL) == &nospec)
1341f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges, NOSPEC_WB, -1, "", "");
1351f5207b7SJohn Levon }
1361f5207b7SJohn Levon 
is_return_statement(void)1371f5207b7SJohn Levon static int is_return_statement(void)
1381f5207b7SJohn Levon {
1391f5207b7SJohn Levon 	if (__cur_stmt && __cur_stmt->type == STMT_RETURN)
1401f5207b7SJohn Levon 		return 1;
1411f5207b7SJohn Levon 	return 0;
1421f5207b7SJohn Levon }
1431f5207b7SJohn Levon 
db_returns_nospec(struct expression * expr,int param,char * key,char * value)1441f5207b7SJohn Levon static void db_returns_nospec(struct expression *expr, int param, char *key, char *value)
1451f5207b7SJohn Levon {
1461f5207b7SJohn Levon 	struct expression *call;
1471f5207b7SJohn Levon 	struct expression *arg;
1481f5207b7SJohn Levon 	char *name;
1491f5207b7SJohn Levon 	struct symbol *sym;
1501f5207b7SJohn Levon 
1511f5207b7SJohn Levon 	call = expr;
1521f5207b7SJohn Levon 	while (call->type == EXPR_ASSIGNMENT)
1531f5207b7SJohn Levon 		call = strip_expr(call->right);
1541f5207b7SJohn Levon 	if (call->type != EXPR_CALL)
1551f5207b7SJohn Levon 		return;
1561f5207b7SJohn Levon 
1571f5207b7SJohn Levon 	if (param == -1 && expr->type == EXPR_ASSIGNMENT) {
1581f5207b7SJohn Levon 		name = get_variable_from_key(expr->left, key, &sym);
1591f5207b7SJohn Levon 	} else if (param == -1 && is_return_statement()) {
1601f5207b7SJohn Levon 		in_nospec_stmt = true;
1611f5207b7SJohn Levon 		return;
1621f5207b7SJohn Levon 	} else {
1631f5207b7SJohn Levon 		arg = get_argument_from_call_expr(call->args, param);
1641f5207b7SJohn Levon 		if (!arg)
1651f5207b7SJohn Levon 			return;
1661f5207b7SJohn Levon 		name = get_variable_from_key(arg, key, &sym);
1671f5207b7SJohn Levon 	}
1681f5207b7SJohn Levon 	if (!name || !sym)
1691f5207b7SJohn Levon 		goto free;
1701f5207b7SJohn Levon 
1711f5207b7SJohn Levon 	set_state(my_id, name, sym, &nospec);
1721f5207b7SJohn Levon free:
1731f5207b7SJohn Levon 	free_string(name);
1741f5207b7SJohn Levon }
1751f5207b7SJohn Levon 
is_nospec_asm(struct statement * stmt)1761f5207b7SJohn Levon static int is_nospec_asm(struct statement *stmt)
1771f5207b7SJohn Levon {
1781f5207b7SJohn Levon 	char *macro;
1791f5207b7SJohn Levon 
1801f5207b7SJohn Levon 	if (!stmt || stmt->type != STMT_ASM)
1811f5207b7SJohn Levon 		return 0;
182*c85f09ccSJohn Levon 	if (!stmt->asm_string)
183*c85f09ccSJohn Levon 		return 0;
1841f5207b7SJohn Levon 	macro = get_macro_name(stmt->asm_string->pos);
1851f5207b7SJohn Levon 	if (!macro || strcmp(macro, "CALL_NOSPEC") != 0)
1861f5207b7SJohn Levon 		return 0;
1871f5207b7SJohn Levon 	return 1;
1881f5207b7SJohn Levon }
1891f5207b7SJohn Levon 
match_asm(struct statement * stmt)1901f5207b7SJohn Levon static void match_asm(struct statement *stmt)
1911f5207b7SJohn Levon {
1921f5207b7SJohn Levon 	if (is_nospec_asm(stmt))
1931f5207b7SJohn Levon 		in_nospec_stmt = true;
1941f5207b7SJohn Levon }
1951f5207b7SJohn Levon 
match_after_nospec_asm(struct statement * stmt)1961f5207b7SJohn Levon static void match_after_nospec_asm(struct statement *stmt)
1971f5207b7SJohn Levon {
1981f5207b7SJohn Levon 	in_nospec_stmt = false;
1991f5207b7SJohn Levon }
2001f5207b7SJohn Levon 
mark_user_data_as_nospec(void)2011f5207b7SJohn Levon static void mark_user_data_as_nospec(void)
2021f5207b7SJohn Levon {
2031f5207b7SJohn Levon 	struct stree *stree;
2041f5207b7SJohn Levon 	struct symbol *type;
2051f5207b7SJohn Levon 	struct sm_state *sm;
2061f5207b7SJohn Levon 
2071f5207b7SJohn Levon 	stree = get_user_stree();
2081f5207b7SJohn Levon 	FOR_EACH_SM(stree, sm) {
2091f5207b7SJohn Levon 		if (is_whole_rl(estate_rl(sm->state)))
2101f5207b7SJohn Levon 			continue;
2111f5207b7SJohn Levon 		type = estate_type(sm->state);
2121f5207b7SJohn Levon 		if (!type || type->type != SYM_BASETYPE)
2131f5207b7SJohn Levon 			continue;
2141f5207b7SJohn Levon 		if (!is_capped_var_sym(sm->name, sm->sym))
2151f5207b7SJohn Levon 			continue;
2161f5207b7SJohn Levon 		set_state(my_id, sm->name, sm->sym, &nospec);
2171f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
2181f5207b7SJohn Levon 	free_stree(&stree);
2191f5207b7SJohn Levon }
2201f5207b7SJohn Levon 
match_barrier(struct statement * stmt)2211f5207b7SJohn Levon static void match_barrier(struct statement *stmt)
2221f5207b7SJohn Levon {
2231f5207b7SJohn Levon 	char *macro;
2241f5207b7SJohn Levon 
2251f5207b7SJohn Levon 	macro = get_macro_name(stmt->pos);
2261f5207b7SJohn Levon 	if (!macro)
2271f5207b7SJohn Levon 		return;
2281f5207b7SJohn Levon 	if (strcmp(macro, "rmb") != 0 &&
2291f5207b7SJohn Levon 	    strcmp(macro, "smp_rmb") != 0 &&
230efe51d0cSJohn Levon 	    strcmp(macro, "barrier_nospec") != 0 &&
231efe51d0cSJohn Levon 	    strcmp(macro, "preempt_disable") != 0)
2321f5207b7SJohn Levon 		return;
2331f5207b7SJohn Levon 
2341f5207b7SJohn Levon 	set_state(barrier_id, "barrier", NULL, &nospec);
2351f5207b7SJohn Levon 	mark_user_data_as_nospec();
2361f5207b7SJohn Levon }
2371f5207b7SJohn Levon 
db_returns_barrier(struct expression * expr,int param,char * key,char * value)2381f5207b7SJohn Levon static void db_returns_barrier(struct expression *expr, int param, char *key, char *value)
2391f5207b7SJohn Levon {
2401f5207b7SJohn Levon 	mark_user_data_as_nospec();
2411f5207b7SJohn Levon }
2421f5207b7SJohn Levon 
select_return_stmt_cnt(struct expression * expr,int param,char * key,char * value)243efe51d0cSJohn Levon static void select_return_stmt_cnt(struct expression *expr, int param, char *key, char *value)
244efe51d0cSJohn Levon {
245efe51d0cSJohn Levon 	int cnt;
246efe51d0cSJohn Levon 
247efe51d0cSJohn Levon 	cnt = atoi(value);
248efe51d0cSJohn Levon 	if (cnt > 400)
249efe51d0cSJohn Levon 		mark_user_data_as_nospec();
250efe51d0cSJohn Levon }
251efe51d0cSJohn Levon 
check_nospec(int id)2521f5207b7SJohn Levon void check_nospec(int id)
2531f5207b7SJohn Levon {
2541f5207b7SJohn Levon 	my_id = id;
2551f5207b7SJohn Levon 
2561f5207b7SJohn Levon 	add_hook(&nospec_assign, ASSIGNMENT_HOOK);
2571f5207b7SJohn Levon 
2581f5207b7SJohn Levon 	select_caller_info_hook(set_param_nospec, NOSPEC);
2591f5207b7SJohn Levon 	add_unmatched_state_hook(my_id, &unmatched_state);
2601f5207b7SJohn Levon 
2611f5207b7SJohn Levon 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
2621f5207b7SJohn Levon 	add_member_info_callback(my_id, struct_member_callback);
2631f5207b7SJohn Levon 	add_split_return_callback(&returned_struct_members);
2641f5207b7SJohn Levon 	select_return_states_hook(NOSPEC, &db_returns_nospec);
2651f5207b7SJohn Levon 	select_return_states_hook(NOSPEC_WB, &db_returns_barrier);
266efe51d0cSJohn Levon 	select_return_states_hook(STMT_CNT, &select_return_stmt_cnt);
2671f5207b7SJohn Levon 
2681f5207b7SJohn Levon 	add_hook(&match_asm, ASM_HOOK);
2691f5207b7SJohn Levon 	add_hook(&match_after_nospec_asm, STMT_HOOK_AFTER);
2701f5207b7SJohn Levon }
2711f5207b7SJohn Levon 
check_nospec_barrier(int id)2721f5207b7SJohn Levon void check_nospec_barrier(int id)
2731f5207b7SJohn Levon {
2741f5207b7SJohn Levon 	barrier_id = id;
2751f5207b7SJohn Levon 
2761f5207b7SJohn Levon 	add_hook(&match_barrier, ASM_HOOK);
2771f5207b7SJohn Levon }
278