Re: [PATCH] block: don't embed integrity_kobj into gendisk

From: Thomas Weißschuh
Date: Thu Mar 09 2023 - 16:23:37 EST


+Cc Andy

Answer below.

On Thu, Mar 09, 2023 at 10:05:32PM +0100, Mirsad Goran Todorovac wrote:
> On 09. 03. 2023. 21:23, Thomas Weißschuh wrote:
> > A struct kobject is only supposed to be embedded into objects which
> > lifetime it will manage.
> > Objects of type struct gendisk however are refcounted by their part0
> > block_device.
> > Therefore the integrity_kobj should not be embedded but split into its
> > own independently managed object.
> >
> > This will also provide a proper .release function for the ktype which
> > avoid warnings like the following:
> > kobject: 'integrity' (000000005198bea8): does not have a release() function, it is broken and must be fixed.
> >
> > While modifying blk_integrity_del() also drop the explicit call to
> > kobject_uevent(KOBJ_REMOVE) as the driver care will do this
> > automatically.
> >
> > Reported-by: Mirsad Todorovac <mirsad.todorovac@xxxxxxxxxxxx>
> > Link: https://lore.kernel.org/lkml/60b2b66c-22c9-1d38-ed1c-7b7d95e32720@xxxxxxxxxxxx/
> > Signed-off-by: Thomas Weißschuh <linux@xxxxxxxxxxxxxx>
> > ---
> > block/blk-integrity.c | 32 ++++++++++++++++++++++++--------
> > include/linux/blkdev.h | 2 +-
> > 2 files changed, 25 insertions(+), 9 deletions(-)
> >
> > diff --git a/block/blk-integrity.c b/block/blk-integrity.c
> > index 8f01d786f5cb..40adf33f5535 100644
> > --- a/block/blk-integrity.c
> > +++ b/block/blk-integrity.c
> > @@ -218,10 +218,15 @@ struct integrity_sysfs_entry {
> > ssize_t (*store)(struct blk_integrity *, const char *, size_t);
> > };
> >
> > +static inline struct gendisk *integrity_kobj_to_disk(struct kobject *kobj)
> > +{
> > + return dev_to_disk(kobj_to_dev(kobj->parent));
> > +}
> > +
> > static ssize_t integrity_attr_show(struct kobject *kobj, struct attribute *attr,
> > char *page)
> > {
> > - struct gendisk *disk = container_of(kobj, struct gendisk, integrity_kobj);
> > + struct gendisk *disk = integrity_kobj_to_disk(kobj);
> > struct blk_integrity *bi = &disk->queue->integrity;
> > struct integrity_sysfs_entry *entry =
> > container_of(attr, struct integrity_sysfs_entry, attr);
> > @@ -233,7 +238,7 @@ static ssize_t integrity_attr_store(struct kobject *kobj,
> > struct attribute *attr, const char *page,
> > size_t count)
> > {
> > - struct gendisk *disk = container_of(kobj, struct gendisk, integrity_kobj);
> > + struct gendisk *disk = integrity_kobj_to_disk(kobj);
> > struct blk_integrity *bi = &disk->queue->integrity;
> > struct integrity_sysfs_entry *entry =
> > container_of(attr, struct integrity_sysfs_entry, attr);
> > @@ -356,9 +361,15 @@ static const struct sysfs_ops integrity_ops = {
> > .store = &integrity_attr_store,
> > };
> >
> > +static void integrity_release(struct kobject *kobj)
> > +{
> > + kfree(kobj);
> > +}
> > +
> > static const struct kobj_type integrity_ktype = {
> > .default_groups = integrity_groups,
> > .sysfs_ops = &integrity_ops,
> > + .release = integrity_release,
> > };
> >
> > static blk_status_t blk_integrity_nop_fn(struct blk_integrity_iter *iter)
> > @@ -442,16 +453,21 @@ int blk_integrity_add(struct gendisk *disk)
> > {
> > int ret;
> >
> > - ret = kobject_init_and_add(&disk->integrity_kobj, &integrity_ktype,
> > + disk->integrity_kobj = kmalloc(sizeof(*disk->integrity_kobj), GFP_KERNEL);
> > + if (!disk->integrity_kobj)
> > + return -ENOMEM;
> > +
> > + ret = kobject_init_and_add(disk->integrity_kobj, &integrity_ktype,
> > &disk_to_dev(disk)->kobj, "%s", "integrity");
> > - if (!ret)
> > - kobject_uevent(&disk->integrity_kobj, KOBJ_ADD);
> > + if (ret)
> > + kobject_put(disk->integrity_kobj);
> > + else
> > + kobject_uevent(disk->integrity_kobj, KOBJ_ADD);
> > +
> > return ret;
> > }
> >
> > void blk_integrity_del(struct gendisk *disk)
> > {
> > - kobject_uevent(&disk->integrity_kobj, KOBJ_REMOVE);
> > - kobject_del(&disk->integrity_kobj);
> > - kobject_put(&disk->integrity_kobj);
> > + kobject_put(disk->integrity_kobj);
> > }
> > diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> > index d1aee08f8c18..2fbfb3277a2b 100644
> > --- a/include/linux/blkdev.h
> > +++ b/include/linux/blkdev.h
> > @@ -164,7 +164,7 @@ struct gendisk {
> > atomic_t sync_io; /* RAID */
> > struct disk_events *ev;
> > #ifdef CONFIG_BLK_DEV_INTEGRITY
> > - struct kobject integrity_kobj;
> > + struct kobject *integrity_kobj;
> > #endif /* CONFIG_BLK_DEV_INTEGRITY */
> >
> > #ifdef CONFIG_BLK_DEV_ZONED
> >
> > ---
> > base-commit: 44889ba56cbb3d51154660ccd15818bc77276696
> > change-id: 20230309-kobj_release-gendisk_integrity-e26c0bc126aa
> >
> > Best regards,
>
> Hello Thomas,
>
> Thank you for Cc:-ing me on this.
>
> The patch applied successfully against 6.3-rc1 commit 44889ba56cbb Merge tag 'net-6.3-rc2'
> and I'm just in a build with
>
> CONFIG_DEBUG_KOBJECT=y
> CONFIG_DEBUG_KOBJECT_RELEASE=y
>
> However, I can see remotely whether the kernel is operating, but not these special messages
> that are emitted past rsyslog's end of lifetime. It looks like it will require physical
> access to the box tomorrow morning, Lord willing.
>
> I hate to object to your solution, still, I fail to see how it releases
>
> security/integrity/iint.c:
> 175 static int __init integrity_iintcache_init(void)
> 176 {
> 177 iint_cache =
> 178 kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
> 179 0, SLAB_PANIC, init_once);
> 180 return 0;
> 181 }
> 182 DEFINE_LSM(integrity) = {
> 183 .name = "integrity",
> 184 .init = integrity_iintcache_init,
> 185 };
>
> It is declared here:
>
> 26 static struct kmem_cache *iint_cache __read_mostly;
>
> so it ought to be kmem_cache_destroy()-ed from this module, unless there is something that
> is not obvious nor transparent.
>
> Can you help me see what I'm doing wrong?

I think this has nothing to do with security/integrity/iint.c.

Instead the problem are these snippets from block/blk-integrity.c:

/* line 359: kobj_type without .release */
static const struct kobj_type integrity_ktype = {
.default_groups = integrity_groups,
.sysfs_ops = &integrity_ops,
};

/* line 445: registration of kobject "integrity" with that type */
ret = kobject_init_and_add(&disk->integrity_kobj, &integrity_ktype,
&disk_to_dev(disk)->kobj, "%s", "integrity");

These kobjects represent the directories /sys/block/*/integrity/.

The patch below can be used to easily diagnose this during kobject
initialization instead of cleanup, which seems much more useful.

It probably makes sense to move the
pr_debug("does not have a release() function");
to kobject_init() in general.

diff --git a/lib/kobject.c b/lib/kobject.c
index 6e2f0bee3560..2708f8344e9b 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -341,6 +341,8 @@ void kobject_init(struct kobject *kobj, const struct kobj_type *ktype)
dump_stack();
}

+ WARN(!ktype->release, "KOBJECT %p, %p: no release function\n", kobj, ktype);
+
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;