ANDROID: usb: f_accessory: Don't corrupt global state on double registration
If acc_setup() is called when there is already an allocated instance, misc_register() will fail but the error path leaves a dangling pointer to freed memory in the global 'acc_dev' state. Fix this by ensuring that the refcount is zero before we start, and then using a cmpxchg() from NULL to serialise any concurrent initialisers. Bug: 173789633 Signed-off-by: Will Deacon <willdeacon@google.com> Change-Id: I2c26289dcce7dbc493964516c49b05d04aaa6839 Signed-off-by: Giuliano Procida <gprocida@google.com>
This commit is contained in:
committed by
Giuliano Procida
parent
4df1d2ffe1
commit
cd4f430770
@@ -1258,6 +1258,9 @@ static int acc_setup(void)
|
|||||||
struct acc_dev *dev;
|
struct acc_dev *dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (kref_read(&ref->kref))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -1274,16 +1277,21 @@ static int acc_setup(void)
|
|||||||
INIT_WORK(&dev->hid_work, acc_hid_work);
|
INIT_WORK(&dev->hid_work, acc_hid_work);
|
||||||
|
|
||||||
dev->ref = ref;
|
dev->ref = ref;
|
||||||
kref_init(&ref->kref);
|
if (cmpxchg_relaxed(&ref->acc_dev, NULL, dev)) {
|
||||||
ref->acc_dev = dev;
|
ret = -EBUSY;
|
||||||
|
goto err_free_dev;
|
||||||
|
}
|
||||||
|
|
||||||
ret = misc_register(&acc_device);
|
ret = misc_register(&acc_device);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err_zap_ptr;
|
||||||
|
|
||||||
|
kref_init(&ref->kref);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err_zap_ptr:
|
||||||
|
ref->acc_dev = NULL;
|
||||||
|
err_free_dev:
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
pr_err("USB accessory gadget driver failed to initialize\n");
|
pr_err("USB accessory gadget driver failed to initialize\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
Reference in New Issue
Block a user