Commit 346907ce authored by Vlastimil Babka's avatar Vlastimil Babka
Browse files

mm, slab: ignore hardened usercopy parameters when disabled



With CONFIG_HARDENED_USERCOPY not enabled, there are no
__check_heap_object() checks happening that would use the struct
kmem_cache useroffset and usersize fields. Yet the fields are still
initialized, preventing merging of otherwise compatible caches.

Also the fields contribute to struct kmem_cache size unnecessarily when
unused. Thus #ifdef them out completely when CONFIG_HARDENED_USERCOPY is
disabled. In kmem_dump_obj() print object_size instead of usersize, as
that's actually the intention.

In a quick virtme boot test, this has reduced the number of caches in
/proc/slabinfo from 131 to 111.

Cc: Kees Cook <keescook@chromium.org>
Signed-off-by: default avatarVlastimil Babka <vbabka@suse.cz>
Acked-by: default avatarRoman Gushchin <roman.gushchin@linux.dev>
Acked-by: default avatarMike Rapoport <rppt@linux.ibm.com>
Reviewed-by: default avatarChristoph Lameter <cl@linux.com>
parent c18c20f1
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -80,8 +80,10 @@ struct kmem_cache {
	unsigned int *random_seq;
#endif

#ifdef CONFIG_HARDENED_USERCOPY
	unsigned int useroffset;	/* Usercopy region offset */
	unsigned int usersize;		/* Usercopy region size */
#endif

	struct kmem_cache_node *node[MAX_NUMNODES];
};
+2 −0
Original line number Diff line number Diff line
@@ -136,8 +136,10 @@ struct kmem_cache {
	struct kasan_cache kasan_info;
#endif

#ifdef CONFIG_HARDENED_USERCOPY
	unsigned int useroffset;	/* Usercopy region offset */
	unsigned int usersize;		/* Usercopy region size */
#endif

	struct kmem_cache_node *node[MAX_NUMNODES];
};
+0 −2
Original line number Diff line number Diff line
@@ -207,8 +207,6 @@ struct kmem_cache {
	unsigned int size;	/* The aligned/padded/added on size  */
	unsigned int align;	/* Alignment as calculated */
	slab_flags_t flags;	/* Active flags on the slab */
	unsigned int useroffset;/* Usercopy region offset */
	unsigned int usersize;	/* Usercopy region size */
	const char *name;	/* Slab name for sysfs */
	int refcount;		/* Use counter */
	void (*ctor)(void *);	/* Called on object slot creation */
+10 −3
Original line number Diff line number Diff line
@@ -143,8 +143,10 @@ int slab_unmergeable(struct kmem_cache *s)
	if (s->ctor)
		return 1;

#ifdef CONFIG_HARDENED_USERCOPY
	if (s->usersize)
		return 1;
#endif

	/*
	 * We may have set a slab to be unmergeable during bootstrap.
@@ -223,8 +225,10 @@ static struct kmem_cache *create_cache(const char *name,
	s->size = s->object_size = object_size;
	s->align = align;
	s->ctor = ctor;
#ifdef CONFIG_HARDENED_USERCOPY
	s->useroffset = useroffset;
	s->usersize = usersize;
#endif

	err = __kmem_cache_create(s, flags);
	if (err)
@@ -317,7 +321,8 @@ kmem_cache_create_usercopy(const char *name,
	flags &= CACHE_CREATE_MASK;

	/* Fail closed on bad usersize of useroffset values. */
	if (WARN_ON(!usersize && useroffset) ||
	if (!IS_ENABLED(CONFIG_HARDENED_USERCOPY) ||
	    WARN_ON(!usersize && useroffset) ||
	    WARN_ON(size < usersize || size - usersize < useroffset))
		usersize = useroffset = 0;

@@ -595,8 +600,8 @@ void kmem_dump_obj(void *object)
		ptroffset = ((char *)object - (char *)kp.kp_objp) - kp.kp_data_offset;
		pr_cont(" pointer offset %lu", ptroffset);
	}
	if (kp.kp_slab_cache && kp.kp_slab_cache->usersize)
		pr_cont(" size %u", kp.kp_slab_cache->usersize);
	if (kp.kp_slab_cache && kp.kp_slab_cache->object_size)
		pr_cont(" size %u", kp.kp_slab_cache->object_size);
	if (kp.kp_ret)
		pr_cont(" allocated at %pS\n", kp.kp_ret);
	else
@@ -640,8 +645,10 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name,
		align = max(align, size);
	s->align = calculate_alignment(flags, align, size);

#ifdef CONFIG_HARDENED_USERCOPY
	s->useroffset = useroffset;
	s->usersize = usersize;
#endif

	err = __kmem_cache_create(s, flags);

+4 −0
Original line number Diff line number Diff line
@@ -5502,11 +5502,13 @@ static ssize_t cache_dma_show(struct kmem_cache *s, char *buf)
SLAB_ATTR_RO(cache_dma);
#endif

#ifdef CONFIG_HARDENED_USERCOPY
static ssize_t usersize_show(struct kmem_cache *s, char *buf)
{
	return sysfs_emit(buf, "%u\n", s->usersize);
}
SLAB_ATTR_RO(usersize);
#endif

static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf)
{
@@ -5803,7 +5805,9 @@ static struct attribute *slab_attrs[] = {
#ifdef CONFIG_FAILSLAB
	&failslab_attr.attr,
#endif
#ifdef CONFIG_HARDENED_USERCOPY
	&usersize_attr.attr,
#endif
#ifdef CONFIG_KFENCE
	&skip_kfence_attr.attr,
#endif