lkml.org 
[lkml]   [2022]   [Jan]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [RFC char-misc-next 1/2] cdev: Add private pointer to struct cdev
On Sun, Jan 02, 2022 at 09:01:39PM -0800, Daniel Xu wrote:
> struct cdev is a kobject managed struct, meaning kobject is ultimately
> responsible for deciding when the object is freed. Because kobject uses
> reference counts, it also means a cdev object isn't guaranteed to be
> cleaned up with a call to cdev_del() -- the cleanup may occur later.
>
> Unfortunately, this can result in subtle use-after-free bugs when struct
> cdev is embedded in another struct, and the larger struct is freed
> immediately after cdev_del(). For example:
>
> struct contains_cdev {
> struct cdev cdev;
> }
>
> void init(struct contains_cdev *cc) {
> cdev_init(&cc->cdev);
> }
>
> void cleanup(struct contains_cdev *cc) {
> cdev_del(&cc->cdev);
> kfree(cc);
> }
>
> This kind of code can reliably trigger a KASAN splat with
> CONFIG_KASAN=y and CONFIG_DEBUG_KOBJECT_RELEASE=y.
>
> A fairly palatable workaround is replacing cdev_init() with cdev_alloc()
> and storing a pointer instead. For example, this is totally fine:
>
> struct contains_cdev_ptr {
> struct cdev *cdev;
> }
>
> int init(struct contains_cdev_ptr *cc) {
> cc->cdev = cdev_alloc();
> if (!cc->cdev) {
> return -ENOMEM;
> }
>
> return 0;
> }
>
> void cleanup(struct contains_cdev_ptr *cc) {
> cdev_del(cc->cdev);
> kfree(cc);
> }
>
> The only downside from this workaround (other than the extra allocation)
> is that container_of() upcasts no longer work. This is quite unfortunate
> for any code that implements struct file_operations and wants to store
> extra data with a struct cdev. With cdev_alloc() pointer, it's no longer
> possible to do something like:
>
> struct contains_cdev *cc = container_of(inode->i_cdev,
> struct contains_cdev,
> cdev);
>
> Thus, I propose to add a void *private field to struct cdev so that
> callers can store a pointer to the containing struct instead of using
> container_of().
>
> Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
> ---
> include/linux/cdev.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/include/linux/cdev.h b/include/linux/cdev.h
> index 0e8cd6293deb..0e674e900512 100644
> --- a/include/linux/cdev.h
> +++ b/include/linux/cdev.h
> @@ -18,6 +18,7 @@ struct cdev {
> struct list_head list;
> dev_t dev;
> unsigned int count;
> + void *private;

I understand your request here, but this is not how to use a cdev. It
should be embedded in a larger structure, and then you can always "cast
out" to get the real structure here. If you can't do that, then this
private pointer doesn't make much sense at all as it too would be
invalid.

Ideally the kobject in the cdev structure would not be used by things
"outside" of the cdev core, but that ship seems long gone. So just rely
on the structure that this kobject protects, and you should be fine.

thanks,

greg k-h

\
 
 \ /
  Last update: 2022-01-03 15:04    [W:0.071 / U:0.028 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site