flow: strip out parentheses before faking a return
[smatch.git] / check_netdev_priv.c
blob2fccdad490a1007883355b4f1d4852b69fa3735c
1 /* SPDX-License-Identifier: MIT
3 * Copyright (C) 2021 Pavel Skripkin
4 */
6 /* TODO:
8 * Try to find a way how to handle situations like:
10 * struct some_dev *dev = get_dev_from_smth(smth);
12 * free_netdev(dev->netdev);
13 * do_clean_up(dev);
16 * In this case dev is dev->netdev private data (exmpl: ems_usb_disconnect())
19 #include "smatch.h"
20 #include "smatch_extra.h"
21 #include "smatch_function_hashtable.h"
23 static int my_id;
24 STATE(freed);
25 STATE(ok);
27 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
29 if (sm->state != &ok)
30 set_state(my_id, sm->name, sm->sym, &ok);
33 static inline char *get_function_name(struct expression *expr)
35 if (!expr || expr->type != EXPR_CALL)
36 return NULL;
37 if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
38 return NULL;
39 return expr->fn->symbol_name->name;
42 static inline int is_netdev_priv(struct expression *call)
44 char *name;
46 if (!call || call->type != EXPR_CALL)
47 return 0;
49 name = get_function_name(call);
50 if (!name)
51 return 0;
53 return !strcmp("netdev_priv", name);
56 static const char *get_parent_netdev_name(struct expression *expr)
58 struct expression *call, *arg_expr = NULL;
59 struct symbol *sym;
61 call = get_assigned_expr(strip_expr(expr));
62 if (is_netdev_priv(call)) {
63 arg_expr = get_argument_from_call_expr(call->args, 0);
64 arg_expr = strip_expr(arg_expr);
65 } else {
66 return NULL;
69 return expr_to_var_sym(arg_expr, &sym);
72 static void match_free_netdev(const char *fn, struct expression *expr, void *_arg_no)
74 struct expression *arg;
75 const char *name;
77 arg = get_argument_from_call_expr(expr->args, PTR_INT(_arg_no));
78 if (!arg)
79 return;
81 name = expr_to_var(arg);
82 if (!name)
83 return;
85 set_state(my_id, name, NULL, &freed);
88 static void match_symbol(struct expression *expr)
90 const char *parent_netdev, *name;
91 struct smatch_state *state;
93 if (!has_states(__get_cur_stree(), my_id))
94 return;
96 name = expr_to_var(expr);
97 if (!name)
98 return;
100 parent_netdev = get_parent_netdev_name(expr);
101 if (!parent_netdev)
102 return;
104 state = get_state(my_id, parent_netdev, NULL);
105 if (state == &freed)
106 sm_error("Using '%s' after free_{netdev,candev}(%s);\n", name, parent_netdev);
109 void check_uaf_netdev_priv(int id)
111 if (option_project != PROJ_KERNEL)
112 return;
114 my_id = id;
116 add_function_hook("free_netdev", &match_free_netdev, NULL);
117 add_function_hook("free_candev", &match_free_netdev, NULL);
118 add_modification_hook(my_id, &ok_to_use);
119 add_hook(&match_symbol, SYM_HOOK);