#include <fcntl.h>
#include <unistd.h>
#include "parse.h"
#include "smatch.h"
#include "smatch_slist.h"
static int my_id;
STATE(allocated);
STATE(ok);
static void set_parent(struct expression *expr, struct smatch_state *state);
static const char *allocation_funcs[] = {
"malloc",
"kmalloc",
"kzalloc",
"kmemdup",
};
static char *alloc_parent_str(struct symbol *sym)
{
static char buf[256];
if (!sym || !sym->ident)
return NULL;
snprintf(buf, 255, "%s", sym->ident->name);
buf[255] = '\0';
return alloc_string(buf);
}
static char *get_parent_from_expr(struct expression *expr, struct symbol **sym)
{
char *name;
expr = strip_expr(expr);
name = expr_to_str_sym(expr, sym);
free_string(name);
if (!name || !*sym || !(*sym)->ident) {
*sym = NULL;
return NULL;
}
return alloc_parent_str(*sym);
}
static int is_local(struct expression *expr)
{
char *name;
struct symbol *sym;
int ret = 0;
name = expr_to_str_sym(expr, &sym);
if (!name || !sym)
goto out;
if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
goto out;
ret = 1;
out:
free_string(name);
return ret;
}
static int is_param(struct expression *expr)
{
char *name;
struct symbol *sym;
struct symbol *tmp;
int ret = 0;
name = expr_to_str_sym(expr, &sym);
if (!name || !sym)
goto out;
FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
if (tmp == sym) {
ret = 1;
goto out;
}
} END_FOR_EACH_PTR(tmp);
out:
free_string(name);
return ret;
}
static void match_alloc(const char *fn, struct expression *expr, void *unused)
{
if (!is_local(expr->left))
return;
if (is_param(expr->left))
return;
if (expr->left->type != EXPR_SYMBOL)
return;
set_state_expr(my_id, expr->left, &allocated);
}
static void match_condition(struct expression *expr)
{
struct sm_state *sm;
expr = strip_expr(expr);
switch (expr->type) {
case EXPR_PREOP:
case EXPR_SYMBOL:
case EXPR_DEREF:
sm = get_sm_state_expr(my_id, expr);
if (sm && slist_has_state(sm->possible, &allocated))
set_true_false_states_expr(my_id, expr, NULL, &ok);
return;
case EXPR_ASSIGNMENT:
match_condition(expr->left);
return;
default:
return;
}
}
static void set_parent(struct expression *expr, struct smatch_state *state)
{
char *name;
struct symbol *sym;
expr = strip_expr(expr);
if (!expr)
return;
if (expr->type == EXPR_CONDITIONAL ||
expr->type == EXPR_SELECT) {
set_parent(expr->cond_true, state);
set_parent(expr->cond_false, state);
return;
}
name = get_parent_from_expr(expr, &sym);
if (!name || !sym)
goto free;
if (state == &ok && !get_state(my_id, name, sym))
goto free;
set_state(my_id, name, sym, state);
free:
free_string(name);
}
static void match_function_call(struct expression *expr)
{
struct expression *tmp;
FOR_EACH_PTR(expr->args, tmp) {
set_parent(tmp, &ok);
} END_FOR_EACH_PTR(tmp);
}
static void warn_if_allocated(struct expression *expr)
{
struct sm_state *sm;
char *name;
sval_t sval;
if (get_implied_value(expr, &sval) && sval.value == 0)
return;
sm = get_sm_state_expr(my_id, expr);
if (!sm)
return;
if (!slist_has_state(sm->possible, &allocated))
return;
name = expr_to_var(expr);
sm_warning("overwrite may leak '%s'", name);
free_string(name);
set_state_expr(my_id, expr, &ok);
}
static void match_assign(struct expression *expr)
{
struct expression *right;
right = expr->right;
while (right->type == EXPR_ASSIGNMENT)
right = right->left;
warn_if_allocated(expr->left);
set_parent(right, &ok);
}
static void check_for_allocated(void)
{
struct stree *stree;
struct sm_state *tmp;
stree = __get_cur_stree();
FOR_EACH_MY_SM(my_id, stree, tmp) {
if (!slist_has_state(tmp->possible, &allocated))
continue;
sm_warning("possible memory leak of '%s'", tmp->name);
} END_FOR_EACH_SM(tmp);
}
static void match_return(struct expression *ret_value)
{
if (__inline_fn)
return;
set_parent(ret_value, &ok);
check_for_allocated();
}
static void match_end_func(struct symbol *sym)
{
if (__inline_fn)
return;
check_for_allocated();
}
void check_leaks(int id)
{
int i;
my_id = id;
for (i = 0; i < ARRAY_SIZE(allocation_funcs); i++)
add_function_assign_hook(allocation_funcs[i], &match_alloc, NULL);
add_hook(&match_condition, CONDITION_HOOK);
add_hook(&match_function_call, FUNCTION_CALL_HOOK);
add_hook(&match_assign, ASSIGNMENT_HOOK);
add_hook(&match_return, RETURN_HOOK);
add_hook(&match_end_func, END_FUNC_HOOK);
}