2 * Copyright (C) 2020 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
21 #include "smatch_extra.h"
22 #include "smatch_slist.h"
28 STATE(param_released
);
31 static unsigned long fn_has_alloc
;
33 struct ref_func_info
{
38 const sval_t
*implies_start
, *implies_end
;
42 static sval_t zero_sval
= { .type
= &int_ctype
};
44 static struct ref_func_info func_table
[] = {
45 { "clk_prepare_enable", ALLOC
, 0, "$", &zero_sval
, &zero_sval
},
46 { "clk_disable_unprepare", RELEASE
, 0, "$" },
48 { "alloc_etherdev_mqs", ALLOC
, -1, "$", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
49 { "free_netdev", RELEASE
, 0, "$" },
52 * FIXME: A common pattern in release functions like amd76xrom_cleanup()
55 * if (window->rsrc.parent)
56 * release_resource(&window->rsrc);
58 * Which is slightly tricky to know how to merge the states so let's
59 * hold off checking request_resource() for now.
61 * { "request_resource", ALLOC, 1, "$", &zero_sval, &zero_sval },
62 * { "release_resource", RELEASE, 0, "$" },
66 { "pci_request_regions", ALLOC
, 0, "$", &zero_sval
, &zero_sval
},
67 { "pci_release_regions", RELEASE
, 0, "$" },
69 { "request_free_mem_region", ALLOC
, -1, "$->start", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
70 { "__request_region", ALLOC
, 1, "$", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
71 { "__release_region", RELEASE
, 1, "$" },
73 { "ioremap", ALLOC
, -1, "$", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
74 { "of_iomap", ALLOC
, -1, "$", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
75 { "ioremap_encrypted", ALLOC
, -1, "$", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
76 { "iounmap", RELEASE
, 0, "$" },
78 { "request_threaded_irq", ALLOC
, 0, "$", &zero_sval
, &zero_sval
},
79 { "request_irq", ALLOC
, 0, "$", &zero_sval
, &zero_sval
},
80 { "free_irq", RELEASE
, 0, "$" },
81 { "pci_request_irq", ALLOC
, 1, "$", &zero_sval
, &zero_sval
},
82 { "pci_free_irq", RELEASE
, 1, "$" },
84 { "register_netdev", ALLOC
, 0, "$", &zero_sval
, &zero_sval
},
85 { "unregister_netdev", RELEASE
, 0, "$" },
87 { "misc_register", ALLOC
, 0, "$", &zero_sval
, &zero_sval
},
88 { "misc_deregister", RELEASE
, 0, "$" },
90 { "ieee80211_alloc_hw", ALLOC
, -1, "$", &valid_ptr_min_sval
, &valid_ptr_max_sval
},
91 { "ieee80211_free_hw", RELEASE
, 0, "$" },
94 struct smatch_state
*unmatched_state(struct sm_state
*sm
)
96 struct smatch_state
*state
;
98 if (sm
->state
!= ¶m_released
)
101 if (is_impossible_path())
102 return ¶m_released
;
104 state
= get_state(SMATCH_EXTRA
, sm
->name
, sm
->sym
);
107 if (!estate_rl(state
) || is_err_or_null(estate_rl(state
)))
108 return ¶m_released
;
109 if (parent_is_err_or_null_var_sym(sm
->name
, sm
->sym
))
110 return ¶m_released
;
115 static struct sm_state
*get_start_sm(const char *name
, struct symbol
*sym
)
119 sm
= get_sm_state(my_id
, name
, sym
);
123 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
124 if (get_comparison_strings(name
, sm
->name
) == SPECIAL_EQUAL
)
126 } END_FOR_EACH_SM(sm
);
131 static bool is_param_var_sym(const char *name
, struct symbol
*sym
)
135 return get_param_key_from_var_sym(name
, sym
, NULL
, &key
) >= 0;
138 static void mark_matches_as_undefined(const char *key
)
141 int start_pos
, state_len
, key_len
;
144 while ((p
= strchr(key
, '-'))) {
149 key_len
= strlen(key
);
151 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
152 state_len
= strlen(sm
->name
);
153 if (state_len
< key_len
)
155 if (!slist_has_state(sm
->possible
, &alloc
))
158 start_pos
= state_len
- key_len
;
159 if ((start_pos
== 0 || !isalnum(sm
->name
[start_pos
- 1])) &&
160 strcmp(sm
->name
+ start_pos
, key
) == 0)
161 set_state(my_id
, sm
->name
, sm
->sym
, &undefined
);
163 } END_FOR_EACH_SM(sm
);
166 static void db_helper(struct expression
*expr
, int param
, const char *key
, int inc_dec
)
168 struct sm_state
*start_sm
;
172 if (inc_dec
== ALLOC
)
175 name
= get_name_sym_from_key(expr
, param
, key
, &sym
);
179 start_sm
= get_start_sm(name
, sym
);
180 if (inc_dec
== RELEASE
&& !start_sm
) {
182 mark_matches_as_undefined(key
);
185 if (is_param_var_sym(name
, sym
)) {
186 set_state(my_id
, name
, sym
, ¶m_released
);
192 if (inc_dec
== ALLOC
)
193 set_state(my_id
, name
, sym
, &alloc
);
196 set_state(my_id
, start_sm
->name
, start_sm
->sym
, &release
);
198 set_state(my_id
, name
, sym
, &release
);
204 static void refcount_function(const char *fn
, struct expression
*expr
, void *data
)
206 struct ref_func_info
*info
= data
;
208 db_helper(expr
, info
->param
, info
->key
, info
->type
);
211 static void refcount_implied(const char *fn
, struct expression
*call_expr
,
212 struct expression
*assign_expr
, void *data
)
214 struct ref_func_info
*info
= data
;
216 db_helper(assign_expr
?: call_expr
, info
->param
, info
->key
, info
->type
);
219 static void ignore_path(const char *fn
, struct expression
*expr
, void *data
)
221 set_state(my_id
, "path", NULL
, &ignore
);
224 static bool is_alloc_primitive(struct expression
*expr
)
228 while (expr
->type
== EXPR_ASSIGNMENT
)
229 expr
= strip_expr(expr
->right
);
230 if (expr
->type
!= EXPR_CALL
)
233 if (expr
->fn
->type
!= EXPR_SYMBOL
)
236 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
237 if (sym_name_is(func_table
[i
].name
, expr
->fn
))
244 static void db_dec(struct expression
*expr
, int param
, char *key
, char *value
)
246 if (is_alloc_primitive(expr
))
248 db_helper(expr
, param
, key
, RELEASE
);
251 static void match_return_info(int return_id
, char *return_ranges
, struct expression
*expr
)
254 const char *param_name
;
257 if (is_impossible_path())
260 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
261 if (sm
->state
!= ¶m_released
)
263 param
= get_param_key_from_sm(sm
, expr
, ¶m_name
);
266 sql_insert_return_states(return_id
, return_ranges
, RELEASE
,
267 param
, param_name
, "");
268 } END_FOR_EACH_SM(sm
);
272 UNKNOWN
, FAIL
, SUCCESS
, NUM_BUCKETS
275 static int success_fail_positive(struct range_list
*rl
)
280 if (sval_is_negative(rl_min(rl
)) && sval_is_negative(rl_max(rl
)))
283 if (rl_min(rl
).value
== 0)
289 static void check_balance(const char *name
, struct symbol
*sym
)
291 struct range_list
*inc_lines
= NULL
;
292 int inc_buckets
[NUM_BUCKETS
] = {};
293 struct stree
*stree
, *orig_stree
;
294 struct smatch_state
*state
;
295 struct sm_state
*return_sm
;
297 sval_t line
= sval_type_val(&int_ctype
, 0);
300 FOR_EACH_PTR(get_all_return_strees(), stree
) {
301 orig_stree
= __swap_cur_stree(stree
);
303 if (is_impossible_path())
307 if (get_state(my_id
, "path", NULL
) == &ignore
)
310 return_sm
= get_sm_state(RETURN_ID
, "return_ranges", NULL
);
313 line
.value
= return_sm
->line
;
315 sm
= get_sm_state(my_id
, name
, sym
);
320 if (state
== ¶m_released
)
323 if (state
!= &alloc
&&
327 bucket
= success_fail_positive(estate_rl(return_sm
->state
));
331 if (state
== &alloc
) {
332 add_range(&inc_lines
, line
, line
);
333 inc_buckets
[bucket
] = true;
336 __swap_cur_stree(orig_stree
);
337 } END_FOR_EACH_PTR(stree
);
339 if (inc_buckets
[FAIL
])
345 sm_warning("'%s' not released on lines: %s.", name
, show_rl(inc_lines
));
348 static void match_check_balanced(struct symbol
*sym
)
352 FOR_EACH_MY_SM(my_id
, get_all_return_states(), sm
) {
353 if (sm
->sym
== NULL
&& strcmp(sm
->name
, "path") == 0)
355 check_balance(sm
->name
, sm
->sym
);
356 } END_FOR_EACH_SM(sm
);
359 void check_unwind(int id
)
361 struct ref_func_info
*info
;
366 if (option_project
!= PROJ_KERNEL
)
369 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
370 info
= &func_table
[i
];
372 if (info
->call_back
) {
373 add_function_hook(info
->name
, info
->call_back
, info
);
374 } else if (info
->implies_start
&& info
->type
== ALLOC
) {
375 return_implies_exact(info
->name
,
376 *info
->implies_start
,
378 &refcount_implied
, info
);
379 } else if (info
->implies_start
) {
380 return_implies_state_sval(info
->name
,
381 *info
->implies_start
,
383 &refcount_implied
, info
);
385 add_function_hook(info
->name
, &refcount_function
, info
);
389 add_function_hook("devm_add_action_or_reset", &ignore_path
, NULL
);
391 add_unmatched_state_hook(my_id
, &unmatched_state
);
392 add_function_data(&fn_has_alloc
);
394 add_split_return_callback(match_return_info
);
395 select_return_states_hook(RELEASE
, &db_dec
);
396 add_hook(&match_check_balanced
, END_FUNC_HOOK
);