Fixes and rework of rpmsg_eptdev_add()
v4 cover Fixes and rework of rpmsg_eptdev_add()
This is V4 of series fixing fallout introduced by anonymous inode eptdev code and rework the exception handing paths.
And it tries to address a long suffering issue related to WARN() in error path.
Change for v4:
- Re-layout the patches in series
1/3: Fix potential WARN In error patch of rpmsg_eptdev_add(). 2/3: Rework exception handling of rpmsg_eptdev_add() and callers. 3/3: Merge branches in rpmsg_eptdev_add().
Link to v3:
https://lore.kernel.org/all/[email protected]/
Change for v3:
- Split it into 3 patches:
1/3: Fix legacy bug.
2/3: Fix new bug introduced by anonymous eptdev code. 3/3: Rework error handling code.
Link to v2:
https://lore.kernel.org/all/[email protected]/
Change for v2:
- Add put_device() when __rpmsg_eptdev_open() failed. Link to v1:
https://lore.kernel.org/all/[email protected]/
Dawei Li (3):
rpmsg: char: Fix WARN() in error path of rpmsg_eptdev_add()
rpmsg: char: Rework of rpmsg_eptdev_add() and its callers
rpmsg: char: Merge cdev branches in rpmsg_eptdev_add()
drivers/rpmsg/rpmsg_char.c | 60 ++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 29 deletions(-)
Signature · 1 line
2.25.1
PATCH v4 1/3 rpmsg: char: Fix WARN() in error path of rpmsg_eptdev_add()
put_device() is called on error path of rpmsg_eptdev_add() to cleanup resource attached to eptdev->dev, unfortunately it's bogus cause dev->release() is not set yet.
When a struct device instance is destroyed, driver core framework checks the possible release() callback from candidates below:
- struct device::release()
- dev->type->release()
- dev->class->dev_release()
Rpmsg eptdev owns none of them so WARN() will complain the absence of release().
Fix it by:
- Pre-assign dev->release() before potential error path.
- Check before ida_free() in dev->release().
By fixing error path of rpmsg_eptdev_add() and fixing potential memory leak in rpmsg_anonymous_eptdev_create(), this work paves the way of rework of rpmsg_eptdev_add() and its callers.
--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
drivers/rpmsg/rpmsg_char.c +13 -13
@@ -408,8 +408,13 @@ static void rpmsg_eptdev_release_device(struct device *dev) {struct rpmsg_eptdev *eptdev =dev_to_eptdev (dev); -ida_free (&rpmsg_ept_ida, dev->id); -if (eptdev->dev.devt) +/* +* release() can be invoked from error path of rpmsg_eptdev_add(), +* WARN() will be fired if ida_free() is feed with invalid ID. +*/ +if (likely (ida_exists (&rpmsg_ept_ida, dev->id))) +ida_free (&rpmsg_ept_ida, dev->id); +if (eptdev->dev.devt &&likely (ida_exists (&rpmsg_minor_ida,MINOR (eptdev->dev.devt))))ida_free (&rpmsg_minor_ida,MINOR (eptdev->dev.devt));kfree (eptdev); }
@@ -458,6 +463,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,struct device *dev = &eptdev->dev;int ret; + dev->release = rpmsg_eptdev_release_device; + eptdev->chinfo = chinfo;if (cdev) {
@@ -471,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_minor_ida; + goto free_eptdev; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);
@@ -480,22 +487,13 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev);if (ret) - goto free_ept_ida; + goto free_eptdev; } -/* We can now rely on the release function for cleanup */ - dev->release = rpmsg_eptdev_release_device; -return ret; -free_ept_ida: -ida_free (&rpmsg_ept_ida, dev->id); -free_minor_ida: -if (cdev) -ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev:put_device (dev); -kfree (eptdev);return ret; }
@@ -561,6 +559,8 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *parif (!ret) *pfd = fd; +else +put_device (&eptdev->dev);return ret; }
Signature · 1 line
2.25.1
PATCH v4 2/3 rpmsg: char: Rework of rpmsg_eptdev_add() and its callers
In reply to Dawei LiFor every caller of rpmsg_eptdev_add(), eptdev is allocated in rpmsg_eptdev_alloc(). Resource should be freed where it's allocated, In this case, eptdev should be freed in caller of rpmsg_eptdev_add() rather than rpmsg_eptdev_add() itself.
Move the error handling from rpmsg_eptdev_add() to its callers.
--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-)
drivers/rpmsg/rpmsg_char.c +15 -11
@@ -470,7 +470,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL );if (ret <0 ) - goto free_eptdev; +return ret; dev->devt =MKDEV (MAJOR (rpmsg_major), ret); }
@@ -478,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_eptdev; +return ret; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);
@@ -486,15 +486,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev); -if (ret) - goto free_eptdev; } -return ret; - -free_eptdev: -put_device (dev); -return ret; }
@@ -507,12 +500,17 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent struct rpmsg_channel_info chinfo) {struct rpmsg_eptdev *eptdev; +int ret; eptdev =rpmsg_chrdev_eptdev_alloc (rpdev, parent);if (IS_ERR (eptdev))return PTR_ERR (eptdev); -return rpmsg_chrdev_eptdev_add (eptdev, chinfo); + ret =rpmsg_chrdev_eptdev_add (eptdev, chinfo); +if (ret) +put_device (&eptdev->dev); + +return ret; }EXPORT_SYMBOL (rpmsg_chrdev_eptdev_create);
@@ -544,6 +542,7 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *par ret =rpmsg_eptdev_add (eptdev, chinfo, false);if (ret) {dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name); +put_device (&eptdev->dev);return ret; }
@@ -571,6 +570,7 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)struct rpmsg_channel_info chinfo;struct rpmsg_eptdev *eptdev;struct device *dev = &rpdev->dev; +int ret;memcpy (chinfo.name, rpdev->id.name,RPMSG_NAME_SIZE ); chinfo.src = rpdev->src;
@@ -589,7 +589,11 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)*/ eptdev->default_ept->priv = eptdev; -return rpmsg_chrdev_eptdev_add (eptdev, chinfo); + ret =rpmsg_chrdev_eptdev_add (eptdev, chinfo); +if (ret) +put_device (&eptdev->dev); + +return ret; }static void rpmsg_chrdev_remove (struct rpmsg_device *rpdev)
Signature · 1 line
2.25.1
PATCH v4 3/3 rpmsg: char: Merge cdev branches in rpmsg_eptdev_add()
In reply to Dawei LiThere are two if(cdev) branches in rpmsg_eptdev_add(), merge them. Note that cdev_device_add() still must be last one since dev->release() doesn't reclaim cdev related resource.
--- drivers/rpmsg/rpmsg_char.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
drivers/rpmsg/rpmsg_char.c +6 -8
@@ -467,14 +467,6 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, eptdev->chinfo = chinfo; -if (cdev) { - ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL ); -if (ret <0 ) -return ret; - - dev->devt =MKDEV (MAJOR (rpmsg_major), ret); - } -/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 )
@@ -485,6 +477,12 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, ret =0 ;if (cdev) { + ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL ); +if (ret <0 ) +return ret; + + dev->devt =MKDEV (MAJOR (rpmsg_major), ret); + ret =cdev_device_add (&eptdev->cdev, &eptdev->dev); }
Signature · 1 line
2.25.1
Re: [PATCH v4 1/3] rpmsg: char: Fix WARN() in error path of rpmsg_eptdev_add()
In reply to Dawei LiQuoted 76 lines · Dawei Li
put_device()is called on error path ofrpmsg_eptdev_add()to cleanup resource attached toeptdev->dev, unfortunately it's bogus causedev->release()is not set yet.When a struct device instance is destroyed, driver core framework checks the possible
release()callback from candidates below:
- structdevice::release()
-dev->type->release()
-dev->class->dev_release()Rpmsg eptdev owns none of them so
WARN()will complain the absence ofrelease().Fix it by:
- Pre-assigndev->release()before potential error path.
- Check beforeida_free()indev->release().By fixing error path of
rpmsg_eptdev_add()and fixing potential memory leak inrpmsg_anonymous_eptdev_create(), this work paves the way of rework ofrpmsg_eptdev_add()and its callers.Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)drivers/rpmsg/rpmsg_char.c +11
-7@@ -408,8 +408,13 @@ static void rpmsg_eptdev_release_device(struct device *dev) {struct rpmsg_eptdev *eptdev =dev_to_eptdev (dev); -ida_free (&rpmsg_ept_ida, dev->id); -if (eptdev->dev.devt) +/* +* release() can be invoked from error path of rpmsg_eptdev_add(), +* WARN() will be fired if ida_free() is feed with invalid ID. +*/ +if (likely (ida_exists (&rpmsg_ept_ida, dev->id))) +ida_free (&rpmsg_ept_ida, dev->id); +if (eptdev->dev.devt &&likely (ida_exists (&rpmsg_minor_ida,MINOR (eptdev->dev.devt))))ida_free (&rpmsg_minor_ida,MINOR (eptdev->dev.devt));kfree (eptdev); }@@ -458,6 +463,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,struct device *dev = &eptdev->dev;int ret; + dev->release = rpmsg_eptdev_release_device; + eptdev->chinfo = chinfo;if (cdev) {@@ -471,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_minor_ida; + goto free_eptdev; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);@@ -480,22 +487,13 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev);if (ret) - goto free_ept_ida; + goto free_eptdev; } -/* We can now rely on the release function for cleanup */ - dev->release = rpmsg_eptdev_release_device; -
drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 498
Quoted 10 lines
drivers/rpmsg/rpmsg_char.c +0
-6return ret; -free_ept_ida: -ida_free (&rpmsg_ept_ida, dev->id); -free_minor_ida: -if (cdev) -ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev:put_device (dev); -kfree (eptdev);
Hi Dawei,
Thanks for your new version~
Patch 1/3 will introduce a use-after-free of eptdev in func rpmsg_anonymous_eptdev_create(),
even though this issue will be resolved in 2/3. However, 1/3, as an independent commit, should not introduce a new bug.
Quoted 12 lines
drivers/rpmsg/rpmsg_char.c +2
-0return ret; }@@ -561,6 +559,8 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *parif (!ret) *pfd = fd; +else +put_device (&eptdev->dev);return ret; }
Signature · 2 lines
Thx and BRs, Zhongqiu Han
Re: [PATCH v4 1/3] rpmsg: char: Fix WARN() in error path of rpmsg_eptdev_add()
Quoted 87 lines · Zhongqiu Han
Quoted 70 lines · Dawei Li
put_device()is called on error path ofrpmsg_eptdev_add()to cleanup resource attached toeptdev->dev, unfortunately it's bogus causedev->release()is not set yet.When a struct device instance is destroyed, driver core framework checks the possible
release()callback from candidates below:
- structdevice::release()
-dev->type->release()
-dev->class->dev_release()Rpmsg eptdev owns none of them so
WARN()will complain the absence ofrelease().Fix it by:
- Pre-assigndev->release()before potential error path.
- Check beforeida_free()indev->release().By fixing error path of
rpmsg_eptdev_add()and fixing potential memory leak inrpmsg_anonymous_eptdev_create(), this work paves the way of rework ofrpmsg_eptdev_add()and its callers.Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)drivers/rpmsg/rpmsg_char.c +11
-6@@ -408,8 +408,13 @@ static void rpmsg_eptdev_release_device(struct device *dev) {struct rpmsg_eptdev *eptdev =dev_to_eptdev (dev); -ida_free (&rpmsg_ept_ida, dev->id); -if (eptdev->dev.devt) +/* +* release() can be invoked from error path of rpmsg_eptdev_add(), +* WARN() will be fired if ida_free() is feed with invalid ID. +*/ +if (likely (ida_exists (&rpmsg_ept_ida, dev->id))) +ida_free (&rpmsg_ept_ida, dev->id); +if (eptdev->dev.devt &&likely (ida_exists (&rpmsg_minor_ida,MINOR (eptdev->dev.devt))))ida_free (&rpmsg_minor_ida,MINOR (eptdev->dev.devt));kfree (eptdev); }@@ -458,6 +463,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,struct device *dev = &eptdev->dev;int ret; + dev->release = rpmsg_eptdev_release_device; + eptdev->chinfo = chinfo;if (cdev) {@@ -471,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_minor_ida; + goto free_eptdev; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);@@ -480,22 +487,13 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev);if (ret) - goto free_ept_ida; + goto free_eptdev; } -/* We can now rely on the release function for cleanup */ - dev->release = rpmsg_eptdev_release_device;drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 496
Quoted 10 lines
drivers/rpmsg/rpmsg_char.c +0
-7-return ret; -free_ept_ida: -ida_free (&rpmsg_ept_ida, dev->id); -free_minor_ida: -if (cdev) -ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev:put_device (dev); -kfree (eptdev);Hi Dawei,
Thanks for your new version~
Quoted 7 lines
Patch 1/3 will introduce a use-after-free of eptdev in func
rpmsg_anonymous_eptdev_create(),even though this issue will be resolved in 2/3. However, 1/3, as an independent commit, should not introduce a new bug.
FWIW, it's not new bug introduced by this commit, it's introduced by 2410558f5f11, which is supposed to be fixed in patch[2/3].
And new reorganize of series is trying to address the comments from Mathieu[1], If I understand it correctly.
Bjorn, Mathieu,
What's your inputs on this? I will respin v5 if you find it necessary.
Diff between v5 from v4:
patch[1/3]: Remove dev_err(&eptdev->dev) from rpmsg_anonymous_eptdev_create();
patch[2/3]: Bring back dev_err(&eptdev->dev) to rpmsg_anonymous_eptdev_create();
[1] https://lore.kernel.org/all/aRd8mDzQWXtEFnmt@p14s/
Quoted 16 lines
Quoted 11 lines
drivers/rpmsg/rpmsg_char.c +2
-0return ret; }@@ -561,6 +559,8 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *parif (!ret) *pfd = fd; +else +put_device (&eptdev->dev);return ret; }Signature · 2 lines
Thx and BRs, Zhongqiu Han
Thanks,
Dawei
Re: [PATCH v4 1/3] rpmsg: char: Fix WARN() in error path of rpmsg_eptdev_add()
Quoted 91 lines · Dawei Li
Quoted 90 lines · Zhongqiu Han
Quoted 70 lines · Dawei Li
put_device()is called on error path ofrpmsg_eptdev_add()to cleanup resource attached toeptdev->dev, unfortunately it's bogus causedev->release()is not set yet.When a struct device instance is destroyed, driver core framework checks the possible
release()callback from candidates below:
- structdevice::release()
-dev->type->release()
-dev->class->dev_release()Rpmsg eptdev owns none of them so
WARN()will complain the absence ofrelease().Fix it by:
- Pre-assigndev->release()before potential error path.
- Check beforeida_free()indev->release().By fixing error path of
rpmsg_eptdev_add()and fixing potential memory leak inrpmsg_anonymous_eptdev_create(), this work paves the way of rework ofrpmsg_eptdev_add()and its callers.Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)drivers/rpmsg/rpmsg_char.c +11
-6@@ -408,8 +408,13 @@ static void rpmsg_eptdev_release_device(struct device *dev) {struct rpmsg_eptdev *eptdev =dev_to_eptdev (dev); -ida_free (&rpmsg_ept_ida, dev->id); -if (eptdev->dev.devt) +/* +* release() can be invoked from error path of rpmsg_eptdev_add(), +* WARN() will be fired if ida_free() is feed with invalid ID. +*/ +if (likely (ida_exists (&rpmsg_ept_ida, dev->id))) +ida_free (&rpmsg_ept_ida, dev->id); +if (eptdev->dev.devt &&likely (ida_exists (&rpmsg_minor_ida,MINOR (eptdev->dev.devt))))ida_free (&rpmsg_minor_ida,MINOR (eptdev->dev.devt));kfree (eptdev); }@@ -458,6 +463,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,struct device *dev = &eptdev->dev;int ret; + dev->release = rpmsg_eptdev_release_device; + eptdev->chinfo = chinfo;if (cdev) {@@ -471,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_minor_ida; + goto free_eptdev; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);@@ -480,22 +487,13 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev);if (ret) - goto free_ept_ida; + goto free_eptdev; } -/* We can now rely on the release function for cleanup */ - dev->release = rpmsg_eptdev_release_device;drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 496
Quoted 10 lines
drivers/rpmsg/rpmsg_char.c +0
-7-return ret; -free_ept_ida: -ida_free (&rpmsg_ept_ida, dev->id); -free_minor_ida: -if (cdev) -ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev:put_device (dev); -kfree (eptdev);Hi Dawei,
Thanks for your new version~
Patch 1/3 will introduce a use-after-free of eptdev in func
rpmsg_anonymous_eptdev_create(),
Quoted 7 lines
Quoted 4 lines
even though this issue will be resolved in 2/3. However, 1/3, as an independent commit, should not introduce a new bug.
FWIW, it's not new bug introduced by this commit, it's introduced by
2410558f5f11, which is supposed to be fixed in patch[2/3].
Hi Dawei,
I checked commit 2410558f5f11 and see that the issue existed before this patch. Thanks for clarifying,
Quoted 34 lines
And new reorganize of series is trying to address the comments from Mathieu[1], If I understand it correctly.
Bjorn, Mathieu,
What's your inputs on this? I will respin v5 if you find it necessary.Diff between v5 from v4:
patch[1/3]: Removedev_err(&eptdev->dev)fromrpmsg_anonymous_eptdev_create();patch[2/3]: Bring back
dev_err(&eptdev->dev)torpmsg_anonymous_eptdev_create();[1] https://lore.kernel.org/all/aRd8mDzQWXtEFnmt@p14s/
Quoted 16 lines
Quoted 11 lines
drivers/rpmsg/rpmsg_char.c +2
-0return ret; }@@ -561,6 +559,8 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *parif (!ret) *pfd = fd; +else +put_device (&eptdev->dev);return ret; }Signature · 2 lines
Thx and BRs, Zhongqiu HanThanks,
Dawei
Signature · 2 lines
Thx and BRs, Zhongqiu Han
Re: [PATCH v4 2/3] rpmsg: char: Rework of rpmsg_eptdev_add() and its callers
In reply to Dawei LiQuoted 8 lines · Dawei Li
For every caller of
rpmsg_eptdev_add(), eptdev is allocated inrpmsg_eptdev_alloc(). Resource should be freed where it's allocated, In this case, eptdev should be freed in caller ofrpmsg_eptdev_add()rather thanrpmsg_eptdev_add()itself.Move the error handling from
rpmsg_eptdev_add()to its callers.Fixes 2410558f5f11 ("rpmsg: char: Implement eptdev based on anonymous inode")
Just for reference, it would be better if the commit message clearly explained what is being fixed. Just like V3 2/3
https://lore.kernel.org/all/[email protected]/
Quoted 91 lines
Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-)drivers/rpmsg/rpmsg_char.c +15
-11@@ -470,7 +470,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL );if (ret <0 ) - goto free_eptdev; +return ret; dev->devt =MKDEV (MAJOR (rpmsg_major), ret); }@@ -478,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_eptdev; +return ret; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);@@ -486,15 +486,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev); -if (ret) - goto free_eptdev; } -return ret; - -free_eptdev: -put_device (dev); -return ret; }@@ -507,12 +500,17 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent struct rpmsg_channel_info chinfo) {struct rpmsg_eptdev *eptdev; +int ret; eptdev =rpmsg_chrdev_eptdev_alloc (rpdev, parent);if (IS_ERR (eptdev))return PTR_ERR (eptdev); -return rpmsg_chrdev_eptdev_add (eptdev, chinfo); + ret =rpmsg_chrdev_eptdev_add (eptdev, chinfo); +if (ret) +put_device (&eptdev->dev); + +return ret; }EXPORT_SYMBOL (rpmsg_chrdev_eptdev_create);@@ -544,6 +542,7 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *par ret =rpmsg_eptdev_add (eptdev, chinfo, false);if (ret) {dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name); +put_device (&eptdev->dev);return ret; }@@ -571,6 +570,7 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)struct rpmsg_channel_info chinfo;struct rpmsg_eptdev *eptdev;struct device *dev = &rpdev->dev; +int ret;memcpy (chinfo.name, rpdev->id.name,RPMSG_NAME_SIZE ); chinfo.src = rpdev->src;@@ -589,7 +589,11 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)*/ eptdev->default_ept->priv = eptdev; -return rpmsg_chrdev_eptdev_add (eptdev, chinfo); + ret =rpmsg_chrdev_eptdev_add (eptdev, chinfo); +if (ret) +put_device (&eptdev->dev); + +return ret; }static void rpmsg_chrdev_remove (struct rpmsg_device *rpdev)
Signature · 2 lines
Thx and BRs, Zhongqiu Han
Mathieu Poirier
maintainerRe: [PATCH v4 1/3] rpmsg: char: Fix WARN() in error path of rpmsg_eptdev_add()
In reply to Dawei LiQuoted 48 lines · Dawei Li
put_device()is called on error path ofrpmsg_eptdev_add()to cleanup resource attached toeptdev->dev, unfortunately it's bogus causedev->release()is not set yet.When a struct device instance is destroyed, driver core framework checks the possible
release()callback from candidates below:
- structdevice::release()
-dev->type->release()
-dev->class->dev_release()Rpmsg eptdev owns none of them so
WARN()will complain the absence ofrelease().Fix it by:
- Pre-assigndev->release()before potential error path.
- Check beforeida_free()indev->release().By fixing error path of
rpmsg_eptdev_add()and fixing potential memory leak inrpmsg_anonymous_eptdev_create(), this work paves the way of rework ofrpmsg_eptdev_add()and its callers.Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)drivers/rpmsg/rpmsg_char.c +7
-2@@ -408,8 +408,13 @@ static void rpmsg_eptdev_release_device(struct device *dev) {struct rpmsg_eptdev *eptdev =dev_to_eptdev (dev); -ida_free (&rpmsg_ept_ida, dev->id); -if (eptdev->dev.devt) +/* +* release() can be invoked from error path of rpmsg_eptdev_add(), +* WARN() will be fired if ida_free() is feed with invalid ID. +*/ +if (likely (ida_exists (&rpmsg_ept_ida, dev->id))) +ida_free (&rpmsg_ept_ida, dev->id); +if (eptdev->dev.devt &&likely (ida_exists (&rpmsg_minor_ida,MINOR (eptdev->dev.devt))))ida_free (&rpmsg_minor_ida,MINOR (eptdev->dev.devt));kfree (eptdev); }
drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 467
Quoted 5 lines
drivers/rpmsg/rpmsg_char.c +2
-0@@ -458,6 +463,8 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,struct device *dev = &eptdev->dev;int ret; + dev->release = rpmsg_eptdev_release_device; +
A device's release function if for an allocated device, not to address an error path. This should have been left where it was.
Quoted 23 lines
drivers/rpmsg/rpmsg_char.c +2
-5eptdev->chinfo = chinfo;if (cdev) {@@ -471,7 +478,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,/* Anonymous inode device still need device name for dev_err() and friends */ ret =ida_alloc (&rpmsg_ept_ida,GFP_KERNEL );if (ret <0 ) - goto free_minor_ida; + goto free_eptdev; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret);@@ -480,22 +487,13 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev) { ret =cdev_device_add (&eptdev->cdev, &eptdev->dev);if (ret) - goto free_ept_ida; + goto free_eptdev; } -/* We can now rely on the release function for cleanup */ - dev->release = rpmsg_eptdev_release_device; -
drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 498
Quoted 10 lines
drivers/rpmsg/rpmsg_char.c +0
-6return ret; -free_ept_ida: -ida_free (&rpmsg_ept_ida, dev->id); -free_minor_ida: -if (cdev) -ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev:put_device (dev); -kfree (eptdev);
You're doing two things at the same time, i.e dealing with the kfree() _and_ put_device(). As indicated before, if this function fails the kfree() needs to happend in the error handling of rpmsg_eptdev_add() in rpmsg_anonymous_eptdev_create() and not in rpmsg_eptdev_release_device().
I am now at a point where I have spent too much time on this patchet - continuing to work on it would be unfair to other people waiting for their patches to be reviewed. As such I have backed-out this feature from the rpmsg-next tree.
Thanks,
Mathieu
Quoted 15 lines
drivers/rpmsg/rpmsg_char.c +2
-0return ret; }@@ -561,6 +559,8 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *parif (!ret) *pfd = fd; +else +put_device (&eptdev->dev);return ret; }Signature · 1 line
2.25.1