/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
   Verify that out of bounds writes by built-ins to objects through pointers
   returned by other built-ins are diagnosed.
   { dg-do compile }
   { dg-options "-O2" } */

#include "range.h"

void* malloc (size_t);
void* memcpy (void*, const void*, size_t);
void* memmove (void*, const void*, size_t);
void* mempcpy (void*, const void*, size_t);

void sink (void*, ...);


void nowarn_memcpy (const void *s)
{
  extern char cpy_a4[4];
  unsigned n = sizeof cpy_a4;

  void *p = cpy_a4;
  p = memcpy (p, s, n);
  sink (p);
  memcpy (p, s, n);
  sink (p);

  p = cpy_a4 + 1;
  p = memcpy (p, s, n - 1);
  sink (p);
  memcpy (p, s, n - 1);
  sink (p);

  p = cpy_a4 + 2;
  p = memcpy (p, s, n - 2);
  sink (p);
  memcpy (p, s, n - 2);
  sink (p);

  p = cpy_a4 + 3;
  p = memcpy (p, s, n - 3);
  sink (p);
  memcpy (p, s, n - 3);
  sink (p);

  p = cpy_a4 + 4;
  p = memcpy (p, s, n - 4);
  sink (p);
  memcpy (p, s, n - 4);
  sink (p);
}


void nowarn_memcpy_chain (const void *s)
{
  extern char cpy_a8[8];

  char *p = cpy_a8;

  p = memcpy (p + 1, s, 7);
  sink (p);

  p = memcpy (p + 2 , s, 5);
  sink (p);

  p = memcpy (p + 3 , s, 2);
  sink (p);

  p = memcpy (p + 1 , s, 1);
  sink (p);

  p = memcpy (p - 7 , s, 8);
  sink (p);

  memcpy (p + 1, s, 7);
}


void warn_memcpy (const void *s)
{
  extern char cpy_a5[5];                // { dg-message "destination object 'cpy_a5'" "note" }

  unsigned n = sizeof cpy_a5;
  void *p = cpy_a5;

  p = memcpy (p, s, n);
  sink (p);
  memcpy (p, s, n + 1);                 // { dg-warning "writing 6 bytes into a region of size 5" }
  sink (p);

  p = cpy_a5;
  p = memcpy (p, s, n);
  sink (p);
  memcpy (p, s, n + 1);                 // { dg-warning "writing 6 bytes into a region of size 5" }
  sink (p);

  p = cpy_a5 + 1;
  p = memcpy (p, s, n - 1);
  sink (p);
  memcpy (p, s, n);                     // { dg-warning "writing 5 bytes into a region of size 4" }
  sink (p);
}


void warn_memcpy_chain (const void *s)
{
  extern char cpy_a8[8];                // { dg-message "destination object 'cpy_a8'" "note" }

  char *p = cpy_a8;

  p = memcpy (p, s, 9);                 // { dg-warning "writing 9 bytes into a region of size 8" }
  sink (p);

  p = memcpy (p + 2, s, 7);             // { dg-warning "writing 7 bytes into a region of size 6" }
  sink (p);

  p = memcpy (p + 3, s, 5);             // { dg-warning "writing 5 bytes into a region of size 3" }
  sink (p);

  p = memcpy (p + 3, s, 3);             // { dg-warning "writing 3 bytes into a region of size 0" }
  sink (p);
}


void nowarn_mempcpy (const void *s)
{
  extern char a4[4];
  unsigned n = sizeof a4;

  char *p = mempcpy (a4, s, n);
  sink (p);
  mempcpy (p - 4, s, n);
  sink (p);

  p = mempcpy (a4 + 1, s, n - 1);
  sink (p);
  mempcpy (p - 4, s, n);
  sink (p);

  p = mempcpy (a4 + 2, s, n - 2);
  sink (p);
  mempcpy (p - 4, s, n);
  sink (p);

  p = mempcpy (a4 + 3, s, n - 3);
  sink (p);
  mempcpy (p - 4, s, n);
  sink (p);

  p = mempcpy (a4 + 4, s, n - 4);
  sink (p);
  mempcpy (p - 4, s, n);
  sink (p);
}


void nowarn_mempcpy_chain (const void *s)
{
  extern char pcpy_a8[8];

  char *p = pcpy_a8;

  p = mempcpy (p + 1, s, 7);
  sink (p);

  p = mempcpy (p - 7 , s, 7);
  sink (p);

  p = mempcpy (p - 5 , s, 5);
  sink (p);

  p = mempcpy (p - 3 , s, 3);
  sink (p);

  p = mempcpy (p - 2 , s, 2);
  sink (p);

  mempcpy (p - 1, s, 1);
  sink (p);

  mempcpy (p - 8, s, 8);
}


void warn_mempcpy (const void *s)
{
  extern char pcpy_a5[5];               // { dg-message "destination object 'pcpy_a5'" "note" }

  char *p = pcpy_a5;

  p = mempcpy (p, s, 5);
  sink (p);
  mempcpy (p - 5, s, 6);                // { dg-warning "writing 6 bytes into a region of size 5 " }
  sink (p);

  p = pcpy_a5;
  p = mempcpy (p, s, 3);
  sink (p);
  mempcpy (p, s, 3);                    // { dg-warning "writing 3 bytes into a region of size 2 " }
  sink (p);

  p = pcpy_a5 + 1;
  p = mempcpy (p, s, 3);
  sink (p);
  mempcpy (p - 1, s, 5);                // { dg-warning "writing 5 bytes into a region of size 2 " }
  sink (p);
}


void warn_mempcpy_chain_3 (const void *s)
{
  char *p = malloc (5);                 // { dg-message "at offset \\\[3, 5] into destination object of size 5" "note" }
  p = mempcpy (p, s, UR (1, 2));
  p = mempcpy (p, s, UR (2, 3));
  p = mempcpy (p, s, UR (3, 4));        // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }

  sink (p);
}

void warn_mempcpy_offrng_chain_3 (const void *s)
{
  char *p = malloc (11);                 // { dg-message "at offset \\\[9, 11] into destination object of size 11 " "note" }
  size_t r1_2 = UR (1, 2);
  size_t r2_3 = r1_2 + 1;
  size_t r3_4 = r2_3 + 1;

  p = mempcpy (p + r1_2, s, r1_2);
  p = mempcpy (p + r2_3, s, r2_3);
  p = mempcpy (p + r3_4, s, r3_4);       // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }

  sink (p);
}

void warn_mempcpy_chain_4 (const void *s)
{
  char *p = malloc (9);                 // { dg-message "at offset \\\[6, 9] into destination object of size 9 " "note" }
  p = mempcpy (p, s, UR (1, 2));
  p = mempcpy (p, s, UR (2, 3));
  p = mempcpy (p, s, UR (3, 4));
  p = mempcpy (p, s, UR (4, 5));        // { dg-warning "writing between 4 and 5 bytes into a region of size 3 " }

  sink (p);
}

void warn_mempcpy_chain_5 (const void *s)
{
  char *p = malloc (14);                // { dg-message "at offset \\\[10, 14] into destination object of size 14 " "note" }
  p = mempcpy (p, s, UR (1, 2));
  p = mempcpy (p, s, UR (2, 3));
  p = mempcpy (p, s, UR (3, 4));
  p = mempcpy (p, s, UR (4, 5));
  p = mempcpy (p, s, UR (5, 6));        // { dg-warning "writing between 5 and 6 bytes into a region of size 4 " }

  sink (p);
}