Commit message: ipmi: fix use-after-free of user->release_barrier.rda

When we do the following test, we got oops in ipmi_msghandler driver
while((1))
do
	service ipmievd restart & service ipmievd restart
done

---------------------------------------------------------------
[  294.230186] Unable to handle kernel paging request at virtual address 0000803fea6ea008
[  294.230188] Mem abort info:
[  294.230190]   ESR = 0x96000004
[  294.230191]   Exception class = DABT (current EL), IL = 32 bits
[  294.230193]   SET = 0, FnV = 0
[  294.230194]   EA = 0, S1PTW = 0
[  294.230195] Data abort info:
[  294.230196]   ISV = 0, ISS = 0x00000004
[  294.230197]   CM = 0, WnR = 0
[  294.230199] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000a1c1b75a
[  294.230201] [0000803fea6ea008] pgd=0000000000000000
[  294.230204] Internal error: Oops: 96000004 [#1] SMP
[  294.235211] Modules linked in: nls_utf8 isofs rpcrdma ib_iser ib_srpt target_core_mod ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_umad rdma_cm ib_cm iw_cm dm_mirror dm_region_hash dm_log dm_mod aes_ce_blk crypto_simd cryptd aes_ce_cipher ghash_ce sha2_ce ses sha256_arm64 sha1_ce hibmc_drm hisi_sas_v2_hw enclosure sg hisi_sas_main sbsa_gwdt ip_tables mlx5_ib ib_uverbs marvell ib_core mlx5_core ixgbe ipmi_si mdio hns_dsaf ipmi_devintf ipmi_msghandler hns_enet_drv hns_mdio
[  294.277745] CPU: 3 PID: 0 Comm: swapper/3 Kdump: loaded Not tainted 5.0.0-rc2+ #113
[  294.285511] Hardware name: Huawei TaiShan 2280 /BC11SPCD, BIOS 1.37 11/21/2017
[  294.292835] pstate: 80000005 (Nzcv daif -PAN -UAO)
[  294.297695] pc : __srcu_read_lock+0x38/0x58
[  294.301940] lr : acquire_ipmi_user+0x2c/0x70 [ipmi_msghandler]
[  294.307853] sp : ffff00001001bc80
[  294.311208] x29: ffff00001001bc80 x28: ffff0000117e5000
[  294.316594] x27: 0000000000000000 x26: dead000000000100
[  294.321980] x25: dead000000000200 x24: ffff803f6bd06800
[  294.327366] x23: 0000000000000000 x22: 0000000000000000
[  294.332752] x21: ffff00001001bd04 x20: ffff80df33d19018
[  294.338137] x19: ffff80df33d19018 x18: 0000000000000000
[  294.343523] x17: 0000000000000000 x16: 0000000000000000
[  294.348908] x15: 0000000000000000 x14: 0000000000000002
[  294.354293] x13: 0000000000000000 x12: 0000000000000000
[  294.359679] x11: 0000000000000000 x10: 0000000000100000
[  294.365065] x9 : 0000000000000000 x8 : 0000000000000004
[  294.370451] x7 : 0000000000000000 x6 : ffff80df34558678
[  294.375836] x5 : 000000000000000c x4 : 0000000000000000
[  294.381221] x3 : 0000000000000001 x2 : 0000803fea6ea000
[  294.386607] x1 : 0000803fea6ea008 x0 : 0000000000000001
[  294.391994] Process swapper/3 (pid: 0, stack limit = 0x0000000083087293)
[  294.398791] Call trace:
[  294.401266]  __srcu_read_lock+0x38/0x58
[  294.405154]  acquire_ipmi_user+0x2c/0x70 [ipmi_msghandler]
[  294.410716]  deliver_response+0x80/0xf8 [ipmi_msghandler]
[  294.416189]  deliver_local_response+0x28/0x68 [ipmi_msghandler]
[  294.422193]  handle_one_recv_msg+0x158/0xcf8 [ipmi_msghandler]
[  294.432050]  handle_new_recv_msgs+0xc0/0x210 [ipmi_msghandler]
[  294.441984]  smi_recv_tasklet+0x8c/0x158 [ipmi_msghandler]
[  294.451618]  tasklet_action_common.isra.5+0x88/0x138
[  294.460661]  tasklet_action+0x2c/0x38
[  294.468191]  __do_softirq+0x120/0x2f8
[  294.475561]  irq_exit+0x134/0x140
[  294.482445]  __handle_domain_irq+0x6c/0xc0
[  294.489954]  gic_handle_irq+0xb8/0x178
[  294.497037]  el1_irq+0xb0/0x140
[  294.503381]  arch_cpu_idle+0x34/0x1a8
[  294.510096]  do_idle+0x1d4/0x290
[  294.516322]  cpu_startup_entry+0x28/0x30
[  294.523230]  secondary_start_kernel+0x184/0x1d0
[  294.530657] Code: d538d082 d2800023 8b010c81 8b020021 (c85f7c25)
[  294.539746] ---[ end trace 8a7a880dee570b29 ]---
[  294.547341] Kernel panic - not syncing: Fatal exception in interrupt
[  294.556837] SMP: stopping secondary CPUs
[  294.563996] Kernel Offset: disabled
[  294.570515] CPU features: 0x002,21006008
[  294.577638] Memory Limit: none
[  294.587178] Starting crashdump kernel...
[  294.594314] Bye!

Because the user->release_barrier.rda is freed in ipmi_destroy_user(), but
the refcount is not zero, when acquire_ipmi_user() uses user->release_barrier.rda
in __srcu_read_lock(), it causes oops.
Fix this by calling cleanup_srcu_struct() when the refcount is zero.

Fixes: e86ee2d44b44 ("ipmi: Rework locking and shutdown for hot remove")
Cc: stable@vger.kernel.org # 4.18
Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>

Signed-off-by: Corey Minyard <cminyard@mvista.com>
CVE: CVE-2019-9003
CWE: CWE-416
Vulnerability description: In the Linux kernel before 4.20.5, attackers can trigger a drivers/char/ipmi/ipmi_msghandler.c use-after-free and OOPS by arranging for certain simultaneous execution of the code, as demonstrated by a "service ipmievd restart" loop.

Project: linux
File: ipmi_msghandler.c

static int __ipmi_bmc_register(struct ipmi_smi *intf,
			       struct ipmi_device_id *id,
			       bool guid_set, guid_t *guid, int intf_num)
{
	int               rv;
	struct bmc_device *bmc;
	struct bmc_device *old_bmc;

	/*
	 * platform_device_register() can cause bmc_reg_mutex to
	 * be claimed because of the is_visible functions of
	 * the attributes.  Eliminate possible recursion and
	 * release the lock.
	 */
	intf->in_bmc_register = true;
	mutex_unlock(&intf->bmc_reg_mutex);

	/*
	 * Try to find if there is an bmc_device struct
	 * representing the interfaced BMC already
	 */
	mutex_lock(&ipmidriver_mutex);
	if (guid_set)
		old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid);
	else
		old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
						    id->product_id,
						    id->device_id);

	/*
	 * If there is already an bmc_device, free the new one,
	 * otherwise register the new BMC device
	 */
	if (old_bmc) {
		bmc = old_bmc;
		/*
		 * Note: old_bmc already has usecount incremented by
		 * the BMC find functions.
		 */
		intf->bmc = old_bmc;
		mutex_lock(&bmc->dyn_mutex);
		list_add_tail(&intf->bmc_link, &bmc->intfs);
		mutex_unlock(&bmc->dyn_mutex);

		dev_info(intf->si_dev,
			 "interfacing existing BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
			 bmc->id.manufacturer_id,
			 bmc->id.product_id,
			 bmc->id.device_id);
	} else {
		bmc = kzalloc(sizeof(*bmc), GFP_KERNEL);
		if (!bmc) {
			rv = -ENOMEM;
			goto out;
		}
		INIT_LIST_HEAD(&bmc->intfs);
		mutex_init(&bmc->dyn_mutex);
		INIT_WORK(&bmc->remove_work, cleanup_bmc_work);

		bmc->id = *id;
		bmc->dyn_id_set = 1;
		bmc->dyn_guid_set = guid_set;
		bmc->guid = *guid;
		bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;

		bmc->pdev.name = "ipmi_bmc";

		rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL);
		if (rv < 0)
			goto out;
		bmc->pdev.dev.driver = &ipmidriver.driver;
		bmc->pdev.id = rv;
		bmc->pdev.dev.release = release_bmc_device;
		bmc->pdev.dev.type = &bmc_device_type;
		kref_init(&bmc->usecount);

		intf->bmc = bmc;
		mutex_lock(&bmc->dyn_mutex);
		list_add_tail(&intf->bmc_link, &bmc->intfs);
		mutex_unlock(&bmc->dyn_mutex);

		rv = platform_device_register(&bmc->pdev);
		if (rv) {
			dev_err(intf->si_dev,
				"Unable to register bmc device: %d\n",
				rv);
			goto out_list_del;
		}

		dev_info(intf->si_dev,
			 "Found new BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
			 bmc->id.manufacturer_id,
			 bmc->id.product_id,
			 bmc->id.device_id);
	}

	/*
	 * create symlink from system interface device to bmc device
	 * and back.
	 */
	rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->pdev.dev.kobj, "bmc");
	if (rv) {
		dev_err(intf->si_dev, "Unable to create bmc symlink: %d\n", rv);
		goto out_put_bmc;
	}

	if (intf_num == -1)
		intf_num = intf->intf_num;
	intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num);
	if (!intf->my_dev_name) {
		rv = -ENOMEM;
		dev_err(intf->si_dev, "Unable to allocate link from BMC: %d\n",
			rv);
		goto out_unlink1;
	}

	rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj,
			       intf->my_dev_name);
	if (rv) {
		kfree(intf->my_dev_name);
		intf->my_dev_name = NULL;
		dev_err(intf->si_dev, "Unable to create symlink to bmc: %d\n",
			rv);
		goto out_free_my_dev_name;
	}

	intf->bmc_registered = true;

out:
	mutex_unlock(&ipmidriver_mutex);
	mutex_lock(&intf->bmc_reg_mutex);
	intf->in_bmc_register = false;
	return rv;


out_free_my_dev_name:
	kfree(intf->my_dev_name);
	intf->my_dev_name = NULL;

out_unlink1:
	sysfs_remove_link(&intf->si_dev->kobj, "bmc");

out_put_bmc:
	mutex_lock(&bmc->dyn_mutex);
	list_del(&intf->bmc_link);
	mutex_unlock(&bmc->dyn_mutex);
	intf->bmc = &intf->tmp_bmc;
	kref_put(&bmc->usecount, cleanup_bmc_device);
	goto out;

out_list_del:
	mutex_lock(&bmc->dyn_mutex);
	list_del(&intf->bmc_link);
	mutex_unlock(&bmc->dyn_mutex);
	intf->bmc = &intf->tmp_bmc;
	put_device(&bmc->pdev.dev);
	goto out;
}