6

I read somewhere: Dereferencing a pointer returned by a "new" allocation of size zero is UB. Is it same in C? If yes, is the following code UB? (assuming size = 0)

 a->object[index].data = malloc(size);  
 memcpy(a->object[index].data, bytes, size);

To my understanding: NO. Just wanted to double check.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • 4
    The return value of `malloc(0)` can only be used to `free` according to the standard. – Richard J. Ross III Jul 13 '14 at 21:28
  • 3
    technically the memcpy would never touch any of the memory, so you'd be OK (you can even memcpy from null if you're copying 0 bytes) – Dave Jul 13 '14 at 21:29
  • 2
    Aside from that, it seems pretty bizarre that you'd ever be in this situation. – Dave Jul 13 '14 at 21:29
  • @RichardJ.RossIII: Just out curiosity, how many bytes does `malloc(0)` allocate? 1? – Jack Jul 13 '14 at 21:30
  • @RichardJ.RossIII: so is it UB or not? –  Jul 13 '14 at 21:32
  • 5
    @Jack it either returns NULL, or it returns a 'unique pointer that can be passed to free'. So it's up to the implementation to determine if it allocates anything at all. – Richard J. Ross III Jul 13 '14 at 21:36
  • @Dave: not really "bizarre". Consider a file format that stores the size of each of its elements. – Jongware Jul 13 '14 at 21:49
  • "it seems pretty bizarre that you'd ever be in this situation" -- only if it's "bizarre" to have 0 bytes of data ... which of course it isn't. – Jim Balter Jul 13 '14 at 21:54
  • @JimBalter: so free has to be called anyway –  Jul 13 '14 at 22:13
  • As I said below: "if you want to avoid leaking memory on those systems that return a non-NULL value". – Jim Balter Jul 13 '14 at 22:18
  • 1
    " it seems pretty bizarre" -- the only bizarre thing here is the three upvotes of misuse of the word "technically". *Technically*, you would not be "OK", because *technically*, this code is UB. – Jim Balter Jul 13 '14 at 22:19
  • 1
    @Deduplicator "the behavior is completely defined by the standard". -- Nope; the standard says this is undefined behavior ... you cannot pass the result of `malloc(0)` to `memcpy`, regardless of the count. – Jim Balter Jul 13 '14 at 22:22
  • 2
    @Deduplicator Try reading the answers below, eh? – Jim Balter Jul 13 '14 at 22:24
  • @Jim: Ok, did that and some more surrounding standardese. That's a bit surprising (not that hidden easter-eggs in the standard are really that surprising) ;-( . (Read the answer before that last bit was added) – Deduplicator Jul 13 '14 at 22:30
  • @Jim: are you sure this is not the same as treating `if (x && 1/x)` as an error for `x=0`? – Jongware Jul 13 '14 at 22:31
  • @Jongware Yes. Why don't *you* try reading the answers below? – Jim Balter Jul 13 '14 at 22:36
  • @Dave memcpy from NULL causes undefined behaviour (even if length 0), [see here](http://stackoverflow.com/questions/5243012/is-it-guaranteed-to-be-safe-to-perform-memcpy0-0-0) – M.M Jul 13 '14 at 22:42

3 Answers3

9

When you pass 0 as an argument to malloc, then it free the allocated memory to the pointer which malloc returns to.

Result is implementation defined.

C11: 7.22.3 Memory management functions:

[...] If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

Also standard says:

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs.

So, in either case of implementation defined behavior, freeing will not invoke undefined behavior.

Now moving to another part of the question.

7.1.4 Use of library functions:

If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.

C11: 7.24.1 p(2):

Where an argument declared as size_t n specifies the length of the array for a function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4. On such a call, a function that locates a character finds no occurrence, a function that compares two character sequences returns zero, and a function that copies characters copies zero characters.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • while the returned value is undefined, in this specific example (passing it to memcpy with a size of 0), it would still be fine (the only undefined part would be whether you *need* to call `free`, and in any case calling `free` wouldn't hurt. So I'd consider this to be defined-enough. – Dave Jul 13 '14 at 21:36
  • @haccks: So, it is not UB? ps. is it needed to call free on malloc(0)? –  Jul 13 '14 at 21:40
  • 2
    @dmcr_code you should *always* call free after *any* malloc. As the spec says here, it could return as if you'd called it with a non-zero argument (i.e. allocate memory). – Dave Jul 13 '14 at 21:44
  • @haccks: ok but the trick in question is that due to 0 probably memcpy does nothing otherwise it would be UB –  Jul 13 '14 at 21:48
  • Dave doesn't seem to understand what the word "undefined" means in C. – Jim Balter Jul 13 '14 at 21:50
  • @dmcr_code " is it needed to call free on malloc(0)" -- if you want to avoid leaking memory on those systems that return a non-NULL value. – Jim Balter Jul 13 '14 at 21:51
  • @JimBalter apologies; I meant implementation defined, not undefined. – Dave Jul 13 '14 at 21:51
  • @haccks: I will accept but main trick it doesn't mention, that if 0 wasn't passed in memcpy probably it would be UB –  Jul 13 '14 at 21:58
  • 4
    @dmcr_code technically calling `memcpy` with a null pointer is UB even if the third argument is `0` – ouah Jul 13 '14 at 22:00
  • @haccks: due to NULL in memcpy even if third argument is 0? nice if someone could put link to it. ps. It seems I will separate this in my code if size=0 and don't do any allocations –  Jul 13 '14 at 22:03
1

According to my reading of the current standard (or n1570, the final public draft of it), the code has Undefined Behaviour:

7.24.1 String function conventions

  1. Where an argument declared as size_t n specifies the length of the array for a function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4.

7.1.4 Use of library functions

  1. Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.

(Emphasis added.)

Community
  • 1
  • 1
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
1
 size_t size = 0;
 a->object[index].data = malloc(size);  
 memcpy(a->object[index].data, bytes, size);

Technically, it is undefined behavior.

malloc may return a null pointer for an allocation of zero bytes and passing a null pointer to memcpy is undefined behavior (even if ìts third argument is 0).

From the mighty C Standard (emphasis mine):

(C99, 7.1.4p1) "[...] unless explicitly stated otherwise in the detailed descriptions that follow: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined"

ouah
  • 142,963
  • 15
  • 272
  • 331
  • If malloc didn't return NULL when calling malloc(0), this would not be UB right? so checking malloc return value could have helped here; tricky this C –  Jul 13 '14 at 22:23
  • @dmcr_code Yes, right. The standard says "If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object." -- Unlike NULL, the latter is not an invalid address, and with a 0 count memcpy doesn't access the object. "tricky this C" -- yes; I wrote in it for decades but have moved on to more modern languages. – Jim Balter Jul 13 '14 at 22:30
  • @JimBalter: Yes, this is tricky. Thanks all for input I think case is resolved. ps. seems one needed to know NULL is not allowed to memcpy as ptr parameter for example... and also other stuff with malloc(0) –  Jul 13 '14 at 22:32
  • 1
    @dmcr_code When I was on the C Language Standards Committee, I protested against this pointlessly implementation-defined behavior ... the committee should have picked one behavior or the other and standardized it. But the committee was dominated by compiler vendors who didn't like changing their products. – Jim Balter Jul 13 '14 at 22:34
  • @JimBalter; Interesting! :) – haccks Jul 13 '14 at 22:35
  • 1
    @JimBalter: ok this is tricky yes, indeed checking return type of malloc could have helped here. But on the other hand one had to know no NULL is allowed on memcpy - and this is not mentioned in online documentation of memcpy for example; interesting about you and commitee :) –  Jul 13 '14 at 22:35
  • @dmcr_code There are many implications of the standard that aren't reported in the man pages. Note that the section of the standard that states that the behavior is UB is in a header section, " Use of library functions", which is not replicated in the description for each function. – Jim Balter Jul 13 '14 at 22:40
  • @JimBalter: maybe I should get a hold of that standard one day –  Jul 13 '14 at 22:43
  • @dmcr_code BTW, what I protested against was that implementations had the choice of returning NULL or some stub allocation for malloc(0), putting the burden on every programmer. I didn't protest against passing NULL being UB, probably because I didn't realize it, but I might have if I had. Somewhere I still have all my comments and criticisms of the standard. A historical nit: I was the first human being to vote to approve the C standard -- alphabetical order. – Jim Balter Jul 13 '14 at 22:44
  • @dmcr_code Official standards cost a bit of money but drafts are free ... grab yourself http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf – Jim Balter Jul 13 '14 at 22:45
  • @JimBalter: It's also weird I have to free(0), but whatever, it is not so relevant anymore - I was more surprised by these facts. In my code I treat it separately now and if size=0 don't allocate anything. I think more has to be done towards tools that can detect UB etc. :) like static analyzers. Thanks for the standard !!! (700 pages, doh :((). ps yes if it didn't return NULL this case would not be UB –  Jul 13 '14 at 22:50
  • @dmcr_code "It's also weird I have to free(0)" -- Not really. If malloc returns non-NULL, that should be a unique object with an address different from any other object. `malloc` needs resources to manage that, and those resources must be released. Anyway, if you don't skip the malloc for 0, why would you skip the free for 0? It's natural to always pair them. – Jim Balter Jul 13 '14 at 22:56
  • @dmcr_code "I treat it separately now and if size=0 don't allocate anything" -- another way is to malloc(1) instead of 0, which is close to what implementations that return non-NULL for malloc(0) do. – Jim Balter Jul 13 '14 at 22:58
  • @JimBalter: I mean logically it is weird to free something that has space of 0 bytes. Anyway thanks for interesting discussion I think i will go for sleep soon now. –  Jul 13 '14 at 23:02
  • @dmcr_code What I'm saying is that it's not weird because there is *overhead* ... when you ask for n bytes, malloc needs to set aside more than n. P.S. via http://xkcd.com/1335/ I now have some idea of where you are. :-) – Jim Balter Jul 13 '14 at 23:04
  • @JimBalter: I see, I am not arguing I am just saying intuitively somehow, somewhere, someone may think that since number of allocated bytes was 0 (malloc(0)) freeing it is not needed –  Jul 13 '14 at 23:06
  • @dmcr_code I lack that intuition ... fortunately. Consider creating a file with 0 bytes ... it needs to be deleted. A list with 0 nodes, likewise. Etc. – Jim Balter Jul 13 '14 at 23:08
  • @JimBalter: maybe we can have a quiz, and see how many people have it :)) (btw just saw, it seems that part from standard 7.1.4 applies to all library functions ... ). final note about the intuition: when you say malloc(0) if someone thinks 0 bytes was allocated, this means *nothing* was allocated -- hence, there is *no* need to de-allocate *nothing*; I think it is a bit different with files, not all know something more is allocated when 0 is passed behind the bars; but again it is not my goal to argue on this one –  Jul 13 '14 at 23:11
  • @dmcr_code Inexperienced people in every field tend to have faulty intuitions ... they are unfounded internal assumptions that get whittled away by experience. For someone who isn't arguing, you're putting up quite a fight! I suggest that you sleep on it. – Jim Balter Jul 13 '14 at 23:14
  • @JimBalter: I was just answering when you said something. Ok, again thanks for discussion. ps. it seems due to this standard passing NULL as pointers is NOT good for any library function? –  Jul 13 '14 at 23:17
  • I see no indication why this part of your answer is true: "even if it's third argument is 0." From a logical standpoint, this restriction also does not make sense, since copying 0 bytes should be a NO-OP without side effects. Can you give a source, why this "even if it's third argument is 0." is correct? – fex Dec 03 '18 at 15:58
  • @fex Did you read the quote of the C Standard in this answer? I explicitly emphasized the part that says it is undefined behavior. – ouah Dec 04 '18 at 19:31
  • @ouah You are right. Since the standard does not say anything about size 0, the "pointer must not be NULL" holds and hence your answer is correct. Nevertheless, its counter intuitive. Any guess why the standard is here so vague? – fex Dec 06 '18 at 12:01