#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"
static int my_id;
static struct stree *vals;
static int save_rl(void *_rl, int argc, char **argv, char **azColName)
{
unsigned long *rl = _rl;
*rl = strtoul(argv[0], NULL, 10);
return 0;
}
static struct range_list *select_orig(mtag_t tag, int offset)
{
struct range_list *rl = NULL;
mem_sql(&save_rl, &rl, "select value from mtag_data where tag = %lld and offset = %d;",
tag, offset);
return rl;
}
static int is_kernel_param(const char *name)
{
struct sm_state *tmp;
char buf[256];
if (option_project != PROJ_KERNEL)
return 0;
snprintf(buf, sizeof(buf), "__param_%s.arg", name);
FOR_EACH_SM(vals, tmp) {
if (strcmp(tmp->name, buf) == 0)
return 1;
} END_FOR_EACH_SM(tmp);
return 0;
}
static bool is_ignored_macro(struct expression *expr)
{
char *macro;
macro = get_macro_name(expr->pos);
if (!macro)
return false;
if (strcmp(macro, "EXPORT_SYMBOL") == 0)
return true;
return false;
}
static bool is_head_next(struct expression *expr)
{
struct symbol *type;
if (option_project != PROJ_KERNEL)
return false;
if (expr->type != EXPR_DEREF)
return false;
if (!expr->member || !expr->member->name ||
strcmp(expr->member->name, "next") != 0)
return false;
type = get_type(expr->deref);
if (!type)
return false;
if (type->type == SYM_PTR)
type = get_real_base_type(type);
if (type->type != SYM_STRUCT)
return false;
if (!type->ident || !type->ident->name ||
strcmp(type->ident->name, "list_head") != 0)
return false;
return true;
}
mtag_t ignored_mtag;
static bool is_ignored_tag(mtag_t tag)
{
if (tag == ignored_mtag)
return true;
return false;
}
static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl)
{
if (is_ignored_tag(tag))
return;
rl = clone_rl_permanent(rl);
mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
tag, offset, DATA_VALUE);
mem_sql(NULL, NULL, "insert into mtag_data values (%lld, %d, %d, '%lu');",
tag, offset, DATA_VALUE, (unsigned long)rl);
}
static bool invalid_type(struct symbol *type)
{
if (!type)
return true;
if (type == &void_ctype)
return true;
if (type->type == SYM_STRUCT ||
type->type == SYM_ARRAY ||
type->type == SYM_UNION)
return true;
return false;
}
static bool parent_is_fresh_alloc(struct expression *expr)
{
struct symbol *sym;
sym = expr_to_sym(expr);
if (!sym || !sym->ident)
return false;
return is_fresh_alloc_var_sym(sym->ident->name, sym);
}
void update_mtag_data(struct expression *expr, struct smatch_state *state)
{
struct range_list *orig, *new;
struct symbol *type;
char *name;
mtag_t tag;
int offset;
if (!expr)
return;
if (is_local_variable(expr))
return;
if (is_ignored_macro(expr))
return;
if (is_head_next(expr))
return;
name = expr_to_var(expr);
if (is_kernel_param(name)) {
free_string(name);
return;
}
free_string(name);
if (!expr_to_mtag_offset(expr, &tag, &offset))
return;
type = get_type(expr);
if (offset == 0 && invalid_type(type))
return;
if (parent_is_fresh_alloc(expr))
orig = NULL;
else
orig = select_orig(tag, offset);
new = rl_union(orig, estate_rl(state));
insert_mtag_data(tag, offset, new);
}
static void match_global_assign(struct expression *expr)
{
struct range_list *rl;
mtag_t tag;
int offset;
char *name;
if (is_ignored_macro(expr))
return;
if (is_head_next(expr->left))
return;
name = expr_to_var(expr->left);
if (is_kernel_param(name)) {
free_string(name);
return;
}
free_string(name);
if (!expr_to_mtag_offset(expr->left, &tag, &offset))
return;
get_absolute_rl(expr->right, &rl);
insert_mtag_data(tag, offset, rl);
}
static int save_mtag_data(void *_unused, int argc, char **argv, char **azColName)
{
struct range_list *rl;
if (argc != 4) {
sm_msg("Error saving mtag data");
return 0;
}
if (!option_info)
return 0;
rl = (struct range_list *)strtoul(argv[3], NULL, 10);
sm_msg("SQL: insert into mtag_data values ('%s', '%s', '%s', '%s');",
argv[0], argv[1], argv[2], show_rl(rl));
return 0;
}
static void match_end_file(struct symbol_list *sym_list)
{
mem_sql(&save_mtag_data, NULL, "select * from mtag_data where type = %d;",
DATA_VALUE);
}
struct db_info {
struct symbol *type;
struct range_list *rl;
};
static int get_vals(void *_db_info, int argc, char **argv, char **azColName)
{
struct db_info *db_info = _db_info;
struct range_list *tmp;
str_to_rl(db_info->type, argv[0], &tmp);
if (db_info->rl)
db_info->rl = rl_union(db_info->rl, tmp);
else
db_info->rl = tmp;
return 0;
}
struct db_cache_results {
mtag_t tag;
struct range_list *rl;
};
static struct db_cache_results cached_results[8];
static int get_rl_from_mtag_offset(mtag_t tag, int offset, struct symbol *type, struct range_list **rl)
{
struct db_info db_info = {};
mtag_t merged = tag | offset;
static int idx;
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
if (merged == cached_results[i].tag) {
if (cached_results[i].rl) {
*rl = cached_results[i].rl;
return 1;
}
return 0;
}
}
db_info.type = type;
run_sql(get_vals, &db_info,
"select value from mtag_data where tag = %lld and offset = %d and type = %d;",
tag, offset, DATA_VALUE);
if (!db_info.rl || is_whole_rl(db_info.rl)) {
db_info.rl = NULL;
ret = 0;
goto update_cache;
}
*rl = db_info.rl;
ret = 1;
update_cache:
cached_results[idx].tag = merged;
cached_results[idx].rl = db_info.rl;
idx = (idx + 1) % ARRAY_SIZE(cached_results);
return ret;
}
static void clear_cache(struct symbol *sym)
{
memset(cached_results, 0, sizeof(cached_results));
}
int get_mtag_rl(struct expression *expr, struct range_list **rl)
{
struct symbol *type;
mtag_t tag;
int offset;
if (is_local_variable(expr))
return 0;
if (!expr_to_mtag_offset(expr, &tag, &offset))
return 0;
if (offset >= MTAG_OFFSET_MASK)
return 0;
type = get_type(expr);
if (invalid_type(type))
return 0;
return get_rl_from_mtag_offset(tag, offset, type, rl);
}
void register_mtag_data(int id)
{
my_id = id;
ignored_mtag = str_to_mtag("extern boot_params");
add_hook(&clear_cache, FUNC_DEF_HOOK);
add_hook(&match_global_assign, GLOBAL_ASSIGNMENT_HOOK);
add_hook(&match_end_file, END_FILE_HOOK);
}