Fix and rework of rpmsg_eptdev_add()
Subsystems (2)
v3 cover Fix and rework of rpmsg_eptdev_add()
Hi,
This series fixes bug introduced by anonymous inode eptdev code and rework the exception handing paths.
And it tries to address a long suffering issue which may be backported to stable kernels.
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: Remove put_device() in rpmsg_eptdev_add()
rpmsg: char: Fix UAF and memory leak in
rpmsg_anonymous_eptdev_create()
rpmsg: char: Rework exception handling of rpmsg_eptdev_add()
drivers/rpmsg/rpmsg_char.c | 61 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 27 deletions(-)
Signature · 1 line
2.25.1
PATCH v3 1/3 rpmsg: char: Remove put_device() in 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 complaint the absence of release():
[ 159.112182] ------------[ cut here ]------------ [ 159.112188] Device '(null)' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst. [ 159.112205] WARNING: CPU: 2 PID: 1975 at drivers/base/core.c:2567 device_release+0x7a/0x90
--- drivers/rpmsg/rpmsg_char.c | 1 - 1 file changed, 1 deletion(-)
drivers/rpmsg/rpmsg_char.c +0 -1
@@ -494,7 +494,6 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev)ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev: -put_device (dev);kfree (eptdev);return ret;
Signature · 1 line
2.25.1
Re: [PATCH v3 1/3] rpmsg: char: Remove put_device() in rpmsg_eptdev_add()
Quoted 4 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.
Quoted 8 lines
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 complaint the absence ofrelease():
Hi Dawei,
Quoted 5 lines
[ 159.112182] ------------[ cut here ]------------ [ 159.112188] Device '(null)' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst. [ 159.112205] WARNING: CPU: 2 PID: 1975 at drivers/base/core.c:2567 device_release+0x7a/0x90
Although my local checkpatch.pl didn’t complain about this log line exceeding 75 characters, could we simplify it or just provide a summary instead?
Quoted 12 lines
Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 1 - 1 file changed, 1 deletion(-)drivers/rpmsg/rpmsg_char.c +0
-0
drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 497
Quoted 4 lines
drivers/rpmsg/rpmsg_char.c +0
-1@@ -494,7 +494,6 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev)ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev: -put_device (dev);
Yes, remove put_device can solve the warning issue, however it would introduce one memleak issue of kobj->name.
dev_set_name(dev, "rpmsg%d", ret); is already called, it depends on put_device to free memory, right?
Quoted 3 lines
drivers/rpmsg/rpmsg_char.c +0
-0kfree (eptdev);return ret;
Signature · 2 lines
Thx and BRs, Zhongqiu Han
Re: [PATCH v3 1/3] rpmsg: char: Remove put_device() in rpmsg_eptdev_add()
Quoted 51 lines · Zhongqiu Han
Quoted 4 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.Quoted 8 lines
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 complaint the absence ofrelease():Hi Dawei,
Quoted 5 lines
[ 159.112182] ------------[ cut here ]------------
[ 159.112188] Device '(null)' does not have arelease()function, it is broken and must be fixed. SeeDocumentation/core-api/kobject.rst. [ 159.112205] WARNING: CPU: 2 PID: 1975 atdrivers/base/core.c:2567device_release+0x7a/0x90Although my local checkpatch.pl didn’t complain about this log line exceeding 75 characters, could we simplify it or just provide a summary instead?
Quoted 15 lines
Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>---
drivers/rpmsg/rpmsg_char.c| 1 -1 file changed, 1 deletion(-)drivers/rpmsg/rpmsg_char.c +0
-0@@ -494,7 +494,6 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, if (cdev) ida_free(&rpmsg_minor_ida, MINOR(dev->devt));Quoted 2 lines
free_eptdev:
-put_device(dev);Yes, remove
put_devicecan solve the warning issue, however it would introduce one memleak issue ofkobj->name.
Quoted 2 lines
https://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux.git/ tree/drivers/rpmsg/rpmsg_char.c#n381
The above link I arised was wrong; it’s now updated to the correct one.
Quoted 8 lines
dev_set_name(dev, "rpmsg%d", ret); is already called, it depends onput_deviceto free memory, right?Quoted 2 lines
kfree(eptdev);
return ret;
Signature · 2 lines
Thx and BRs, Zhongqiu Han
Re: [PATCH v3 1/3] rpmsg: char: Remove put_device() in rpmsg_eptdev_add()
In reply to Zhongqiu HanHi,
Thanks for the review.
Quoted 52 lines · Zhongqiu Han
Quoted 4 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.Quoted 8 lines
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 complaint the absence ofrelease():Hi Dawei,
Quoted 5 lines
[ 159.112182] ------------[ cut here ]------------ [ 159.112188] Device '(null)' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst. [ 159.112205] WARNING: CPU: 2 PID: 1975 at drivers/base/core.c:2567 device_release+0x7a/0x90Although my local checkpatch.pl didn’t complain about this log line exceeding 75 characters, could we simplify it or just provide a summary instead?
Quoted 12 lines
Fixes c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface")Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 1 - 1 file changed, 1 deletion(-)drivers/rpmsg/rpmsg_char.c +0
-0drivers/rpmsg/rpmsg_char.c static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev, · line 497
Quoted 4 lines
drivers/rpmsg/rpmsg_char.c +0
-1@@ -494,7 +494,6 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev)ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev: -put_device (dev);Yes, remove
put_devicecan solve the warning issue, however it would introduce one memleak issue ofkobj->name.
Quoted 5 lines
dev_set_name(dev, "rpmsg%d", ret); is already called, it depends onput_deviceto free memory, right?
Good catch.
If it's just device name being leaked, just postpone dev_set_name till every resource was allocated successfully.
[Copying your comment on patch3/3]
Quoted 6 lines
As I mentioned about the potential memory leak issue in patch 1/3, we could consider still using
put_devicefor management, as this better aligns with the driver model standards and avoids potential issue. However, this requires assigning the release function in advance and also handling the special case where ida allocation fails inrpmsg_eptdev_add(removing the manual ida release).
But I agree with you, every data structure embedding struct device should bind its life cycle management to struct devcice, that's what driver core is designed. But it's bit tricky to implement your proposed approach, especially considering backing port to stable kernel. A possible solution could be:
drivers/rpmsg/rpmsg_char.c +11 -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 invaid 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; }
ida_exists() is introduced in 7fe6b987166b9, which is beyond the coverage of every stable kernel, and the commit this patch is fixing (c0cdc19f84a4) is contained in almost every stable kernel maintained.
Thanks,
Dawei
Quoted 9 lines
Quoted 4 lines
drivers/rpmsg/rpmsg_char.c +0
-0kfree (eptdev);return ret;Signature · 2 lines
Thx and BRs, Zhongqiu Han
PATCH v3 2/3 rpmsg: char: Fix UAF and memory leak in rpmsg_anonymous_eptdev_create()
In reply to Dawei LiPotential UAF and memory leak exsit in exception handling paths for rpmsg_anonymous_eptdev_create(), fix them.
- If rpmsg_add_eptdev() failes, eptdev is freed in it. Subsequent call of dev_err(&eptdev->device) triggers a UAF.
- If __rpmsg_eptdev_open() fails, eptdev is supposed to be freed by put_device().
--- drivers/rpmsg/rpmsg_char.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/rpmsg/rpmsg_char.c +3 -1
@@ -494,6 +494,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev)ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev: +dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name);kfree (eptdev);return ret;
@@ -544,7 +545,6 @@ 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);return ret; }
@@ -560,6 +560,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
Mathieu Poirier
maintainerRe: [PATCH v3 2/3] rpmsg: char: Fix UAF and memory leak in rpmsg_anonymous_eptdev_create()
Quoted 29 lines · Dawei Li
Potential UAF and memory leak exsit in exception handling paths for
rpmsg_anonymous_eptdev_create(), fix them.- If
rpmsg_add_eptdev()failes, eptdev is freed in it. Subsequent call ofdev_err(&eptdev->device)triggers a UAF.
- If__rpmsg_eptdev_open()fails, eptdev is supposed to be freed byput_device().Fixes 2410558f5f11 ("rpmsg: char: Implement eptdev based on anonymous inode")Reported-by Dan Carpenter <[email protected]>Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)drivers/rpmsg/rpmsg_char.c +1
-0@@ -494,6 +494,7 @@ static int rpmsg_eptdev_add(struct rpmsg_eptdev *eptdev,if (cdev)ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); free_eptdev: +dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name);kfree (eptdev);return ret;
drivers/rpmsg/rpmsg_char.c int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *par · line 547
Quoted 4 lines
drivers/rpmsg/rpmsg_char.c +0
-1@@ -544,7 +545,6 @@ 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);
This doesn't neet to be done if kfree(eptdev) is moved out of rpmsg_eptdev_add().
drivers/rpmsg/rpmsg_char.c int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *par · line 564
Quoted 9 lines
drivers/rpmsg/rpmsg_char.c +2
-0return ret; }@@ -560,6 +560,8 @@ int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *parif (!ret) *pfd = fd; +else +put_device (&eptdev->dev);
This goes in the previous patch.
Quoted 6 lines
drivers/rpmsg/rpmsg_char.c +0
-0return ret; }Signature · 1 line
2.25.1
PATCH v3 3/3 rpmsg: char: Rework exception handling of rpmsg_eptdev_add()
In reply to Dawei LiRework error handling of rpmsg_eptdev_add() and its callers, following rule of "release resource where it's allocated".
--- drivers/rpmsg/rpmsg_char.c | 60 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 27 deletions(-)
drivers/rpmsg/rpmsg_char.c +33 -27
@@ -460,44 +460,34 @@ 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 ) - goto free_eptdev; - - 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 ) - goto free_minor_ida; +return ret; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret); - ret =0 ; -if (cdev) { + ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL ); +if (ret <0 ) { +ida_free (&rpmsg_ept_ida, dev->id); +return ret; + } + + dev->devt =MKDEV (MAJOR (rpmsg_major), ret); + ret =cdev_device_add (&eptdev->cdev, &eptdev->dev); -if (ret) - goto free_ept_ida; +if (ret) { +ida_free (&rpmsg_ept_ida, dev->id); +ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); +return ret; + } }/* 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: -dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name); -kfree (eptdev); - -return ret; +return 0 ; }static int rpmsg_chrdev_eptdev_add (struct rpmsg_eptdev *eptdev,struct rpmsg_channel_info chinfo)
@@ -509,12 +499,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) +kfree (eptdev); + +return ret; }EXPORT_SYMBOL (rpmsg_chrdev_eptdev_create);
@@ -545,6 +540,12 @@ 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); +/* +* Avoid put_device() or WARN() will be triggered due to absence of +* device::release(), refer to device_release(). +*/ +kfree (eptdev);return ret; }
@@ -572,6 +573,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;
@@ -590,7 +592,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) +kfree (eptdev); + +return ret; }static void rpmsg_chrdev_remove (struct rpmsg_device *rpdev)
Signature · 1 line
2.25.1
Re: [PATCH v3 3/3] rpmsg: char: Rework exception handling of rpmsg_eptdev_add()
Quoted 96 lines · Dawei Li
Rework error handling of
rpmsg_eptdev_add()and its callers, following rule of "release resource where it's allocated".Fixes 2410558f5f11 ("rpmsg: char: Implement eptdev based on anonymous inode")Reported-by Dan Carpenter <[email protected]>Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 60 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 27 deletions(-)drivers/rpmsg/rpmsg_char.c +21
-26@@ -460,44 +460,34 @@ 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 ) - goto free_eptdev; - - 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 ) - goto free_minor_ida; +return ret; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret); - ret =0 ; -if (cdev) { + ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL ); +if (ret <0 ) { +ida_free (&rpmsg_ept_ida, dev->id); +return ret; + } + + dev->devt =MKDEV (MAJOR (rpmsg_major), ret); + ret =cdev_device_add (&eptdev->cdev, &eptdev->dev); -if (ret) - goto free_ept_ida; +if (ret) { +ida_free (&rpmsg_ept_ida, dev->id); +ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); +return ret; + } }/* 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: -dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name); -kfree (eptdev); - -return ret; +return 0 ; }static int rpmsg_chrdev_eptdev_add (struct rpmsg_eptdev *eptdev,struct rpmsg_channel_info chinfo)@@ -509,12 +499,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) +kfree (eptdev); + +return ret; }EXPORT_SYMBOL (rpmsg_chrdev_eptdev_create);
drivers/rpmsg/rpmsg_char.c int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *par · line 546
Quoted 7 lines
drivers/rpmsg/rpmsg_char.c +4
-0@@ -545,6 +540,12 @@ 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); +/* +* Avoid put_device() or WARN() will be triggered due to absence of +* device::release(), refer to device_release().
Hi Dawei,
As I mentioned about the potential memory leak issue in patch 1/3, we could consider still using put_device for management, as this better aligns with the driver model standards and avoids potential issue. However, this requires assigning the release function in advance and also handling the special case where ida allocation fails in rpmsg_eptdev_add (removing the manual ida release).
Quoted 26 lines
drivers/rpmsg/rpmsg_char.c +8
-1+*/ +kfree (eptdev);return ret; }@@ -572,6 +573,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;@@ -590,7 +592,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) +kfree (eptdev); + +return ret; }static void rpmsg_chrdev_remove (struct rpmsg_device *rpdev)
Signature · 2 lines
Thx and BRs, Zhongqiu Han
Mathieu Poirier
maintainerRe: [PATCH v3 3/3] rpmsg: char: Rework exception handling of rpmsg_eptdev_add()
In reply to Dawei LiQuoted 80 lines · Dawei Li
Rework error handling of
rpmsg_eptdev_add()and its callers, following rule of "release resource where it's allocated".Fixes 2410558f5f11 ("rpmsg: char: Implement eptdev based on anonymous inode")Reported-by Dan Carpenter <[email protected]>Signed-off-by Dawei Li <[email protected]>--- drivers/rpmsg/rpmsg_char.c | 60 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 27 deletions(-)drivers/rpmsg/rpmsg_char.c +15
-25@@ -460,44 +460,34 @@ 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 ) - goto free_eptdev; - - 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 ) - goto free_minor_ida; +return ret; dev->id = ret;dev_set_name (dev,"rpmsg%d" , ret); - ret =0 ; -if (cdev) { + ret =ida_alloc_max (&rpmsg_minor_ida,RPMSG_DEV_MAX -1 ,GFP_KERNEL ); +if (ret <0 ) { +ida_free (&rpmsg_ept_ida, dev->id); +return ret; + } + + dev->devt =MKDEV (MAJOR (rpmsg_major), ret); + ret =cdev_device_add (&eptdev->cdev, &eptdev->dev); -if (ret) - goto free_ept_ida; +if (ret) { +ida_free (&rpmsg_ept_ida, dev->id); +ida_free (&rpmsg_minor_ida,MINOR (dev->devt)); +return ret; + } }/* 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: -dev_err (&eptdev->dev,"failed to add %s\n" , eptdev->chinfo.name); -kfree (eptdev); - -return ret; +return 0 ; }static int rpmsg_chrdev_eptdev_add (struct rpmsg_eptdev *eptdev,struct rpmsg_channel_info chinfo)@@ -509,12 +499,17 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent struct rpmsg_channel_info chinfo) {struct rpmsg_eptdev *eptdev;
drivers/rpmsg/rpmsg_char.c int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent · line 510
Quoted 10 lines
drivers/rpmsg/rpmsg_char.c +4
-1+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) +kfree (eptdev);
Previous patch.
Quoted 6 lines
drivers/rpmsg/rpmsg_char.c +2
-0+ +return ret; }EXPORT_SYMBOL (rpmsg_chrdev_eptdev_create);
drivers/rpmsg/rpmsg_char.c int rpmsg_anonymous_eptdev_create(struct rpmsg_device *rpdev, struct device *par · line 548
Quoted 9 lines
drivers/rpmsg/rpmsg_char.c +6
-0@@ -545,6 +540,12 @@ 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); +/* +* Avoid put_device() or WARN() will be triggered due to absence of +* device::release(), refer to device_release(). +*/ +kfree (eptdev);
Previous patch.
Quoted 12 lines
drivers/rpmsg/rpmsg_char.c +1
-0return ret; }@@ -572,6 +573,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;
drivers/rpmsg/rpmsg_char.c static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) · line 597
Quoted 7 lines
drivers/rpmsg/rpmsg_char.c +3
-1@@ -590,7 +592,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) +kfree (eptdev);
Previous patch.
Quoted 8 lines
drivers/rpmsg/rpmsg_char.c +2
-0+ +return ret; }static void rpmsg_chrdev_remove (struct rpmsg_device *rpdev)Signature · 1 line
2.25.1