#include "smatch.h"
#include "smatch_extra.h"
#include "smatch_slist.h"
static int my_id;
static const struct bit_info unknown_bit_info = {
.possible = -1ULL,
};
ALLOCATOR(bit_info, "bit data");
static struct bit_info *alloc_bit_info(unsigned long long set, unsigned long long possible)
{
struct bit_info *bit_info = __alloc_bit_info(0);
bit_info->set = set;
bit_info->possible = possible;
return bit_info;
}
static struct smatch_state *alloc_bstate(unsigned long long set, unsigned long long possible)
{
struct smatch_state *state;
char buf[64];
state = __alloc_smatch_state(0);
snprintf(buf, sizeof(buf), "0x%llx + 0x%llx", set, possible);
state->name = alloc_sname(buf);
state->data = alloc_bit_info(set, possible);
return state;
}
struct bit_info *rl_to_binfo(struct range_list *rl)
{
struct bit_info *ret = __alloc_bit_info(0);
sval_t sval;
if (rl_to_sval(rl, &sval)) {
ret->set = sval.uvalue;
ret->possible = sval.uvalue;
return ret;
}
ret->set = 0;
ret->possible = sval_fls_mask(rl_max(rl));
return ret;
}
static int is_unknown_binfo(struct symbol *type, struct bit_info *binfo)
{
if (!type)
type = &ullong_ctype;
if (binfo->set != 0)
return 0;
if (binfo->possible < (-1ULL >> (64 - type_bits(type))))
return 0;
return 1;
}
static struct smatch_state *unmatched_state(struct sm_state *sm)
{
struct smatch_state *estate;
struct symbol *type;
unsigned long long possible;
struct bit_info *p;
estate = get_state(SMATCH_EXTRA, sm->name, sm->sym);
if (estate_rl(estate)) {
p = rl_to_binfo(estate_rl(estate));
return alloc_bstate(p->set, p->possible);
}
type = estate_type(estate);
if (!type)
return alloc_bstate(0, -1ULL);
if (type_bits(type) == 64)
possible = -1ULL;
else
possible = (1ULL << type_bits(type)) - 1;
return alloc_bstate(0, possible);
}
static bool is_loop_iterator(struct expression *expr)
{
struct statement *pre_stmt, *loop_stmt;
pre_stmt = expr_get_parent_stmt(expr);
if (!pre_stmt || pre_stmt->type != STMT_EXPRESSION)
return false;
loop_stmt = stmt_get_parent_stmt(pre_stmt);
if (!loop_stmt || loop_stmt->type != STMT_ITERATOR)
return false;
if (loop_stmt->iterator_pre_statement != pre_stmt)
return false;
return true;
}
static bool handled_by_assign_hook(struct expression *expr)
{
if (!expr || expr->type != EXPR_ASSIGNMENT)
return false;
if (__in_fake_assign)
return false;
if (is_loop_iterator(expr))
return false;
if (expr->op == '=' ||
expr->op == SPECIAL_OR_ASSIGN ||
expr->op == SPECIAL_AND_ASSIGN)
return true;
return false;
}
static void match_modify(struct sm_state *sm, struct expression *mod_expr)
{
if (handled_by_assign_hook(mod_expr))
return;
set_state(my_id, sm->name, sm->sym, alloc_bstate(0, -1ULL));
}
static int binfo_equiv(struct bit_info *one, struct bit_info *two)
{
if (one->set == two->set &&
one->possible == two->possible)
return 1;
return 0;
}
static struct smatch_state *merge_bstates(struct smatch_state *one_state, struct smatch_state *two_state)
{
struct bit_info *one, *two;
one = one_state->data;
two = two_state->data;
if (binfo_equiv(one, two))
return one_state;
return alloc_bstate(one->set & two->set, one->possible | two->possible);
}
static struct bit_info *combine_bit_info(struct bit_info *one, struct bit_info *two)
{
struct bit_info *ret = __alloc_bit_info(0);
if ((one->set & two->possible) != one->set)
return alloc_bit_info(0, -1ULL);
if ((two->set & one->possible) != two->set)
return alloc_bit_info(0, -1ULL);
ret->set = one->set | two->set;
ret->possible = one->possible & two->possible;
return ret;
}
static struct bit_info *binfo_AND(struct bit_info *left, struct bit_info *right)
{
unsigned long long set = 0;
unsigned long long possible = -1ULL;
if (!left && !right) {
} else if (!left) {
possible = right->possible;
} else if (!right) {
possible = left->possible;
} else {
set = left->set & right->set;
possible = left->possible & right->possible;
}
return alloc_bit_info(set, possible);
}
static struct bit_info *binfo_OR(struct bit_info *left, struct bit_info *right)
{
unsigned long long set = 0;
unsigned long long possible = -1ULL;
if (!left && !right) {
} else if (!left) {
set = right->set;
} else if (!right) {
set = left->set;
} else {
set = left->set | right->set;
possible = left->possible | right->possible;
}
return alloc_bit_info(set, possible);
}
struct bit_info *get_bit_info(struct expression *expr)
{
struct range_list *rl;
struct smatch_state *bstate;
struct bit_info tmp;
struct bit_info *extra_info;
struct bit_info *bit_info;
sval_t known;
expr = strip_parens(expr);
if (get_implied_value(expr, &known))
return alloc_bit_info(known.value, known.value);
if (expr->type == EXPR_BINOP) {
if (expr->op == '&')
return binfo_AND(get_bit_info(expr->left),
get_bit_info(expr->right));
if (expr->op == '|')
return binfo_OR(get_bit_info(expr->left),
get_bit_info(expr->right));
}
if (get_implied_rl(expr, &rl))
extra_info = rl_to_binfo(rl);
else {
struct symbol *type;
tmp = unknown_bit_info;
extra_info = &tmp;
type = get_type(expr);
if (!type)
type = &ullong_ctype;
if (type_bits(type) == 64)
extra_info->possible = -1ULL;
else
extra_info->possible = (1ULL << type_bits(type)) - 1;
}
bstate = get_state_expr(my_id, expr);
if (bstate)
bit_info = bstate->data;
else
bit_info = (struct bit_info *)&unknown_bit_info;
return combine_bit_info(extra_info, bit_info);
}
static int is_single_bit(sval_t sval)
{
int i;
int count = 0;
for (i = 0; i < 64; i++) {
if (sval.uvalue & 1ULL << i &&
count++)
return 0;
}
if (count == 1)
return 1;
return 0;
}
static void match_compare(struct expression *expr)
{
sval_t val;
if (expr->type != EXPR_COMPARE)
return;
if (expr->op != SPECIAL_EQUAL &&
expr->op != SPECIAL_NOTEQUAL)
return;
if (!get_implied_value(expr->right, &val))
return;
set_true_false_states_expr(my_id, expr->left,
(expr->op == SPECIAL_EQUAL) ? alloc_bstate(val.uvalue, val.uvalue) : NULL,
(expr->op == SPECIAL_EQUAL) ? NULL : alloc_bstate(val.uvalue, val.uvalue));
}
static void match_assign(struct expression *expr)
{
struct bit_info *start, *binfo;
struct smatch_state *new;
if (!handled_by_assign_hook(expr))
return;
binfo = get_bit_info(expr->right);
if (!binfo)
return;
if (expr->op == '=') {
if (is_unknown_binfo(get_type(expr->left), binfo))
return;
set_state_expr(my_id, expr->left, alloc_bstate(binfo->set, binfo->possible));
} else if (expr->op == SPECIAL_OR_ASSIGN) {
start = get_bit_info(expr->left);
new = alloc_bstate(start->set | binfo->set, start->possible | binfo->possible);
set_state_expr(my_id, expr->left, new);
} else if (expr->op == SPECIAL_AND_ASSIGN) {
start = get_bit_info(expr->left);
new = alloc_bstate(start->set & binfo->set, start->possible & binfo->possible);
set_state_expr(my_id, expr->left, new);
}
}
static void match_condition(struct expression *expr)
{
struct bit_info *orig;
struct bit_info true_info;
struct bit_info false_info;
sval_t right;
if (expr->type != EXPR_BINOP ||
expr->op != '&')
return;
if (!get_value(expr->right, &right))
return;
orig = get_bit_info(expr->left);
true_info = *orig;
false_info = *orig;
if (right.uvalue == 0 || is_single_bit(right))
true_info.set &= right.uvalue;
true_info.possible &= right.uvalue;
false_info.possible &= ~right.uvalue;
set_true_false_states_expr(my_id, expr->left,
alloc_bstate(true_info.set, true_info.possible),
alloc_bstate(false_info.set, false_info.possible));
}
static void match_call_info(struct expression *expr)
{
struct bit_info *binfo, *rl_binfo;
struct expression *arg;
struct range_list *rl;
char buf[64];
int i;
i = -1;
FOR_EACH_PTR(expr->args, arg) {
i++;
binfo = get_bit_info(arg);
if (!binfo)
continue;
if (is_unknown_binfo(get_type(arg), binfo))
continue;
if (get_implied_rl(arg, &rl)) {
rl_binfo = rl_to_binfo(rl);
if (binfo_equiv(rl_binfo, binfo))
continue;
}
snprintf(buf, sizeof(buf), "0x%llx,0x%llx", binfo->set, binfo->possible);
sql_insert_caller_info(expr, BIT_INFO, i, "$", buf);
} END_FOR_EACH_PTR(arg);
}
static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
{
struct bit_info *binfo = sm->state->data;
struct smatch_state *estate;
struct bit_info *implied_binfo;
char buf[64];
if (!binfo)
return;
if (binfo->set == binfo->possible)
return;
estate = get_state(SMATCH_EXTRA, sm->name, sm->sym);
if (is_unknown_binfo(estate_type(estate), binfo))
return;
if (estate_rl(estate)) {
sval_t sval;
if (estate_get_single_value(estate, &sval))
return;
implied_binfo = rl_to_binfo(estate_rl(estate));
if (binfo_equiv(implied_binfo, binfo))
return;
}
snprintf(buf, sizeof(buf), "0x%llx,0x%llx", binfo->set, binfo->possible);
sql_insert_caller_info(call, BIT_INFO, param, printed_name, buf);
}
static void set_param_bits(const char *name, struct symbol *sym, char *key, char *value)
{
char fullname[256];
unsigned long long set, possible;
if (strcmp(key, "*$") == 0)
snprintf(fullname, sizeof(fullname), "*%s", name);
else if (strncmp(key, "$", 1) == 0)
snprintf(fullname, 256, "%s%s", name, key + 1);
else
return;
set = strtoull(value, &value, 16);
if (*value != ',')
return;
value++;
possible = strtoull(value, &value, 16);
set_state(my_id, fullname, sym, alloc_bstate(set, possible));
}
void register_bits(int id)
{
my_id = id;
set_dynamic_states(my_id);
add_unmatched_state_hook(my_id, &unmatched_state);
add_merge_hook(my_id, &merge_bstates);
add_hook(&match_condition, CONDITION_HOOK);
add_hook(&match_compare, CONDITION_HOOK);
add_hook(&match_assign, ASSIGNMENT_HOOK);
add_modification_hook(my_id, &match_modify);
add_hook(&match_call_info, FUNCTION_CALL_HOOK);
add_member_info_callback(my_id, struct_member_callback);
select_caller_info_hook(set_param_bits, BIT_INFO);
}