From 55aae9c3de7a19e1aa1ad072889429040b70aba9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 9 Mar 2023 16:38:21 +0300 Subject: [PATCH] param_clear: add support for BUF_ADD This is a sort similar situation to PARAM_ADD. PARAM_ADD means you take a function parameter "*x" and you leave some or all of the original values but you also assign it a value. void frob(int *x) { if (something) *x = 100; } The value *x after the call is all the original possible values, but also potentially 100. This is the same except with buffers. You have a memcpy() which happens on one path but not on the other paths. Originally there was a possibility that this would get translated into over a thousand useless PARAM_ADD values. I want it instead to be saved as a single (probably still useless) BUF_ADD entry. It's hard to know what to do with this information in the caller. I guess the only thing to do is ensure that everything is marked as unknown in smatch_extra.c. Signed-off-by: Dan Carpenter --- smatch.h | 1 + smatch_data/db/smdb.py | 1 + smatch_extra.c | 33 +++++++++++++++++++++++++++++++++ smatch_param_cleared.c | 32 ++++++++++++++++++++++++++++---- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/smatch.h b/smatch.h index 710e4e71..ebdc232a 100644 --- a/smatch.h +++ b/smatch.h @@ -896,6 +896,7 @@ enum info_type { RELEASE = 500, BUF_CLEARED = 501, + BUF_ADD = 502, PARAM_VALUE = 1001, BUF_SIZE = 1002, diff --git a/smatch_data/db/smdb.py b/smatch_data/db/smdb.py index 8a297b35..09b8418d 100755 --- a/smatch_data/db/smdb.py +++ b/smatch_data/db/smdb.py @@ -59,6 +59,7 @@ db_types = { 0: "INTERNAL", 104: "PARAM_FILTER", 500: "RELEASE", 501: "BUF_CLEARED", + 502: "BUF_ADD", 1001: "PARAM_VALUE", 1002: "BUF_SIZE", 1004: "CAPPED_DATA", diff --git a/smatch_extra.c b/smatch_extra.c index a202db53..ba0875aa 100644 --- a/smatch_extra.c +++ b/smatch_extra.c @@ -2920,6 +2920,38 @@ static void set_param_value(const char *name, struct symbol *sym, char *key, cha set_state(SMATCH_EXTRA, fullname, sym, state); } +static void db_buf_add_helper(struct expression *expr, void *data) +{ + struct expression *left = expr->left; + struct sm_state *sm; + + sm = get_sm_state_expr(my_id, left); + if (!sm) + return; + + set_state(my_id, sm->name, sm->sym, alloc_estate_whole(estate_type(sm->state))); +} + +static void db_buf_add(struct expression *expr, int param, char *key, char *value) +{ + struct expression *arg; + + /* + * There really isn't much we can do with a BUF_ADD. A BUF_CLEAR is + * like a full modification which some stuff cares about. A no_mod + * thing is useful because that's normally bounds checking etc. But + * with BUF_ADD the only thing we can say is do is delete any previous + * known states. + * + */ + + arg = gen_expr_from_param_key(expr, param, key); + if (!arg) + return; + + create_recursive_fake_assignments(deref_expression(arg), &db_buf_add_helper, NULL); +} + static void set_param_fuzzy_max(const char *name, struct symbol *sym, char *key, char *value) { struct expression *expr; @@ -3029,6 +3061,7 @@ void register_smatch_extra(int id) select_return_states_hook(PARAM_FILTER, &db_param_filter); select_return_states_hook(PARAM_ADD, &db_param_add); select_return_states_hook(PARAM_SET, &db_param_set); + select_return_states_hook(BUF_ADD, &db_buf_add); add_lost_param_hook(&match_lost_param); select_return_states_hook(PARAM_VALUE, &db_param_value); select_return_states_after(&db_limited_after); diff --git a/smatch_param_cleared.c b/smatch_param_cleared.c index 92cac8ec..a678b26b 100644 --- a/smatch_param_cleared.c +++ b/smatch_param_cleared.c @@ -34,6 +34,7 @@ static int my_id; STATE(cleared); STATE(zeroed); +STATE(add); struct func_info { const char *name; @@ -205,6 +206,15 @@ static struct func_info func_table[] = { {"mutex_lock_io_nested", BUF_CLEARED, 0, "*$"}, }; +static struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2) +{ + if (s1 == &cleared && s2 == &zeroed) + return &cleared; + if (s1 == &zeroed && s2 == &cleared) + return &cleared; + return &add; +} + static void db_param_cleared(struct expression *expr, int param, char *key, char *value) { struct expression *arg; @@ -230,6 +240,11 @@ free: free_string(name); } +static void db_buf_add(struct expression *expr, const char *name, struct symbol *sym, void *data) +{ + set_state(my_id, name, sym, &add); +} + static void match_memcpy(const char *fn, struct expression *expr, void *arg) { db_param_cleared(expr, PTR_INT(arg), (char *)"*$", (char *)""); @@ -263,12 +278,15 @@ static void return_info_callback(int return_id, char *return_ranges, if (param < 0) return; - if (sm->state != &zeroed && - sm->state != &cleared) + if (sm->state != &add && + sm->state != &cleared && + sm->state != &zeroed) return; - sql_insert_return_states(return_id, return_ranges, BUF_CLEARED, param, - printed_name, (sm->state == &zeroed) ? "0" : ""); + sql_insert_return_states(return_id, return_ranges, + (sm->state == &add) ? BUF_ADD : BUF_CLEARED, + param, printed_name, + (sm->state == &zeroed) ? "0" : ""); } static bool is_parent(struct sm_state *sm, const char *name, struct symbol *sym, int name_len) @@ -311,6 +329,7 @@ static bool is_parent(struct sm_state *sm, const char *name, struct symbol *sym, enum clear_zero { CLEAR, ZERO, + ADD, ANY, }; @@ -348,6 +367,8 @@ static bool parent_was_clear(const char *name, struct symbol *sym, enum clear_ze return true; if (zero == CLEAR && sm->state == &cleared) return true; + if (zero == ADD && sm->state == &add) + return true; if (zero == ANY) return true; return false; @@ -626,12 +647,15 @@ void register_param_cleared(int id) { my_id = id; + add_merge_hook(my_id, &merge_hook); + add_hook(&match_assign, ASSIGNMENT_HOOK); add_hook(&match_array_assign, ASSIGNMENT_HOOK); register_clears_param(); select_return_states_hook(BUF_CLEARED, &db_param_cleared); + select_return_param_key(BUF_ADD, &db_buf_add); add_return_info_callback(my_id, return_info_callback); if (option_project == PROJ_KERNEL) -- 2.11.4.GIT