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
19 #include "smatch_extra.h"
22 extern int second_half_id
;
23 extern void set_spectre_first_half(struct expression
*expr
);
25 enum kernel_data_types
{
30 static int suppress_multiple
= 1;
31 static int analyzed_data_type
= USER_DATA_TYPE
;
33 static int is_write(struct expression
*expr
)
38 static int is_read(struct expression
*expr
)
40 struct expression
*parent
, *last_parent
;
41 struct statement
*stmt
;
47 while ((parent
= expr_get_parent_expr(expr
))){
51 /* If we pass a value as a parameter that's a read, probably? */
52 // if (parent->type == EXPR_CALL)
55 if (parent
->type
== EXPR_ASSIGNMENT
) {
56 if (parent
->right
== expr
)
58 if (parent
->left
== expr
)
64 stmt
= expr_get_parent_stmt(last_parent
);
65 if (stmt
&& stmt
->type
== STMT_RETURN
)
71 static int is_harmless(struct expression
*expr
)
73 struct expression
*tmp
, *parent
;
74 struct statement
*stmt
;
78 while ((tmp
= expr_get_parent_expr(parent
))) {
79 if (tmp
->type
== EXPR_ASSIGNMENT
|| tmp
->type
== EXPR_CALL
)
86 stmt
= expr_get_parent_stmt(parent
);
89 if (stmt
->type
== STMT_IF
&& stmt
->if_conditional
== parent
)
91 if (stmt
->type
== STMT_ITERATOR
&&
92 (stmt
->iterator_pre_condition
== parent
||
93 stmt
->iterator_post_condition
== parent
))
99 static unsigned long long get_max_by_type(struct expression
*expr
)
102 .type
= &ullong_ctype
,
109 expr
= strip_parens(expr
);
110 type
= get_type(expr
);
111 if (type
&& sval_type_max(type
).uvalue
< max
.uvalue
)
112 max
= sval_type_max(type
);
113 if (expr
->type
== EXPR_PREOP
) {
115 } else if (expr
->type
== EXPR_BINOP
) {
116 if (expr
->op
== '%' || expr
->op
== '&')
121 expr
= get_assigned_expr(expr
);
132 static unsigned long long get_mask(struct expression
*expr
)
134 struct expression
*tmp
;
138 expr
= strip_expr(expr
);
140 tmp
= get_assigned_expr(expr
);
145 tmp
= get_assigned_expr(expr
);
148 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '&') {
149 if (get_value(expr
->right
, &mask
)) /* right is the common case */
151 if (get_value(expr
->left
, &mask
))
158 static void array_check(struct expression
*expr
)
160 struct expression_list
*conditions
;
161 struct expression
*array_expr
, *offset
;
162 unsigned long long mask
;
166 expr
= strip_expr(expr
);
170 if (is_impossible_path())
172 if (is_harmless(expr
))
175 array_expr
= get_array_base(expr
);
176 if (suppress_multiple
&& is_ignored_expr(my_id
, array_expr
)) {
177 set_spectre_first_half(expr
);
181 offset
= get_array_offset(expr
);
182 switch(analyzed_data_type
) {
184 if (!is_user_rl(offset
))
188 if (!is_host_rl(offset
))
195 if (is_nospec(offset
))
198 array_size
= get_array_size(array_expr
);
199 if (array_size
> 0 && get_max_by_type(offset
) < array_size
)
201 // binfo = get_bit_info(offset);
202 // if (array_size > 0 && binfo && binfo->possible < array_size)
205 mask
= get_mask(offset
);
206 if (mask
<= array_size
)
209 conditions
= get_conditions(offset
);
211 name
= expr_to_str(array_expr
);
212 sm_warning("potential spectre issue '%s' [%s]%s",
214 is_read(expr
) ? "r" : "w",
215 conditions
? " (local cap)" : "");
217 set_spectre_first_half(expr
);
218 if (suppress_multiple
)
219 add_ignore_expr(my_id
, array_expr
);
223 void check_spectre(int id
)
227 suppress_multiple
= getenv("FULL_SPECTRE") == NULL
;
228 if (getenv("ANALYZE_HOST_DATA"))
229 analyzed_data_type
= HOST_DATA_TYPE
;
231 if (option_project
!= PROJ_KERNEL
)
234 add_hook(&array_check
, OP_HOOK
);