| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Date: Mon, 7 May 2018 19:10:31 +0900 |
| Subject: driver core: Don't ignore class_dir_create_and_add() failure. |
| |
| commit 84d0c27d6233a9ba0578b20f5a09701eb66cee42 upstream. |
| |
| syzbot is hitting WARN() at kernfs_add_one() [1]. |
| This is because kernfs_create_link() is confused by previous device_add() |
| call which continued without setting dev->kobj.parent field when |
| get_device_parent() failed by memory allocation fault injection. |
| Fix this by propagating the error from class_dir_create_and_add() to |
| the calllers of get_device_parent(). |
| |
| [1] https://syzkaller.appspot.com/bug?id=fae0fb607989ea744526d1c082a5b8de6529116f |
| |
| Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Reported-by: syzbot <syzbot+df47f81c226b31d89fb1@syzkaller.appspotmail.com> |
| Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/base/core.c | 14 ++++++++++++-- |
| 1 file changed, 12 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/base/core.c |
| +++ b/drivers/base/core.c |
| @@ -709,7 +709,7 @@ class_dir_create_and_add(struct class *c |
| |
| dir = kzalloc(sizeof(*dir), GFP_KERNEL); |
| if (!dir) |
| - return NULL; |
| + return ERR_PTR(-ENOMEM); |
| |
| dir->class = class; |
| kobject_init(&dir->kobj, &class_dir_ktype); |
| @@ -719,7 +719,7 @@ class_dir_create_and_add(struct class *c |
| retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name); |
| if (retval < 0) { |
| kobject_put(&dir->kobj); |
| - return NULL; |
| + return ERR_PTR(retval); |
| } |
| return &dir->kobj; |
| } |
| @@ -1000,6 +1000,10 @@ int device_add(struct device *dev) |
| |
| parent = get_device(dev->parent); |
| kobj = get_device_parent(dev, parent); |
| + if (IS_ERR(kobj)) { |
| + error = PTR_ERR(kobj); |
| + goto parent_error; |
| + } |
| if (kobj) |
| dev->kobj.parent = kobj; |
| |
| @@ -1097,6 +1101,7 @@ done: |
| kobject_del(&dev->kobj); |
| Error: |
| cleanup_device_parent(dev); |
| +parent_error: |
| if (parent) |
| put_device(parent); |
| name_error: |
| @@ -1867,6 +1872,11 @@ int device_move(struct device *dev, stru |
| device_pm_lock(); |
| new_parent = get_device(new_parent); |
| new_parent_kobj = get_device_parent(dev, new_parent); |
| + if (IS_ERR(new_parent_kobj)) { |
| + error = PTR_ERR(new_parent_kobj); |
| + put_device(new_parent); |
| + goto out; |
| + } |
| |
| pr_debug("device: '%s': %s: moving to '%s'\n", dev_name(dev), |
| __func__, new_parent ? dev_name(new_parent) : "<NULL>"); |