mm: krealloc: consider spare memory for __GFP_ZERO
Subsystems (2)
PATCH v2 1/2 mm: krealloc: consider spare memory for __GFP_ZERO
As long as krealloc() is called with __GFP_ZERO consistently, starting with the initial memory allocation, __GFP_ZERO should be fully honored.
However, if for an existing allocation krealloc() is called with a decreased size, it is not ensured that the spare portion the allocation is zeroed. Thus, if krealloc() is subsequently called with a larger size again, __GFP_ZERO can't be fully honored, since we don't know the previous size, but only the bucket size.
Example:
buf = kzalloc(64, GFP_KERNEL); memset(buf, 0xff, 64); buf = krealloc(buf, 48, GFP_KERNEL | __GFP_ZERO); /* After this call the last 16 bytes are still 0xff. */ buf = krealloc(buf, 64, GFP_KERNEL | __GFP_ZERO);
Fix this, by explicitly setting spare memory to zero, when shrinking an allocation with __GFP_ZERO flag set or init_on_alloc enabled.
--- mm/slab_common.c | 7 +++++++ 1 file changed, 7 insertions(+)
mm/slab_common.c +7 -0
@@ -1273,6 +1273,13 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)/* If the object still fits, repoison it precisely. */ if (ks >= new_size) { +/* Zero out spare memory. */ +if (want_init_on_alloc (flags)) { +kasan_disable_current (); +memset ((void *)p + new_size,0 , ks - new_size); +kasan_enable_current (); + } + p =kasan_krealloc ((void *)p, new_size, flags);return (void *)p; }
base-commit: b8dbbb7fe1db26c450a9d2c3302013154b3431df
Signature · 1 line
2.46.0
PATCH v2 2/2 mm: krealloc: clarify valid usage of __GFP_ZERO
Properly document that if __GFP_ZERO logic is requested, callers must ensure that, starting with the initial memory allocation, every subsequent call to this API for the same memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that __GFP_ZERO is not fully honored by this API.
---
Changes in v2:
- additionally to what we require callers to do, briefly explain what the implementation does (Andrew)
--- include/linux/slab.h | 10 ++++++++++ mm/slab_common.c | 20 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-)
include/linux/slab.h +10 -0
@@ -733,6 +733,16 @@ static inline __alloc_size(1, 2) void *kmalloc_array_noprof(size_t n, size_t siz* @new_n: new number of elements to alloc * @new_size: new size of a single member of the array * @flags: the type of memory to allocate (see kmalloc) +* +* If __GFP_ZERO logic is requested, callers must ensure that, starting with the +* initial memory allocation, every subsequent call to this API for the same +* memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that +* __GFP_ZERO is not fully honored by this API. +* +* See krealloc_noprof() for further details. +* +* In any case, the contents of the object pointed to are preserved up to the +* lesser of the new and old sizes. */ static inline __realloc_size(2, 3) void * __must_check krealloc_array_noprof(void *p, size_t new_n,
mm/slab_common.c +18 -2
@@ -1301,11 +1301,27 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)* @new_size: how many bytes of memory are required. * @flags: the type of memory to allocate. * -* The contents of the object pointed to are preserved up to the -* lesser of the new and old sizes (__GFP_ZERO flag is effectively ignored). * If @p is %NULL, krealloc() behaves exactly like kmalloc(). If @new_size * is 0 and @p is not a %NULL pointer, the object pointed to is freed. * +* If __GFP_ZERO logic is requested, callers must ensure that, starting with the +* initial memory allocation, every subsequent call to this API for the same +* memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that +* __GFP_ZERO is not fully honored by this API. +* +* This is the case, since krealloc() only knows about the bucket size of an +* allocation (but not the exact size it was allocated with) and hence +* implements the following semantics for shrinking and growing buffers with +* __GFP_ZERO. +* +* new bucket +* 0 size size +* |--------|----------------| +* | keep | zero | +* +* In any case, the contents of the object pointed to are preserved up to the +* lesser of the new and old sizes. +* * Return: pointer to the allocated memory or %NULL in case of error */ void *krealloc_noprof (const void *p,size_t new_size,gfp_t flags)
Signature · 1 line
2.46.0
Re: [PATCH v2 2/2] mm: krealloc: clarify valid usage of __GFP_ZERO
Quoted 7 lines · Danilo Krummrich
Properly document that if
__GFP_ZEROlogic is requested, callers must ensure that, starting with the initial memory allocation, every subsequent call to this API for the same memory allocation is flagged with__GFP_ZERO. Otherwise, it is possible that__GFP_ZEROis not fully honored by this API.Signed-off-by Danilo Krummrich <[email protected]>
Quoted 19 lines · Danilo Krummrich
As long as
krealloc()is called with__GFP_ZEROconsistently, starting with the initial memory allocation,__GFP_ZEROshould be fully honored.However, if for an existing allocation
krealloc()is called with a decreased size, it is not ensured that the spare portion the allocation is zeroed. Thus, ifkrealloc()is subsequently called with a larger size again,__GFP_ZEROcan't be fully honored, since we don't know the previous size, but only the bucket size.Example:
buf = kzalloc(64, GFP_KERNEL); memset(buf, 0xff, 64); buf = krealloc(buf, 48, GFP_KERNEL | __GFP_ZERO); /* After this call the last 16 bytes are still 0xff. */ buf = krealloc(buf, 64, GFP_KERNEL | __GFP_ZERO);
Quoted 5 lines
Fix this, by explicitly setting spare memory to zero, when shrinking an allocation with
__GFP_ZEROflag set orinit_on_allocenabled.Acked-by Vlastimil Babka <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
On 8/13/24 12:34 AM, Danilo Krummrich wrote:
Gentle reminder on this one.
Quoted 19 lines
As long as
krealloc()is called with__GFP_ZEROconsistently, starting with the initial memory allocation,__GFP_ZEROshould be fully honored.However, if for an existing allocation
krealloc()is called with a decreased size, it is not ensured that the spare portion the allocation is zeroed. Thus, ifkrealloc()is subsequently called with a larger size again,__GFP_ZEROcan't be fully honored, since we don't know the previous size, but only the bucket size.Example:
buf = kzalloc(64, GFP_KERNEL); memset(buf, 0xff, 64); buf = krealloc(buf, 48, GFP_KERNEL | __GFP_ZERO); /* After this call the last 16 bytes are still 0xff. */ buf = krealloc(buf, 64, GFP_KERNEL | __GFP_ZERO);
Quoted 5 lines
Fix this, by explicitly setting spare memory to zero, when shrinking an allocation with
__GFP_ZEROflag set orinit_on_allocenabled.Acked-by Vlastimil Babka <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
I think I forgot to add:
Not sure if there is a useful commit for "Fixes" though. AFAICT, this has been broken since forever.
Quoted 24 lines
--- mm/slab_common.c | 7 +++++++ 1 file changed, 7 insertions(+)mm/slab_common.c +7
-0@@ -1273,6 +1273,13 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)/* If the object still fits, repoison it precisely. */ if (ks >= new_size) { +/* Zero out spare memory. */ +if (want_init_on_alloc (flags)) { +kasan_disable_current (); +memset ((void *)p + new_size,0 , ks - new_size); +kasan_enable_current (); + } + p =kasan_krealloc ((void *)p, new_size, flags);return (void *)p; }base-commit:
b8dbbb7fe1db26c450a9d2c3302013154b3431df