Unboxed tuple returns generate BCOs with incorrect bitmaps
(This issue was extracted from #23066 (comment 485667).)
Loading the following program into GHCi causes a BCO to be generated with an incorrect bitmap:
{-# LANGUAGE MagicHash, UnboxedTuples #-}
module UTup where
import GHC.Exts
f :: () -> (# Int, Int #)
f () = (# 0, 0 #)
The BCO in question is generated by GHC.StgToByteCode.tupleBCO
and looks like this:
ProtoBCO tuple_E0#0 []:
bitmap: 3 [11]
SLIDE 0 1
RETURN_TUPLE
This is clearly wrong, as 11
(1011
in binary) is too big for a bitmap of size 3. Moreover, it suggests the tuple only includes a single nonpointer, which is clearly incorrect.
It is fairly unlikely that the incorrect bitmap will cause any trouble in practice, as this BCO only exists to annotate the contents of an unboxed tuple on the stack when switching from native code to the interpreter. In ordinary operation, the only way for this to go wrong is if the garbage collector runs during that context switch. This seems fairly unlikely, which is probably why it has not caused any trouble to date.
Unfortunately, it does make debugging bytecode issues significantly more difficult. The problem is that the debug RTS also needs to be able to walk the stack to print out stack frames, so incorrect bitmaps can cause it to dereference nonpointers, which triggers a segfault. This modified program illustrates the issue:
{-# LANGUAGE MagicHash, UnboxedTuples #-}
module UTup2 where
import GHC.Exts
f :: () -> (# Int#, Int#, Int #)
f () = (# 0#, 0#, 0 #)
main :: IO ()
main = case f () of
(# _, _, _ #) -> return ()
Given a GHC that has been linked with -debug
, this program causes a segfault when run via echo main | ghc +RTS -Di -RTS --interactive UTup2
. The segfault is triggered when the RTS printer attempts to print a stg_ret_t
stack frame and dereferences a null pointer.