From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id 096493858428; Sun, 24 Jul 2022 20:40:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 096493858428 From: "eggert at cs dot ucla.edu" To: gcc-bugs@gcc.gnu.org Subject: [Bug analyzer/106428] New: -Wanalyzer-file-leak false positive with if ((ptr = fopen(...)) == NULL) ... Date: Sun, 24 Jul 2022 20:40:02 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: new X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gcc X-Bugzilla-Component: analyzer X-Bugzilla-Version: 12.1.1 X-Bugzilla-Keywords: X-Bugzilla-Severity: normal X-Bugzilla-Who: eggert at cs dot ucla.edu X-Bugzilla-Status: UNCONFIRMED X-Bugzilla-Resolution: X-Bugzilla-Priority: P3 X-Bugzilla-Assigned-To: dmalcolm at gcc dot gnu.org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: bug_id short_desc product version bug_status bug_severity priority component assigned_to reporter target_milestone attachments.created Message-ID: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://gcc.gnu.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 X-BeenThere: gcc-bugs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-bugs mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 24 Jul 2022 20:40:03 -0000 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D106428 Bug ID: 106428 Summary: -Wanalyzer-file-leak false positive with if ((ptr =3D fopen(...)) =3D=3D NULL) ... Product: gcc Version: 12.1.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: eggert at cs dot ucla.edu Target Milestone: --- Created attachment 53342 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=3D53342&action=3Dedit Compile with '-O2 -S -fanalyzer' to demonstrate the bug This is GCC 12.1.1 20220507 (Red Hat 12.1.1-1) on x86-64. Compile the attac= hed program t.i with 'gcc -fanalyzer -O2 -S t.i'. It outputs a false alarm as s= hown at the end of this comment. If I replace lines 12046-12049: if ((ent->v.file.fp =3D fopen (ent->v.file.name, "r")) =3D=3D=20 ((void *)0) ) open_fatal (ent->v.file.name); with the following (which is equivalent, since ent->v.file.fp is NULL before the code is executed): FILE *fp =3D fopen (ent->v.file.name, "r"); if (!fp) open_fatal (ent->v.file.name); ent->v.file.fp =3D fp; then the first diagnostic, which is a false positive, goes away. Here is the diagnostic output, the first of which is the false positive. t.i: In function =E2=80=98read_next_name=E2=80=99: t.i:12046:7: warning: leak of FILE =E2=80=98fopen(*ent.v.file.name,= "r")=E2=80=99 [CWE-775] [-Wanalyzer-file-leak] 12046 | if ((ent->v.file.fp =3D fopen (ent->v.file.name, "r")) = =3D=3D | ^ =E2=80=98name_scan=E2=80=99: events 1-2 | |13033 | name_scan (const char *file_name) | | ^~~~~~~~~ | | | | | (1) entry to =E2=80=98name_scan=E2=80=99 |...... |13039 | struct name *cursor =3D namelist_match (file_nam= e, length); | |=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (2) calling =E2=80=98namel= ist_match=E2=80=99 from =E2=80=98name_scan=E2=80=99 | +--> =E2=80=98namelist_match=E2=80=99: events 3-5 | |12316 | namelist_match (char const *file_name, size_t length) | | ^~~~~~~~~~~~~~ | | | | | (3) entry to =E2=80=98namelist_match=E2=80=99 |...... |12320 | for (p =3D namelist; p; p =3D p->next) | | ~ | | | | | (4) following =E2=80=98tru= e=E2=80=99 branch (when =E2=80=98p=E2=80=99 is non-NULL)... |12321 | { |12322 | if (p->name[0] | | ~~~~~~~ | | | | | (5) ...to here | <------+ | =E2=80=98name_scan=E2=80=99: events 6-11 | |13039 | struct name *cursor =3D namelist_match (file_nam= e, length); | |=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (6) returning to =E2=80=98= name_scan=E2=80=99 from =E2=80=98namelist_match=E2=80=99 |13040 | if (cursor) | | ~=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20 | | | | | (7) following =E2=80=98false=E2=80=99 branch = (when =E2=80=98cursor=E2=80=99 is NULL)... |...... |13048 | if (same_order_option && namelist && namelist->found_count) | | ~~~~~~~~~~~~~~~~~~=20=20 | | || | | |(8) ...to here | | (9) following =E2=80=98true=E2=80=99 branch... |13049 | { |13050 | name_gather (); | | ~~~~~~~~~~~~~~=20=20=20=20=20=20=20=20=20=20=20=20 | | | | | (10) ...to here | | (11) calling =E2=80=98name_gather=E2=80=99 from =E2= =80=98name_scan=E2=80=99 | +--> =E2=80=98name_gather=E2=80=99: events 12-13 | |12170 | name_gather (void) | | ^~~~~~~~~~~ | | | | | (12) entry to =E2=80=98name_gather=E2=80=99 |...... |12179 | if (same_order_option) | | ~ | | | | | (13) following =E2=80=98true=E2=80=99 bran= ch... | =E2=80=98name_gather=E2=80=99: event 14 | |cc1: | (14): ...to here | =E2=80=98name_gather=E2=80=99: event 15 | |12183 | while ((ep =3D name_next_elt (0)) && ep->= type =3D=3D NELT_CHDIR) | | ^~~~~~~~~~~~~~~~~ | | | | | (15) calling =E2=80=98name_n= ext_elt=E2=80=99 from =E2=80=98name_gather=E2=80=99 | +--> =E2=80=98name_next_elt=E2=80=99: events 16-19 | |12110 | name_next_elt (int change_dirs) | | ^~~~~~~~~~~~~ | | | | | (16) entry to =E2=80=98name_next_elt=E2= =80=99 |...... |12115 | while ((ep =3D name_head) !=3D | | ~~~~~~~~~~~~~~~~~~~ | | | | | (17) following =E2=80=98true=E2=80=99 branch (when =E2=80=98ep=E2=80=99 is non-NULL)... |12116 | ((void *)0) | | ~~~~~~~~~~~ |...... |12119 | switch (ep->type) | | ~~~~~~~~ | | | | | (18) ...to here |...... |12122 | name_list_advance (); | | ~~~~~~~~~~~~~~~~~~~~ | | | | | (19) calling =E2=80=98name_list_advan= ce=E2=80=99 from =E2=80=98name_next_elt=E2=80=99 | +--> =E2=80=98name_list_advance=E2=80=99: events = 20-24 | |11759 | name_list_advance (void) | | ^~~~~~~~~~~~~~~~~ | | | | | (20) entry to =E2=80=98name_list_= advance=E2=80=99 |...... |11767 | if (elt->type =3D=3D NELT_OPTIO= N || elt->type =3D=3D NELT_CHDIR) | | ~ | | | | | (21) following =E2=80=98fals= e=E2=80=99 branch... |...... |11775 | if (elt->type !=3D NELT_NOO= P) | | ~ | | | | | (22) ...to here | | (23) following =E2=80=98= false=E2=80=99 branch... |11776 | unconsumed_option_free (); |11777 | free (elt); | | ~~~~~~~~~~ | | | | | (24) ...to here | <------+ | =E2=80=98name_next_elt=E2=80=99: events 25-28 | |12115 | while ((ep =3D name_head) !=3D | | ~~~~~~~~~~~~~~~~~~~ | | | | | (26) following =E2=80=98true=E2=80=99 branch (when =E2=80=98ep=E2=80=99 is non-NULL)... |12116 | ((void *)0) | | ~~~~~~~~~~~ |...... |12119 | switch (ep->type) | | ~~~~~~~~ | | | | | (27) ...to here |...... |12122 | name_list_advance (); | | ^~~~~~~~~~~~~~~~~~~~ | | | | | (25) returning to =E2=80=98name_next_= elt=E2=80=99 from =E2=80=98name_list_advance=E2=80=99 |...... |12126 | if (read_next_name (ep, &entry) =3D= =3D 0) | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (28) calling =E2=80=98read_next_n= ame=E2=80=99 from =E2=80=98name_next_elt=E2=80=99 | +--> =E2=80=98read_next_name=E2=80=99: events 29-= 34 | |12028 | read_next_name (struct name_elt *= ent, struct name_elt *ret) | | ^~~~~~~~~~~~~~ | | | | | (29) entry to =E2=80=98read_next_= name=E2=80=99 |12029 | { |12030 | if (!ent->v.file.fp) | | ~ | | | | | (30) following =E2=80=98true= =E2=80=99 branch... |12031 | { |12032 | if (!strcmp (ent->v.file.na= me, "-")) | | ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (31) ...to here | | (32) following =E2=80=98= false=E2=80=99 branch (when the strings are non-equal)... |...... |12041 | if (add_file_id (ent->v.file.name)) | | ~~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~~ | | | | | (33) ...to here | | (34) calling =E2=80=98add_= file_id=E2=80=99 from =E2=80=98read_next_name=E2=80=99 | +--> =E2=80=98add_file_id=E2=80=99: events= 35-37 | |11891 | add_file_id (const char *filename) | | ^~~~~~~~~~~ | | | | | (35) entry to =E2=80=98add= _file_id=E2=80=99 |...... |11897 | if (stat (filename, &st)) | | ~ | | | | | (36) following =E2=80= =98false=E2=80=99 branch... |11898 | stat_fatal (filename); |11899 | reading_from =3D file_list_name (); | |=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~ | | | | | (37) ...to here | <------+ | =E2=80=98read_next_name=E2=80=99: events 38-= 42 | |12041 | if (add_file_id (ent->v.file.name)) | | ~^~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~~ | | || | | |(38) returning to =E2=80=98read_next_name=E2=80=99 from =E2=80=98add_file_id=E2=80=99 | | (39) following =E2=80=98fal= se=E2=80=99 branch... |...... |12046 | if ((ent->v.file.fp =3D fopen (ent->v.file.name, "r")) =3D=3D | | ~=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (40) ...= to here | | | (41) ope= ned here | | (42) =E2=80=98fopen(*ent.v.= file.name, "r")=E2=80=99 leaks here; was opened at (41) | t.i:12046:7: warning: leak of =E2=80=98fopen(*ent.v.file.name, "r")= =E2=80=99 [CWE-401] [-Wanalyzer-malloc-leak] 12046 | if ((ent->v.file.fp =3D fopen (ent->v.file.name, "r")) = =3D=3D | ^ =E2=80=98name_scan=E2=80=99: events 1-2 | |13033 | name_scan (const char *file_name) | | ^~~~~~~~~ | | | | | (1) entry to =E2=80=98name_scan=E2=80=99 |...... |13039 | struct name *cursor =3D namelist_match (file_nam= e, length); | |=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (2) calling =E2=80=98namel= ist_match=E2=80=99 from =E2=80=98name_scan=E2=80=99 | +--> =E2=80=98namelist_match=E2=80=99: events 3-5 | |12316 | namelist_match (char const *file_name, size_t length) | | ^~~~~~~~~~~~~~ | | | | | (3) entry to =E2=80=98namelist_match=E2=80=99 |...... |12320 | for (p =3D namelist; p; p =3D p->next) | | ~ | | | | | (4) following =E2=80=98tru= e=E2=80=99 branch (when =E2=80=98p=E2=80=99 is non-NULL)... |12321 | { |12322 | if (p->name[0] | | ~~~~~~~ | | | | | (5) ...to here | <------+ | =E2=80=98name_scan=E2=80=99: events 6-11 | |13039 | struct name *cursor =3D namelist_match (file_nam= e, length); | |=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (6) returning to =E2=80=98= name_scan=E2=80=99 from =E2=80=98namelist_match=E2=80=99 |13040 | if (cursor) | | ~=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20 | | | | | (7) following =E2=80=98false=E2=80=99 branch = (when =E2=80=98cursor=E2=80=99 is NULL)... |...... |13048 | if (same_order_option && namelist && namelist->found_count) | | ~~~~~~~~~~~~~~~~~~=20=20 | | || | | |(8) ...to here | | (9) following =E2=80=98true=E2=80=99 branch... |13049 | { |13050 | name_gather (); | | ~~~~~~~~~~~~~~=20=20=20=20=20=20=20=20=20=20=20=20 | | | | | (10) ...to here | | (11) calling =E2=80=98name_gather=E2=80=99 from =E2= =80=98name_scan=E2=80=99 | +--> =E2=80=98name_gather=E2=80=99: events 12-13 | |12170 | name_gather (void) | | ^~~~~~~~~~~ | | | | | (12) entry to =E2=80=98name_gather=E2=80=99 |...... |12179 | if (same_order_option) | | ~ | | | | | (13) following =E2=80=98true=E2=80=99 bran= ch... | =E2=80=98name_gather=E2=80=99: event 14 | |cc1: | (14): ...to here | =E2=80=98name_gather=E2=80=99: event 15 | |12183 | while ((ep =3D name_next_elt (0)) && ep->= type =3D=3D NELT_CHDIR) | | ^~~~~~~~~~~~~~~~~ | | | | | (15) calling =E2=80=98name_n= ext_elt=E2=80=99 from =E2=80=98name_gather=E2=80=99 | +--> =E2=80=98name_next_elt=E2=80=99: events 16-19 | |12110 | name_next_elt (int change_dirs) | | ^~~~~~~~~~~~~ | | | | | (16) entry to =E2=80=98name_next_elt=E2= =80=99 |...... |12115 | while ((ep =3D name_head) !=3D | | ~~~~~~~~~~~~~~~~~~~ | | | | | (17) following =E2=80=98true=E2=80=99 branch (when =E2=80=98ep=E2=80=99 is non-NULL)... |12116 | ((void *)0) | | ~~~~~~~~~~~ |...... |12119 | switch (ep->type) | | ~~~~~~~~ | | | | | (18) ...to here |...... |12122 | name_list_advance (); | | ~~~~~~~~~~~~~~~~~~~~ | | | | | (19) calling =E2=80=98name_list_advan= ce=E2=80=99 from =E2=80=98name_next_elt=E2=80=99 | +--> =E2=80=98name_list_advance=E2=80=99: events = 20-24 | |11759 | name_list_advance (void) | | ^~~~~~~~~~~~~~~~~ | | | | | (20) entry to =E2=80=98name_list_= advance=E2=80=99 |...... |11767 | if (elt->type =3D=3D NELT_OPTIO= N || elt->type =3D=3D NELT_CHDIR) | | ~ | | | | | (21) following =E2=80=98fals= e=E2=80=99 branch... |...... |11775 | if (elt->type !=3D NELT_NOO= P) | | ~ | | | | | (22) ...to here | | (23) following =E2=80=98= false=E2=80=99 branch... |11776 | unconsumed_option_free (); |11777 | free (elt); | | ~~~~~~~~~~ | | | | | (24) ...to here | <------+ | =E2=80=98name_next_elt=E2=80=99: events 25-28 | |12115 | while ((ep =3D name_head) !=3D | | ~~~~~~~~~~~~~~~~~~~ | | | | | (26) following =E2=80=98true=E2=80=99 branch (when =E2=80=98ep=E2=80=99 is non-NULL)... |12116 | ((void *)0) | | ~~~~~~~~~~~ |...... |12119 | switch (ep->type) | | ~~~~~~~~ | | | | | (27) ...to here |...... |12122 | name_list_advance (); | | ^~~~~~~~~~~~~~~~~~~~ | | | | | (25) returning to =E2=80=98name_next_= elt=E2=80=99 from =E2=80=98name_list_advance=E2=80=99 |...... |12126 | if (read_next_name (ep, &entry) =3D= =3D 0) | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (28) calling =E2=80=98read_next_n= ame=E2=80=99 from =E2=80=98name_next_elt=E2=80=99 | +--> =E2=80=98read_next_name=E2=80=99: events 29-= 34 | |12028 | read_next_name (struct name_elt *= ent, struct name_elt *ret) | | ^~~~~~~~~~~~~~ | | | | | (29) entry to =E2=80=98read_next_= name=E2=80=99 |12029 | { |12030 | if (!ent->v.file.fp) | | ~ | | | | | (30) following =E2=80=98true= =E2=80=99 branch... |12031 | { |12032 | if (!strcmp (ent->v.file.na= me, "-")) | | ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (31) ...to here | | (32) following =E2=80=98= false=E2=80=99 branch (when the strings are non-equal)... |...... |12041 | if (add_file_id (ent->v.file.name)) | | ~~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~~ | | | | | (33) ...to here | | (34) calling =E2=80=98add_= file_id=E2=80=99 from =E2=80=98read_next_name=E2=80=99 | +--> =E2=80=98add_file_id=E2=80=99: events= 35-37 | |11891 | add_file_id (const char *filename) | | ^~~~~~~~~~~ | | | | | (35) entry to =E2=80=98add= _file_id=E2=80=99 |...... |11897 | if (stat (filename, &st)) | | ~ | | | | | (36) following =E2=80= =98false=E2=80=99 branch... |11898 | stat_fatal (filename); |11899 | reading_from =3D file_list_name (); | |=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~ | | | | | (37) ...to here | <------+ | =E2=80=98read_next_name=E2=80=99: events 38-= 42 | |12041 | if (add_file_id (ent->v.file.name)) | | ~^~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~~ | | || | | |(38) returning to =E2=80=98read_next_name=E2=80=99 from =E2=80=98add_file_id=E2=80=99 | | (39) following =E2=80=98fal= se=E2=80=99 branch... |...... |12046 | if ((ent->v.file.fp =3D fopen (ent->v.file.name, "r")) =3D=3D | | ~=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (40) ...= to here | | | (41) allocated here | | (42) =E2=80=98fopen(*ent.v.= file.name, "r")=E2=80=99 leaks here; was allocated at (41) | t.i: In function =E2=80=98name_gather=E2=80=99: t.i:12184:13: warning: leak of =E2=80=98xstrdup(*ep.v.name)=E2=80= =99 [CWE-401] [-Wanalyzer-malloc-leak] 12184 | change_dir =3D chdir_arg (xstrdup (ep->v.name)); | ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =E2=80=98name_scan=E2=80=99: events 1-2 | |13033 | name_scan (const char *file_name) | | ^~~~~~~~~ | | | | | (1) entry to =E2=80=98name_scan=E2=80=99 |...... |13039 | struct name *cursor =3D namelist_match (file_nam= e, length); | |=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (2) calling =E2=80=98namel= ist_match=E2=80=99 from =E2=80=98name_scan=E2=80=99 | +--> =E2=80=98namelist_match=E2=80=99: events 3-5 | |12316 | namelist_match (char const *file_name, size_t length) | | ^~~~~~~~~~~~~~ | | | | | (3) entry to =E2=80=98namelist_match=E2=80=99 |...... |12320 | for (p =3D namelist; p; p =3D p->next) | | ~ | | | | | (4) following =E2=80=98tru= e=E2=80=99 branch (when =E2=80=98p=E2=80=99 is non-NULL)... |12321 | { |12322 | if (p->name[0] | | ~~~~~~~ | | | | | (5) ...to here | <------+ | =E2=80=98name_scan=E2=80=99: events 6-11 | |13039 | struct name *cursor =3D namelist_match (file_nam= e, length); | |=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (6) returning to =E2=80=98= name_scan=E2=80=99 from =E2=80=98namelist_match=E2=80=99 |13040 | if (cursor) | | ~=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20 | | | | | (7) following =E2=80=98false=E2=80=99 branch = (when =E2=80=98cursor=E2=80=99 is NULL)... |...... |13048 | if (same_order_option && namelist && namelist->found_count) | | ~~~~~~~~~~~~~~~~~~=20=20 | | || | | |(8) ...to here | | (9) following =E2=80=98true=E2=80=99 branch... |13049 | { |13050 | name_gather (); | | ~~~~~~~~~~~~~~=20=20=20=20=20=20=20=20=20=20=20=20 | | | | | (10) ...to here | | (11) calling =E2=80=98name_gather=E2=80=99 from =E2= =80=98name_scan=E2=80=99 | +--> =E2=80=98name_gather=E2=80=99: events 12-13 | |12170 | name_gather (void) | | ^~~~~~~~~~~ | | | | | (12) entry to =E2=80=98name_gather=E2=80=99 |...... |12179 | if (same_order_option) | | ~ | | | | | (13) following =E2=80=98true=E2=80=99 bran= ch... | =E2=80=98name_gather=E2=80=99: event 14 | |cc1: | (14): ...to here | =E2=80=98name_gather=E2=80=99: event 15 | |12183 | while ((ep =3D name_next_elt (0)) && ep->= type =3D=3D NELT_CHDIR) | | ^~~~~~~~~~~~~~~~~ | | | | | (15) calling =E2=80=98name_n= ext_elt=E2=80=99 from =E2=80=98name_gather=E2=80=99 | +--> =E2=80=98name_next_elt=E2=80=99: events 16-20 | |12110 | name_next_elt (int change_dirs) | | ^~~~~~~~~~~~~ | | | | | (16) entry to =E2=80=98name_next_elt=E2= =80=99 |...... |12115 | while ((ep =3D name_head) !=3D | | ~~~~~~~~~~~~~~~~~~~ | | | | | (17) following =E2=80=98true=E2=80=99 branch (when =E2=80=98ep=E2=80=99 is non-NULL)... |12116 | ((void *)0) | | ~~~~~~~~~~~ |...... |12119 | switch (ep->type) | | ~~~~~~~~ | | | | | (18) ...to here |...... |12131 | if (change_dirs) | | ~ | | | | | (19) following =E2=80=98false=E2= =80=99 branch (when =E2=80=98change_dirs =3D=3D 0=E2=80=99)... |...... |12139 | copy_name (ep); | | ~~~~~~~~~~~~~~ | | | | | (20) ...to here | <------+ | =E2=80=98name_gather=E2=80=99: events 21-25 | |12183 | while ((ep =3D name_next_elt (0)) && ep->= type =3D=3D NELT_CHDIR) | |=20=20=20=20=20=20=20=20=20=20=20=20=20 ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (22) following =E2=80=98true=E2=80=99 branch... | | (21) returning to =E2=80=98n= ame_gather=E2=80=99 from =E2=80=98name_next_elt=E2=80=99 |12184 | change_dir =3D chdir_arg (xstrdup (ep->v.name)= ); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (23) ...to here | | | (24) allocated here | | (25) =E2=80=98xstrdup(*ep.v.name)= =E2=80=99 leaks here; was allocated at (24) |=