LWN.net Logo

The thorny case of kmalloc(0)

People running 2.6.22-rc kernels have likely noticed the occasional warning and traceback associated with zero-length allocations. It turns out that there is code in the kernel which asks kmalloc() to allocate a zero-sized object. Nobody really knew how often this happens until the warning went in as part of the SLUB allocator patch set; now that these cases are turning up, it seems that deciding what to do about them is harder than one might expect.

One possibility is to return NULL. On the face of it, this option would appear to make sense; the caller has requested that no memory be allocated, and kmalloc() has complied. The problem here is that a NULL pointer is already loaded with meaning. It says that the allocation has failed (which it didn't - there is always enough memory left to allocate another zero bytes) and is often used as an indicator that a particular structure or subsystem has not been initialized. More to the point, it seems that there is an occasional situation where a zero-length allocation is not entirely incorrect; consider the allocation of a structure which, as a result of the kernel's configuration options, has been optimized down to zero members. Coding around such cases is possible, but it is not clear that adding more twists and turns is worth the trouble when zero-length allocations can just be handled in kmalloc().

Another possibility is to return the smallest object that kmalloc() can manage - currently eight bytes. That is what kmalloc() has silently done for years. This solution appears to work, but it has the disadvantage of returning memory which can be written to. A zero-length allocation can arguably be correct, but it's hard to find anybody who would agree that storing data into a zero-length chunk of memory makes sense. Even highly compressed data cannot be expected to fit into that space in all situations. People who worry about finding bugs would much prefer that any attempt to actually write to memory allocated with kmalloc(0) caused the kernel to protest in a very noisy way.

That brings us to the third possibility: this patch from Christoph Lameter which causes kmalloc(0) to return a special ZERO_SIZE_PTR value. It is a non-NULL value which looks like a legitimate pointer, but which causes a fault on any attempt at dereferencing it. Any attempt to call kfree() with this special value will do the right thing, of course.

The final option seems like it should be the right course, allowing zero-length allocations without masking any subsequent incorrect behavior. Surprisingly, though, there is an objection here too: now every call to kmalloc(0) returns the same value. One might not think this would be a problem; subsequent zero-length allocations will all be zero bytes apart, just like the C standard says they should be. But some developers are worried that this behavior might confuse code which compares pointers to see if two objects are the same. There is also, apparently, an established coding pattern (in user space) which uses zero-length allocations as a way of generating a unique cookie value. If all zero-length allocations return the same pointer, these cookies lose their uniqueness.

That worry appears unlikely to carry the day, though; Linus says:

If people can't be bothered to create a "random ID generator" themselves, they had damn well better use "kmalloc(1)" rather than "kmalloc(0)" to get a unique cookie. Asking the allocator to do something idiotic because some idiot thinks a memory allocator is a cookie allocator is just crazy.

I can understand that things like user-level libraries have to take crazy people into account, but the kernel internal libraries definitely do not.

Add to this argument the fact that nobody seems to have discovered such a use of kmalloc() in the kernel yet, and the "unique cookie" argument runs out of steam. So some form of the ZERO_SIZE_PTR patch, with the warning removed, will probably find its way into the mainline - but probably not before 2.6.23.


(Log in to post comments)

The thorny case of kmalloc(0)

Posted Jun 7, 2007 10:02 UTC (Thu) by evgeny (guest, #774) [Link]

> The problem here is that a NULL pointer is already loaded with meaning. It says that the allocation has failed

Right, but inventing ZERO_SIZE_PTR doesn't solve this problem; instead it delegates the punishment of the extra check to all possible users of the object, i.e. instead of

if (!a) {
allocate(a) || die;
}

one has to use

if (!a || a == ZERO_SIZE_PTR) everywhere. Not?

PS. Why in the "Plain text" format the indentation is not preserved?

The thorny case of kmalloc(0)

Posted Jun 7, 2007 10:58 UTC (Thu) by dvrabel (subscriber, #9500) [Link]

No. Either: a) you wanted a zero-size allocation know not to use the pointer; or b) the zero size allocation was a bug and should be detected as a error that's distinct from a out of memory error (i.e., any attempted use should fault).

If you wish to defensively program a function against a caller requesting zero-sized allocations it would be better to test before attempting the allocation.

The thorny case of kmalloc(0)

Posted Jun 7, 2007 11:17 UTC (Thu) by evgeny (guest, #774) [Link]

> If you wish to defensively program a function against a caller requesting zero-sized allocations it would be better to test before attempting the allocation.

Well, that's the right way in my opinion. It needs only one check per kmalloc() call, while using two flavors of NULL forces to implement this check for each (out of, in general, >= 1 cases) access to pointer.

The thorny case of kmalloc(0)

Posted Jun 7, 2007 11:59 UTC (Thu) by HenrikH (guest, #31152) [Link]

And you are correct, but since there is people who apparantly uses kmalloc (0) I can se no other way to solve this problem. Either we let kmalloc return NULL only on error and not let size=0 be an error or we have to convert all these kmalloc(0) cases which turnoed out be quite a job.

The thorny case of kmalloc(0)

Posted Jun 8, 2007 0:41 UTC (Fri) by bronson (subscriber, #4806) [Link]

The article gives a perfectly good reason why kmalloc(0) is a decent thing to do: when the config has resulted in a struct with 0 members. Much easier just to kmalloc(0) and continue as normal rather than scattering more #ifdefs or macros everywhere.

(there's no need to test for ZERO_SIZE_PTR in this case because the struct has no members so the pointer can not be dereferenced anyway; you'll get a compile-time error instead of an oops)

The thorny case of kmalloc(0)

Posted Jun 7, 2007 13:49 UTC (Thu) by dvrabel (subscriber, #9500) [Link]

You're not supposed to test for ZERO_SIZE_PTR, but let the system oops if it's dereferenced.

The thorny case of kmalloc(0)

Posted Jun 7, 2007 18:27 UTC (Thu) by pj (subscriber, #4506) [Link]

Exactly... it should be valid to allocate 0 bytes of memory as long as you don't try and dereference the pointer and use it for anything. Consider code like:

items[] itemblock = kmalloc( itemcount * sizeof(items) );

ASSERT(itemblock != NULL);

for(int i = 0; i++ ; i < itemcount) {

...do something with itemblock[i]...

}

itemcount is allowed to be 0 in the above code, with no problems. a kmalloc(0) occurs, but the for loop never gets its body run, so itemblock is never dereferenced. At the same time, the ASSERT only happens when there's a problem allocating memory.

It's a good solution. And I like Linus' comment :)

The thorny case of kmalloc(0)

Posted Jun 8, 2007 16:05 UTC (Fri) by giraffedata (subscriber, #1954) [Link]

the ASSERT only happens when ...

Grrr. The ASSERT always "happens" -- it's right there in unconditional code. The assertion fails only when ...

And really, it shouldn't even be discussed. If you're asserting that the pointer is not null, that means you're assuming for simplicity that the the allocation didn't fail and if you discuss the possibility that it did fail, you've defeated the purpose of the assertion. This code should probaby be instead an explicit test of the pointer for NULL.

The thorny case of kmalloc(0)

Posted Jun 7, 2007 23:43 UTC (Thu) by jzbiciak (✭ supporter ✭, #5246) [Link]

Even highly compressed data cannot be expected to fit into that space in all situations.

But isn't 1 close to 0 for small enough values of 1? ;-)

The thorny case of kmalloc(0)

Posted Jun 8, 2007 19:03 UTC (Fri) by jengelh (subscriber, #33263) [Link]

Bits are not infinitesimal in software.

The thorny case of kmalloc(0)

Posted Jun 8, 2007 20:03 UTC (Fri) by jzbiciak (✭ supporter ✭, #5246) [Link]

*nyrrroooooooooooooom* *nyyyyyrrrrrrrrrrrrrrrrooooooooooooooooom*

That's the sound of both jokes going over your head. :-)

The thorny case of kmalloc(0)

Posted Jun 8, 2007 21:12 UTC (Fri) by oak (guest, #2786) [Link]

> Even highly compressed data cannot be expected to fit into that space in
all situations.

Sure it can. Just use a state-of-the-art 100% lossy algorithm.

(Maybe something similar to the "iterative compression" announced few
years ago. No, wait... You can't, that's already patented...)

The thorny case of kmalloc(0)

Posted Jun 8, 2007 21:22 UTC (Fri) by jzbiciak (✭ supporter ✭, #5246) [Link]

Wow, that totally one-ups encryption's one-time-pad. See, with an OTP, the ciphertext can decrypt to any plaintext of the same length. With this compression algorithm, it can decompress to any message of any length!

Copyright © 2007, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds