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-----