#include <string.h>
#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"
static int my_id;
STATE(remaining);
STATE(ok);
static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
{
if (sm->state != &ok)
set_state(my_id, sm->name, sm->sym, &ok);
}
static void match_copy(const char *fn, struct expression *expr, void *unused)
{
if (expr->op == SPECIAL_SUB_ASSIGN)
return;
set_state_expr(my_id, expr->left, &remaining);
}
static void match_condition(struct expression *expr)
{
if (!get_state_expr(my_id, expr))
return;
set_true_false_states_expr(my_id, expr, NULL, &ok);
}
static void match_return_var(struct expression *ret_value)
{
struct smatch_state *state;
struct sm_state *sm;
sval_t min;
sm = get_sm_state_expr(my_id, ret_value);
if (!sm)
return;
if (!slist_has_state(sm->possible, &remaining))
return;
state = get_state_expr(SMATCH_EXTRA, ret_value);
if (!state)
return;
if (!get_absolute_min(ret_value, &min))
return;
if (min.value == 0)
return;
sm_warning("maybe return -EFAULT instead of the bytes remaining?");
}
static void match_return_call(struct expression *ret_value)
{
struct expression *fn;
struct range_list *rl;
const char *fn_name;
char *cur_func;
if (!ret_value || ret_value->type != EXPR_CALL)
return;
cur_func = get_function();
if (!cur_func)
return;
if (strstr(cur_func, "_to_user") ||
strstr(cur_func, "_from_user"))
return;
fn = strip_expr(ret_value->fn);
if (fn->type != EXPR_SYMBOL)
return;
fn_name = fn->symbol_name->name;
if (strcmp(fn_name, "copy_to_user") != 0 &&
strcmp(fn_name, "__copy_to_user") != 0 &&
strcmp(fn_name, "copy_from_user") != 0 &&
strcmp(fn_name, "__copy_from_user"))
return;
rl = db_return_vals_from_str(get_function());
if (!rl)
return;
if (!sval_is_negative(rl_min(rl)))
return;
sm_warning("maybe return -EFAULT instead of the bytes remaining?");
}
void check_return_efault(int id)
{
if (option_project != PROJ_KERNEL)
return;
my_id = id;
add_function_assign_hook("copy_to_user", &match_copy, NULL);
add_function_assign_hook("__copy_to_user", &match_copy, NULL);
add_function_assign_hook("copy_from_user", &match_copy, NULL);
add_function_assign_hook("__copy_from_user", &match_copy, NULL);
add_function_assign_hook("clear_user", &match_copy, NULL);
add_hook(&match_condition, CONDITION_HOOK);
add_hook(&match_return_var, RETURN_HOOK);
add_hook(&match_return_call, RETURN_HOOK);
add_modification_hook(my_id, &ok_to_use);
}