From de329024a8f85292543c23747d9d42ccfe41a88c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 18 Jan 2018 13:19:34 +0300 Subject: [PATCH] function_hooks: fake an assignment when functions return "0-s32max[$0->bar]" Say a function returns a parameter or a member of a parameter like this: struct my_type *get_bar(struct foo *foo) { return foo->bar; } Then we can create a fake assignment for the caller: my_bar = foo->bar; The reason I didn't do this before was that it was a bit complicated to take a string and generate an expression. It turns out that when you call a function, then you've already got most of the expression, you just need to add the "->bar" part which isn't so hard. Signed-off-by: Dan Carpenter --- check_check_deref.c | 3 +++ check_deref_check.c | 3 +++ smatch_expressions.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ smatch_extra.h | 1 + smatch_function_hooks.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/check_check_deref.c b/check_check_deref.c index b3d4987a..ba63c39f 100644 --- a/check_check_deref.c +++ b/check_check_deref.c @@ -50,6 +50,9 @@ static void check_dereference(struct expression *expr) struct sm_state *sm; struct sm_state *tmp; + if (__in_fake_assign) + return; + expr = strip_expr(expr); sm = get_sm_state_expr(my_id, expr); diff --git a/check_deref_check.c b/check_deref_check.c index 8b82b35a..d60033b7 100644 --- a/check_deref_check.c +++ b/check_deref_check.c @@ -29,6 +29,9 @@ static void underef(struct sm_state *sm, struct expression *mod_expr) static void match_dereference(struct expression *expr) { + if (__in_fake_assign) + return; + if (expr->type != EXPR_PREOP) return; expr = strip_expr(expr->unop); diff --git a/smatch_expressions.c b/smatch_expressions.c index 115cacc2..05c8564c 100644 --- a/smatch_expressions.c +++ b/smatch_expressions.c @@ -116,3 +116,49 @@ struct expression *compare_expression(struct expression *left, int op, struct ex expr->right = right; return expr; } + +struct expression *gen_expression_from_key(struct expression *arg, const char *key) +{ + struct expression *ret = NULL; + struct token *token, *end; + const char *p = key; + char buf[4095]; + char *alloc; + size_t len; + + /* The idea is that we can parse either $0->foo or $->foo */ + if (key[0] != '$') + goto free; + p++; + while (*p >= '0' && *p <= '9') + p++; + len = snprintf(buf, sizeof(buf), "%s\n", p); + alloc = alloc_string(buf); + + token = tokenize_buffer(alloc, len, &end); + if (!token) + goto free; + if (token_type(token) != TOKEN_STREAMBEGIN) + goto free; + token = token->next; + + ret = arg; + while (token_type(token) == TOKEN_SPECIAL && + token->special == SPECIAL_DEREFERENCE) { + token = token->next; + if (token_type(token) != TOKEN_IDENT) { + ret = NULL; + goto free; + } + ret = deref_expression(ret); + ret = member_expression(ret, '*', token->ident); + token = token->next; + } + + if (token_type(token) != TOKEN_STREAMEND) + ret = NULL; + +free: + return ret; +} + diff --git a/smatch_extra.h b/smatch_extra.h index de010dc7..6d028a99 100644 --- a/smatch_extra.h +++ b/smatch_extra.h @@ -201,6 +201,7 @@ struct expression *symbol_expression(struct symbol *sym); struct expression *compare_expression(struct expression *left, int op, struct expression *right); struct expression *unknown_value_expression(struct expression *expr); int is_fake_call(struct expression *expr); +struct expression *gen_expression_from_key(struct expression *arg, const char *key); /* smatch_param_limit.c */ struct smatch_state *get_orig_estate(const char *name, struct symbol *sym); diff --git a/smatch_function_hooks.c b/smatch_function_hooks.c index ac8b7fce..473113d7 100644 --- a/smatch_function_hooks.c +++ b/smatch_function_hooks.c @@ -754,6 +754,56 @@ static void call_ranged_return_hooks(struct db_callback_info *db_info) } END_FOR_EACH_PTR(tmp); } +static void fake_a_param_assignment(struct expression *expr, const char *return_str) +{ + struct expression *arg, *left, *right, *fake_assign; + char *p; + int param; + char buf[256]; + + if (expr->type != EXPR_ASSIGNMENT) + return; + left = expr->left; + right = expr->right; + + while (right->type == EXPR_ASSIGNMENT) + right = strip_expr(right->right); + if (!right || right->type != EXPR_CALL) + return; + + p = strchr(return_str, '['); + if (!p) + return; + + p++; + if (p[0] == '=' && p[1] == '=') + p += 2; + if (p[0] != '$') + return; + + snprintf(buf, sizeof(buf), "%s", p); + + p = buf; + p += 1; + param = strtol(p, &p, 10); + + p = strchr(p, ']'); + if (!p || *p != ']') + return; + *p = '\0'; + + arg = get_argument_from_call_expr(right->args, param); + if (!arg) + return; + right = gen_expression_from_key(arg, buf); + if (!right) /* Mostly fails for binops like [$0 + 4032] */ + return; + fake_assign = assign_expression(left, right); + __in_fake_assign++; + __split_expr(fake_assign); + __in_fake_assign--; +} + static int db_assign_return_states_callback(void *_info, int argc, char **argv, char **azColName) { struct db_callback_info *db_info = _info; @@ -811,6 +861,7 @@ static int db_assign_return_states_callback(void *_info, int argc, char **argv, set_state(-1, "unnull_path", NULL, &true_state); __add_return_comparison(strip_expr(db_info->expr->right), argv[1]); __add_comparison_info(db_info->expr->left, strip_expr(db_info->expr->right), argv[1]); + fake_a_param_assignment(db_info->expr, argv[1]); __add_return_to_param_mapping(db_info->expr, argv[1]); } -- 2.11.4.GIT