https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86259

--- Comment #14 from Martin Sebor <msebor at gcc dot gnu.org> ---
> You say that
> 
>  struct { int a; int b; } s, s2;
>  memcpy (&s2.a, &s.a, sizeof (s));
> 
> is invalid, aka not copying the whole structure since you pass in a
> pointer to s2.a rather than s2?

Yes.  It's invalid for the same reason as the following:

  int *p = &s.a;
  int *q = &s2.a;

  *q = *p;    // okay
  ++q, ++p;   // okay
  *q = *p;    // undefined #1
  ++q, ++p;   // undefined #2

In s.a and s.b are effectively arrays of one element, i.e., int[1].  As Joseph
explained in comment #13, pointer arithmetic is defined only for and within
each array object, irrespective of whether the array is itself an element of
another array or a member of a struct.  So above, #1 is undefined because it
accesses a past-the-end element of an array, and #2 is undefined because the
only valid arithmetic on a past-the-end pointer is to subtract one from it.

This is specified in 6.5.6 Additive operators, p8 of C11.  In C89, the
corresponding text is in section 3.3.6.  The same restriction applies to all
library functions, including memcpy and strcpy.

> Alternatively
> 
>  struct { int x; int a; int b; } s, s2;
>  memcpy (&s2.a, &s.a, sizeof (s) - sizeof (int));
> 
> is similarly invalid, not copying the last two elements?

Yes.  It's invalid for the same reasons as above.  This would be valid:

  struct S { int x; int a; int b; } s, s2;
  memcpy ((char*)&s2 + offseof (struct S, a),
          (char*)&2 + offseof (struct S, a),
          sizeof (s) - offseof (struct S, a));

This is valid because &s and &s2 point to the objects s and s2.  Any object can
be interpreted as an array of char with a size equal to the object itself and
the pointer arithmetic is valid anywhere within the object.

> 
> I don't see how str* are in anyway less special than mem* here in case you
> come up with an exception for mem*.

In the standard, the restrictions above apply equally to expressions as well as
library functions.  For better or worse, GCC treats memxxx() functions
differently than strxxx() functions because of legacy invalid code that relies
on the second memcpy example working.  This is evident from the effects of
_FORTIFY_SOURCE.  For example, the following gets a warning when compiled with
_FORTIFY_SOURCE=1 and aborts with a buffer overflow error at runtime:

  #include <string.h>

  int main (void)
  {
    struct {
      char a[4];
      char b[3];
    } a;

    a[0] = '0';
    strcpy (a.a + 1, "12345");

    __builtin_puts (a.a);
  }

In function ‘strcpy’,
    inlined from ‘main’ at c.c:12:5:
/usr/include/bits/string3.h:110:10: warning: ‘__builtin___memcpy_chk’ writing 6
bytes into a region of size 3 overflows the destination [-Wstringop-overflow=]
...
*** buffer overflow detected ***: ./a.out terminated

(The region of 3 is the amount of space in the a.a array at offset 1.)

but because the special treatment applies only to the memxxx functions and not
to string functions, the equivalent program using memcpy() compiles without a
warning and runs successfully to completion:

  #include <string.h>

  int main (void)
  {
    struct {
      char a[4];
      char b[3];
    } a;

    a[0] = '0';
    memcpy (a.a + 1, "12345", 6);

    __builtin_puts (a.a);
  }

Because the special treatment applies only to raw memory functions like memcpy
and memmove and not to string functions like strcpy or strlen, none of the test
cases here is valid even under GCC's relaxed rules.

Reply via email to