2 * Copyright (C) 2018 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
21 #include "smatch_slist.h"
22 #include "smatch_extra.h"
25 static int barrier_id
;
29 static bool in_nospec_stmt
;
31 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
33 struct range_list
*rl
;
35 if (__in_function_def
&& !get_user_rl_var_sym(sm
->name
, sm
->sym
, &rl
))
40 bool is_nospec(struct expression
*expr
)
48 if (get_state_expr(my_id
, expr
) == &nospec
)
50 macro
= get_macro_name(expr
->pos
);
51 if (macro
&& strcmp(macro
, "array_index_nospec") == 0)
56 static void nospec_assign(struct expression
*expr
)
58 if (is_nospec(expr
->right
))
59 set_state_expr(my_id
, expr
->left
, &nospec
);
62 static void set_param_nospec(const char *name
, struct symbol
*sym
, char *key
, char *value
)
66 if (strcmp(key
, "*$") == 0)
67 snprintf(fullname
, sizeof(fullname
), "*%s", name
);
68 else if (strncmp(key
, "$", 1) == 0)
69 snprintf(fullname
, 256, "%s%s", name
, key
+ 1);
73 set_state(my_id
, fullname
, sym
, &nospec
);
76 static void match_call_info(struct expression
*expr
)
78 struct expression
*arg
;
81 FOR_EACH_PTR(expr
->args
, arg
) {
82 if (get_state_expr(my_id
, arg
) == &nospec
)
83 sql_insert_caller_info(expr
, NOSPEC
, i
, "$", "");
85 } END_FOR_EACH_PTR(arg
);
88 static void struct_member_callback(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
90 struct range_list
*rl
;
92 if (!get_user_rl_var_sym(sm
->name
, sm
->sym
, &rl
))
94 sql_insert_caller_info(call
, NOSPEC
, param
, printed_name
, "");
97 static void returned_struct_members(int return_id
, char *return_ranges
, struct expression
*expr
)
99 struct stree
*start_states
= get_start_states();
100 struct symbol
*returned_sym
;
102 const char *param_name
;
103 struct range_list
*rl
;
106 returned_sym
= expr_to_sym(expr
);
108 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
109 if (get_state_stree(start_states
, my_id
, sm
->name
, sm
->sym
) == sm
->state
)
111 param
= get_param_num_from_sym(sm
->sym
);
113 if (!returned_sym
|| returned_sym
!= sm
->sym
)
118 param_name
= get_param_name(sm
);
121 if (param
!= -1 && strcmp(param_name
, "$") == 0)
124 if (!get_user_rl_var_sym(sm
->name
, sm
->sym
, &rl
))
127 sql_insert_return_states(return_id
, return_ranges
, NOSPEC
, param
, param_name
, "");
128 } END_FOR_EACH_SM(sm
);
130 if (is_nospec(expr
) && get_user_rl(expr
, &rl
))
131 sql_insert_return_states(return_id
, return_ranges
, NOSPEC
, -1, "$", "");
133 if (get_state(barrier_id
, "barrier", NULL
) == &nospec
)
134 sql_insert_return_states(return_id
, return_ranges
, NOSPEC_WB
, -1, "", "");
137 static int is_return_statement(void)
139 if (__cur_stmt
&& __cur_stmt
->type
== STMT_RETURN
)
144 static void db_returns_nospec(struct expression
*expr
, int param
, char *key
, char *value
)
146 struct expression
*call
;
147 struct expression
*arg
;
152 while (call
->type
== EXPR_ASSIGNMENT
)
153 call
= strip_expr(call
->right
);
154 if (call
->type
!= EXPR_CALL
)
157 if (param
== -1 && expr
->type
== EXPR_ASSIGNMENT
) {
158 name
= get_variable_from_key(expr
->left
, key
, &sym
);
159 } else if (param
== -1 && is_return_statement()) {
160 in_nospec_stmt
= true;
163 arg
= get_argument_from_call_expr(call
->args
, param
);
166 name
= get_variable_from_key(arg
, key
, &sym
);
171 set_state(my_id
, name
, sym
, &nospec
);
176 static int is_nospec_asm(struct statement
*stmt
)
180 if (!stmt
|| stmt
->type
!= STMT_ASM
)
182 if (!stmt
->asm_string
)
184 macro
= get_macro_name(stmt
->asm_string
->pos
);
185 if (!macro
|| strcmp(macro
, "CALL_NOSPEC") != 0)
190 static void match_asm(struct statement
*stmt
)
192 if (is_nospec_asm(stmt
))
193 in_nospec_stmt
= true;
196 static void match_after_nospec_asm(struct statement
*stmt
)
198 in_nospec_stmt
= false;
201 static void mark_user_data_as_nospec(void)
207 stree
= get_user_stree();
208 FOR_EACH_SM(stree
, sm
) {
209 if (is_whole_rl(estate_rl(sm
->state
)))
211 type
= estate_type(sm
->state
);
212 if (!type
|| type
->type
!= SYM_BASETYPE
)
214 if (!is_capped_var_sym(sm
->name
, sm
->sym
))
216 set_state(my_id
, sm
->name
, sm
->sym
, &nospec
);
217 } END_FOR_EACH_SM(sm
);
221 static void match_barrier(struct statement
*stmt
)
225 macro
= get_macro_name(stmt
->pos
);
228 if (strcmp(macro
, "rmb") != 0 &&
229 strcmp(macro
, "smp_rmb") != 0 &&
230 strcmp(macro
, "barrier_nospec") != 0 &&
231 strcmp(macro
, "preempt_disable") != 0)
234 set_state(barrier_id
, "barrier", NULL
, &nospec
);
235 mark_user_data_as_nospec();
238 static void db_returns_barrier(struct expression
*expr
, int param
, char *key
, char *value
)
240 mark_user_data_as_nospec();
243 static void select_return_stmt_cnt(struct expression
*expr
, int param
, char *key
, char *value
)
249 mark_user_data_as_nospec();
252 void check_nospec(int id
)
256 add_hook(&nospec_assign
, ASSIGNMENT_HOOK
);
258 select_caller_info_hook(set_param_nospec
, NOSPEC
);
259 add_unmatched_state_hook(my_id
, &unmatched_state
);
261 add_hook(&match_call_info
, FUNCTION_CALL_HOOK
);
262 add_member_info_callback(my_id
, struct_member_callback
);
263 add_split_return_callback(&returned_struct_members
);
264 select_return_states_hook(NOSPEC
, &db_returns_nospec
);
265 select_return_states_hook(NOSPEC_WB
, &db_returns_barrier
);
266 select_return_states_hook(STMT_CNT
, &select_return_stmt_cnt
);
268 add_hook(&match_asm
, ASM_HOOK
);
269 add_hook(&match_after_nospec_asm
, STMT_HOOK_AFTER
);
272 void check_nospec_barrier(int id
)
276 add_hook(&match_barrier
, ASM_HOOK
);