2 * Copyright (C) 2014 Oracle.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
19 * This file is sort of like check_dereferences_param.c. In theory the one
20 * difference should be that the param is NULL it should still be counted as a
21 * free. But for now I don't handle that case.
25 #include "smatch_extra.h"
26 #include "smatch_slist.h"
33 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
35 if (sm
->state
!= &freed
)
37 if (parent_is_null_var_sym(sm
->name
, sm
->sym
))
42 static void set_ignore(struct sm_state
*sm
, struct expression
*mod_expr
)
44 set_state(my_id
, sm
->name
, sm
->sym
, &ignore
);
47 static int counter_was_inced(struct expression
*expr
)
54 name
= expr_to_var_sym(expr
, &sym
);
58 snprintf(buf
, sizeof(buf
), "%s->users.counter", name
);
59 ret
= was_inced(buf
, sym
);
65 static void match_free(const char *fn
, struct expression
*expr
, void *param
)
67 struct expression
*arg
, *tmp
;
70 arg
= get_argument_from_call_expr(expr
->args
, PTR_INT(param
));
73 while ((tmp
= get_assigned_expr(arg
))) {
74 arg
= strip_expr(tmp
);
79 if (get_param_num(arg
) < 0)
81 if (param_was_set(arg
))
83 if (strcmp(fn
, "kfree_skb") == 0 && counter_was_inced(arg
))
86 set_state_expr(my_id
, arg
, &freed
);
89 static void set_param_freed(struct expression
*expr
, int param
, char *key
, char *value
)
91 struct expression
*arg
;
95 while (expr
->type
== EXPR_ASSIGNMENT
)
96 expr
= strip_expr(expr
->right
);
97 if (expr
->type
!= EXPR_CALL
)
100 arg
= get_argument_from_call_expr(expr
->args
, param
);
103 name
= get_variable_from_key(arg
, key
, &sym
);
106 if (get_param_num_from_sym(sym
) < 0)
109 if (param_was_set_var_sym(name
, sym
))
112 set_state(my_id
, name
, sym
, &freed
);
117 static void param_freed_info(int return_id
, char *return_ranges
, struct expression
*expr
)
121 const char *param_name
;
123 if (on_atomic_dec_path())
126 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
127 if (sm
->state
!= &freed
)
130 param
= get_param_num_from_sym(sm
->sym
);
134 param_name
= get_param_name(sm
);
138 sql_insert_return_states(return_id
, return_ranges
, PARAM_FREED
,
139 param
, param_name
, "");
140 } END_FOR_EACH_SM(sm
);
143 void check_frees_param_strict(int id
)
147 if (option_project
!= PROJ_KERNEL
)
150 add_function_hook("kfree", &match_free
, INT_PTR(0));
151 add_function_hook("kmem_cache_free", &match_free
, INT_PTR(1));
152 add_function_hook("kfree_skb", &match_free
, INT_PTR(0));
153 add_function_hook("kfree_skbmem", &match_free
, INT_PTR(0));
154 add_function_hook("dma_pool_free", &match_free
, INT_PTR(1));
155 add_function_hook("spi_unregister_controller", &match_free
, INT_PTR(0));
157 select_return_states_hook(PARAM_FREED
, &set_param_freed
);
158 add_modification_hook(my_id
, &set_ignore
);
159 add_split_return_callback(¶m_freed_info
);
161 add_unmatched_state_hook(my_id
, &unmatched_state
);