/* Apply edge_info to state. */
impl_region_model_context
bifurcation_ctxt (*this,
- NULL, // enode_for_diag
+ node, // enode_for_diag
&path_ctxt.get_state_at_bifurcation (),
&bifurcated_new_state,
NULL, // uncertainty_t *uncertainty
const call_details cd (get_call_details (model, ctxt));
const svalue *ptr_sval = cd.get_arg_svalue (0);
const svalue *size_sval = cd.get_arg_svalue (1);
- if (const region *buffer_reg = ptr_sval->maybe_get_region ())
+
+ /* We can only grow in place with a non-NULL pointer. */
+ {
+ const svalue *null_ptr
+ = model->m_mgr->get_or_create_int_cst (ptr_sval->get_type (), 0);
+ if (!model->add_constraint (ptr_sval, NE_EXPR, null_ptr,
+ cd.get_ctxt ()))
+ return false;
+ }
+
+ if (const region *buffer_reg = model->deref_rvalue (ptr_sval, NULL_TREE,
+ ctxt))
if (compat_types_p (size_sval->get_type (), size_type_node))
model->set_dynamic_extents (buffer_reg, size_sval, ctxt);
if (cd.get_lhs_region ())
= model->create_region_for_heap_alloc (new_size_sval, ctxt);
const svalue *new_ptr_sval
= model->m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+ if (!model->add_constraint (new_ptr_sval, NE_EXPR, old_ptr_sval,
+ cd.get_ctxt ()))
+ return false;
+
if (cd.get_lhs_type ())
cd.maybe_set_lhs (new_ptr_sval);
- if (const region *freed_reg = old_ptr_sval->maybe_get_region ())
+ if (const region *freed_reg = model->deref_rvalue (old_ptr_sval,
+ NULL_TREE, ctxt))
{
/* Copy the data. */
const svalue *old_size_sval = model->get_dynamic_extents (freed_reg);
old_size_sval);
const svalue *buffer_content_sval
= model->get_store_value (sized_old_reg, cd.get_ctxt ());
- model->set_value (new_reg, buffer_content_sval, cd.get_ctxt ());
+ const region *sized_new_reg
+ = model->m_mgr->get_sized_region (new_reg, NULL,
+ old_size_sval);
+ model->set_value (sized_new_reg, buffer_content_sval,
+ cd.get_ctxt ());
+ }
+ else
+ {
+ /* We don't know how big the old region was;
+ mark the new region as having been touched to avoid uninit
+ issues. */
+ model->mark_region_as_unknown (new_reg, cd.get_uncertainty ());
}
/* Free the old region, so that pointers to the old buffer become
: taint_diagnostic (sm, arg, has_bounds),
m_mem_space (mem_space)
{
- gcc_assert (mem_space == MEMSPACE_STACK || mem_space == MEMSPACE_HEAP);
+ gcc_assert (mem_space == MEMSPACE_STACK
+ || mem_space == MEMSPACE_HEAP
+ || mem_space == MEMSPACE_UNKNOWN);
}
const char *get_kind () const FINAL OVERRIDE
diagnostic_metadata m;
/* "CWE-789: Memory Allocation with Excessive Size Value". */
m.add_cwe (789);
- gcc_assert (m_mem_space == MEMSPACE_STACK || m_mem_space == MEMSPACE_HEAP);
+ gcc_assert (m_mem_space == MEMSPACE_STACK
+ || m_mem_space == MEMSPACE_HEAP
+ || m_mem_space == MEMSPACE_UNKNOWN);
// TODO: make use of m_mem_space
if (m_arg)
switch (m_has_bounds)
const svalue *size_in_bytes,
region_model_context *ctxt) const
{
- gcc_assert (mem_space == MEMSPACE_STACK || mem_space == MEMSPACE_HEAP);
+ gcc_assert (mem_space == MEMSPACE_STACK
+ || mem_space == MEMSPACE_HEAP
+ || mem_space == MEMSPACE_UNKNOWN);
gcc_assert (size_in_bytes);
gcc_assert (ctxt);
--- /dev/null
+/* { dg-additional-options "--param analyzer-max-enodes-per-program-point=10" } */
+// TODO: remove need for this option
+
+typedef __SIZE_TYPE__ size_t;
+#define NULL ((void *)0)
+#define POLLIN 0x001
+
+typedef struct {
+ unsigned long int __val[(1024 / (8 * sizeof(unsigned long int)))];
+} __sigset_t;
+
+typedef unsigned int socklen_t;
+
+struct timespec {
+ /* [...snip...] */
+};
+
+typedef unsigned long int nfds_t;
+
+struct pollfd {
+ int fd;
+ short int events;
+ short int revents;
+};
+
+extern int ppoll(struct pollfd *__fds, nfds_t __nfds,
+ const struct timespec *__timeout, const __sigset_t *__ss);
+
+struct sockaddr_un {
+ /* [...snip...] */
+ char sun_path[108];
+};
+
+typedef union {
+ /* [...snip...] */
+ struct sockaddr_un *__restrict __sockaddr_un__;
+ /* [...snip...] */
+} __SOCKADDR_ARG __attribute__((transparent_union));
+
+extern int accept(int __fd, __SOCKADDR_ARG __addr,
+ socklen_t *__restrict __addr_len);
+
+extern void *calloc(size_t __nmemb, size_t __size)
+ __attribute__((__nothrow__, __leaf__))
+ __attribute__((__malloc__))
+ __attribute__((__alloc_size__(1, 2)));
+
+extern void *realloc(void *__ptr, size_t __size)
+ __attribute__((__nothrow__, __leaf__))
+ __attribute__((__warn_unused_result__))
+ __attribute__((__alloc_size__(2)));
+
+extern void exit(int __status)
+ __attribute__((__nothrow__, __leaf__))
+ __attribute__((__noreturn__));
+
+int main() {
+ int rc;
+ int nsockets = 1;
+ struct pollfd *pollfds, *newpollfds;
+ struct sockaddr_un remote;
+ socklen_t len = sizeof(remote);
+
+ pollfds = calloc(1, sizeof(struct pollfd));
+ if (!pollfds) {
+ exit(1);
+ }
+
+ while (1) {
+ rc = ppoll(pollfds, nsockets, NULL, NULL);
+ if (rc < 0) {
+ continue;
+ }
+
+ if (pollfds[0].revents & POLLIN) {
+ nsockets++;
+ newpollfds = realloc(pollfds, nsockets * sizeof(*pollfds));
+ if (!newpollfds) {
+ exit(1);
+ }
+ pollfds = newpollfds;
+ pollfds[nsockets - 1].fd = accept(pollfds[0].fd, &remote, &len);
+ }
+ }
+ return 0;
+}
--- /dev/null
+typedef __SIZE_TYPE__ size_t;
+#define NULL ((void *)0)
+#define POLLIN 0x001
+
+typedef struct {
+ unsigned long int __val[(1024 / (8 * sizeof(unsigned long int)))];
+} __sigset_t;
+
+typedef unsigned int socklen_t;
+
+struct timespec {
+ /* [...snip...] */
+};
+
+typedef unsigned long int nfds_t;
+
+struct pollfd {
+ int fd;
+ short int events;
+ short int revents;
+};
+
+extern int ppoll(struct pollfd *__fds, nfds_t __nfds,
+ const struct timespec *__timeout, const __sigset_t *__ss);
+
+struct sockaddr_un {
+ /* [...snip...] */
+ char sun_path[108];
+};
+
+typedef union {
+ /* [...snip...] */
+ struct sockaddr_un *__restrict __sockaddr_un__;
+ /* [...snip...] */
+} __SOCKADDR_ARG __attribute__((transparent_union));
+
+extern int accept(int __fd, __SOCKADDR_ARG __addr,
+ socklen_t *__restrict __addr_len);
+
+extern void *calloc(size_t __nmemb, size_t __size)
+ __attribute__((__nothrow__, __leaf__))
+ __attribute__((__malloc__))
+ __attribute__((__alloc_size__(1, 2)));
+
+extern void *realloc(void *__ptr, size_t __size)
+ __attribute__((__nothrow__, __leaf__))
+ __attribute__((__warn_unused_result__))
+ __attribute__((__alloc_size__(2)));
+
+extern void exit(int __status)
+ __attribute__((__nothrow__, __leaf__))
+ __attribute__((__noreturn__));
+
+int main() {
+ int rc;
+ int nsockets = 1;
+ struct pollfd *pollfds, *newpollfds;
+ struct sockaddr_un remote;
+ socklen_t len = sizeof(remote);
+
+ pollfds = calloc(1, sizeof(struct pollfd));
+ if (!pollfds) {
+ exit(1);
+ }
+
+ rc = ppoll(pollfds, nsockets, NULL, NULL);
+ if (rc < 0) {
+ exit(2);
+ }
+
+ nsockets++;
+ newpollfds = realloc(pollfds, nsockets * sizeof(*pollfds));
+ if (!newpollfds) {
+ exit(3);
+ }
+ pollfds = newpollfds;
+ pollfds[nsockets - 1].fd = accept(pollfds[0].fd, &remote, &len);
+ exit(4);
+}
--- /dev/null
+#include "analyzer-decls.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+#define NULL ((void *)0)
+
+extern void *calloc (size_t __nmemb, size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__malloc__))
+ __attribute__ ((__alloc_size__ (1, 2))) ;
+extern void *malloc (size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__malloc__))
+ __attribute__ ((__alloc_size__ (1)));
+extern void *realloc (void *__ptr, size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__warn_unused_result__))
+ __attribute__ ((__alloc_size__ (2)));
+extern void free (void *__ptr)
+ __attribute__ ((__nothrow__ , __leaf__));
+
+/* realloc of calloc buffer. */
+
+char *test_8 (size_t sz)
+{
+ char *p, *q;
+
+ p = calloc (1, 3);
+ if (!p)
+ return NULL;
+
+ __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(\[^\n\r\]*\\)3'" } */
+
+ __analyzer_eval (p[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[1] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[2] == 0); /* { dg-warning "TRUE" } */
+
+ q = realloc (p, 6);
+
+ /* We should have 3 nodes, corresponding to "failure",
+ "success without moving", and "success with moving". */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+
+ if (q)
+ {
+ __analyzer_dump_capacity (q); /* { dg-warning "capacity: '\\(\[^\n\r\]*\\)6'" } */
+ q[3] = 'd';
+ q[4] = 'e';
+ q[5] = 'f';
+ if (q == p)
+ {
+ /* "realloc" success, growing the buffer in-place. */
+ __analyzer_eval (p[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[1] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[2] == 0); /* { dg-warning "TRUE" } */
+ }
+ else
+ {
+ /* "realloc" success, moving the buffer (and thus freeing "p"). */
+ __analyzer_eval (q[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[1] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[2] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[0] == 'a'); /* { dg-warning "UNKNOWN" "unknown" } */
+ /* { dg-warning "use after 'free' of 'p'" "use after free" { target *-*-* } .-1 } */
+ }
+ __analyzer_eval (q[3] == 'd'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[4] == 'e'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[5] == 'f'); /* { dg-warning "TRUE" } */
+ }
+ else
+ {
+ /* "realloc" failure. p should be unchanged. */
+ __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(\[^\n\r\]*\\)3'" } */
+ __analyzer_eval (p[0] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[1] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[2] == 0); /* { dg-warning "TRUE" } */
+ return p;
+ }
+
+ return q;
+}
--- /dev/null
+#include "analyzer-decls.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+#define NULL ((void *)0)
+
+extern void *calloc (size_t __nmemb, size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__malloc__))
+ __attribute__ ((__alloc_size__ (1, 2))) ;
+extern void *malloc (size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__malloc__))
+ __attribute__ ((__alloc_size__ (1)));
+extern void *realloc (void *__ptr, size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__warn_unused_result__))
+ __attribute__ ((__alloc_size__ (2)));
+extern void free (void *__ptr)
+ __attribute__ ((__nothrow__ , __leaf__));
+
+/* realloc where we don't know the original size of the region. */
+
+char *test_8 (char *p, size_t sz)
+{
+ char *q;
+
+ __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(\[^\n\r\]*\\)'" } */
+
+ p[0] = 'a';
+ p[1] = 'b';
+ p[2] = 'c';
+ __analyzer_eval (p[0] == 'a'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[1] == 'b'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[2] == 'c'); /* { dg-warning "TRUE" } */
+
+ q = realloc (p, 6);
+
+ /* We should have 3 nodes, corresponding to "failure",
+ "success without moving", and "success with moving". */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 processed enodes" } */
+
+ if (q)
+ {
+ q[3] = 'd';
+ q[4] = 'e';
+ q[5] = 'f';
+ if (q == p)
+ {
+ /* "realloc" success, growing the buffer in-place. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_capacity (q); /* { dg-warning "capacity: '\\(\[^\n\r\]*\\)6'" } */
+ __analyzer_eval (q[0] == 'a'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[1] == 'b'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[2] == 'c'); /* { dg-warning "TRUE" } */
+ }
+ else
+ {
+ /* "realloc" success, moving the buffer (and thus freeing "p"). */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_capacity (q); /* { dg-warning "capacity: '\\(\[^\n\r\]*\\)6'" } */
+ /* We don't know how much of the buffer is copied. */
+ __analyzer_eval (q[0] == 'a'); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (q[1] == 'b'); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (q[2] == 'c'); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[0] == 'a'); /* { dg-warning "UNKNOWN" "unknown" } */
+ /* { dg-warning "use after 'free' of 'p'" "use after free" { target *-*-* } .-1 } */
+ }
+ __analyzer_eval (q[3] == 'd'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[4] == 'e'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[5] == 'f'); /* { dg-warning "TRUE" } */
+ }
+ else
+ {
+ /* "realloc" failure. p should be unchanged. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
+ __analyzer_dump_capacity (q); /* { dg-warning "capacity: 'UNKNOWN\\(\[^\n\r\]*\\)'" } */
+ __analyzer_eval (p[0] == 'a'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[1] == 'b'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[2] == 'c'); /* { dg-warning "TRUE" } */
+ return p;
+ }
+
+ return q;
+}
--- /dev/null
+// TODO: remove need for this option:
+/* { dg-additional-options "-fanalyzer-checker=taint" } */
+
+#include "analyzer-decls.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* realloc with tainted size. */
+
+void *p;
+
+void __attribute__((tainted_args))
+test_1 (size_t sz) /* { dg-message "\\(1\\) function 'test_1' marked with '__attribute__\\(\\(tainted_args\\)\\)'" } */
+{
+ void *q;
+
+ __analyzer_dump_state ("taint", sz); /* { dg-warning "state: 'tainted'" } */
+
+ q = realloc (p, sz); /* { dg-warning "use of attacker-controlled value 'sz' as allocation size without upper-bounds checking" } */
+}