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

Martin Sebor <msebor at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |RESOLVED
         Resolution|---                         |INVALID

--- Comment #4 from Martin Sebor <msebor at gcc dot gnu.org> ---
I've created a test case using the canonicalize_pathname function showing that
it does, in fact, cause an overlap to take place.  The following line in the
output of the test case

  strcpy (0x217f265 = "//bar", 0x217f267 = "bar"): 3

shows that strcpy is being called to copy the 4 bytes "bar\0" from 0x217f267 to
0x217f265, with the last two bytes of the copy overlapping its first two bytes.

$ cat canonicalize_pathname.c && gcc -DPATHNAME='"/foo///bar"' -O2 -Wall
canonicalize_pathname.c && ./a.out
char*
strcpy (char *d, const char *s)
{
  unsigned n = __builtin_strlen (s);
  __builtin_printf ("%s (%p = \"%s\", %p = \"%s\"): %u\n",
                    __func__, d, d, s, s, n);
  __builtin_memcpy (d, s, n + 1);
  return d;
}

char *
canonicalize_pathname (char *path)
{
  int i, start;
  char stub_char, *result;

  result = __builtin_strdup( path );

  stub_char = (*path == '/') ? '/' : '.';

  i = 0;
  while (result[i]) {
    while (result[i] != '\0' && result[i] != '/')
      i++;

    start = i++;

    if (!result[start])
      break;

    while (result[i] == '/')
      i++;

    if ((start + 1) != i)
      {
        strcpy( result + start + 1, result + i );
        i = start + 1;
      }

    if (start > 0 && result[start - 1] == '\\')
      continue;

    if ((start && !result[i])
        || (result[i] == '.' && !result[i+1])) {
      result[--i] = '\0';
      break;
    }

    if (result[i] == '.') {

      if (result[i + 1] == '/') {
        strcpy( result + i, result + i + 1 );
        i = (start < 0) ? 0 : start;
        continue;
      }

      if (result[i + 1] == '.' &&
          (result[i + 2] == '/' || !result[i + 2])) {
        while (--start > -1 && result[start] != '/')
          ;
        strcpy( result + start + 1, result + i + 2 );
        i = (start < 0) ? 0 : start;
        continue;
      }
    }
  }

  if (!*result) {
    *result = stub_char;
    result[1] = '\0';
  }

  return result;
}


int main (void)
{
  char *p = canonicalize_pathname (PATHNAME);
  __builtin_printf ("%s\n", p);
}
canonicalize_pathname.c: In function ‘canonicalize_pathname’:
canonicalize_pathname.c:61:2: warning: ‘strcpy’ accessing 1 byte at offsets [0,
9223372036854775807] and [0, 9223372036854775807] may overlap 1 byte at offset
0 [-Wrestrict]
   61 |  strcpy( result + start + 1, result + i + 2 );
      |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
strcpy (0x217f265 = "//bar", 0x217f267 = "bar"): 3
/foo/bar

Reply via email to