integer overflows in kernel/relay.c
Subsystems (5)
My static checker is warning about integer overflows in kernel/relay.c
relay_create_buf()
170
171 buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^This can only overflow on 32bit systems.
172 if (!buf->padding)
173 goto free_buf;
174
relay_open()
582 chan->version = RELAYFS_CHANNEL_VERSION;
583 chan->n_subbufs = n_subbufs;
584 chan->subbuf_size = subbuf_size;
585 chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs);
^^^^^^^^^^^^^^^^^^^^^^^ 586 chan->parent = parent;
These come from the user in blk_trace_setup() and they aren't capped. I'm not sure what the maximum size to use is.
regards,
dan carpenter
Jens Axboe
maintainerQuoted 7 lines · Dan Carpenter
My static checker is warning about integer overflows in
kernel/relay.c
relay_create_buf()
170171 buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^This can only overflow on 32bit systems.
Correct
Quoted 10 lines
172 if (!buf->padding) 173 goto free_buf; 174
relay_open()582 chan->version = RELAYFS_CHANNEL_VERSION; 583 chan->n_subbufs = n_subbufs; 584 chan->subbuf_size = subbuf_size; 585 chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); ^^^^^^^^^^^^^^^^^^^^^^^
Quoted 4 lines
586
chan->parent= parent;These come from the user in
blk_trace_setup()and they aren't capped. I'm not sure what the maximum size to use is.
They are both u32 types, so can overflow on 32-bit as well. By default, blktrace is using 4 for n_subbufs and 512k for subbuf_size, but they are configurable. As a fix, I would suggest just checking if the products overflow, and if they do, return an error. That's better than imposing some hard limits. In reality, only a malicious users would trigger these.
Signature · 1 line
Jens Axboe
Andrew Morton
maintainerQuoted 10 lines · On Wed, 08 Feb 2012 09:34:16 +0100 Jens Axboe <[email protected]> wrote:
Quoted 7 lines · Dan Carpenter
My static checker is warning about integer overflows in
kernel/relay.c
relay_create_buf()
170171 buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^This can only overflow on 32bit systems.
Correct
This is a relatively common problem. Converting a kzalloc() call to kcalloc() fixes it. But we do not have a non-zeroing version of kcalloc().
Please, someone complete this patch and send it to me!
include/linux/slab.h renamed from include/linux/slab.h~a +7 -2
@@ -240,11 +240,16 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *wtf_do_i_call_this (size_t n,size_t size,gfp_t flags) {if (size !=0 && n >ULONG_MAX / size)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +return __kmalloc (n * size, flags); +} + +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +return wtf_do_i_call_this (n, size, flags | __GFP_ZERO); }#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )
_
RFC PATCH 1/1 slab: introduce knalloc/kxnalloc
This patch introduces knalloc/kxnalloc wrappers that perform integer overflow checks without zeroing the memory.
knalloc(n, size, flags) is the non-zeroing version of kcalloc(), which allocates n * size bytes.
kxnalloc(xsize, n, size, flags) allocates xsize + n * size bytes. It is useful to allocate a structure ending with a zero-length array, which is a commonly used pattern. For example, in posix_acl_alloc() to allocate a posix_acl object one could call
kxnalloc(sizeof(struct posix_acl),
count, sizeof(struct posix_acl_entry), flags);to avoid overflowing the allocation size.
---
I coined the name knalloc() for Andrew's wtf_do_i_call_this() in the previous email. A better name is welcome.
I additionally added kxnalloc() since it's a common idiom.
--- include/linux/slab.h | 31 +++++++++++++++++++++++++++---- 1 files changed, 27 insertions(+), 4 deletions(-)
include/linux/slab.h +27 -4
@@ -190,7 +190,8 @@ size_t ksize(const void *);#endif /** -* kcalloc - allocate memory for an array. The memory is set to zero. +* kxnalloc - allocate memory for an array with an extra header. +* @xsize: extra header size. * @n: number of elements. * @size: element size. * @flags: the type of memory to allocate.
@@ -240,11 +241,33 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kxnalloc (size_t xsize,size_t n,size_t size,gfp_t flags) { -if (size !=0 && n >ULONG_MAX / size) +if (size !=0 && n > (ULONG_MAX - xsize) / size)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +return __kmalloc (xsize + n * size, flags); +} + +/** +* knalloc - allocate memory for an array. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *knalloc (size_t n,size_t size,gfp_t flags) +{ +return kxnalloc (0 , n, size, flags); +} + +/** +* kcalloc - allocate memory for an array. The memory is set to zero. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +return knalloc (n, size, flags | __GFP_ZERO); }#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )
Signature · 1 line
1.7.5.4
Re: [PATCH RFC] slab: introduce knalloc/kxnalloc
Quoted 11 lines · Xi Wang
This patch introduces knalloc/kxnalloc wrappers that perform integer overflow checks without zeroing the memory.
knalloc(n, size, flags)is the non-zeroing version ofkcalloc(), which allocates n * size bytes.
kxnalloc(xsize, n, size, flags)allocates xsize + n * size bytes. It is useful to allocate a structure ending with a zero-length array, which is a commonly used pattern. For example, inposix_acl_alloc()to allocate aposix_aclobject one could call
Quoted 7 lines
kxnalloc(sizeof(struct
posix_acl),
count,sizeof(struct posix_acl_entry), flags);to avoid overflowing the allocation size.
Suggested-by Andrew Morton <[email protected]>Signed-off-by Xi Wang <[email protected]>
Are there really enough potential users to justify adding both?
Quoted 60 lines
---
I coined the nameknalloc()for Andrew'swtf_do_i_call_this()in the previous email. A better name is welcome.I additionally added
kxnalloc()since it's a common idiom.
---
include/linux/slab.h | 31 +++++++++++++++++++++++++++----1 files changed, 27 insertions(+), 4 deletions(-)include/linux/slab.h +26
-4@@ -190,7 +190,8 @@ size_t ksize(const void *); #endif /** -* kcalloc - allocate memory for an array. The memory is set to zero. +* kxnalloc - allocate memory for an array with an extra header. +* @xsize: extra header size. * @n: number of elements. * @size: element size. * @flags: the type of memory to allocate. @@ -240,11 +241,33 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kxnalloc (size_t xsize,size_t n,size_t size,gfp_t flags) { - if (size != 0 && n > ULONG_MAX / size) + if (size != 0 && n > (ULONG_MAX - xsize) / size) return NULL; - return __kmalloc(n * size, flags | __GFP_ZERO); + return __kmalloc(xsize + n * size, flags); +} + +/** +* knalloc - allocate memory for an array. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *knalloc (size_t n,size_t size,gfp_t flags) +{ + return kxnalloc(0, n, size, flags); +} +/** +* kcalloc - allocate memory for an array. The memory is set to zero. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ + return knalloc(n, size, flags | __GFP_ZERO); }#if !defined(CONFIG_NUMA) && !defined(CONFIG_SLOB)
Jens Axboe
maintainerRe: [PATCH RFC] slab: introduce knalloc/kxnalloc
Quoted 15 lines · Pekka Enberg
Quoted 14 lines · Xi Wang
This patch introduces knalloc/kxnalloc wrappers that perform integer overflow checks without zeroing the memory.
knalloc(n, size, flags)is the non-zeroing version ofkcalloc(), which allocates n * size bytes.
kxnalloc(xsize, n, size, flags)allocates xsize + n * size bytes. It is useful to allocate a structure ending with a zero-length array, which is a commonly used pattern. For example, inposix_acl_alloc()to allocate aposix_aclobject one could callkxnalloc(sizeof(struct posix_acl), count, sizeof(struct posix_acl_entry), flags);
Quoted 6 lines
Quoted 4 lines
to avoid overflowing the allocation size.
Suggested-by Andrew Morton <[email protected]>Signed-off-by Xi Wang <[email protected]>Are there really enough potential users to justify adding both?
Agree, lets not overdesign. kmalloc_array() sounds good to me, fwiw.
Signature · 1 line
Jens Axboe
Re: [PATCH RFC] slab: introduce knalloc/kxnalloc
Quoted 1 line · Jens Axboe
Agree, lets not overdesign.
kmalloc_array()sounds good to me, fwiw.
I like kmalloc_array. "knalloc" can be easily confused with kmalloc.
- xi
RFC PATCH v2 1/1 slab: introduce kmalloc_array
This patch introduces a kmalloc_array() wrapper that performs integer overflow checking without zeroing the memory.
---
Let's take "kxnalloc" off for now and keep the patch simple.
--- include/linux/slab.h | 17 ++++++++++++++--- 1 files changed, 14 insertions(+), 3 deletions(-)
include/linux/slab.h +14 -3
@@ -190,7 +190,7 @@ size_t ksize(const void *);#endif /** -* kcalloc - allocate memory for an array. The memory is set to zero. +* kmalloc_array - allocate memory for an array. * @n: number of elements. * @size: element size. * @flags: the type of memory to allocate.
@@ -240,11 +240,22 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags) {if (size !=0 && n >ULONG_MAX / size)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +return __kmalloc (n * size, flags); +} + +/** +* kcalloc - allocate memory for an array. The memory is set to zero. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +return kmalloc_array (n, size, flags | __GFP_ZERO); }#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )
Signature · 1 line
1.7.5.4
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 6 lines · Xi Wang
This patch introduces a
kmalloc_array()wrapper that performs integer overflow checking without zeroing the memory.Suggested-by Andrew Morton <[email protected]>Suggested-by Jens Axboe <[email protected]>Signed-off-by Xi Wang <[email protected]>
assuming there's at least one new user or one existing user that can be modified to use the new interface and this won't just sit around not being used.
Andrew Morton
maintainerRe: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 9 lines · On Thu, 9 Feb 2012 14:42:49 -0800 (PST) David Rientjes <[email protected]>…
Quoted 6 lines · Xi Wang
This patch introduces a
kmalloc_array()wrapper that performs integer overflow checking without zeroing the memory.Suggested-by Andrew Morton <[email protected]>Suggested-by Jens Axboe <[email protected]>Signed-off-by Xi Wang <[email protected]>
Quoted 5 lines
Acked-by David Rientjes <[email protected]>assuming there's at least one new user or one existing user that can be modified to use the new interface and this won't just sit around not being used.
The need is there - I've whined about the absence of kmalloc_array() maybe 3 times in the past year.
Often kcalloc() is suitable - quite a lot of arrays which are sized based on a userspace-provided dimension are also zeroed out. For some reason.
Re: [PATCH RFC v2] slab: introduce kmalloc_array
In reply to Xi WangQuoted 41 lines · Xi Wang
This patch introduces a
kmalloc_array()wrapper that performs integer overflow checking without zeroing the memory.Suggested-by Andrew Morton <[email protected]>Suggested-by Jens Axboe <[email protected]>Signed-off-by Xi Wang <[email protected]>---
Let's take "kxnalloc" off for now and keep the patch simple.--- include/linux/slab.h | 17 ++++++++++++++--- 1 files changed, 14 insertions(+), 3 deletions(-)include/linux/slab.h +7
-3@@ -190,7 +190,7 @@ size_t ksize(const void *);#endif /** -* kcalloc - allocate memory for an array. The memory is set to zero. +* kmalloc_array - allocate memory for an array. * @n: number of elements. * @size: element size. * @flags: the type of memory to allocate. @@ -240,11 +240,22 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags) {if (size !=0 && n >ULONG_MAX / size)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +return __kmalloc (n * size, flags); +} + +/** +* kcalloc - allocate memory for an array. The memory is set to zero.
include/linux/slab.h size_t ksize(const void *); · line 261
Quoted 10 lines
include/linux/slab.h +7
-0+* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +return kmalloc_array (n, size, flags | __GFP_ZERO); }#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )
Does this want adding to Documentation/CodingStyle "Chapter 14: Allocating memory" ?
Signature · 3 lines
Jesper Juhl <[email protected]> http://www.chaosbits.net/ Don't top-post http://www.catb.org/jargon/html/T/top-post.html Plain text mails only, please.
Andrew Morton
maintainerRe: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 20 lines · On Thu, 9 Feb 2012 23:47:42 +0100 (CET) Jesper Juhl <[email protected]> wrote:
Quoted 20 lines
include/linux/slab.h +13
-2-static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags) {if (size !=0 && n >ULONG_MAX / size)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +return __kmalloc (n * size, flags); +} + +/** +* kcalloc - allocate memory for an array. The memory is set to zero. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +return kmalloc_array (n, size, flags | __GFP_ZERO); }
Quoted 5 lines
Quoted 2 lines
#if !defined(CONFIG_NUMA) && !defined(CONFIG_SLOB)
Does this want adding to
Documentation/CodingStyle"Chapter 14: Allocating memory" ?
Spose so. We could possibly cook up a checkpatch rule too ;)
<gad, CodingStyle has 18 chapters and an appendix??>
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 10 lines · Andrew Morton
Quoted 2 lines · On Thu, 9 Feb 2012 23:47:42 +0100 (CET) Jesper Juhl <[email protected]> wrote:
include/linux/slab.h size_t ksize(const void *); · line 243
Quoted 2 lines
include/linux/slab.h +1
-1-static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags)[]
Quoted 2 lines
Does this want adding to
Documentation/CodingStyle"Chapter 14: Allocating memory" ?Spose so. We could possibly cook up a checkpatch rule too ;) <gad, CodingStyle has 18 chapters and an appendix??>
Symmetry please.
[kv]malloc_array
[kv]malloc_node_array
Re: [PATCH RFC v2] slab: introduce kmalloc_array
In reply to Andrew MortonHere goes a quick summary.
Proposed names
==============
kaalloc (Alexey Dobriyan)
kcallocn (Andrew Morton)
kmalloc_array (Pekka Enberg)
knalloc (Andrew Morton, Xi Wang)
kncalloc (Andrew Morton)
Other changes
=============
Documentation/CodingStyle "Chapter 14: Allocating memory"
* Mention the new array allocator whatever-it-is-called.
* Add some description:
"The preferred form for allocating an array is the following:
p = whatever-it-is-called(n, sizeof(...), ...);
To return zeroed items, the preferred form is the following:
p = kcalloc(n, sizeof(...), ...);
Both forms avoid overflowing the allocation size n * sizeof(...)."
* Add some checkpatch rule?
- xi
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Well I think the best is just not do any of this. One can already do k[zm]alloc(x * sizeof(struct whatever)). Do x*x for 2 dimensions etc etc. No need to change the API.
If you add these variants then please think
about the necessity to add other variants (like the kmalloc_node() NUMA call) etc in the future.
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 4 lines · Christoph Lameter
Well I think the best is just not do any of this. One can already do k[zm]alloc(x * sizeof(struct whatever)). Do x*x for 2 dimensions etc etc. No need to change the API.
The point was that there are a bunch of places where we have had integer overflows caused by doing kmalloc(x * sizeof(struct whatever)). For kzalloc(x * sizeof(struct whatever)), you just write it like kcalloc(x, sizeof(struct whatever)) and avoid the overflow, but we don't have a non-zeroing version of kcalloc() to do that.
Probably once we have the kmalloc_array() and people start using it, we get a bunch of overflow checking automatically and it's a kernel hardenning thing. As well we could remove the duplicative checking so it's a cleanup.
Quoted 4 lines
If you add these variants then please think
about the necessity to add other variants (like thekmalloc_node()NUMA call) etc in the future.
We don't have a kcalloc_node(), so I don't think this is likely to be a big issue.
regards,
dan carpenter
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 11 lines · Dan Carpenter
The point was that there are a bunch of places where we have had integer overflows caused by doing
kmalloc(x * sizeof(struct whatever)). Forkzalloc(x * sizeof(struct whatever)), you just write it likekcalloc(x, sizeof(struct whatever))and avoid the overflow, but we don't have a non-zeroing version ofkcalloc()to do that.Probably once we have the
kmalloc_array()and people start using it, we get a bunch of overflow checking automatically and it's a kernel hardenning thing. As well we could remove the duplicative checking so it's a cleanup.
Could you just do a macro that can be used in any location where the size of an array needs to be calculation. For example:
SAFE_ARRAY_SIZE(<nr>,<struct>)
So you'd do
kmalloc_node(SAFE_ARRAY_SIZE(10, struct page), 2, GFP_KERNEL)
or if you want multiple dimensions
SAFE_ARRAY_SIZE_2(<nr1>,<nr2>,<struct>)
?
Quoted 7 lines
Quoted 4 lines
If you add these variants then please think
about the necessity to add other variants (like thekmalloc_node()NUMA call) etc in the future.We don't have a
kcalloc_node(), so I don't think this is likely to be a big issue.
Yes and so if you need to allocate on a particular node then you need to do the calculation manually and therefore may not check for overflows.
Get rid of kcalloc and replace it with
kzalloc(SAFE_ARRAY_SIZE(x, y), ....)
?
Re: [PATCH RFC v2] slab: introduce kmalloc_array
SAFE_ARRAY_SIZE() would return the size if there were no overflow and -1 on errors? We can't return zero on errors because there are a lot of places which do zero size allocations and it's valid. It seems ugly.
I really think that's over thinking things. Let's just match kcalloc() exactly except without zeroing. The BUILD_BUG_ON() thing is an over complication as well. We haven't needed it for kcalloc().
The only impossible bit is picking the right name.
regards,
dan carpenter
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 9 lines · Dan Carpenter
SAFE_ARRAY_SIZE()would return the size if there were no overflow and -1 on errors? We can't return zero on errors because there are a lot of places which do zero size allocations and it's valid. It seems ugly.I really think that's over thinking things. Let's just match
kcalloc()exactly except without zeroing. TheBUILD_BUG_ON()thing is an over complication as well. We haven't needed it forkcalloc().
It is and we're not going to phase out a userspace-like kcalloc() API with something as verbose as SAFE_ARRAY_SIZE().
Pekka
Re: [PATCH RFC v2] slab: introduce kmalloc_array
In reply to Dan CarpenterQuoted 4 lines · Dan Carpenter
I really think that's over thinking things. Let's just match
kcalloc()exactly except without zeroing. TheBUILD_BUG_ON()thing is an over complication as well. We haven't needed it forkcalloc().
I don't think the BUILD_BUG_ON macro is useful here nor in kcalloc (besides gcc won't give you much handy information).
Just matching the new non-zeroing array allocator with kcalloc seems like the right thing to do. We had quite a few patches that could be simpler with it. For now I don't feel like it's necessary to change the API (e.g., making gfp_t as the first argument), do "smart" optimizations (e.g., recognizing the first argument is a constant), nor add more allocators (e.g., to match vmalloc, kmalloc_node).
- xi
Re: [PATCH RFC v2] slab: introduce kmalloc_array
In reply to Dan CarpenterQuoted 4 lines · Dan Carpenter
SAFE_ARRAY_SIZE()would return the size if there were no overflow and -1 on errors? We can't return zero on errors because there are a lot of places which do zero size allocations and it's valid. It seems ugly.
We could also catch these issues with BUG() or WARN_ON() and then return zero.
Quoted 4 lines
I really think that's over thinking things. Let's just match
kcalloc()exactly except without zeroing. TheBUILD_BUG_ON()thing is an over complication as well. We haven't needed it forkcalloc().
The best thing is to remove kcalloc and get it all cleaned up with some mechanism to safely calculate the size of an array to be allocated.
The other way will lead to naming issues and then to a multiplication of variants of the allocator interface. It makes things difficult to understand and handle.
Keep it simple by providing a function that determines the array size and handles any possible error condition.
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 2 lines · Christoph Lameter
We could also catch these issues with
BUG()orWARN_ON()and then return zero.
You cannot have SAFE_ARRAY_SIZE return 0 when an integer overflow occurs.
1) kmalloc(0) has a different semantics.
2) Using kmalloc(0) allows DoS attacks because often after kmalloc() there is some initialization code that writes to the allocated memory, such as:
p = kmalloc(SAFE_ARRAY_SIZE(n, size), ...);
for (i = 0; i < n; ++i)
p[i] = ...;Besides, BUG() still allows DoS attacks and WARN_ON() would flood the log, especially if n is controlled from user space. Neither seems appropriate here.
- xi
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 6 lines · Xi Wang
Quoted 2 lines · Christoph Lameter
We could also catch these issues with
BUG()orWARN_ON()and then return zero.You cannot have
SAFE_ARRAY_SIZEreturn 0 when an integer overflow occurs.
You can if you check the results later. A zero size return would be an indication of an error. No need to pass it on to kmalloc.
Quoted 3 lines
Besides,
BUG()still allows DoS attacks andWARN_ON()would flood the log, especially if n is controlled from user space. Neither seems appropriate here.
We have means at various level to control the "flood."
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 2 lines · Christoph Lameter
You can if you check the results later. A zero size return would be an indication of an error. No need to pass it on to kmalloc.
Maybe I misunderstood something here. How do you not pass it to kmalloc() with kmalloc(SAFE_ARRAY_SIZE(n, size), ...)?
- xi
Uninline kcalloc
Quoted 6 lines · Xi Wang
Quoted 2 lines · Christoph Lameter
You can if you check the results later. A zero size return would be an indication of an error. No need to pass it on to kmalloc.
Maybe I misunderstood something here. How do you not pass it to
kmalloc()withkmalloc(SAFE_ARRAY_SIZE(n, size), ...)?
Ok two patches to address this. First one
Subject: Uninline kcalloc
kcalloc is not used in performance critical ways. So it does not need to be inline. If we would add diagnostics to track the overflow occurrences then such code would be replicated at all call sites in the kernel.
--- include/linux/slab.h | 7 +------ mm/util.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-)
include/linux/slab.h +1 -6
@@ -240,12 +240,7 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) -{ -if (size !=0 && n >ULONG_MAX / size) -return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); -} +void *kcalloc (size_t n,size_t size,gfp_t flags);#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )/**
mm/util.c +15 -0
@@ -75,6 +75,21 @@ void *kmemdup(const void *src, size_t leEXPORT_SYMBOL (kmemdup);/** +* kcalloc - allocate memory for an array. The memory is set to zero. +* +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate. +*/ +void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +if (size !=0 && n >ULONG_MAX / size) +return NULL ; +return __kmalloc (n * size, flags | __GFP_ZERO); +} +EXPORT_SYMBOL (kcalloc); + +/** * memdup_user - duplicate memory region from user space * * @src: source address in user space
Re: Uninline kcalloc
This patch still preserves kcalloc. But note that if kcalloc returns NULL then multiple conditions may have caused it. One is that the array is simply too large. The other may be that such an allocation is not possible due to fragmentation.
Subject: Introduce calculate_array_size
calculate_array_size calculates the size of an array while checking for errors. Returns 0 if the size is too large.
--- include/linux/slab.h | 15 +++++++++++++++ mm/util.c | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-)
include/linux/slab.h +15 -0
@@ -242,6 +242,21 @@ size_t ksize(const void *);*/ void *kcalloc (size_t n,size_t size,gfp_t flags); +/* +* calculate_array_size - Calculate an array size given the size of a +* particular element with checking for overflow. +* +* Return 0 if there is an overflow. +*/ +static inline long calculate_array_size (size_t n,size_t size) +{ +if (size !=0 && n >ULONG_MAX / size) + +return 0 ; + +return n * size; +} +#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )/** * kmalloc_node - allocate memory from a specific node
mm/util.c +6 -3
@@ -83,9 +83,12 @@ EXPORT_SYMBOL(kmemdup);*/ void *kcalloc (size_t n,size_t size,gfp_t flags) { -if (size !=0 && n >ULONG_MAX / size) -return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +size_t s =calculate_array_size (n, size); + +if (s) +return kzalloc (s, flags); + +return NULL ; }EXPORT_SYMBOL (kcalloc);
Andrew Morton
maintainerRe: Uninline kcalloc
Quoted 54 lines · On Tue, 14 Feb 2012 13:37:44 -0600 (CST) Christoph Lameter <[email protected]> wrote:
This patch still preserves kcalloc. But note that if kcalloc returns NULL then multiple conditions may have caused it. One is that the array is simply too large. The other may be that such an allocation is not possible due to fragmentation.
Subject: Introduce
calculate_array_size
calculate_array_sizecalculates the size of an array while checking for errors. Returns 0 if the size is too large.Signed-off-by Christoph Lameter <[email protected]>--- include/linux/slab.h | 15 +++++++++++++++ mm/util.c | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-)Index:
linux-2.6/include/linux/slab.h
===================================================================include/linux/slab.h +15
-0@@ -242,6 +242,21 @@ size_t ksize(const void *);*/ void *kcalloc (size_t n,size_t size,gfp_t flags); +/* +* calculate_array_size - Calculate an array size given the size of a +* particular element with checking for overflow. +* +* Return 0 if there is an overflow. +*/ +static inline long calculate_array_size (size_t n,size_t size) +{ +if (size !=0 && n >ULONG_MAX / size) + +return 0 ; + +return n * size; +} +#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )/** * kmalloc_node - allocate memory from a specific node Index:
linux-2.6/mm/util.c
===================================================================mm/util.c +0
-1@@ -83,9 +83,12 @@ EXPORT_SYMBOL(kmemdup);*/ void *kcalloc (size_t n,size_t size,gfp_t flags) { -if (size !=0 && n >ULONG_MAX / size)
mm/util.c EXPORT_SYMBOL(kmemdup); · line 93
Quoted 10 lines
mm/util.c +6
-2-return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +size_t s =calculate_array_size (n, size); + +if (s) +return kzalloc (s, flags); + +return NULL ; }EXPORT_SYMBOL (kcalloc);
The patch appears to be a no-op. Confused.
Re: Uninline kcalloc
In reply to Christoph LameterQuoted 4 lines · Christoph Lameter
This patch still preserves kcalloc. But note that if kcalloc returns NULL then multiple conditions may have caused it. One is that the array is simply too large. The other may be that such an allocation is not possible due to fragmentation.
[...]
include/linux/slab.h size_t ksize(const void *); · line 255
Quoted 5 lines
include/linux/slab.h +5
-0+static inline long calculate_array_size (size_t n,size_t size) +{ +if (size !=0 && n >ULONG_MAX / size) + +return 0 ;
This isn't right. The above tests whether or not the result of the multiplication will not be representable in an 'unsigned long'...
Review
Quoted 2 lines
(quoted diff) +2
-0+ + return n * size;
but then the result is assigned to a (signed) long, which may overflow if it's greater than LONG_MAX.
Quoted 1 line
+}
[...]
mm/util.c EXPORT_SYMBOL(kmemdup); · line 92
Quoted 12 lines
void *kcalloc(size_t n,
size_tsize,gfp_tflags) {mm/util.c +6
-3-if (size !=0 && n >ULONG_MAX / size) -return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +size_t s =calculate_array_size (n, size); + +if (s) +return kzalloc (s, flags); + +return NULL ; }
This hunk changes the behaviour of kcalloc if either of the two size parameters is 0.
Cheers,
Signature · 1 line
Nick Bowler, Elliptic Technologies (http://www.elliptictech.com/)
Re: Uninline kcalloc
Quoted 6 lines · Nick Bowler
Quoted 4 lines · Christoph Lameter
This patch still preserves kcalloc. But note that if kcalloc returns NULL then multiple conditions may have caused it. One is that the array is simply too large. The other may be that such an allocation is not possible due to fragmentation.
[...]
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+static inline long calculate_array_size(size_t n, size_t size) +{ + if (size != 0 && n > ULONG_MAX / size) + + return 0;This isn't right. The above tests whether or not the result of the multiplication will not be representable in an 'unsigned long'...
Yes and so does the current kcalloc.
Quoted 4 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ return n * size;but then the result is assigned to a (signed) long, which may overflow if it's greater than
LONG_MAX.
That can happen? But you are right its better to use the size_t as the result of the argument. There is a gooh of long / size_t confusion already. This useless function should not be in the kernel.
Quoted 17 lines
Quoted 1 line
+}
[...]
Review
Quoted 12 lines
void *kcalloc(size_t n,
size_tsize,gfp_tflags) {(quoted diff) +6
-3- if (size != 0 && n > ULONG_MAX / size) - return NULL; - return __kmalloc(n * size, flags | __GFP_ZERO); + size_t s = calculate_array_size(n, size); + + if (s) + return kzalloc(s, flags); + + return NULL; }This hunk changes the behaviour of kcalloc if either of the two size parameters is 0.
You want ZERO_PTR returns?
NULL is one permissible return value of calloc() if size == 0. So we are now deviating from user space conventions.
Subject: Introduce calculate_array_size
calculate_array_size calculates the size of an array while checking for errors. Returns 0 if the size is too large.
--- include/linux/slab.h | 15 +++++++++++++++ mm/util.c | 7 +++++-- 2 files changed, 20 insertions(+), 2 deletions(-)
include/linux/slab.h +15 -0
@@ -242,6 +242,21 @@ size_t ksize(const void *);*/ void *kcalloc (size_t n,size_t size,gfp_t flags); +/* +* calculate_array_size - Calculate an array size given the size of a +* particular element with checking for overflow. +* +* Return 0 if there is an overflow. +*/ +static inline size_t calculate_array_size (size_t n,size_t size) +{ +if (n >ULONG_MAX / size) + +return 0 ; + +return n * size; +} +#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )/** * kmalloc_node - allocate memory from a specific node
mm/util.c +5 -2
@@ -83,9 +83,12 @@ EXPORT_SYMBOL(kmemdup);*/ void *kcalloc (size_t n,size_t size,gfp_t flags) { -if (size !=0 && n >ULONG_MAX / size) +size_t s =calculate_array_size (n, size); + +if (size && !s)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); + +return kzalloc (s, flags); }EXPORT_SYMBOL (kcalloc);
Re: Uninline kcalloc
Quoted 17 lines · Christoph Lameter
Quoted 6 lines · Nick Bowler
Quoted 4 lines · Christoph Lameter
This patch still preserves kcalloc. But note that if kcalloc returns NULL then multiple conditions may have caused it. One is that the array is simply too large. The other may be that such an allocation is not possible due to fragmentation.
[...]
Quoted 8 lines
include/linux/slab.h size_t ksize(const void *); · line 255
Quoted 5 lines
include/linux/slab.h +5
-0+static inline long calculate_array_size (size_t n,size_t size) +{ +if (size !=0 && n >ULONG_MAX / size) + +return 0 ;This isn't right. The above tests whether or not the result of the multiplication will not be representable in an 'unsigned long'...
Quoted 1 line
Yes and so does the current kcalloc.
Well, the current kcalloc doesn't assign the result to a signed long. However, it does assign the result to a size_t, which makes one wonder why it's not testing against SIZE_MAX. If size_t has the same range as unsigned long on all architectures, then this confusion doesn't matter, but is that actually the case?
Quoted 6 lines
Quoted 4 lines
include/linux/slab.h size_t ksize(const void *); · line 257
Quoted 1 line
include/linux/slab.h +1
-0+return n * size;but then the result is assigned to a (signed) long, which may overflow if it's greater than
LONG_MAX.That can happen?
Yes, because LONG_MAX (the maximum value of your return type) is strictly less than ULONG_MAX (what you test against). It's not hard to pick input numbers that multiply to something between LONG_MAX and ULONG_MAX, which will cause your function to return a negative value (standard C leaves the result of such a conversion implementation- defined, but I'll assume for now that it works this way for everything that compiles Linux).
Admittedly, your kcalloc change then assigns this negative value to a size_t, which will result in the correct positive value assuming SIZE_MAX == ULONG_MAX, but that's gratuitously roundabout.
[...]
Quoted 17 lines
Quoted 16 lines
[...]
mm/util.c EXPORT_SYMBOL(kmemdup); · line 92
Quoted 12 lines
void *kcalloc(size_t n,
size_tsize,gfp_tflags) {mm/util.c +6
-3-if (size !=0 && n >ULONG_MAX / size) -return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +size_t s =calculate_array_size (n, size); + +if (s) +return kzalloc (s, flags); + +return NULL ; }This hunk changes the behaviour of kcalloc if either of the two size parameters is 0.
Quoted 4 lines
You want
ZERO_PTRreturns?NULL is one permissible return value of
calloc()if size == 0. So we are now deviating from user space conventions.
Sort of. While standard C leaves it implementation-defined whether successful zero-sized allocations are possible, all sane implementations let them succeed. Hence, portable C apps need to handle 0 as a special case, because there are insane implementations out there. There's no reason for the kernel to be one of them.
Regardless, this was still a (presumably unintentional) change from the previous behaviour.
Cheers,
Signature · 1 line
Nick Bowler, Elliptic Technologies (http://www.elliptictech.com/)
Re: Uninline kcalloc
Quoted 18 lines · Nick Bowler
Quoted 17 lines · Christoph Lameter
Quoted 14 lines · Nick Bowler
Quoted 4 lines · Christoph Lameter
This patch still preserves kcalloc. But note that if kcalloc returns NULL then multiple conditions may have caused it. One is that the array is simply too large. The other may be that such an allocation is not possible due to fragmentation.
[...]
include/linux/slab.h size_t ksize(const void *); · line 255
Quoted 5 lines
include/linux/slab.h +5
-0+static inline long calculate_array_size (size_t n,size_t size) +{ +if (size !=0 && n >ULONG_MAX / size) + +return 0 ;This isn't right. The above tests whether or not the result of the multiplication will not be representable in an 'unsigned long'...
Quoted 5 lines
Quoted 1 line
Yes and so does the current kcalloc.
Well, the current kcalloc doesn't assign the result to a signed long. However, it does assign the result to a
size_t, which makes one wonder why it's not testing againstSIZE_MAX.
Of course, right after I send this I realize that we do not appear to define SIZE_MAX in the kernel. So s/SIZE_MAX/((size_t)-1)/g.
Quoted 55 lines
If
size_thas the same range as unsigned long on all architectures, then this confusion doesn't matter, but is that actually the case?Quoted 6 lines
Quoted 4 lines
include/linux/slab.h size_t ksize(const void *); · line 257
Quoted 1 line
include/linux/slab.h +1
-0+return n * size;but then the result is assigned to a (signed) long, which may overflow if it's greater than
LONG_MAX.That can happen?
Yes, because
LONG_MAX(the maximum value of your return type) is strictly less thanULONG_MAX(what you test against). It's not hard to pick input numbers that multiply to something betweenLONG_MAXandULONG_MAX, which will cause your function to return a negative value (standard C leaves the result of such a conversion implementation- defined, but I'll assume for now that it works this way for everything that compiles Linux).Admittedly, your kcalloc change then assigns this negative value to a
size_t, which will result in the correct positive value assumingSIZE_MAX==ULONG_MAX, but that's gratuitously roundabout.[...]
Quoted 17 lines
Quoted 16 lines
[...]
mm/util.c EXPORT_SYMBOL(kmemdup); · line 92
Quoted 12 lines
void *kcalloc(size_t n,
size_tsize,gfp_tflags) {mm/util.c +6
-3-if (size !=0 && n >ULONG_MAX / size) -return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +size_t s =calculate_array_size (n, size); + +if (s) +return kzalloc (s, flags); + +return NULL ; }This hunk changes the behaviour of kcalloc if either of the two size parameters is 0.
Quoted 4 lines
You want
ZERO_PTRreturns?NULL is one permissible return value of
calloc()if size == 0. So we are now deviating from user space conventions.Sort of. While standard C leaves it implementation-defined whether successful zero-sized allocations are possible, all sane implementations let them succeed. Hence, portable C apps need to handle 0 as a special case, because there are insane implementations out there. There's no reason for the kernel to be one of them.
Regardless, this was still a (presumably unintentional) change from the previous behaviour.
Cheers,
Signature · 1 line
Nick Bowler, Elliptic Technologies (http://www.elliptictech.com/)
Andrew Morton
maintainerRe: Uninline kcalloc
In reply to Christoph LameterQuoted 5 lines · On Tue, 14 Feb 2012 13:33:40 -0600 (CST) Christoph Lameter <[email protected]> wrote:
Subject: Uninline kcalloc
kcalloc is not used in performance critical ways. So it does not need to be inline. If we would add diagnostics to track the overflow occurrences then such code would be replicated at all call sites in the kernel.
Uninlining kcalloc() seems reasonable. But if we're going to uninline kcalloc() then we also should uninline kmalloc_array().
And yes, it's still called kmalloc_array() in my tree. I've been following this discussion for N days waiting for a reason for changing the original patch and I ain't seen one yet.
From: Xi Wang <[email protected]>
Subject: slab: introduce kmalloc_array()
Introduce a kmalloc_array() wrapper that performs integer overflow checking without zeroing the memory.
---
include/linux/slab.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
include/linux/slab.h renamed from include/linux/slab.h~slab-introduce-kmalloc_array +14 -3
@@ -190,7 +190,7 @@ size_t ksize(const void *);#endif /** -* kcalloc - allocate memory for an array. The memory is set to zero. +* kmalloc_array - allocate memory for an array. * @n: number of elements. * @size: element size. * @flags: the type of memory to allocate.
@@ -240,11 +240,22 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags) {if (size !=0 && n >ULONG_MAX / size)return NULL ; -return __kmalloc (n * size, flags | __GFP_ZERO); +return __kmalloc (n * size, flags); +} + +/** +* kcalloc - allocate memory for an array. The memory is set to zero. +* @n: number of elements. +* @size: element size. +* @flags: the type of memory to allocate (see kmalloc). +*/ +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +{ +return kmalloc_array (n, size, flags | __GFP_ZERO); }#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB )
_
Re: Uninline kcalloc
Quoted 11 lines · Andrew Morton
Quoted 5 lines · On Tue, 14 Feb 2012 13:33:40 -0600 (CST) Christoph Lameter <[email protected]> wrote:
Subject: Uninline kcalloc
kcalloc is not used in performance critical ways. So it does not need to be inline. If we would add diagnostics to track the overflow occurrences then such code would be replicated at all call sites in the kernel.
Uninlining
kcalloc()seems reasonable. But if we're going to uninlinekcalloc()then we also should uninlinekmalloc_array().
Well, kcalloc() used to be uninline and we made it inline on purpose at some point. I don't have a git tree here so I can't check. IIRC it had something to do with kernel text size reduction.
Quoted 3 lines
And yes, it's still called
kmalloc_array()in my tree. I've been following this discussion for N days waiting for a reason for changing the original patch and I ain't seen one yet.
Me neither. I don't think Christoph's SAFE_ARRAY_SIZE() suggestion makes much sense, really. It's more verbose, less obvious API, and doesn't really deal with the overflow case cleanly.
Pekka
Re: Uninline kcalloc
Quoted 3 lines · Pekka Enberg
Me neither. I don't think Christoph's
SAFE_ARRAY_SIZE()suggestion makes much sense, really. It's more verbose, less obvious API, and doesn't really deal with the overflow case cleanly.
IMHO Having a function to deal with the overflow of a multiplication and then do an allocation based on the result is a conflation of two different things that need to be separate. kcalloc only exists because there is an ancient user space function that somehow got a second parameter instead of just using the same as malloc().
Re: Uninline kcalloc
Quoted 3 lines · Pekka Enberg
Quoted 3 lines
Me neither. I don't think Christoph's
SAFE_ARRAY_SIZE()suggestion makes much sense, really. It's more verbose, less obvious API, and doesn't really deal with the overflow case cleanly.
Quoted 5 lines · Christoph Lameter
IMHO Having a function to deal with the overflow of a multiplication and then do an allocation based on the result is a conflation of two different things that need to be separate. kcalloc only exists because there is an ancient user space function that somehow got a second parameter instead of just using the same as
malloc().
It's an ancient userspace function that everybody knows how to use. There really isn't big enough gains from SAFE_ARRAY_SIZE() to justify a new API. And it's not as if the macro is an elegant way to solve the problem at hand either.
Pekka
Re: Uninline kcalloc
Quoted 12 lines · Pekka Enberg
Quoted 3 lines · Pekka Enberg
Quoted 3 lines
Me neither. I don't think Christoph's
SAFE_ARRAY_SIZE()suggestion makes much sense, really. It's more verbose, less obvious API, and doesn't really deal with the overflow case cleanly.Quoted 5 lines · Christoph Lameter
IMHO Having a function to deal with the overflow of a multiplication and then do an allocation based on the result is a conflation of two different things that need to be separate. kcalloc only exists because there is an ancient user space function that somehow got a second parameter instead of just using the same as
malloc().
Quoted 4 lines
It's an ancient userspace function that everybody knows how to use. There really isn't big enough gains from
SAFE_ARRAY_SIZE()to justify a new API. And it's not as if the macro is an elegant way to solve the problem at hand either.
Ok then keep the calloc style API but at least allow the refactoring with an additional function. That way one is able to check if there is an overflow in size calculation and can differentiate that condition from an out of memory situation due to a large contiguous allocation attempt.
Re: Uninline kcalloc
In reply to Christoph LameterQuoted 5 lines · Christoph Lameter
IMHO Having a function to deal with the overflow of a multiplication and then do an allocation based on the result is a conflation of two different things that need to be separate. kcalloc only exists because there is an ancient user space function that somehow got a second parameter instead of just using the same as
malloc().
I don't understand why these kcalloc patches have anything to do with kmalloc(SAFE_ARRAY_SIZE(...), ...) you proposed.
It also doesn't make much sense to force the caller to check the result of SAFE_ARRAY_SIZE() or calculate_array_size() before passing it to kmalloc(). This is too verbose.
- xi
Re: Uninline kcalloc
Quoted 9 lines · Xi Wang
Quoted 5 lines · Christoph Lameter
IMHO Having a function to deal with the overflow of a multiplication and then do an allocation based on the result is a conflation of two different things that need to be separate. kcalloc only exists because there is an ancient user space function that somehow got a second parameter instead of just using the same as
malloc().I don't understand why these kcalloc patches have anything to do with
kmalloc(SAFE_ARRAY_SIZE(...), ...)you proposed.
Not sure why you are so hung up on SAFE_ARRAY_SIZE. It was an idea to discuss. Certainly having an inline function seems to be better.
Quoted 3 lines
It also doesn't make much sense to force the caller to check the result of
SAFE_ARRAY_SIZE()orcalculate_array_size()before passing it tokmalloc(). This is too verbose.
kcalloc is still there. Certainly useful for legacy purposes. But I'd feel better if I had fine grained control over the size of my allocation rather than rely on the slab allocators to check up on my multiplication.
With these patches both is possible. And if you want the check of an allocation that is not zeroed then you can do so because you have a function that will perform the size check for you without calling into the slab allocator.
Re: Uninline kcalloc
Quoted 8 lines · Christoph Lameter
kcalloc is still there. Certainly useful for legacy purposes. But I'd feel better if I had fine grained control over the size of my allocation rather than rely on the slab allocators to check up on my multiplication.
With these patches both is possible. And if you want the check of an allocation that is not zeroed then you can do so because you have a function that will perform the size check for you without calling into the slab allocator.
In the code you proposed, where calculate_array_size() returns 0 for overflow, one has to write:
size_t s = calculate_array_size(n, size);
if (s)
p = kmalloc(s, ...);This "if" thing is just too verbose --- you need three lines to allocate an array.
We could change calculate_array_size() to return ULONG_MAX or some large number with which kmalloc() would fail. Then one would write:
p = kmalloc(calculate_array_size(n, size), ...);
This looks better to me. The advantage is that we don't need another allocator (and avoid this name picking game). The disadvantage is that the semantics of calculate_array_size(), returning ULONG_MAX on overflow, sounds sort of strange.
- xi
Re: Uninline kcalloc
Quoted 11 lines · Xi Wang
for overflow, one has to write:
size_t s = calculate_array_size(n, size); if (s) p = kmalloc(s, ...);This "if" thing is just too verbose --- you need three lines to allocate an array.
We could change
calculate_array_size()to returnULONG_MAXor some large number with whichkmalloc()would fail. Then one would write:
Any allocation larger than MAX_ORDER << PAGE_SHIFT will fail since the page allocator cannot serve larger contigous pages.
Re: Uninline kcalloc
Quoted 2 lines · Christoph Lameter
Any allocation larger than
MAX_ORDER<<PAGE_SHIFTwill fail since the page allocator cannot serve larger contigous pages.
Yes, any number larger than KMALLOC_MAX_SIZE would do the trick. The question is whether people would like to adopt
kmalloc(ARRAY_BYTES(n, size), ...);
or
knalloc(n, size, ...);
Besides, I am worried that ARRAY_BYTES is error-prone because you have to make sure that ARRAY_BYTES is used only in *alloc, not elsewhere, not in forms like ARRAY_BYTES(n, size) + padding_size, etc. If ARRAY_BYTES is mostly used with kmalloc(), and the only safe form is kmalloc(ARRAY_BYTES(n, size), ...), then adding a new allocator knalloc(n, size, ...) seems like a simpler choice.
BTW, here goes a partial list of places where the new allocator could be used to simplify existing checks.
fs/cifs/cifsacl.c:912
if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *))
return;
ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
GFP_KERNEL);
fs/cifs/asn1.c:428
if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
return 0;
*oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC);fs/nfs/callback_xdr.c:308
if (n > ULONG_MAX / sizeof(*args->devs)) {
status = htonl(NFS4ERR_BADXDR);
goto out;
}
args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL);net/ipv4/netfilter/nf_nat_snmp_basic.c:447
if (size < 2 || size > ULONG_MAX/sizeof(unsigned long))
return 0;
*oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC);drivers/staging/comedi/comedi_fops.c:673
insns =
kcalloc(insnlist.n_insns, sizeof(struct comedi_insn), GFP_KERNEL);I believe there are more places without those checks, where the new allocator can offer better security.
- xi
Re: Uninline kcalloc
Quoted 8 lines · Xi Wang
Quoted 2 lines · Christoph Lameter
Any allocation larger than
MAX_ORDER<<PAGE_SHIFTwill fail since the page allocator cannot serve larger contigous pages.Yes, any number larger than
KMALLOC_MAX_SIZEwould do the trick. The question is whether people would like to adopt
kmalloc(ARRAY_BYTES(n, size), ...);
Well it would be f.e.
kmalloc_node(calculate_array_size(sizeof(struct bla), n), GFP_KERNEL, my_numa_node) or vmalloc(calculate_array_size(sizeof(struct blu), n));
Then there is
vzalloc
kzallocvmalloc_32alloc_bootmem (MAXORDER limit may not work)alloc_remap
...
This would also work for special subsystem allocations like
usb_alloc_coherentdm_vcallocdevres_alloc
....
The use of a function or macro makes the overflow check much more universal and allows these array allocations to occur with different allocation functions throughout the kernel.
Please do not increase the number of allocation functions needlessly with new variants.
Re: Uninline kcalloc
Quoted 16 lines · Christoph Lameter
Then there is
vzalloc
kzallocvmalloc_32alloc_bootmem(MAXORDER limit may not work)alloc_remap...
This would also work for special subsystem allocations like
usb_alloc_coherentdm_vcallocdevres_alloc
Quoted 6 lines
....
The use of a function or macro makes the overflow check much more universal and allows these array allocations to occur with different allocation functions throughout the kernel.
No, it does NOT. It can be easily misued to introduce more bugs.
1) Should calculate_array_size() return 0 on overflow, as you orginally proposed?
No, as Dan, Pekka, and some others already pointed out.
2) Should calculate_array_size() return something like KMALLOC_MAX_SIZE + 1?
No, because you intended to use it with other allocators such as vmalloc().
3) Should calculate_array_size() return ULONG_MAX/SIZE_MAX/-1?
No! Consider devres_alloc() you mentioned. Then you do
devres_alloc(..., calculate_array_size(n, size), ...).
It internally invokes kmalloc() with allocation size:
sizeof(struct devres) + calculate_array_size(n, size).
When n * size overflows, calculate_array_size() returns ULONG_MAX, and the allocation size wraps around to a small integer!
I like the idea of "do not add an allocator unless necessary". However, "universal" calculate_array_size() just doesn't work, unless you can find the correct semantics or limit its use. It can be easily misused and bring more trouble than it's worth.
- xi
Re: Uninline kcalloc
Quoted 8 lines · Xi Wang
Quoted 3 lines
The use of a function or macro makes the overflow check much more universal and allows these array allocations to occur with different allocation functions throughout the kernel.
No, it does NOT. It can be easily misued to introduce more bugs.
1) Should
calculate_array_size()return 0 on overflow, as you orginally proposed?
I thought you worked that out?
Quoted 7 lines
No, as Dan, Pekka, and some others already pointed out.
2) Should
calculate_array_size()return something likeKMALLOC_MAX_SIZE+ 1?No, because you intended to use it with other allocators such as
vmalloc().
vmalloc will also fail if you pass a large number.
Quoted 9 lines
3) Should
calculate_array_size()return ULONG_MAX/SIZE_MAX/-1?No! Consider
devres_alloc()you mentioned. Then you do
devres_alloc(..., calculate_array_size(n, size), ...).It internally invokes
kmalloc()with allocation size:sizeof(struct devres) +
calculate_array_size(n, size).
That is an addition which is less likely to overflow. Especially if you return MAX_ORDER << PAGE_SIZE from the array size calculation.
It would be best to be universal if you want to introduce size overflow checking rather than just dealing with one case and let the rest of the kernel fend for themselves. I think we need something like calculate_array_size() with proper error handling (and no I am not particular set on having it done a particular way. Just that is it done).
Re: [PATCH RFC v2] slab: introduce kmalloc_array
In reply to Xi WangQuoted 8 lines · Xi Wang
include/linux/slab.h +1
-1-static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags){
if (size != 0 && n >ULONG_MAX/ size) return NULL;
- return__kmalloc(n * size, flags | __GFP_ZERO);
+ return__kmalloc(n * size, flags);
+}
It should be named kaalloc(), I think.
Why it is ULONG_MAX, when size_t is used?
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 12 lines · Alexey Dobriyan
Quoted 8 lines · Xi Wang
include/linux/slab.h +1
-1-static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *kmalloc_array (size_t n,size_t size,gfp_t flags){
if (size != 0 && n >ULONG_MAX/ size) return NULL;
- return__kmalloc(n * size, flags | __GFP_ZERO);
+ return__kmalloc(n * size, flags);
+}It should be named
kaalloc(), I think.
Why it isULONG_MAX, whensize_tis used?
Also, it could be written more "robust" against people who will make sizeof() the first argument with __builtin_constant_p().
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 1 line · Alexey Dobriyan
Quoted 1 line
It should be named
kaalloc(), I think.
I like this shorter name. Let's see what others think. ;-)
Quoted 1 line
Quoted 1 line
Why it is
ULONG_MAX, whensize_tis used?
Is there a SIZE_MAX or something similar?
Quoted 2 lines
Also, it could be written more "robust" against people who will make
sizeof()the first argument with__builtin_constant_p().
Do you mean something like this?
BUILD_BUG_ON(__builtin_constant_p(n));
or
BUILD_BUG_ON(__builtin_constant_p(n) && !__builtin_constant_p(size));
- xi
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 4 lines · Xi Wang
Quoted 2 lines · Alexey Dobriyan
Also, it could be written more "robust" against people who will make
sizeof()the first argument with__builtin_constant_p().
No,
If one dimension is constant, limit should be divided by it, so compiler would have less chance
to screw up compile time evaluation.
Also, gfp_t mask could be made first argument if we ever want to expand it to >2 dimensional arrays
without adding kaalloc3().
Quoted 9 lines
Do you mean something like this?
BUILD_BUG_ON(__builtin_constant_p(n));
or
BUILD_BUG_ON(__builtin_constant_p(n) && !__builtin_constant_p(size));
- xi
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 5 lines · Alexey Dobriyan
No,
If one dimension is constant, limit should be divided by it, so compiler would have less chance
to screw up compile time evaluation.
I think the BUILD_BUG_ON macro can prevent that misuse.
- xi
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 8 lines · Xi Wang
Quoted 5 lines · Alexey Dobriyan
No,
If one dimension is constant, limit should be divided by it, so compiler would have less chance
to screw up compile time evaluation.I think the
BUILD_BUG_ONmacro can prevent that misuse.
But there is no misuse.
Both kaalloc(non-const, const) and kaalloc(const, non-const) are OK. It's just first case happens more often.
Re: [PATCH RFC v2] slab: introduce kmalloc_array
Quoted 4 lines · Alexey Dobriyan
But there is no misuse.
Both
kaalloc(non-const, const)andkaalloc(const, non-const)are OK. It's just first case happens more often.
Any k*alloc(const, non-const) example? I feel like in most cases it is the caller code that should get fixed, such as:
https://lkml.org/lkml/2012/2/10/135
- xi
Jens Axboe
maintainerQuoted 24 lines · Andrew Morton
Quoted 10 lines · On Wed, 08 Feb 2012 09:34:16 +0100 Jens Axboe <[email protected]> wrote:
Quoted 7 lines · Dan Carpenter
My static checker is warning about integer overflows in
kernel/relay.c
relay_create_buf()
170171 buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^This can only overflow on 32bit systems.
Correct
This is a relatively common problem. Converting a
kzalloc()call tokcalloc()fixes it. But we do not have a non-zeroing version ofkcalloc().Please, someone complete this patch and send it to me!
include/linux/slab.h size_t ksize(const void *); · line 243
Quoted 5 lines
include/linux/slab.h renamed from include/linux/slab.h~a +1
-1@@ -240,11 +240,16 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *wtf_do_i_call_this (size_t n,size_t size,gfp_t flags)
kzcalloc()? Either that, or long_and_verbose().
Signature · 1 line
Jens Axboe
Andrew Morton
maintainerQuoted 22 lines · On Thu, 09 Feb 2012 13:41:38 +0100 Jens Axboe <[email protected]> wrote:
Quoted 21 lines · Andrew Morton
Quoted 10 lines · On Wed, 08 Feb 2012 09:34:16 +0100 Jens Axboe <[email protected]> wrote:
Quoted 7 lines · Dan Carpenter
My static checker is warning about integer overflows in
kernel/relay.c
relay_create_buf()
170171 buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^This can only overflow on 32bit systems.
Correct
This is a relatively common problem. Converting a
kzalloc()call tokcalloc()fixes it. But we do not have a non-zeroing version ofkcalloc().Please, someone complete this patch and send it to me!
Quoted 10 lines
include/linux/slab.h size_t ksize(const void *); · line 243
Quoted 8 lines
include/linux/slab.h renamed from include/linux/slab.h~a +1
-1@@ -240,11 +240,16 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *wtf_do_i_call_this (size_t n,size_t size,gfp_t flags)
kzcalloc()? Either that, orlong_and_verbose().
I would need to be k_not_z_calloc() because it's the non-zeroing version.
kcallocn()? knalloc()? kncalloc()?
Quoted 24 lines · Andrew Morton
Quoted 10 lines · On Wed, 08 Feb 2012 09:34:16 +0100 Jens Axboe <[email protected]> wrote:
Quoted 7 lines · Dan Carpenter
My static checker is warning about integer overflows in
kernel/relay.c
relay_create_buf()
170
171 buf->padding =kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This can only overflow on 32bit systems.Correct
This is a relatively common problem. Converting a
kzalloc()call tokcalloc()fixes it. But we do not have a non-zeroing version ofkcalloc().Please, someone complete this patch and send it to me!
include/linux/slab.h size_t ksize(const void *); · line 243
Quoted 5 lines
include/linux/slab.h renamed from include/linux/slab.h~a +1
-1@@ -240,11 +240,16 @@ size_t ksize(const void *);* for general use, and so are not documented here. For a full list of * potential flags, always refer to linux/gfp.h. */ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) +static inline void *wtf_do_i_call_this (size_t n,size_t size,gfp_t flags)
I'd call it kmalloc_array().
Quoted 6 lines
include/linux/slab.h renamed from include/linux/slab.h~a +2
-1{ if (size != 0 && n > ULONG_MAX / size) return NULL; - return __kmalloc(n * size, flags | __GFP_ZERO); + return __kmalloc(n * size, flags); +}
PATCH 1/1 relay: prevent integer overflow in relay_open()
In reply to Jens Axboe"subbuf_size" and "n_subbufs" come from the user and they need to be capped to prevent an integer overflow.
kernel/relay.c +8 -2
@@ -164,10 +164,14 @@ depopulate:*/ static struct rchan_buf *relay_create_buf (struct rchan *chan) { -struct rchan_buf *buf =kzalloc (sizeof (struct rchan_buf ),GFP_KERNEL ); -if (!buf) +struct rchan_buf *buf; + +if (chan->n_subbufs >UINT_MAX /sizeof (size_t *))return NULL ; + buf =kzalloc (sizeof (struct rchan_buf ),GFP_KERNEL ); +if (!buf) +return NULL ; buf->padding =kmalloc (chan->n_subbufs *sizeof (size_t *),GFP_KERNEL );if (!buf->padding) goto free_buf;
@@ -574,6 +578,8 @@ struct rchan *relay_open(const char *base_filename,if (!(subbuf_size && n_subbufs))return NULL ; +if (subbuf_size >UINT_MAX / n_subbufs) +return NULL ; chan =kzalloc (sizeof (struct rchan ),GFP_KERNEL );if (!chan)
Re: [patch] relay: prevent integer overflow in relay_open()
Am 09.02.2012 11:44, schrieb Dan Carpenter:
Quoted 27 lines
"
subbuf_size" and "n_subbufs" come from the user and they need to be capped to prevent an integer overflow.Signed-off-by Dan Carpenter <[email protected]>kernel/relay.c +6
-2@@ -164,10 +164,14 @@ depopulate:*/ static struct rchan_buf *relay_create_buf (struct rchan *chan) { -struct rchan_buf *buf =kzalloc (sizeof (struct rchan_buf ),GFP_KERNEL ); -if (!buf) +struct rchan_buf *buf; + +if (chan->n_subbufs >UINT_MAX /sizeof (size_t *))return NULL ; + buf =kzalloc (sizeof (struct rchan_buf ),GFP_KERNEL ); +if (!buf) +return NULL ; buf->padding =kmalloc (chan->n_subbufs *sizeof (size_t *),GFP_KERNEL );if (!buf->padding) goto free_buf;
kernel/relay.c struct rchan *relay_open(const char *base_filename,
Quoted 9 lines
kernel/relay.c +2
-1@@ -574,6 +578,8 @@ struct rchan *relay_open(const char *base_filename,if (!(subbuf_size && n_subbufs))return NULL ; +if (subbuf_size >UINT_MAX / n_subbufs) +return NULL ; chan =kzalloc (sizeof (struct rchan ),GFP_KERNEL );if (!chan) --
numerical this is ok, but ...
maybe it is better to cap the chan->n_subbufs at a useful number ?
The user can still allocate an insane number of bytes.
Restricting subbuf_size*n_subbufs seems more logical (otherwise is this a real problem ?)
re,
wh
Re: [patch] relay: prevent integer overflow in relay_open()
Quoted 2 lines · walter harms
numerical this is ok, but ...
maybe it is better to cap thechan->n_subbufsat a useful number ?
We considered this question already earlier in the thread.
Quoted 3 lines
The user can still allocate an insane number of bytes.
Restricting subbuf_size*n_subbufs seems more logical (otherwise is this a real problem ?)
Yes. It is a real problem.
regards,
dan carpenter