diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-06-22 15:02:44 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-06-22 15:02:44 -0400 |
| commit | e10e57718544a321b97c036cf124d2a0b3f91d38 (patch) | |
| tree | 88dadc46ca90eff37a966fb9437f1fb216d2f4f3 | |
| parent | b71055beaaa7fddbbfd2a60644dcb308408f686c (diff) | |
| parent | 875979368eb4cfecff9f0e97625b90cc6009269d (diff) | |
Merge tag 'driver-core-3.10-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core fix from Greg Kroah-Hartman:
"Here's a single patch for the firmware core that resolves a reported
oops in the firmware core that people have been hitting."
* tag 'driver-core-3.10-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core:
firmware loader: fix use-after-free by double abort
| -rw-r--r-- | drivers/base/firmware_class.c | 27 |
1 files changed, 18 insertions, 9 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 4b1f9265887f..01e21037d8fe 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
| @@ -450,8 +450,18 @@ static void fw_load_abort(struct firmware_priv *fw_priv) | |||
| 450 | { | 450 | { |
| 451 | struct firmware_buf *buf = fw_priv->buf; | 451 | struct firmware_buf *buf = fw_priv->buf; |
| 452 | 452 | ||
| 453 | /* | ||
| 454 | * There is a small window in which user can write to 'loading' | ||
| 455 | * between loading done and disappearance of 'loading' | ||
| 456 | */ | ||
| 457 | if (test_bit(FW_STATUS_DONE, &buf->status)) | ||
| 458 | return; | ||
| 459 | |||
| 453 | set_bit(FW_STATUS_ABORT, &buf->status); | 460 | set_bit(FW_STATUS_ABORT, &buf->status); |
| 454 | complete_all(&buf->completion); | 461 | complete_all(&buf->completion); |
| 462 | |||
| 463 | /* avoid user action after loading abort */ | ||
| 464 | fw_priv->buf = NULL; | ||
| 455 | } | 465 | } |
| 456 | 466 | ||
| 457 | #define is_fw_load_aborted(buf) \ | 467 | #define is_fw_load_aborted(buf) \ |
| @@ -528,7 +538,12 @@ static ssize_t firmware_loading_show(struct device *dev, | |||
| 528 | struct device_attribute *attr, char *buf) | 538 | struct device_attribute *attr, char *buf) |
| 529 | { | 539 | { |
| 530 | struct firmware_priv *fw_priv = to_firmware_priv(dev); | 540 | struct firmware_priv *fw_priv = to_firmware_priv(dev); |
| 531 | int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status); | 541 | int loading = 0; |
| 542 | |||
| 543 | mutex_lock(&fw_lock); | ||
| 544 | if (fw_priv->buf) | ||
| 545 | loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status); | ||
| 546 | mutex_unlock(&fw_lock); | ||
| 532 | 547 | ||
| 533 | return sprintf(buf, "%d\n", loading); | 548 | return sprintf(buf, "%d\n", loading); |
| 534 | } | 549 | } |
| @@ -570,12 +585,12 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 570 | const char *buf, size_t count) | 585 | const char *buf, size_t count) |
| 571 | { | 586 | { |
| 572 | struct firmware_priv *fw_priv = to_firmware_priv(dev); | 587 | struct firmware_priv *fw_priv = to_firmware_priv(dev); |
| 573 | struct firmware_buf *fw_buf = fw_priv->buf; | 588 | struct firmware_buf *fw_buf; |
| 574 | int loading = simple_strtol(buf, NULL, 10); | 589 | int loading = simple_strtol(buf, NULL, 10); |
| 575 | int i; | 590 | int i; |
| 576 | 591 | ||
| 577 | mutex_lock(&fw_lock); | 592 | mutex_lock(&fw_lock); |
| 578 | 593 | fw_buf = fw_priv->buf; | |
| 579 | if (!fw_buf) | 594 | if (!fw_buf) |
| 580 | goto out; | 595 | goto out; |
| 581 | 596 | ||
| @@ -777,10 +792,6 @@ static void firmware_class_timeout_work(struct work_struct *work) | |||
| 777 | struct firmware_priv, timeout_work.work); | 792 | struct firmware_priv, timeout_work.work); |
| 778 | 793 | ||
| 779 | mutex_lock(&fw_lock); | 794 | mutex_lock(&fw_lock); |
| 780 | if (test_bit(FW_STATUS_DONE, &(fw_priv->buf->status))) { | ||
| 781 | mutex_unlock(&fw_lock); | ||
| 782 | return; | ||
| 783 | } | ||
| 784 | fw_load_abort(fw_priv); | 795 | fw_load_abort(fw_priv); |
| 785 | mutex_unlock(&fw_lock); | 796 | mutex_unlock(&fw_lock); |
| 786 | } | 797 | } |
| @@ -861,8 +872,6 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
| 861 | 872 | ||
| 862 | cancel_delayed_work_sync(&fw_priv->timeout_work); | 873 | cancel_delayed_work_sync(&fw_priv->timeout_work); |
| 863 | 874 | ||
| 864 | fw_priv->buf = NULL; | ||
| 865 | |||
| 866 | device_remove_file(f_dev, &dev_attr_loading); | 875 | device_remove_file(f_dev, &dev_attr_loading); |
| 867 | err_del_bin_attr: | 876 | err_del_bin_attr: |
| 868 | device_remove_bin_file(f_dev, &firmware_attr_data); | 877 | device_remove_bin_file(f_dev, &firmware_attr_data); |
