From 44c9ebcb65537044540e171f594ea544ea2c20ea Mon Sep 17 00:00:00 2001
From: Moritz Angermann <moritz.angermann@gmail.com>
Date: Fri, 23 Jul 2021 21:20:26 +0800
Subject: [PATCH] [rts] Untag bq->bh prior to reading the info table

In `checkBlockingQueues` we must always untag the `bh` field of an `StgBlockingQueue`.
While at first glance it might seem a sensible assumption that `bh` will
always be a blackhole and therefore never be tagged, the GC could
shortcut the indirection and put a tagged pointer into the indirection.

This blew up on aarch64-darwin with a misaligned access. `bh` pointed
to an address that always ended in 0xa. On architectures that
are a little less strict about alignment, this would have read
a garbage info table pointer, which very, very unlikely would have been equal to
`stg_BLACKHOLE_info` and therefore things accidentally worked. However,
on AArch64, the read of the info table pointer resulted in a SIGBUS due
to misaligned read.

Fixes #20093.

(cherry picked from commit 1832676aba0a5d75ac934a62eff55e35f95587d5)
---
 rts/Threads.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/rts/Threads.c b/rts/Threads.c
index e1a401c9b3a4..fc9a895ed89a 100644
--- a/rts/Threads.c
+++ b/rts/Threads.c
@@ -407,7 +407,18 @@ checkBlockingQueues (Capability *cap, StgTSO *tso)
             continue;
         }
 
-        p = bq->bh;
+        // We need to always ensure we untag bh.  While it might seem a
+        // sensible assumption that bh will never be tagged, the GC could
+        // shortcut the indirection and put a tagged pointer into the
+        // indirection.
+        //
+        // This blew up on aarch64-darwin with misaligned access.  bh pointing
+        // to an address that always ended in 0xa.  Thus on architectures that
+        // are a little less strict about alignment, this would have read a
+        // garbage pinfo, which very, very unlikely would have been equal to
+        // stg_BLACKHOLE_info.  Thus while the code would have done the wrong
+        // thing the result would be the same in almost all cases. See #20093.
+        p = UNTAG_CLOSURE(bq->bh);
         const StgInfoTable *pinfo = ACQUIRE_LOAD(&p->header.info);
         if (pinfo != &stg_BLACKHOLE_info ||
             ((StgInd *)p)->indirectee != (StgClosure*)bq)
-- 
GitLab