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>
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 592d59c..e04b9d7 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -1258,6 +1258,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;
@@ -1274,16 +1277,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;