#include "smatch.h"
#include "smatch_extra.h"
#include "smatch_slist.h"
static int my_id;
STATE(fresh);
struct alloc_info *alloc_funcs;
struct alloc_info kernel_allocation_funcs[] = {
{"kmalloc", 0},
{"kmalloc_node", 0},
{"kzalloc", 0},
{"kzalloc_node", 0},
{"vmalloc", 0},
{"__vmalloc", 0},
{"kvmalloc", 0},
{"kcalloc", 0, 1},
{"kmalloc_array", 0, 1},
{"sock_kmalloc", 1},
{"kmemdup", 1},
{"kmemdup_user", 1},
{"dma_alloc_attrs", 1},
{"pci_alloc_consistent", 1},
{"pci_alloc_coherent", 1},
{"devm_kmalloc", 1},
{"devm_kzalloc", 1},
{"krealloc", 1},
{"__alloc_bootmem", 0},
{"alloc_bootmem", 0},
{"dma_alloc_contiguous", 1},
{"dma_alloc_coherent", 1},
{},
};
struct alloc_info general_allocation_funcs[] = {
{"malloc", 0},
{"calloc", 0, 1},
{"memdup", 1},
{"realloc", 1},
{},
};
static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
{
struct smatch_state *state;
sval_t sval;
state = get_state(SMATCH_EXTRA, cur->name, cur->sym);
if (estate_get_single_value(state, &sval) && sval.value == 0)
set_state(my_id, cur->name, cur->sym, &undefined);
}
static int fresh_callback(void *fresh, int argc, char **argv, char **azColName)
{
*(int *)fresh = 1;
return 0;
}
static int fresh_from_db(struct expression *call)
{
int fresh = 0;
if (call->fn->type != EXPR_SYMBOL)
return 0;
run_sql(&fresh_callback, &fresh,
"select * from return_states where %s and type = %d and parameter = -1 and key = '$' limit 1;",
get_static_filter(call->fn->symbol), FRESH_ALLOC);
return fresh;
}
bool is_fresh_alloc_var_sym(const char *var, struct symbol *sym)
{
return get_state(my_id, var, sym) == &fresh;
}
bool is_fresh_alloc(struct expression *expr)
{
sval_t sval;
int i;
if (!expr)
return false;
if (get_implied_value_fast(expr, &sval) && sval.value == 0)
return false;
if (get_state_expr(my_id, expr) == &fresh)
return true;
if (expr->type != EXPR_CALL)
return false;
if (fresh_from_db(expr))
return true;
i = -1;
while (alloc_funcs[++i].fn) {
if (sym_name_is(kernel_allocation_funcs[i].fn, expr->fn))
return true;
}
return false;
}
static void record_alloc_func(int return_id, char *return_ranges, struct expression *expr)
{
if (!is_fresh_alloc(expr))
return;
sql_insert_return_states(return_id, return_ranges, FRESH_ALLOC, -1, "$", "");
}
static void set_unfresh(struct expression *expr)
{
struct sm_state *sm;
sm = get_sm_state_expr(my_id, expr);
if (!sm)
return;
if (!slist_has_state(sm->possible, &fresh))
return;
set_state_expr(my_id, expr, &undefined);
}
static void match_assign(struct expression *expr)
{
set_unfresh(expr->right);
}
static void match_call(struct expression *expr)
{
struct expression *arg;
FOR_EACH_PTR(expr->args, arg) {
set_unfresh(arg);
} END_FOR_EACH_PTR(arg);
}
static struct expression *handled;
static void set_fresh(struct expression *expr)
{
struct range_list *rl;
expr = strip_expr(expr);
if (expr->type != EXPR_SYMBOL)
return;
if (expr == handled)
return;
get_absolute_rl(expr, &rl);
rl = rl_intersection(rl, valid_ptr_rl);
if (!rl)
return;
set_state_expr(my_id, expr, &fresh);
handled = expr;
}
static void returns_fresh_alloc(struct expression *expr, int param, char *key, char *value)
{
if (param != -1 || !key || strcmp(key, "$") != 0)
return;
if (expr->type != EXPR_ASSIGNMENT)
return;
set_fresh(expr->left);
}
static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
{
set_fresh(expr->left);
}
void register_fresh_alloc(int id)
{
int i;
my_id = id;
if (option_project == PROJ_KERNEL)
alloc_funcs = kernel_allocation_funcs;
else
alloc_funcs = general_allocation_funcs;
i = -1;
while (alloc_funcs[++i].fn)
add_function_assign_hook(alloc_funcs[i].fn, &match_alloc, 0);
add_split_return_callback(&record_alloc_func);
select_return_states_hook(FRESH_ALLOC, &returns_fresh_alloc);
add_hook(&match_assign, ASSIGNMENT_HOOK);
add_hook(&match_call, FUNCTION_CALL_HOOK);
add_pre_merge_hook(my_id, &pre_merge_hook);
}