Commit 8446a4de authored by David Laight's avatar David Laight Committed by Vlastimil Babka
Browse files

slab: kmalloc_size_roundup() must not return 0 for non-zero size



The typical use of kmalloc_size_roundup() is:

	ptr = kmalloc(sz = kmalloc_size_roundup(size), ...);
	if (!ptr) return -ENOMEM.

This means it is vitally important that the returned value isn't less
than the argument even if the argument is insane.
In particular if kmalloc_slab() fails or the value is above
(MAX_ULONG - PAGE_SIZE) zero is returned and kmalloc() will return
its single zero-length buffer ZERO_SIZE_PTR.

Fix this by returning the input size if the size exceeds
KMALLOC_MAX_SIZE. kmalloc() will then return NULL as the size really is
too big.

kmalloc_slab() should not normally return NULL, unless called too early.
Again, returning zero is not the correct action as it can be in some
usage scenarios stored to a variable and only later cause kmalloc()
return ZERO_SIZE_PTR and subsequent crashes on access. Instead we can
simply stop checking the kmalloc_slab() result completely, as calling
kmalloc_size_roundup() too early would then result in an immediate crash
during boot and the developer noticing an issue in their code.

[vbabka@suse.cz: remove kmalloc_slab() result check, tweak comments and
 commit log]
Fixes: 05a94065 ("slab: Introduce kmalloc_size_roundup()")
Signed-off-by: default avatarDavid Laight <david.laight@aculab.com>
Signed-off-by: default avatarVlastimil Babka <vbabka@suse.cz>
parent 46a9ea66
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -745,24 +745,24 @@ struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags, unsigned long caller)

size_t kmalloc_size_roundup(size_t size)
{
	struct kmem_cache *c;
	if (size && size <= KMALLOC_MAX_CACHE_SIZE) {
		/*
		 * The flags don't matter since size_index is common to all.
		 * Neither does the caller for just getting ->object_size.
		 */
		return kmalloc_slab(size, GFP_KERNEL, 0)->object_size;
	}

	/* Short-circuit the 0 size case. */
	if (unlikely(size == 0))
		return 0;
	/* Short-circuit saturated "too-large" case. */
	if (unlikely(size == SIZE_MAX))
		return SIZE_MAX;
	/* Above the smaller buckets, size is a multiple of page size. */
	if (size > KMALLOC_MAX_CACHE_SIZE)
	if (size && size <= KMALLOC_MAX_SIZE)
		return PAGE_SIZE << get_order(size);

	/*
	 * The flags don't matter since size_index is common to all.
	 * Neither does the caller for just getting ->object_size.
	 * Return 'size' for 0 - kmalloc() returns ZERO_SIZE_PTR
	 * and very large size - kmalloc() may fail.
	 */
	c = kmalloc_slab(size, GFP_KERNEL, 0);
	return c ? c->object_size : 0;
	return size;

}
EXPORT_SYMBOL(kmalloc_size_roundup);