Print this page
11506 smatch resync
*** 27,54 ****
static int size_id;
static int link_id;
/*
! * We need this for code which does:
*
* if (size)
* foo = malloc(size);
*
! * We want to record that the size of "foo" is "size" even after the merge.
*
*/
static struct smatch_state *unmatched_state(struct sm_state *sm)
{
- struct expression *size_expr;
- sval_t sval;
-
- if (!sm->state->data)
- return &undefined;
- size_expr = sm->state->data;
- if (!get_implied_value(size_expr, &sval) || sval.value != 0)
- return &undefined;
return sm->state;
}
static struct smatch_state *merge_links(struct smatch_state *s1, struct smatch_state *s2)
{
--- 27,48 ----
static int size_id;
static int link_id;
/*
! * There is a bunch of code which does this:
*
* if (size)
* foo = malloc(size);
*
! * So if "size" is non-zero then the size of "foo" is size. But really it's
! * also true if size is zero. It's just better to assume to not trample over
! * the data that we have by merging &undefined states.
*
*/
static struct smatch_state *unmatched_state(struct sm_state *sm)
{
return sm->state;
}
static struct smatch_state *merge_links(struct smatch_state *s1, struct smatch_state *s2)
{
*** 80,98 ****
set_state_expr(size_id, expr, &undefined);
} END_FOR_EACH_PTR(tmp);
set_state(link_id, sm->name, sm->sym, &undefined);
}
! static struct smatch_state *alloc_expr_state(struct expression *expr)
{
struct smatch_state *state;
char *name;
state = __alloc_smatch_state(0);
expr = strip_expr(expr);
name = expr_to_str(expr);
! state->name = alloc_sname(name);
free_string(name);
state->data = expr;
return state;
}
--- 74,127 ----
set_state_expr(size_id, expr, &undefined);
} END_FOR_EACH_PTR(tmp);
set_state(link_id, sm->name, sm->sym, &undefined);
}
! static const char *limit_map[] = {
! "byte_count",
! "elem_count",
! "elem_last",
! "used_count",
! "used_last",
! };
!
! int state_to_limit(struct smatch_state *state)
{
+ int i;
+
+ if (!state || !state->data)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(limit_map); i++) {
+ if (strncmp(state->name, limit_map[i], strlen(limit_map[i])) == 0)
+ return i + BYTE_COUNT;
+ }
+
+ return -1;
+ }
+
+ const char *limit_type_str(unsigned int limit_type)
+ {
+ if (limit_type - BYTE_COUNT >= ARRAY_SIZE(limit_map)) {
+ sm_msg("internal: wrong size type %u", limit_type);
+ return "unknown";
+ }
+
+ return limit_map[limit_type - BYTE_COUNT];
+ }
+
+ static struct smatch_state *alloc_compare_size(int limit_type, struct expression *expr)
+ {
struct smatch_state *state;
char *name;
+ char buf[256];
state = __alloc_smatch_state(0);
expr = strip_expr(expr);
name = expr_to_str(expr);
! snprintf(buf, sizeof(buf), "%s %s", limit_type_str(limit_type), name);
! state->name = alloc_sname(buf);
free_string(name);
state->data = expr;
return state;
}
*** 109,132 ****
type = get_base_type(type);
return type_bytes(type);
}
! static void db_save_type_links(struct expression *array, struct expression *size)
{
const char *array_name;
array_name = get_data_info_name(array);
if (!array_name)
array_name = "";
! sql_insert_data_info(size, ARRAY_LEN, array_name);
}
static void match_alloc_helper(struct expression *pointer, struct expression *size)
{
struct expression *tmp;
struct sm_state *sm;
sval_t sval;
int cnt = 0;
pointer = strip_expr(pointer);
size = strip_expr(size);
--- 138,162 ----
type = get_base_type(type);
return type_bytes(type);
}
! static void db_save_type_links(struct expression *array, int type_limit, struct expression *size)
{
const char *array_name;
array_name = get_data_info_name(array);
if (!array_name)
array_name = "";
! sql_insert_data_info(size, type_limit, array_name);
}
static void match_alloc_helper(struct expression *pointer, struct expression *size)
{
struct expression *tmp;
struct sm_state *sm;
+ int limit_type = ELEM_COUNT;
sval_t sval;
int cnt = 0;
pointer = strip_expr(pointer);
size = strip_expr(size);
*** 157,171 ****
/* Only save links to variables, not fixed sizes */
if (get_value(size, &sval))
return;
! db_save_type_links(pointer, size);
! sm = set_state_expr(size_id, pointer, alloc_expr_state(size));
if (!sm)
return;
! set_state_expr(link_id, size, alloc_expr_state(pointer));
}
static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
{
int size_arg = PTR_INT(_size_arg);
--- 187,207 ----
/* Only save links to variables, not fixed sizes */
if (get_value(size, &sval))
return;
! if (size->type == EXPR_BINOP && size->op == '+' &&
! get_value(size->right, &sval) && sval.value == 1) {
! size = size->left;
! limit_type = ELEM_LAST;
! }
!
! db_save_type_links(pointer, limit_type, size);
! sm = set_state_expr(size_id, pointer, alloc_compare_size(limit_type, size));
if (!sm)
return;
! set_state_expr(link_id, size, alloc_state_expr(pointer));
}
static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
{
int size_arg = PTR_INT(_size_arg);
*** 180,213 ****
static void match_calloc(const char *fn, struct expression *expr, void *_start_arg)
{
int start_arg = PTR_INT(_start_arg);
struct expression *pointer, *call, *arg;
struct sm_state *tmp;
sval_t sval;
pointer = strip_expr(expr->left);
call = strip_expr(expr->right);
arg = get_argument_from_call_expr(call->args, start_arg);
if (get_implied_value(arg, &sval) &&
sval.value == bytes_per_element(pointer))
arg = get_argument_from_call_expr(call->args, start_arg + 1);
! db_save_type_links(pointer, arg);
! tmp = set_state_expr(size_id, pointer, alloc_expr_state(arg));
if (!tmp)
return;
! set_state_expr(link_id, arg, alloc_expr_state(pointer));
}
! struct expression *get_size_variable(struct expression *buf)
{
struct smatch_state *state;
state = get_state_expr(size_id, buf);
! if (state)
! return state->data;
return NULL;
}
struct expression *get_array_variable(struct expression *size)
{
struct smatch_state *state;
--- 216,257 ----
static void match_calloc(const char *fn, struct expression *expr, void *_start_arg)
{
int start_arg = PTR_INT(_start_arg);
struct expression *pointer, *call, *arg;
struct sm_state *tmp;
+ int limit_type = ELEM_COUNT;
sval_t sval;
pointer = strip_expr(expr->left);
call = strip_expr(expr->right);
arg = get_argument_from_call_expr(call->args, start_arg);
if (get_implied_value(arg, &sval) &&
sval.value == bytes_per_element(pointer))
arg = get_argument_from_call_expr(call->args, start_arg + 1);
! if (arg->type == EXPR_BINOP && arg->op == '+' &&
! get_value(arg->right, &sval) && sval.value == 1) {
! arg = arg->left;
! limit_type = ELEM_LAST;
! }
!
! db_save_type_links(pointer, limit_type, arg);
! tmp = set_state_expr(size_id, pointer, alloc_compare_size(limit_type, arg));
if (!tmp)
return;
! set_state_expr(link_id, arg, alloc_state_expr(pointer));
}
! struct expression *get_size_variable(struct expression *buf, int *limit_type)
{
struct smatch_state *state;
state = get_state_expr(size_id, buf);
! if (!state)
return NULL;
+ *limit_type = state_to_limit(state);
+ return state->data;
}
struct expression *get_array_variable(struct expression *size)
{
struct smatch_state *state;
*** 222,240 ****
{
struct expression *array;
struct expression *size;
struct expression *offset;
char *array_str, *offset_str;
expr = strip_expr(expr);
if (!is_array(expr))
return;
array = get_array_base(expr);
! size = get_size_variable(array);
if (!size)
return;
offset = get_array_offset(expr);
if (!possible_comparison(size, SPECIAL_EQUAL, offset))
return;
array_str = expr_to_str(array);
--- 266,287 ----
{
struct expression *array;
struct expression *size;
struct expression *offset;
char *array_str, *offset_str;
+ int limit_type;
expr = strip_expr(expr);
if (!is_array(expr))
return;
array = get_array_base(expr);
! size = get_size_variable(array, &limit_type);
if (!size)
return;
+ if (limit_type != ELEM_COUNT)
+ return;
offset = get_array_offset(expr);
if (!possible_comparison(size, SPECIAL_EQUAL, offset))
return;
array_str = expr_to_str(array);
*** 315,339 ****
ARRAY_LEN, size_name);
return db_info.ret;
}
! static int known_access_ok_comparison(struct expression *expr)
{
struct expression *array;
struct expression *size;
struct expression *offset;
int comparison;
array = get_array_base(expr);
! size = get_size_variable(array);
if (!size)
return 0;
offset = get_array_offset(expr);
! comparison = get_comparison(size, offset);
! if (comparison == '>' || comparison == SPECIAL_UNSIGNED_GT)
return 1;
return 0;
}
static int known_access_ok_numbers(struct expression *expr)
--- 362,396 ----
ARRAY_LEN, size_name);
return db_info.ret;
}
! int buf_comparison_index_ok(struct expression *expr)
{
struct expression *array;
struct expression *size;
struct expression *offset;
+ int limit_type;
int comparison;
array = get_array_base(expr);
! size = get_size_variable(array, &limit_type);
if (!size)
return 0;
offset = get_array_offset(expr);
! comparison = get_comparison(offset, size);
! if (!comparison)
! return 0;
!
! if ((limit_type == ELEM_COUNT || limit_type == ELEM_LAST) &&
! (comparison == '<' || comparison == SPECIAL_UNSIGNED_LT))
return 1;
+ if (limit_type == ELEM_LAST &&
+ (comparison == SPECIAL_LTE ||
+ comparison == SPECIAL_UNSIGNED_LTE ||
+ comparison == SPECIAL_EQUAL))
+ return 1;
return 0;
}
static int known_access_ok_numbers(struct expression *expr)
*** 370,380 ****
if (!is_array(expr))
return;
if (known_access_ok_numbers(expr))
return;
! if (known_access_ok_comparison(expr))
return;
array = get_array_base(expr);
offset = get_array_offset(expr);
offset_name = expr_to_var(offset);
--- 427,437 ----
if (!is_array(expr))
return;
if (known_access_ok_numbers(expr))
return;
! if (buf_comparison_index_ok(expr))
return;
array = get_array_base(expr);
offset = get_array_offset(expr);
offset_name = expr_to_var(offset);
*** 414,463 ****
static void add_allocation_function(const char *func, void *call_back, int param)
{
add_function_assign_hook(func, call_back, INT_PTR(param));
}
! static char *buf_size_param_comparison(struct expression *array, struct expression_list *args)
{
! struct expression *arg;
struct expression *size;
static char buf[32];
int i;
! size = get_size_variable(array);
if (!size)
return NULL;
i = -1;
! FOR_EACH_PTR(args, arg) {
i++;
if (arg == array)
continue;
! if (!expr_equiv(arg, size))
! continue;
snprintf(buf, sizeof(buf), "==$%d", i);
return buf;
! } END_FOR_EACH_PTR(arg);
return NULL;
}
static void match_call(struct expression *call)
{
struct expression *arg;
char *compare;
int param;
param = -1;
FOR_EACH_PTR(call->args, arg) {
param++;
if (!is_pointer(arg))
continue;
! compare = buf_size_param_comparison(arg, call->args);
if (!compare)
continue;
! sql_insert_caller_info(call, ARRAY_LEN, param, "$", compare);
} END_FOR_EACH_PTR(arg);
}
static int get_param(int param, char **name, struct symbol **sym)
{
--- 471,586 ----
static void add_allocation_function(const char *func, void *call_back, int param)
{
add_function_assign_hook(func, call_back, INT_PTR(param));
}
! static int is_sizeof(struct expression *expr)
{
! const char *name;
!
! if (expr->type == EXPR_SIZEOF)
! return 1;
! name = pos_ident(expr->pos);
! if (name && strcmp(name, "sizeof") == 0)
! return 1;
! return 0;
! }
!
! static int match_size_binop(struct expression *size, struct expression *expr, int *limit_type)
! {
! int orig_type = *limit_type;
! struct expression *left;
! sval_t sval;
!
! left = expr->left;
! if (!expr_equiv(size, left))
! return 0;
!
! if (expr->op == '-' &&
! get_value(expr->right, &sval) &&
! sval.value == 1 &&
! orig_type == ELEM_COUNT) {
! *limit_type = ELEM_LAST;
! return 1;
! }
!
! if (expr->op == '+' &&
! get_value(expr->right, &sval) &&
! sval.value == 1 &&
! orig_type == ELEM_LAST) {
! *limit_type = ELEM_COUNT;
! return 1;
! }
!
! if (expr->op == '*' &&
! is_sizeof(expr->right) &&
! orig_type == ELEM_COUNT) {
! *limit_type = BYTE_COUNT;
! return 1;
! }
!
! if (expr->op == '/' &&
! is_sizeof(expr->right) &&
! orig_type == BYTE_COUNT) {
! *limit_type = ELEM_COUNT;
! return 1;
! }
!
! return 0;
! }
!
! static char *buf_size_param_comparison(struct expression *array, struct expression_list *args, int *limit_type)
! {
! struct expression *tmp, *arg;
struct expression *size;
static char buf[32];
int i;
! size = get_size_variable(array, limit_type);
if (!size)
return NULL;
+ if (*limit_type == USED_LAST)
+ *limit_type = ELEM_LAST;
+ if (*limit_type == USED_COUNT)
+ *limit_type = ELEM_COUNT;
+
i = -1;
! FOR_EACH_PTR(args, tmp) {
i++;
+ arg = tmp;
if (arg == array)
continue;
! if (expr_equiv(arg, size) ||
! (arg->type == EXPR_BINOP &&
! match_size_binop(size, arg, limit_type))) {
snprintf(buf, sizeof(buf), "==$%d", i);
return buf;
! }
! } END_FOR_EACH_PTR(tmp);
return NULL;
}
static void match_call(struct expression *call)
{
struct expression *arg;
char *compare;
int param;
+ char buf[5];
+ int limit_type;
param = -1;
FOR_EACH_PTR(call->args, arg) {
param++;
if (!is_pointer(arg))
continue;
! compare = buf_size_param_comparison(arg, call->args, &limit_type);
if (!compare)
continue;
! snprintf(buf, sizeof(buf), "%d", limit_type);
! sql_insert_caller_info(call, limit_type, param, compare, buf);
} END_FOR_EACH_PTR(arg);
}
static int get_param(int param, char **name, struct symbol **sym)
{
*** 493,536 ****
struct expression *size_expr;
struct symbol *size_sym;
char *size_name;
long param;
struct sm_state *tmp;
! if (strncmp(value, "==$", 3) != 0)
return;
! param = strtol(value + 3, NULL, 10);
if (!get_param(param, &size_name, &size_sym))
return;
array_expr = symbol_expression(array_sym);
size_expr = symbol_expression(size_sym);
! tmp = set_state_expr(size_id, array_expr, alloc_expr_state(size_expr));
if (!tmp)
return;
! set_state_expr(link_id, size_expr, alloc_expr_state(array_expr));
}
! static void set_arraysize_arg(const char *array_name, struct symbol *array_sym, char *key, char *value)
{
- struct expression *array_expr;
struct expression *size_expr;
struct symbol *size_sym;
char *size_name;
long param;
struct sm_state *tmp;
! param = strtol(key, NULL, 10);
if (!get_param(param, &size_name, &size_sym))
return;
- array_expr = symbol_expression(array_sym);
size_expr = symbol_expression(size_sym);
! tmp = set_state_expr(size_id, array_expr, alloc_expr_state(size_expr));
if (!tmp)
return;
! set_state_expr(link_id, size_expr, alloc_expr_state(array_expr));
}
static void munge_start_states(struct statement *stmt)
{
struct state_list *slist = NULL;
--- 616,663 ----
struct expression *size_expr;
struct symbol *size_sym;
char *size_name;
long param;
struct sm_state *tmp;
+ int limit_type;
! if (strncmp(key, "==$", 3) != 0)
return;
! param = strtol(key + 3, NULL, 10);
if (!get_param(param, &size_name, &size_sym))
return;
array_expr = symbol_expression(array_sym);
size_expr = symbol_expression(size_sym);
+ limit_type = strtol(value, NULL, 10);
! tmp = set_state_expr(size_id, array_expr, alloc_compare_size(limit_type, size_expr));
if (!tmp)
return;
! set_state_expr(link_id, size_expr, alloc_state_expr(array_expr));
}
! static void set_implied(struct expression *call, struct expression *array_expr, char *key, char *value)
{
struct expression *size_expr;
struct symbol *size_sym;
char *size_name;
long param;
struct sm_state *tmp;
+ int limit_type;
! if (strncmp(key, "==$", 3) != 0)
! return;
! param = strtol(key + 3, NULL, 10);
if (!get_param(param, &size_name, &size_sym))
return;
size_expr = symbol_expression(size_sym);
! limit_type = strtol(value, NULL, 10);
! tmp = set_state_expr(size_id, array_expr, alloc_compare_size(limit_type, size_expr));
if (!tmp)
return;
! set_state_expr(link_id, size_expr, alloc_state_expr(array_expr));
}
static void munge_start_states(struct statement *stmt)
{
struct state_list *slist = NULL;
*** 558,571 ****
--- 685,808 ----
} END_FOR_EACH_PTR(sm);
free_slist(&slist);
}
+ static void set_used(struct expression *expr)
+ {
+ struct expression *parent;
+ struct expression *array;
+ struct expression *offset;
+ struct sm_state *tmp;
+ int limit_type;
+
+ if (expr->op != SPECIAL_INCREMENT)
+ return;
+
+ limit_type = USED_LAST;
+ if (expr->type == EXPR_POSTOP)
+ limit_type = USED_COUNT;
+
+ parent = expr_get_parent_expr(expr);
+ if (!parent || parent->type != EXPR_BINOP)
+ return;
+ parent = expr_get_parent_expr(parent);
+ if (!parent || !is_array(parent))
+ return;
+
+ array = get_array_base(parent);
+ offset = get_array_offset(parent);
+ if (offset != expr)
+ return;
+
+ tmp = set_state_expr(size_id, array, alloc_compare_size(limit_type, offset->unop));
+ if (!tmp)
+ return;
+ set_state_expr(link_id, offset->unop, alloc_state_expr(array));
+ }
+
+ static int match_assign_array(struct expression *expr)
+ {
+ // FIXME: implement
+ return 0;
+ }
+
+ static int match_assign_size(struct expression *expr)
+ {
+ struct expression *right, *size, *array;
+ struct smatch_state *state;
+ struct sm_state *tmp;
+ int limit_type;
+
+ right = expr->right;
+ size = right;
+ if (size->type == EXPR_BINOP)
+ size = size->left;
+
+ array = get_array_variable(size);
+ if (!array)
+ return 0;
+ state = get_state_expr(size_id, array);
+ if (!state || !state->data)
+ return 0;
+
+ limit_type = state_to_limit(state);
+ if (limit_type < 0)
+ return 0;
+
+ if (right->type == EXPR_BINOP && !match_size_binop(size, right, &limit_type))
+ return 0;
+
+ tmp = set_state_expr(size_id, array, alloc_compare_size(limit_type, expr->left));
+ if (!tmp)
+ return 0;
+ set_state_expr(link_id, expr->left, alloc_state_expr(array));
+ return 1;
+ }
+
+ static void match_assign(struct expression *expr)
+ {
+ if (expr->op != '=')
+ return;
+
+ if (match_assign_array(expr))
+ return;
+ match_assign_size(expr);
+ }
+
+ static void match_copy(const char *fn, struct expression *expr, void *unused)
+ {
+ struct expression *src, *size;
+ int src_param, size_param;
+
+ src = get_argument_from_call_expr(expr->args, 1);
+ size = get_argument_from_call_expr(expr->args, 2);
+ src = strip_expr(src);
+ size = strip_expr(size);
+ if (!src || !size)
+ return;
+ if (src->type != EXPR_SYMBOL || size->type != EXPR_SYMBOL)
+ return;
+
+ src_param = get_param_num_from_sym(src->symbol);
+ size_param = get_param_num_from_sym(size->symbol);
+ if (src_param < 0 || size_param < 0)
+ return;
+
+ sql_insert_cache(call_implies, "'%s', '%s', 0, %d, %d, %d, '==$%d', '%d'",
+ get_base_file(), get_function(), fn_static(),
+ BYTE_COUNT, src_param, size_param, BYTE_COUNT);
+ }
+
void register_buf_comparison(int id)
{
+ int i;
+
size_id = id;
+ set_dynamic_states(size_id);
+
add_unmatched_state_hook(size_id, &unmatched_state);
add_allocation_function("malloc", &match_alloc, 0);
add_allocation_function("memdup", &match_alloc, 1);
add_allocation_function("realloc", &match_alloc, 1);
*** 584,605 ****
add_allocation_function("devm_kzalloc", &match_alloc, 1);
add_allocation_function("kcalloc", &match_calloc, 0);
add_allocation_function("devm_kcalloc", &match_calloc, 1);
add_allocation_function("kmalloc_array", &match_calloc, 0);
add_allocation_function("krealloc", &match_alloc, 1);
}
add_hook(&array_check, OP_HOOK);
add_hook(&array_check_data_info, OP_HOOK);
add_hook(&match_call, FUNCTION_CALL_HOOK);
- select_caller_info_hook(set_param_compare, ARRAY_LEN);
- select_caller_info_hook(set_arraysize_arg, ARRAYSIZE_ARG);
add_hook(&munge_start_states, AFTER_DEF_HOOK);
}
void register_buf_comparison_links(int id)
{
link_id = id;
add_merge_hook(link_id, &merge_links);
add_modification_hook(link_id, &match_link_modify);
}
--- 821,853 ----
add_allocation_function("devm_kzalloc", &match_alloc, 1);
add_allocation_function("kcalloc", &match_calloc, 0);
add_allocation_function("devm_kcalloc", &match_calloc, 1);
add_allocation_function("kmalloc_array", &match_calloc, 0);
add_allocation_function("krealloc", &match_alloc, 1);
+
+ add_function_hook("copy_from_user", &match_copy, NULL);
+ add_function_hook("__copy_from_user", &match_copy, NULL);
}
add_hook(&array_check, OP_HOOK);
add_hook(&array_check_data_info, OP_HOOK);
+ add_hook(&set_used, OP_HOOK);
add_hook(&match_call, FUNCTION_CALL_HOOK);
add_hook(&munge_start_states, AFTER_DEF_HOOK);
+
+ add_hook(&match_assign, ASSIGNMENT_HOOK);
+
+ for (i = BYTE_COUNT; i <= USED_COUNT; i++) {
+ select_call_implies_hook(i, &set_implied);
+ select_caller_info_hook(set_param_compare, i);
+ select_return_implies_hook(i, &set_implied);
+ }
}
void register_buf_comparison_links(int id)
{
link_id = id;
+ set_dynamic_states(link_id);
add_merge_hook(link_id, &merge_links);
add_modification_hook(link_id, &match_link_modify);
}