/* Verify that the analyzer correctly purges state when it sees a call to
   an unknown function.  */

#include <stdlib.h>

/* Verify fix for false-positive when checking for CVE-2005-1689.  */

typedef struct _krb5_data {
  char *data;
} krb5_data;

extern void krb5_read_message(krb5_data *buf);

void
test_1 (krb5_data inbuf)
{
  free(inbuf.data);
  krb5_read_message(&inbuf); 
  free(inbuf.data); /* { dg-bogus "double-'free'" } */
}

/* Verify that __pure__ functions are treated as not having side-effects.  */

extern int called_by_test_1a (void *)
  __attribute__ ((__pure__));
void test_1a (krb5_data inbuf)
{
  free (inbuf.data);
  called_by_test_1a (&inbuf);
  free (inbuf.data); /* { dg-warning "double-'free'" } */
}

/* Verify that global pointers can be affected by an unknown function.  */

void *global_ptr;
extern void unknown_side_effects (void);

void test_2 (void)
{
  free (global_ptr);
  unknown_side_effects ();
  free (global_ptr);
}

extern void called_by_test_3 (void *);

void test_3a (void)
{
  void *ptr = malloc (1024);
  called_by_test_3 (ptr);
}  /* { dg-bogus "leak" } */

void test_3b (void)
{
  krb5_data k;
  k.data = malloc (1024);
  called_by_test_3 (&k);
} /* { dg-bogus "leak" } */

/* Verify that we traverse the graph of regions that are reachable from
   the call.  */

struct foo
{
  struct foo *next;
  int *ptr;
};

/* First, without a call to an unknown function.  */

void test_4a (void)
{
  struct foo node_a;
  struct foo node_b;
  node_a.next = &node_b;
  node_b.ptr = malloc (sizeof (int));
  global_ptr = &node_a;
  *node_b.ptr = 42; /* { dg-warning "possibly-NULL" "possibly-NULL" } */
  /* Although there's a chain of pointers to the allocation, pointed to
     by global_ptr, the chain goes through the stack frame and thus
     there's a leak when it is popped.  */
} /* { dg-warning "leak of 'node_b.ptr'" } */

/* With a call to an unknown function.  */

void test_4b (void)
{
  struct foo node_a;
  struct foo node_b;
  node_a.next = &node_b;
  node_b.ptr = malloc (sizeof (int));
  global_ptr = &node_a;
  unknown_side_effects (); /* everything potentially visible through global_ptr.  */
  *node_b.ptr = 42; /* { dg-bogus "possibly-NULL" } */
} /* { dg-bogus "leak" } */

extern void called_by_test_5 (const char *);
void test_5 (void)
{
  called_by_test_5 ("???");
}

extern void called_by_test_6 (const struct foo *);
void test_6 (void)
{
  struct foo node;
  node.next = NULL;
  node.ptr = malloc (sizeof (int));

  /* This is a const ptr, but struct foo's ptr is non-const,
     so we ought to assume it could be written to.  */
  called_by_test_6 (&node);
} /* { dg-bogus "leak" } */

/* TODO: things reachable from "outside" i.e. by params to caller to entrypoint.  */
