Btw, here is the diff between my code and the released code. To be honest, I don't look at these results or I would have noticed when devm_add_action_or_reset() changed from a function to a macro. I think the nla_nest_start() stuff is totally wrong but I don't understand the rules with that. regards, dan carpenter
diff --git a/check_unwind.c b/check_unwind.c index e2102ec76c14..4c3a743be8e6 100644 --- a/check_unwind.c +++ b/check_unwind.c @@ -35,6 +35,8 @@ STATE(unknown); static unsigned long fn_has_alloc; +static void handle_ida_destroy(const char *fn, struct expression *expr, void *data); + struct ref_func_info { const char *name; int type; @@ -88,14 +90,173 @@ static struct ref_func_info func_table[] = { { "pci_request_irq", ALLOC, 1, "$", &int_zero, &int_zero }, { "pci_free_irq", RELEASE, 1, "$" }, + { "irq_of_parse_and_map", ALLOC, -1, "$", &int_one, &int_max}, + { "irq_create_of_mapping", ALLOC, -1, "$", &int_one, &int_max}, + { "irq_dispose_mapping", RELEASE, 0, "$"}, + { "register_netdev", ALLOC, 0, "$", &int_zero, &int_zero }, { "unregister_netdev", RELEASE, 0, "$" }, { "misc_register", ALLOC, 0, "$", &int_zero, &int_zero }, { "misc_deregister", RELEASE, 0, "$" }, + { "__platform_driver_register", ALLOC, 0, "$", &int_zero, &int_zero }, + { "platform_driver_unregister", RELEASE, 0, "$" }, + { "ieee80211_alloc_hw", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, { "ieee80211_free_hw", RELEASE, 0, "$" }, + +// { "init_timer_key", ALLOC, 0, "$" }, + { "del_timer_sync", RELEASE, 0, "$" }, + { "del_timer", RELEASE, 0, "$" }, + { "cancel_delayed_work_sync", RELEASE, 0, "$" }, + + { "dma_alloc_coherent", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dma_free_coherent", RELEASE, 2, "$" }, + + { "ida_alloc_max", ALLOC, -1, "$", &int_zero, &int_max }, + { "ida_alloc_range", ALLOC, -1, "$", &int_zero, &int_max }, + { "ida_free", RELEASE, 1, "$" }, + { "ida_destroy", RELEASE, 0, "$", NULL, NULL, &handle_ida_destroy }, + + { "alloc_workqueue", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "destroy_workqueue", RELEASE, 0, "$" }, + + { "__class_register", ALLOC, 0, "$", &int_zero, &int_zero }, + { "class_unregister", RELEASE, 0, "$" }, + + { "clk_get", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "clk_put", RELEASE, 0, "$" }, + + { "dev_pm_domain_attach", ALLOC, 0, "$", &int_zero, &int_zero }, + { "dev_pm_domain_attach_by_id", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dev_pm_domain_attach_by_name", ALLOC, 0, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dev_pm_domain_detach", RELEASE, 0, "$" }, + + +// To be honest, I don't understand the point of devfres_free(). I thought all +// this stuff was supposed to be automatic. (Probably everyone else doesn't +// understand either which is we have all these bugs). +// { "devres_alloc", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, +// { "__devres_alloc_node", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, +// { "devres_free", RELEASE, 0, "$" }, + +// { "device_initialize", ALLOC, 0, "$" }, +// { "dev_set_name", ALLOC, 0, "$", &int_zero, &int_zero }, + { "device_register", ALLOC, 0, "$", &int_zero, &int_zero }, + { "put_device", RELEASE, 0, "$" }, + { "device_unregister", RELEASE, 0, "$" }, + { "device_del", RELEASE, 0, "$" }, + + // FIXME: dma returns are slightly different I think... + { "dma_map_resource", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dma_unmap_resource", RELEASE, 1, "$" }, + + { "dma_map_single_attrs", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dma_unmap_single_attrs", RELEASE, 1, "$" }, + { "usb_gadget_unmap_request", RELEASE, 1, "$->dma" }, + + { "dma_request_slave_channel", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dma_request_chan", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dma_release_channel", RELEASE, 0, "$" }, + + { "framebuffer_alloc", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "framebuffer_release", RELEASE, 0, "$" }, + + { "get_device", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "put_device", RELEASE, 0, "$" }, + + { "iio_channel_get", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "iio_channel_release", RELEASE, 0, "$" }, + + { "input_allocate_device", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "input_free_device", RELEASE, 0, "$" }, + + { "input_register_handle", ALLOC, 0, "$", &int_zero, &int_zero }, + { "input_unregister_handle", RELEASE, 0, "$" }, + + { "mempool_alloc", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "mempool_free", RELEASE, 0, "$" }, + + { "of_node_get", ALLOC, 0, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "of_get_next_available_child", RELEASE, 1, "$" }, + { "__of_find_node_by_full_path", RELEASE, 0, "$" }, + { "of_find_node_by_name", RELEASE, 0, "$" }, + { "of_node_put", RELEASE, 0, "$" }, + + { "of_reserved_mem_device_init", ALLOC, 0, "$", &int_zero, &int_zero }, + { "of_reserved_mem_device_release", RELEASE, 0, "$" }, + + { "pinctrl_register", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "pinctrl_unregister", RELEASE, 0, "$" }, + +// sun6i_hwspinlock_probe() deliberately dasserts on success and asserts on failure +// { "reset_control_assert", ALLOC, 0, "$", &int_zero, &int_zero }, +// { "reset_control_deassert", RELEASE, 0, "$" }, + + { "scsi_host_alloc", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "scsi_host_put", RELEASE, 0, "$" }, + +// Are these affected by pcim_enable_device()? +// { "pci_alloc_irq_vectors", ALLOC, 0, "$", &int_one, &int_max }, +// { "pci_free_irq_vectors", RELEASE, 0, "$" }, +// { "pci_dev_get", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, +// { "pci_dev_put", RELEASE, 0, "$" }, + + // There are to many pci_enable_device warnings and it's not clear + // if we really want to disable the pci device anyway + // { "pci_enable_device", ALLOC, 0, "$", &int_zero, &int_zero }, + // { "pci_disable_device", RELEASE, 0, "$" }, + + { "spi_dev_get", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "spi_dev_put", RELEASE, 0, "$" }, + + { "spi_message_alloc", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "spi_message_free", RELEASE, 0, "$" }, + + { "spi_register_controller", ALLOC, 0, "$", &int_zero, &int_zero }, + { "spi_unregister_master", RELEASE, 0, "$" }, + { "spi_unregister_controller", RELEASE, 0, "$" }, + + { "ovl_override_creds", ALLOC, -1, "$" }, + { "revert_creds", RELEASE, 0, "$" }, + + { "dma_alloc_attrs", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dma_free_attrs", RELEASE, 2, "$" }, + + { "alloc_pages", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "__get_free_pages", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "free_pages", RELEASE, 0, "$" }, + { "__free_pages", RELEASE, 0, "$" }, + { "put_page", RELEASE, 0, "$" }, + { "safe_put_page", RELEASE, 0, "$" }, + { "skb_fill_page_desc", IGNORE, 2, "$" }, + { "__skb_frag_set_page", IGNORE, 1, "$" }, + { "sg_set_page", IGNORE, 1, "$" }, + { "sg_assign_page", IGNORE, 1, "$" }, + { "ib_dma_map_page", IGNORE, 1, "$" }, + + { "usb_get_intf", ALLOC, 0, "$"}, + { "usb_put_intf", RELEASE, 0, "$"}, + + { "request_firmware", ALLOC, 0, "*$", &int_zero, &int_zero}, + { "release_firmware", RELEASE, 0, "$"}, + + { "get_cred", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "put_cred", RELEASE, 0, "$" }, + + { "damon_new_target", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "damon_add_target", RELEASE, 1, "$" }, + { "damon_add_target", ALLOC, 0, "$" }, + { "damon_sysfs_destroy_targets", RELEASE, 0, "$" }, + + { "nla_nest_start_noflag", ALLOC, 0, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "nla_nest_start", ALLOC, 0, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "nla_nest_end", RELEASE, 0, "$" }, + { "nla_nest_cancel", RELEASE, 0, "$" }, + + { "debugfs_lookup", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval }, + { "dput", RELEASE, 0, "$" }, }; static struct smatch_state *unmatched_state(struct sm_state *sm) @@ -275,6 +436,21 @@ static void return_param_ignore(struct expression *expr, const char *name, struc update_ssa_sm(my_id, start_sm, &ignore); } +static void handle_ida_destroy(const char *fn, struct expression *expr, void *data) +{ + struct sm_state *sm, *tmp; + + FOR_EACH_SM_SAFE(__get_cur_stree(), sm) { + if (sm->owner != my_id) + continue; + FOR_EACH_PTR(sm->possible, tmp) { + if (strcmp(tmp->state->name, "ida_alloc_max") == 0 || + strcmp(tmp->state->name, "ida_alloc_range") == 0) + set_state(sm->owner, sm->name, sm->sym, &release); + } END_FOR_EACH_PTR(tmp); + } END_FOR_EACH_SM_SAFE(sm); +} + static void match_sm_assign(struct sm_state *sm, struct expression *mod_expr) { char *left; @@ -344,12 +520,14 @@ static void check_balance(const char *name, struct symbol *sym) if (is_impossible_path()) goto swap_stree; - if (db_incomplete()) - goto swap_stree; +// if (db_incomplete()) +// goto swap_stree; if (get_state(path_id, "path", NULL) == &ignore) goto swap_stree; return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL); + if (local_debug) + sm_msg("%s: RETURN: return_sm='%s'", __func__, show_sm(return_sm)); if (!return_sm) goto swap_stree; line.value = return_sm->line; @@ -358,6 +536,9 @@ static void check_balance(const char *name, struct symbol *sym) if (!sm) goto swap_stree; + // FIXME: if allocated and it's a local variable and it's not + // shared then it's a leak + state = sm->state; if (state == ¶m_released) state = &release;