memcpy(3), strict aliasing, pointer provenance rules (was: [PATCH] QUIC: better sockaddr initialization)

Alejandro Colomar alx.manpages at
Mon May 22 14:23:56 UTC 2023


On 5/22/23 04:35, Maxim Dounin wrote:

> As you've correctly noticed, your example is different from the 
> nginx code, since it uses a local variable (and not an allocated 
> object, like nginx does).
> The next question you might consider is: what difference it makes?  
> And if at all?  In particular, you might want to consider what 
> memcpy() actually does.  As per the quoted paragraph, it does two 
> things:
> 1. Modifies the object as pointed out by (void *) &y.t;
> 2. Changes the effective type of the modified object for 
>    subsequent accesses (to the effective type of the object from 
>    which the value is copied) if there is no declared type.
> For your example, (2) is not relevant, since there is a 
> declared type.  So the remaining part is (1).


In fact, I'm now closer to thinking that the same thing via
malloc(3) was similarly UB.

> What actually memcpy() modifies, and what it is allowed to modify?  

According to ISO C, memcpy() isn't special in this regard.  6.5.6
(Additive operators) applies:

So accesses outside the bounds of the (sub)object to which the
pointer points, are UB, even via memcpy(3).

You won't find any specification that memcpy(3) is allowed to
cross subobject boundaries in order to copy bytes.

However, GCC relaxes the rules for mem*() functions in an
undocumented way, so that memcpy() can cross subobject boundaries
to perform the copy.  I asked that GCC documents this, and
specifies under which rules this works, and what are the limits.

str*() functions seem to have gotten the same exception recently
in GCC.


> What makes you think that compiler is free to assume that you've 
> wrote only to the y.t member, and not just some bytes in the y 
> object?

Even if memcpy(3) is allowed to write bytes to subobjects beyond
the one pointed to by the pointer passed to it, I'm not convinced
that this has a valid meaning when there is no subobject which to
write.  Since in this case, the write happens to padding bytes in
the parent object, I'm not sure if the GCC extension applies to
padding bytes.

So, according to ISO C, I'm now convinced that it's UB, even via
memcpy(3).  According to the more relaxed GNU C rules, I'm not
sure, since it's not even documented.  We only know that it seems
to work so far.


GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <>

More information about the nginx-devel mailing list