#include "smatch.h"
#include "smatch_extra.h"
static int my_id;
extern int second_half_id;
extern void set_spectre_first_half(struct expression *expr);
static int suppress_multiple = 1;
static int is_write(struct expression *expr)
{
return 0;
}
static int is_read(struct expression *expr)
{
struct expression *parent, *last_parent;
struct statement *stmt;
if (is_write(expr))
return 0;
last_parent = expr;
while ((parent = expr_get_parent_expr(expr))){
last_parent = parent;
if (parent->type == EXPR_ASSIGNMENT) {
if (parent->right == expr)
return 1;
if (parent->left == expr)
return 0;
}
expr = parent;
}
stmt = expr_get_parent_stmt(last_parent);
if (stmt && stmt->type == STMT_RETURN)
return 1;
return 0;
}
static int is_harmless(struct expression *expr)
{
struct expression *tmp, *parent;
struct statement *stmt;
int count = 0;
parent = expr;
while ((tmp = expr_get_parent_expr(parent))) {
if (tmp->type == EXPR_ASSIGNMENT || tmp->type == EXPR_CALL)
return 0;
parent = tmp;
if (count++ > 4)
break;
}
stmt = expr_get_parent_stmt(parent);
if (!stmt)
return 0;
if (stmt->type == STMT_IF && stmt->if_conditional == parent)
return 1;
if (stmt->type == STMT_ITERATOR &&
(stmt->iterator_pre_condition == parent ||
stmt->iterator_post_condition == parent))
return 1;
return 0;
}
static unsigned long long get_max_by_type(struct expression *expr)
{
struct symbol *type;
int cnt = 0;
sval_t max;
max.type = &ullong_ctype;
max.uvalue = -1ULL;
while (true) {
expr = strip_parens(expr);
type = get_type(expr);
if (type && sval_type_max(type).uvalue < max.uvalue)
max = sval_type_max(type);
if (expr->type == EXPR_PREOP) {
expr = expr->unop;
} else if (expr->type == EXPR_BINOP) {
if (expr->op == '%' || expr->op == '&')
expr = expr->right;
else
return max.uvalue;
} else {
expr = get_assigned_expr(expr);
if (!expr)
return max.uvalue;
}
if (cnt++ > 5)
return max.uvalue;
}
return max.uvalue;
}
static unsigned long long get_mask(struct expression *expr)
{
struct expression *tmp;
sval_t mask;
int cnt = 0;
expr = strip_expr(expr);
tmp = get_assigned_expr(expr);
while (tmp) {
expr = tmp;
if (++cnt > 3)
break;
tmp = get_assigned_expr(expr);
}
if (expr->type == EXPR_BINOP && expr->op == '&') {
if (get_value(expr->right, &mask))
return mask.uvalue;
if (get_value(expr->left, &mask))
return mask.uvalue;
}
return ULLONG_MAX;
}
static void array_check(struct expression *expr)
{
struct expression_list *conditions;
struct expression *array_expr, *offset;
unsigned long long mask;
int array_size;
char *name;
expr = strip_expr(expr);
if (!is_array(expr))
return;
if (is_impossible_path())
return;
if (is_harmless(expr))
return;
array_expr = get_array_base(expr);
if (suppress_multiple && is_ignored_expr(my_id, array_expr)) {
set_spectre_first_half(expr);
return;
}
offset = get_array_offset(expr);
if (!is_user_rl(offset))
return;
if (is_nospec(offset))
return;
array_size = get_array_size(array_expr);
if (array_size > 0 && get_max_by_type(offset) < array_size)
return;
mask = get_mask(offset);
if (mask <= array_size)
return;
conditions = get_conditions(offset);
name = expr_to_str(array_expr);
sm_warning("potential spectre issue '%s' [%s]%s",
name,
is_read(expr) ? "r" : "w",
conditions ? " (local cap)" : "");
set_spectre_first_half(expr);
if (suppress_multiple)
add_ignore_expr(my_id, array_expr);
free_string(name);
}
void check_spectre(int id)
{
my_id = id;
suppress_multiple = getenv("FULL_SPECTRE") == NULL;
if (option_project != PROJ_KERNEL)
return;
add_hook(&array_check, OP_HOOK);
}