/* Semmle test case for SuspiciousCallToStrncat.ql
   Associated with CWE-131  http://cwe.mitre.org/data/definitions/131.html
   Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
*/

///// Library functions //////
typedef unsigned long size_t;
char *strcpy(char *s1, const char *s2);
char *strncat(char *s1, const char *s2, size_t n);
size_t strlen(const char *s);

//// Test code /////

void good0(char *s) {
	char buf[80];
	strcpy(buf, "s = ");
	strncat(buf, s, sizeof(buf)-5); // GOOD
	strncat(buf, ".", 1); // $ MISSING: Alert // BAD [NOT DETECTED] -- there might not be even 1 character of space
}

void bad0(char *s) {
	char buf[80];
	strcpy(buf, "s = ");
	strncat(buf, s, sizeof(buf));  // $ Alert // BAD -- Forgot to allow for "s = "
	strncat(buf, ".", 1); // $ MISSING: Alert // BAD [NOT DETECTED] -- there might not be even 1 character of space
}

void good1(char *s) {
	char buf[80];
	strcpy(buf, "s = ");
	strncat(buf, s, sizeof(buf)-strlen("s = ")); // GOOD
	strncat(buf, ".", sizeof(buf)-strlen("s = ")-strlen(s)); // GOOD
}

void bad1(char *s) {
	char buf[80];
	strcpy(buf, "s = ");
	strncat(buf, s, sizeof(buf)-strlen("s = ")); // GOOD
	strncat(buf, ".", 1); // $ MISSING: Alert // BAD [NOT DETECTED] -- Need to check if any space is left
}

void strncat_test1(char *s) {
  char buf[80];
  strncat(buf, s, sizeof(buf) - strlen(buf) - 1); // GOOD
  strncat(buf, s, sizeof(buf) - strlen(buf));  // $ Alert // BAD
}

void* malloc(size_t);

void strncat_test2(char *s) {
  int len = 80;
  char* buf = (char *)malloc(len);
  strncat(buf, s, len - strlen(buf) - 1); // GOOD
  strncat(buf, s, len - strlen(buf));  // $ MISSING: Alert // BAD [NOT DETECTED]
}

struct buffers
{
  char array[50];
  char* pointer;
};

void strncat_test3(char* s, struct buffers* buffers) {
  unsigned len_array = strlen(buffers->array);
  unsigned max_size = sizeof(buffers->array);
  unsigned free_size = max_size - len_array;
  strncat(buffers->array, s, free_size); // $ Alert // BAD
}

#define MAX_SIZE 80

void strncat_test4(char *s) {
  char buf[MAX_SIZE];
  strncat(buf, s, MAX_SIZE - strlen(buf) - 1); // GOOD
  strncat(buf, s, MAX_SIZE - strlen(buf));  // $ Alert // BAD
  strncat(buf, "...", MAX_SIZE - strlen(buf)); // $ Alert // BAD
}

void strncat_test5(char *s) {
  int len = 80;
  char* buf = (char *) malloc(len + 1);
  strncat(buf, s, len - strlen(buf) - 1); // GOOD
  strncat(buf, s, len - strlen(buf)); // GOOD
}

void strncat_test6() {
  {
  char dest[60];
  dest[0] = '\0';
  // Will write `dest[0 .. 5]`
  strncat(dest, "small", sizeof(dest)); // $ SPURIOUS: Alert // GOOD [FALSE POSITIVE]
  }

  {
  char dest[60];
  memset(dest, 'a', sizeof(dest));
  dest[54] = '\0';
  // Will write `dest[54 .. 59]`
  strncat(dest, "small", sizeof(dest)); // $ SPURIOUS: Alert // GOOD [FALSE POSITIVE]
  }
}
