Problem report: Struct aliasing problem causes Thread_Ready_Chain corruption in 4.6.99.3

Till Straumann strauman at slac.stanford.edu
Mon Dec 4 23:14:49 UTC 2006


Eric Norum wrote:
> On Dec 4, 2006, at 4:11 PM, Till Straumann wrote:
>
>> The C99 standard invents special, magic semantics for 'memcpy' 
>> 'malloc' etc.
>> so that allocated memory can be 'assigned' an 'effective type'.
>>
>> To me, this shows how inherently broken the alias rule is.
>> There are many occasions where you might want to provide
>> an allocator of some kind (e.g., allocate a buffer from a pool)
>> w/o knowing what type of object the item finally should represent.
>>
>> However, it seems to me that you cannot 'c99-legally' write
>> such an allocator.
>>
>> I can't figure out a way to, e.g., allocate a (generic) buffer,
>> read data into it and figuring out later that it is a UDP packet
>> and then access it as as 'struct udp_packet'.
>>
>
> Does storing through a (char *) pointer not work for this?  I was 
> under the impression that memory accessed through such a pointer had 
> to be treated by the compiler as possibly aliasing with any other 
> pointer access.

Yes. If you declare

struct udp_packet {
    struct ip_header iph;
    struct udp_h       udph;
    char                   data[];
};

int read_packet(char*);

then

udp_packet x;

     read_packet((char*)&x);

would be OK ( 'char *' read_packet arg may alias struct udp_packet).

However,

/* a generic buffer */
typedef char Pbuf[2048];

Pbuf *buf_alloc();

Pbuf *abuf = buf_alloc();

/* code goes here that determines that abuf is actually an IP/UDP packet */

struct udp_packet *pudp = (struct udp_packet*)abuf;

/* Dereferencing pudp as struct udp_packet is illegal */
if ( pudp->iph.source == some_addr )
   ...

I believe you would have to declare a union

union clumsy_union {
   Pbuf                       raw_buffer;
   struct udp_packet packet;
} xxx;

xxx *abuf = (union clumsy_union *)buf_alloc();

/* looking at union member as struct udp_packet is OK */

if ( abuf->packet.iph.source == some_addr )
   ...

The problem with this approach is that you don't know
upfront that your data is a udp packet.

So you'd need unions of all possible variants at each
layer of a protocol stack.

Again: the old-fashioned way [w/o unions] 'just works'
in many cases because nobody really enforces or even
uses the alias rule but I believe the 1st example violates
the C99 standard.

Plus, it seems that there is a gcc (extension?) so that

extern char *some_allocator();

int tst()
{
char *rval = some_allocator();
    rval[0]      = 0;
    *(int*)rval = 0xffffffff;
    return rval[0];
}

returns indeed 0xff (i.e., gcc assumes *(int*)rval may alias
*rval) whereas

extern short *some_other_allocator();

int tst()
{
short *rval = some_other_allocator();
    rval[0]  = 0;
    *(int*)rval = 0xffffffff;
    return rval[0];
}

returns 0. So gcc seems to not only allow char to alias type anything but
also conversely anything to alias char. This is more relaxed than the
standard which says

"The effective type of an object for an access to its stored value is the
 declared type of the object, if any. [here comes wording about the case
 where the object has no declared type]."

"An object shall have its value accessed only by an lvalue expression
 that has one of the following types:
  - a type compatible with the effective type of the object.
  - [ qualified and signed versions ...]
  - a character type.
"

In my example, it seems that the effective type of *rval is char
which is not compatible with the type of the lvalue *(int*)rval
nor is it a character type -> illegal alias.

OTOH, accessing an int via char * is OK.

-- Till
>
>
> --Eric Norum <norume at aps.anl.gov>
> Advanced Photon Source
> Argonne National Laboratory
> (630) 252-4793
>
>




More information about the users mailing list