REVERSE_LIST macro aliasing issue
Sam James <[email protected]> Mon, 30 Mar 2026 20:05:30 +0100
| Newsgroups | gmane.comp.shells.bash.bugs |
|---|---|
| Organization | Gentoo |
| Message-ID | <[email protected]> |
When built with GCC 16 and -O2 -flto, and with -rdynamic stripped out of
configure (*), bash hangs in stop_pipeline when iterating through
newjob->pipe->next.
```
^C
Program received signal SIGINT, Interrupt.
0x0000555555591ca6 in stop_pipeline.constprop.0 (async=async@entry=0, deferred=0x0) at /home/sam/git/bash/jobs.c:644
644 for (p = newjob->pipe; p->next; p = p->next)
(gdb) bt
#0 0x0000555555591ca6 in stop_pipeline.constprop.0 (async=async@entry=0, deferred=0x0) at /home/sam/git/bash/jobs.c:644
#1 0x0000555555577e6b in execute_command_internal (command=0x555555695090, asynchronous=<optimized out>, pipe_in=-1, pipe_out=-1, fds_to_close=<optimized out>) at /home/sam/git/bash/execute_cmd.c:963
#2 0x00005555555e5061 in parse_and_execute (string=<optimized out>, from_file=from_file@entry=0x555555692650 "/home/sam/.bashrc", flags=flags@entry=1044) at builtins/evalstring.c:567
#3 0x00005555555e5c96 in evalfile_internal (filename=filename@entry=0x555555692650 "/home/sam/.bashrc", flags=flags@entry=521) at builtins/evalfile.c:302
#4 0x00005555555e5eec in maybe_execute_file.constprop.0 (fname=<optimized out>, force_noninteractive=1) at builtins/evalfile.c:348
#5 0x0000555555557879 in run_startup_files () at /home/sam/git/bash/shell.c:1236
#6 main (argc=1, argv=0x7fffffffd6e8, env=0x7fffffffd6f8) at /home/sam/git/bash/shell.c:722
```
```
(gdb) p p->next
$1 = (struct process *) 0x555555692050
(gdb) p p->next->next
$2 = (struct process *) 0x555555692050
(gdb) p p->next->next->next
$3 = (struct process *) 0x555555692050
```
In jobs.c [0]:
```
/* Add the current pipeline to the job list. */
if (the_pipeline)
{
register PROCESS *p;
int any_running, any_stopped, n;
newjob = (JOB *)xmalloc (sizeof (JOB));
for (n = 1, p = the_pipeline; p->next != the_pipeline; n++, p = p->next)
;
p->next = (PROCESS *)NULL;
newjob->pipe = REVERSE_LIST (the_pipeline, PROCESS *);
for (p = newjob->pipe; p->next; p = p->next)
```
When bash works, p->next is just NULL.
The issue is REVERSE_LIST [1] calls list_reverse [2] which reverses the
linked list while doing accesses as GENERIC_LIST* [3], but the original
elements are PROCESS*.
I think GCC has an exemption for doing this with void* but even that
isn't required to work by the standard. I think it either needs to be
done with char* or with GENERIC_LIST* marked with
__attribute__((may_alias)) (though that isn't portable). I've attached
the trivial patch to do the latter which I'm using, as it works fine for
our environments, but it's not suitable as-is for bash upstream.
[0] jobs.c#L643 (637f5c8696a6adc9b4519f1cd74aa78492266b7f)
[1] general.h#L135 (637f5c8696a6adc9b4519f1cd74aa78492266b7f)
[2] list.c#L55 (637f5c8696a6adc9b4519f1cd74aa78492266b7f)
[3] general.h#L123 (637f5c8696a6adc9b4519f1cd74aa78492266b7f)
(*) We've done this since before my time in Gentoo when the user
disables plugin support. GCC has more freedom with it removed which is
how this bug happens.
thanks,
sam
bash-5.3_p9-general-workaround-aliasing-violation-in-REVERSE_LIS.patch
(text/x-patch, 1.7 KB)
From 7309ee9f263dfcaf11eeca679cfec5cf5c8f9455 Mon Sep 17 00:00:00 2001 Message-ID: <7309ee9f263dfcaf11eeca679cfec5cf5c8f9455.1774896688.git.sam@gentoo.org> From: Sam James <[email protected]> Date: Mon, 30 Mar 2026 19:19:33 +0100 Subject: [PATCH] general: workaround aliasing violation in REVERSE_LIST macro In jobs.c, say, we have: ``` p->next = (PROCESS *)NULL; newjob->pipe = REVERSE_LIST (the_pipeline, PROCESS *); for (p = newjob->pipe; p->next; p = p->next) ``` REVERSE_LIST (-> list_reverse) reverses the linked list `the_pipeline` while doing accesses as GENERIC_LIST*, but the original elements are PROCESS*. I think GCC has an exemption for doing this with void* but even that isn't required to work by the standard. I think it either needs to be done with char* or with GENERIC_LIST marked with __attribute__((may_alias)) (though that isn't portable). For Gentoo, the alias approach is fine until this gets fixed upstream, so do that. This fixes bash being miscompiled by GCC 16 with -O2 -flto and USE=-plugins (for -rdynamic to be dropped). Bug: https://bugs.gentoo.org/971782 Signed-off-by: Sam James <[email protected]> --- general.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/general.h b/general.h index 5b1eac08..883939a7 100644 --- general.h +++ general.h @@ -122,7 +122,7 @@ extern char *strcpy (char *, const char *); can be written to handle the general case for linked lists. */ typedef struct g_list { struct g_list *next; -} GENERIC_LIST; +} __attribute__((may_alias)) GENERIC_LIST; /* Here is a generic structure for associating character strings with integers. It is used in the parser for shell tokenization. */ base-commit: 637f5c8696a6adc9b4519f1cd74aa78492266b7f -- 2.53.0
signature.asc
(application/pgp-signature, 418 B)
-----BEGIN PGP SIGNATURE----- iQEBBAEWCgCpFiEEJaa7iN2bdkxrVUHCc4QJ9SDfkZAFAmnKyXobFIAAAAAABAAO bWFudTIsMi41KzEuMTIsMiwyXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25z Lm9wZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXQyNUE2QkI4OEREOUI3NjRDNkI1NTQx QzI3Mzg0MDlGNTIwREY5MTkwDxxzYW1AZ2VudG9vLm9yZwAKCRBzhAn1IN+RkDqt AP0WANhRPbAYAIEYTpnBvUEL3+Kho2Gyiy13jN/0ySfRHQD/Ri20V21oHCED63jV SSUdfkIezixq1T+8ygKpCDlXfwU= =DL9T -----END PGP SIGNATURE-----