aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2022-10-24 16:38:22 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2022-10-24 16:38:22 -0400
commit792f039fc37faa3446725a643c8018f084e8ccab (patch)
treed6f5bfae9af0528c090314d46b2c14859640d221
parent244021b6c1a7bdeb777874ddc2ebcecb95610ef1 (diff)
downloadgcc-792f039fc37faa3446725a643c8018f084e8ccab.zip
gcc-792f039fc37faa3446725a643c8018f084e8ccab.tar.gz
gcc-792f039fc37faa3446725a643c8018f084e8ccab.tar.bz2
analyzer: handle "pipe" and "pipe2" [PR106300]
gcc/analyzer/ChangeLog: PR analyzer/106300 * engine.cc (impl_region_model_context::get_fd_map): New. * exploded-graph.h (impl_region_model_context::get_fd_map): New decl. * region-model-impl-calls.cc (region_model::impl_call_pipe): New. * region-model.cc (region_model::update_for_int_cst_return): New, based on... (region_model::update_for_zero_return): ...this. Reimplement in terms of the former. (region_model::on_call_pre): Handle "pipe" and "pipe2". (region_model::on_call_post): Likewise. * region-model.h (region_model::impl_call_pipe): New decl. (region_model::update_for_int_cst_return): New decl. (region_model::mark_as_valid_fd): New decl. (region_model_context::get_fd_map): New pure virtual fn. (noop_region_model_context::get_fd_map): New. (region_model_context_decorator::get_fd_map): New. * sm-fd.cc: Include "analyzer/program-state.h". (fd_state_machine::describe_state_change): Handle transitions from start state to valid states. (fd_state_machine::mark_as_valid_fd): New. (fd_state_machine::on_stmt): Add missing return for "creat". (region_model::mark_as_valid_fd): New. gcc/ChangeLog: PR analyzer/106300 * doc/invoke.texi (Static Analyzer Options): Add "pipe" and "pipe2" to the list of functions the analyzer has hardcoded knowledge of. gcc/testsuite/ChangeLog: PR analyzer/106300 * gcc.dg/analyzer/pipe-1.c: New test. * gcc.dg/analyzer/pipe-glibc.c: New test. * gcc.dg/analyzer/pipe-manpages.c: New test. * gcc.dg/analyzer/pipe2-1.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
-rw-r--r--gcc/analyzer/engine.cc15
-rw-r--r--gcc/analyzer/exploded-graph.h3
-rw-r--r--gcc/analyzer/region-model-impl-calls.cc70
-rw-r--r--gcc/analyzer/region-model.cc35
-rw-r--r--gcc/analyzer/region-model.h26
-rw-r--r--gcc/analyzer/sm-fd.cc56
-rw-r--r--gcc/doc/invoke.texi1
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pipe-1.c38
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pipe-glibc.c71
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pipe-manpages.c76
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pipe2-1.c38
11 files changed, 420 insertions, 9 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 46bcaed..a664a99 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -229,6 +229,21 @@ impl_region_model_context::get_malloc_map (sm_state_map **out_smap,
}
bool
+impl_region_model_context::get_fd_map (sm_state_map **out_smap,
+ const state_machine **out_sm,
+ unsigned *out_sm_idx)
+{
+ unsigned fd_sm_idx;
+ if (!m_ext_state.get_sm_idx_by_name ("file-descriptor", &fd_sm_idx))
+ return false;
+
+ *out_smap = m_new_state->m_checker_states[fd_sm_idx];
+ *out_sm = &m_ext_state.get_sm (fd_sm_idx);
+ *out_sm_idx = fd_sm_idx;
+ return true;
+}
+
+bool
impl_region_model_context::get_taint_map (sm_state_map **out_smap,
const state_machine **out_sm,
unsigned *out_sm_idx)
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 11e46ca..ad278e2 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -96,6 +96,9 @@ class impl_region_model_context : public region_model_context
{
return &m_ext_state;
}
+ bool get_fd_map (sm_state_map **out_smap,
+ const state_machine **out_sm,
+ unsigned *out_sm_idx) final override;
bool get_malloc_map (sm_state_map **out_smap,
const state_machine **out_sm,
unsigned *out_sm_idx) final override;
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 8f4940a..52c4205 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -563,6 +563,76 @@ region_model::impl_call_memset (const call_details &cd)
fill_region (sized_dest_reg, fill_value_u8);
}
+/* Handle the on_call_post part of "pipe". */
+
+void
+region_model::impl_call_pipe (const call_details &cd)
+{
+ class failure : public failed_call_info
+ {
+ public:
+ failure (const call_details &cd) : failed_call_info (cd) {}
+
+ bool update_model (region_model *model,
+ const exploded_edge *,
+ region_model_context *ctxt) const final override
+ {
+ /* Return -1; everything else is unchanged. */
+ const call_details cd (get_call_details (model, ctxt));
+ model->update_for_int_cst_return (cd, -1, true);
+ return true;
+ }
+ };
+
+ class success : public success_call_info
+ {
+ public:
+ success (const call_details &cd) : success_call_info (cd) {}
+
+ bool update_model (region_model *model,
+ const exploded_edge *,
+ region_model_context *ctxt) const final override
+ {
+ const call_details cd (get_call_details (model, ctxt));
+
+ /* Return 0. */
+ model->update_for_zero_return (cd, true);
+
+ /* Update fd array. */
+ region_model_manager *mgr = cd.get_manager ();
+ tree arr_tree = cd.get_arg_tree (0);
+ const svalue *arr_sval = cd.get_arg_svalue (0);
+ for (int idx = 0; idx < 2; idx++)
+ {
+ const region *arr_reg
+ = model->deref_rvalue (arr_sval, arr_tree, cd.get_ctxt ());
+ const svalue *idx_sval
+ = mgr->get_or_create_int_cst (integer_type_node, idx);
+ const region *element_reg
+ = mgr->get_element_region (arr_reg, integer_type_node, idx_sval);
+ conjured_purge p (model, cd.get_ctxt ());
+ const svalue *fd_sval
+ = mgr->get_or_create_conjured_svalue (integer_type_node,
+ cd.get_call_stmt (),
+ element_reg,
+ p);
+ model->set_value (element_reg, fd_sval, cd.get_ctxt ());
+ model->mark_as_valid_fd (fd_sval, cd.get_ctxt ());
+
+ }
+ return true;
+ }
+ };
+
+ /* Body of region_model::impl_call_pipe. */
+ if (cd.get_ctxt ())
+ {
+ cd.get_ctxt ()->bifurcate (new failure (cd));
+ cd.get_ctxt ()->bifurcate (new success (cd));
+ cd.get_ctxt ()->terminate_path ();
+ }
+}
+
/* A subclass of pending_diagnostic for complaining about 'putenv'
called on an auto var. */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 81ef41e..608fcd5 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1976,23 +1976,36 @@ maybe_get_const_fn_result (const call_details &cd)
return sval;
}
-/* Update this model for an outcome of a call that returns zero.
+/* Update this model for an outcome of a call that returns a specific
+ integer constant.
If UNMERGEABLE, then make the result unmergeable, e.g. to prevent
the state-merger code from merging success and failure outcomes. */
void
-region_model::update_for_zero_return (const call_details &cd,
- bool unmergeable)
+region_model::update_for_int_cst_return (const call_details &cd,
+ int retval,
+ bool unmergeable)
{
if (!cd.get_lhs_type ())
return;
const svalue *result
- = m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
+ = m_mgr->get_or_create_int_cst (cd.get_lhs_type (), retval);
if (unmergeable)
result = m_mgr->get_or_create_unmergeable (result);
set_value (cd.get_lhs_region (), result, cd.get_ctxt ());
}
+/* Update this model for an outcome of a call that returns zero.
+ If UNMERGEABLE, then make the result unmergeable, e.g. to prevent
+ the state-merger code from merging success and failure outcomes. */
+
+void
+region_model::update_for_zero_return (const call_details &cd,
+ bool unmergeable)
+{
+ update_for_int_cst_return (cd, 0, unmergeable);
+}
+
/* Update this model for an outcome of a call that returns non-zero. */
void
@@ -2302,6 +2315,14 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
impl_call_memset (cd);
return false;
}
+ else if (is_named_call_p (callee_fndecl, "pipe", call, 1)
+ || is_named_call_p (callee_fndecl, "pipe2", call, 2))
+ {
+ /* Handle in "on_call_post"; bail now so that fd array
+ is left untouched so that we can detect use-of-uninit
+ for the case where the call fails. */
+ return false;
+ }
else if (is_named_call_p (callee_fndecl, "putenv", call, 1)
&& POINTER_TYPE_P (cd.get_arg_type (0)))
{
@@ -2382,6 +2403,12 @@ region_model::on_call_post (const gcall *call,
impl_call_operator_delete (cd);
return;
}
+ else if (is_named_call_p (callee_fndecl, "pipe", call, 1)
+ || is_named_call_p (callee_fndecl, "pipe2", call, 2))
+ {
+ impl_call_pipe (cd);
+ return;
+ }
/* Was this fndecl referenced by
__attribute__((malloc(FOO)))? */
if (lookup_attribute ("*dealloc", DECL_ATTRIBUTES (callee_fndecl)))
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 635a0c2..d849e0d 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -356,6 +356,7 @@ class region_model
void impl_call_malloc (const call_details &cd);
void impl_call_memcpy (const call_details &cd);
void impl_call_memset (const call_details &cd);
+ void impl_call_pipe (const call_details &cd);
void impl_call_putenv (const call_details &cd);
void impl_call_realloc (const call_details &cd);
void impl_call_strchr (const call_details &cd);
@@ -373,6 +374,9 @@ class region_model
const svalue *maybe_get_copy_bounds (const region *src_reg,
const svalue *num_bytes_sval);
+ void update_for_int_cst_return (const call_details &cd,
+ int retval,
+ bool unmergeable);
void update_for_zero_return (const call_details &cd,
bool unmergeable);
void update_for_nonzero_return (const call_details &cd);
@@ -539,6 +543,9 @@ class region_model
const region *src_reg,
region_model_context *ctxt);
+ /* Implemented in sm-fd.cc */
+ void mark_as_valid_fd (const svalue *sval, region_model_context *ctxt);
+
/* Implemented in sm-malloc.cc */
void on_realloc_with_move (const call_details &cd,
const svalue *old_ptr_sval,
@@ -730,8 +737,12 @@ class region_model_context
virtual const extrinsic_state *get_ext_state () const = 0;
- /* Hook for clients to access the "malloc" state machine in
+ /* Hook for clients to access the "fd" state machine in
any underlying program_state. */
+ virtual bool get_fd_map (sm_state_map **out_smap,
+ const state_machine **out_sm,
+ unsigned *out_sm_idx) = 0;
+ /* Likewise for the "malloc" state machine. */
virtual bool get_malloc_map (sm_state_map **out_smap,
const state_machine **out_sm,
unsigned *out_sm_idx) = 0;
@@ -785,6 +796,12 @@ public:
const extrinsic_state *get_ext_state () const override { return NULL; }
+ bool get_fd_map (sm_state_map **,
+ const state_machine **,
+ unsigned *) override
+ {
+ return false;
+ }
bool get_malloc_map (sm_state_map **,
const state_machine **,
unsigned *) override
@@ -912,6 +929,13 @@ class region_model_context_decorator : public region_model_context
return m_inner->get_ext_state ();
}
+ bool get_fd_map (sm_state_map **out_smap,
+ const state_machine **out_sm,
+ unsigned *out_sm_idx) override
+ {
+ return m_inner->get_fd_map (out_smap, out_sm, out_sm_idx);
+ }
+
bool get_malloc_map (sm_state_map **out_smap,
const state_machine **out_sm,
unsigned *out_sm_idx) override
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index c4ad91c..8a4c208 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "bitmap.h"
+#include "analyzer/program-state.h"
#if ENABLE_ANALYZER
@@ -121,6 +122,12 @@ public:
/* Function for one-to-one correspondence between valid
and unchecked states. */
state_t valid_to_unchecked_state (state_t state) const;
+
+ void mark_as_valid_fd (region_model *model,
+ sm_state_map *smap,
+ const svalue *fd_sval,
+ const extrinsic_state &ext_state) const;
+
/* State for a constant file descriptor (>= 0) */
state_t m_constant_fd;
@@ -201,15 +208,19 @@ public:
describe_state_change (const evdesc::state_change &change) override
{
if (change.m_old_state == m_sm.get_start_state ()
- && m_sm.is_unchecked_fd_p (change.m_new_state))
+ && (m_sm.is_unchecked_fd_p (change.m_new_state)
+ || m_sm.is_valid_fd_p (change.m_new_state)))
{
- if (change.m_new_state == m_sm.m_unchecked_read_write)
+ if (change.m_new_state == m_sm.m_unchecked_read_write
+ || change.m_new_state == m_sm.m_valid_read_write)
return change.formatted_print ("opened here as read-write");
- if (change.m_new_state == m_sm.m_unchecked_read_only)
+ if (change.m_new_state == m_sm.m_unchecked_read_only
+ || change.m_new_state == m_sm.m_valid_read_only)
return change.formatted_print ("opened here as read-only");
- if (change.m_new_state == m_sm.m_unchecked_write_only)
+ if (change.m_new_state == m_sm.m_unchecked_write_only
+ || change.m_new_state == m_sm.m_valid_write_only)
return change.formatted_print ("opened here as write-only");
}
@@ -748,6 +759,15 @@ fd_state_machine::valid_to_unchecked_state (state_t state) const
return NULL;
}
+void
+fd_state_machine::mark_as_valid_fd (region_model *model,
+ sm_state_map *smap,
+ const svalue *fd_sval,
+ const extrinsic_state &ext_state) const
+{
+ smap->set_state (model, fd_sval, m_valid_read_write, NULL, ext_state);
+}
+
bool
fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
const gimple *stmt) const
@@ -764,6 +784,7 @@ fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
if (is_named_call_p (callee_fndecl, "creat", call, 2))
{
on_creat (sm_ctxt, node, stmt, call);
+ return true;
} // "creat"
if (is_named_call_p (callee_fndecl, "close", call, 1))
@@ -1186,6 +1207,33 @@ make_fd_state_machine (logger *logger)
{
return new fd_state_machine (logger);
}
+
+/* Specialcase hook for handling pipe, for use by
+ region_model::impl_call_pipe::success::update_model. */
+
+void
+region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
+{
+ if (!ctxt)
+ return;
+ const extrinsic_state *ext_state = ctxt->get_ext_state ();
+ if (!ext_state)
+ return;
+
+ sm_state_map *smap;
+ const state_machine *sm;
+ unsigned sm_idx;
+ if (!ctxt->get_fd_map (&smap, &sm, &sm_idx))
+ return;
+
+ gcc_assert (smap);
+ gcc_assert (sm);
+
+ const fd_state_machine &fd_sm = (const fd_state_machine &)*sm;
+
+ fd_sm.mark_as_valid_fd (this, smap, sval, *ext_state);
+}
+
} // namespace ana
#endif // ENABLE_ANALYZER
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 09548c4..d49c137 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10459,6 +10459,7 @@ of the following functions for working with file descriptors:
@item @code{close}
@item @code{creat}
@item @code{dup}, @code{dup2} and @code{dup3}
+@item @code{pipe}, and @code{pipe2}
@item @code{read}
@item @code{write}
@end itemize
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe-1.c b/gcc/testsuite/gcc.dg/analyzer/pipe-1.c
new file mode 100644
index 0000000..6b95442
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe-1.c
@@ -0,0 +1,38 @@
+#include "analyzer-decls.h"
+
+extern int pipe(int pipefd[2]);
+extern int close(int fd);
+
+void
+test_leak (void)
+{
+ int fds[2];
+ if (pipe (fds) == -1) /* { dg-message "when 'pipe' succeeds" } */
+ /* { dg-message "opened here as read-write" "sm msg" { target *-*-* } .-1 }} */
+ return;
+} /* { dg-line leak } */
+/* { dg-warning "leak of file descriptor 'fds\\\[0\\\]'" "leak of 0" { target *-*-* } leak } */
+/* { dg-warning "leak of file descriptor 'fds\\\[1\\\]'" "leak of 1" { target *-*-* } leak } */
+/* { dg-message "'fds\\\[0\\\]' leaks here" "final msg 0" { target *-*-* } leak }} */
+/* { dg-message "'fds\\\[1\\\]' leaks here" "final msg 1" { target *-*-* } leak }} */
+
+void
+test_close (void)
+{
+ int fds[2];
+ if (pipe (fds) == -1)
+ return;
+ __analyzer_describe (0, fds[0]); /* { dg-warning "CONJURED" } */
+ __analyzer_describe (0, fds[1]); /* { dg-warning "CONJURED" } */
+ close (fds[0]);
+ close (fds[1]);
+}
+
+void
+test_unchecked (void)
+{
+ int fds[2];
+ pipe (fds); /* { dg-message "when 'pipe' fails" } */
+ close (fds[0]); /* { dg-warning "use of uninitialized value 'fds\\\[0\\\]'" } */
+ close (fds[1]); /* { dg-warning "use of uninitialized value 'fds\\\[1\\\]'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe-glibc.c b/gcc/testsuite/gcc.dg/analyzer/pipe-glibc.c
new file mode 100644
index 0000000..a8546ea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe-glibc.c
@@ -0,0 +1,71 @@
+/* Example of pipe usage from glibc manual. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Read characters from the pipe and echo them to stdout. */
+
+void
+read_from_pipe (int file)
+{
+ FILE *stream;
+ int c;
+ stream = fdopen (file, "r");
+ while ((c = fgetc (stream)) != EOF)
+ putchar (c);
+ fclose (stream);
+}
+
+/* Write some random text to the pipe. */
+
+void
+write_to_pipe (int file)
+{
+ FILE *stream;
+ stream = fdopen (file, "w");
+ fprintf (stream, "hello, world!\n");
+ fprintf (stream, "goodbye, world!\n");
+ fclose (stream);
+}
+
+int
+main (void)
+{
+ pid_t pid;
+ int mypipe[2];
+
+ /* Create the pipe. */
+ if (pipe (mypipe))
+ {
+ fprintf (stderr, "Pipe failed.\n");
+ return EXIT_FAILURE;
+ }
+
+
+ /* Create the child process. */
+ pid = fork ();
+ if (pid == (pid_t) 0)
+ {
+ /* This is the child process.
+ Close other end first. */
+ close (mypipe[1]);
+ read_from_pipe (mypipe[0]);
+ return EXIT_SUCCESS;
+ }
+ else if (pid < (pid_t) 0)
+ {
+ /* The fork failed. */
+ fprintf (stderr, "Fork failed.\n");
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ /* This is the parent process.
+ Close other end first. */
+ close (mypipe[0]);
+ write_to_pipe (mypipe[1]);
+ return EXIT_SUCCESS;
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe-manpages.c b/gcc/testsuite/gcc.dg/analyzer/pipe-manpages.c
new file mode 100644
index 0000000..6b9ae4d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe-manpages.c
@@ -0,0 +1,76 @@
+/* Example of "pipe" from release 5.13 of the Linux man-pages project.
+
+Copyright (C) 2005, 2008, Michael Kerrisk <mtk.manpages@gmail.com>
+(A few fragments remain from an earlier (1992) version by
+Drew Eckhardt <drew@cs.colorado.edu>.)
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Since the Linux kernel and libraries are constantly changing, this
+manual page may be incorrect or out-of-date. The author(s) assume no
+responsibility for errors or omissions, or for damages resulting from
+the use of the information contained herein. The author(s) may not
+have taken the same level of care in the production of this manual,
+which is licensed free of charge, as they might when working
+professionally.
+
+Formatted or processed versions of this manual, if unaccompanied by
+the source, must acknowledge the copyright and authors of this work.
+
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+int
+main(int argc, char *argv[])
+{
+ int pipefd[2];
+ pid_t cpid;
+ char buf;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <string>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (pipe(pipefd) == -1) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+
+ cpid = fork();
+ if (cpid == -1) {
+ perror("fork");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cpid == 0) { /* Child reads from pipe */
+ close(pipefd[1]); /* Close unused write end */
+
+ while (read(pipefd[0], &buf, 1) > 0)
+ write(STDOUT_FILENO, &buf, 1);
+
+ write(STDOUT_FILENO, "\n", 1);
+ close(pipefd[0]);
+ _exit(EXIT_SUCCESS);
+
+ } else { /* Parent writes argv[1] to pipe */
+ close(pipefd[0]); /* Close unused read end */
+ write(pipefd[1], argv[1], strlen(argv[1]));
+ close(pipefd[1]); /* Reader will see EOF */
+ wait(NULL); /* Wait for child */
+ exit(EXIT_SUCCESS);
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c b/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
new file mode 100644
index 0000000..d7afc9c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
@@ -0,0 +1,38 @@
+#include "analyzer-decls.h"
+
+extern int pipe2(int pipefd[2], int flags);
+extern int close(int fd);
+
+void
+test_leak (void)
+{
+ int fds[2];
+ if (pipe2 (fds, 0) == -1) /* { dg-message "when 'pipe2' succeeds" } */
+ /* { dg-message "opened here as read-write" "sm msg" { target *-*-* } .-1 }} */
+ return;
+} /* { dg-line leak } */
+/* { dg-warning "leak of file descriptor 'fds\\\[0\\\]'" "leak of 0" { target *-*-* } leak } */
+/* { dg-warning "leak of file descriptor 'fds\\\[1\\\]'" "leak of 1" { target *-*-* } leak } */
+/* { dg-message "'fds\\\[0\\\]' leaks here" "final msg 0" { target *-*-* } leak }} */
+/* { dg-message "'fds\\\[1\\\]' leaks here" "final msg 1" { target *-*-* } leak }} */
+
+void
+test_close (void)
+{
+ int fds[2];
+ if (pipe2 (fds, 0) == -1)
+ return;
+ __analyzer_describe (0, fds[0]); /* { dg-warning "CONJURED" } */
+ __analyzer_describe (0, fds[1]); /* { dg-warning "CONJURED" } */
+ close (fds[0]);
+ close (fds[1]);
+}
+
+void
+test_unchecked (void)
+{
+ int fds[2];
+ pipe2 (fds, 0); /* { dg-message "when 'pipe2' fails" } */
+ close (fds[0]); /* { dg-warning "use of uninitialized value 'fds\\\[0\\\]'" } */
+ close (fds[1]); /* { dg-warning "use of uninitialized value 'fds\\\[1\\\]'" } */
+}