10
votes

As pointed out in an answer to this question, the compiler (in this case gcc-4.1.2, yes it's old, no I can't change it) can replace struct assignments with memcpy where it thinks it is appropriate.

I'm running some code under valgrind and got a warning about memcpy source/destination overlap. When I look at the code, I see this (paraphrasing):

struct outer
{
    struct inner i;
    // lots of other stuff
};

struct inner
{
    int x;
    // lots of other stuff
};

void frob(struct inner* i, struct outer* o)
{
    o->i = *i;
}

int main()
{
    struct outer o;

    // assign a bunch of fields in o->i...

    frob(&o.i, o);
    return 0;
}

If gcc decides to replace that assignment with memcpy, then it's an invalid call because the source and dest overlap.

Obviously, if I change the assignment statement in frob to call memmove instead, then the problem goes away.

But is this a compiler bug, or is that assignment statement somehow invalid?

3
I'm pretty sure you meant to write frob(..., &o), and not (..., 0). - Andy Finkenstadt
It's a compiler bug. The entire gcc 4.x series is full of crap like this, including breaking LIST_ENTRY / LIST_HEAD type punning which predates gcc 1.0. Join the gcc-4 resistance! - Heath Hunnicutt
@Andy - My fat fingers. Thanks. - bstpierre
@Heath: The entire 4.x series? Are you seriously suggesting that we move back to 3.x? Or did you invent a time machine and you have access to the awesome futuristic 5.x series? - Adam Rosenfield
Accepted Jens' answer as I think it most clearly defines the issue. R.. is probably right in thinking that this is a gcc bug (at least a lurking bug) but in my particular combination it isn't a problem. At the very least I changed it to memmove to get valgrind to shut up. Thanks for the answers. - bstpierre

3 Answers

4
votes

I think that you are mixing up the levels. gcc is perfectly correct to replace an assignment operation by a call to any library function of its liking, as long as it can guarantee the correct behavior.

It is not "calling" memcpy or whatsoever in the sense of the standard. It is just using one function it its library for which it might have additional information that guarantees correctness. The properties of memcpy as they are described in the standard are properties seen as interfaces for the programmer, not for the compiler/environment implementor.

Whether or not memcpy in that implementation in question implements a behavior that makes it valid for the assignment operation is another question. It should not be so difficult to check that or even to inspect the code.

4
votes

As far as I can tell, this is a compiler bug. i is allowed to alias &o.i according to the aliasing rules, since the types match and the compiler cannot prove that the address of o.i could not have been previously taken. And of course calling memcpy with overlapping (or same) pointers invokes UB.

By the way note that, in your example, o->i is nonsense. You meant o.i I think...

1
votes

I suppose that there is a typo: "&o" instead of "0". Under this hypothesis, the "overlap" is actually a strict overwrite: memcpy(&o->i,&o->i,sizeof(o->i)). In this particular case memcpy behaves correctly.