From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2209) id 3C100385840C; Mon, 24 Oct 2022 20:49:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3C100385840C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1666644545; bh=4YY6wosyC9sPdFTiuMomhIWbmwnP6oLO7DE5OT53slo=; h=From:To:Subject:Date:From; b=xyKBf62OUIByx2d10zxPuhuWx2Py2gZwKi6Io9RRXTv/XCwdxyXZyMC43ocoeG0E/ i7UvUnRteaIRsA8quHA6IL/7dM9asRYoJQIBVW3xlmCxaI6ppv3YiMaeemqCFspeKt xngpkP+vSIQJLWp1EUlJAVx8B5SY8qisI5K/gw+o= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: David Malcolm To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-3466] analyzer: handle "pipe" and "pipe2" [PR106300] X-Act-Checkin: gcc X-Git-Author: David Malcolm X-Git-Refname: refs/heads/master X-Git-Oldrev: 244021b6c1a7bdeb777874ddc2ebcecb95610ef1 X-Git-Newrev: 792f039fc37faa3446725a643c8018f084e8ccab Message-Id: <20221024204905.3C100385840C@sourceware.org> Date: Mon, 24 Oct 2022 20:49:03 +0000 (GMT) List-Id: https://gcc.gnu.org/g:792f039fc37faa3446725a643c8018f084e8ccab commit r13-3466-g792f039fc37faa3446725a643c8018f084e8ccab Author: David Malcolm Date: Mon Oct 24 16:38:22 2022 -0400 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 Diff: --- gcc/analyzer/engine.cc | 15 ++++++ gcc/analyzer/exploded-graph.h | 3 ++ gcc/analyzer/region-model-impl-calls.cc | 70 ++++++++++++++++++++++++ gcc/analyzer/region-model.cc | 35 ++++++++++-- gcc/analyzer/region-model.h | 26 ++++++++- gcc/analyzer/sm-fd.cc | 56 ++++++++++++++++++-- gcc/doc/invoke.texi | 1 + gcc/testsuite/gcc.dg/analyzer/pipe-1.c | 38 ++++++++++++++ gcc/testsuite/gcc.dg/analyzer/pipe-glibc.c | 71 +++++++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/pipe-manpages.c | 76 +++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/pipe2-1.c | 38 ++++++++++++++ 11 files changed, 420 insertions(+), 9 deletions(-) diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 46bcaeda837..a664a99eb78 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -228,6 +228,21 @@ impl_region_model_context::get_malloc_map (sm_state_map **out_smap, return true; } +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, diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index 11e46cab160..ad278e277dc 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 8f4940a4d55..52c4205cbeb 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 81ef41edee4..608fcd58fab 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 635a0c27330..d849e0d774b 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 c4ad91cfeb2..8a4c2088c74 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 09548c4528c..d49c1374d06 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 00000000000..6b95442e322 --- /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 00000000000..a8546ea9549 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pipe-glibc.c @@ -0,0 +1,71 @@ +/* Example of pipe usage from glibc manual. */ + +#include +#include +#include +#include + +/* 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 00000000000..6b9ae4d2602 --- /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 +(A few fragments remain from an earlier (1992) version by +Drew Eckhardt .) + +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 +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + int pipefd[2]; + pid_t cpid; + char buf; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \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 00000000000..d7afc9caaf9 --- /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\\\]'" } */ +}