diff options
author | Maxime Bizon <mbizon@freebox.fr> | 2013-08-29 14:28:13 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-30 15:04:27 -0400 |
commit | 1eeeef153c02f5856ec109fa532eb5f31c39f85c (patch) | |
tree | 4ba21b5842f9a986ac85105c2787c50d2a8dae09 /drivers/base/firmware_class.c | |
parent | 7315f0ccfc283ae998ca4d8102de83bba21936fa (diff) |
firmware loader: fix pending_fw_head list corruption
Got the following oops just before reboot:
Unable to handle kernel NULL pointer dereference at virtual address 00000000
[<8028d300>] (__list_del_entry+0x44/0xac)
[<802e3320>] (__fw_load_abort.part.13+0x1c/0x50)
[<802e337c>] (fw_shutdown_notify+0x28/0x50)
[<80034f80>] (notifier_call_chain.isra.1+0x5c/0x9c)
[<800350ec>] (__blocking_notifier_call_chain+0x44/0x58)
[<80035114>] (blocking_notifier_call_chain+0x14/0x18)
[<80035d64>] (kernel_restart_prepare+0x14/0x38)
[<80035d94>] (kernel_restart+0xc/0x50)
The following race condition triggers here:
_request_firmware_load()
device_create_file(...)
kobject_uevent(...)
(schedule)
(resume)
firmware_loading_store(1)
firmware_loading_store(0)
list_del_init(&buf->pending_list)
(schedule)
(resume)
list_add(&buf->pending_list, &pending_fw_head);
wait_for_completion(&buf->completion);
causing an oops later when walking pending_list after the firmware has
been released.
The proposed fix is to move the list_add() before sysfs attribute
creation.
Signed-off-by: Maxime Bizon <mbizon@freebox.fr>
Acked-by: Ming Lei <ming.lei@canonical.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/firmware_class.c')
-rw-r--r-- | drivers/base/firmware_class.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index e4107d5f036b..10a4467c63f1 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
@@ -865,8 +865,15 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
865 | goto err_del_dev; | 865 | goto err_del_dev; |
866 | } | 866 | } |
867 | 867 | ||
868 | mutex_lock(&fw_lock); | ||
869 | list_add(&buf->pending_list, &pending_fw_head); | ||
870 | mutex_unlock(&fw_lock); | ||
871 | |||
868 | retval = device_create_file(f_dev, &dev_attr_loading); | 872 | retval = device_create_file(f_dev, &dev_attr_loading); |
869 | if (retval) { | 873 | if (retval) { |
874 | mutex_lock(&fw_lock); | ||
875 | list_del_init(&buf->pending_list); | ||
876 | mutex_unlock(&fw_lock); | ||
870 | dev_err(f_dev, "%s: device_create_file failed\n", __func__); | 877 | dev_err(f_dev, "%s: device_create_file failed\n", __func__); |
871 | goto err_del_bin_attr; | 878 | goto err_del_bin_attr; |
872 | } | 879 | } |
@@ -881,10 +888,6 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
881 | kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); | 888 | kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); |
882 | } | 889 | } |
883 | 890 | ||
884 | mutex_lock(&fw_lock); | ||
885 | list_add(&buf->pending_list, &pending_fw_head); | ||
886 | mutex_unlock(&fw_lock); | ||
887 | |||
888 | wait_for_completion(&buf->completion); | 891 | wait_for_completion(&buf->completion); |
889 | 892 | ||
890 | cancel_delayed_work_sync(&fw_priv->timeout_work); | 893 | cancel_delayed_work_sync(&fw_priv->timeout_work); |