summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Deacon <willdeacon@google.com>2020-12-15 16:24:55 +0000
committerGiuliano Procida <gprocida@google.com>2021-01-05 15:58:52 +0000
commitb5b38b010efc2330816691b98bb6cb9ec7e80d68 (patch)
treeef0babd693af520bc93eec2155b9947bf026d520
parent65ab8d94757f5e5198da6584c7f94509128e333e (diff)
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>
-rw-r--r--drivers/usb/gadget/function/f_accessory.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 4aa4a57d0fc5..b48d75657012 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -1259,6 +1259,9 @@ static int acc_setup(void)
struct acc_dev *dev;
int ret;
+ if (kref_read(&ref->kref))
+ return -EBUSY;
+
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -1275,16 +1278,21 @@ static int acc_setup(void)
INIT_WORK(&dev->hid_work, acc_hid_work);
dev->ref = ref;
- kref_init(&ref->kref);
- ref->acc_dev = dev;
+ if (cmpxchg_relaxed(&ref->acc_dev, NULL, dev)) {
+ ret = -EBUSY;
+ goto err_free_dev;
+ }
ret = misc_register(&acc_device);
if (ret)
- goto err;
+ goto err_zap_ptr;
+ kref_init(&ref->kref);
return 0;
-err:
+err_zap_ptr:
+ ref->acc_dev = NULL;
+err_free_dev:
kfree(dev);
pr_err("USB accessory gadget driver failed to initialize\n");
return ret;