Skip to content

Avoid reading text section in generate code

Summary

Some OSs (Android, OpenBSD) are moving to execute-only (xonly) unreadable code as a mitigation technique. GHC should put any data that it reads at runtime outside .text section and into .rodata.

Steps to reproduce

OpenBSD 7.3 is likely to ship with xonly enabled by default. Right now this is a development set of patches. With these patches GHC build process fails as soon as it creates and runs the first program. The program happens to be ghc-cabal which gets a SIGSEGV promptly at startup. The problem is easy to reproduce with other binaries, but I didn't minimize the repro.

The details indicate reading from text.

% /usr/ports/pobj/ghc-9.2.5/ghc-9.2.5/inplace/bin/ghc-cabal 
[1]    99461 segmentation fault (core dumped)  /usr/ports/pobj/ghc-9.2.5/ghc-9.2.5/inplace/bin/ghc-cabal
% egdb /usr/ports/pobj/ghc-9.2.5/ghc-9.2.5/inplace/bin/ghc-cabal ghc-cabal.core 
Core was generated by `ghc-cabal'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000077e6f693de3 in stg_enter_info ()
(gdb) disassemble

Dump of assembler code for function stg_enter_info:
   0x0000077e6f693dd8 <+0>:	mov    0x8(%rbp),%rax
   0x0000077e6f693ddc <+4>:	test   $0x7,%al
   0x0000077e6f693dde <+6>:	jne    0x77e6f693e2f <stg_enter_info+87>
   0x0000077e6f693de0 <+8>:	mov    (%rax),%rbx
=> 0x0000077e6f693de3 <+11>:	movslq -0x8(%rbx),%rcx
   0x0000077e6f693de7 <+15>:	cmp    $0x1a,%rcx
   0x0000077e6f693deb <+19>:	jb     0x77e6f693e15 <stg_enter_info+61>
   0x0000077e6f693ded <+21>:	cmp    $0x1c,%rcx
   0x0000077e6f693df1 <+25>:	jb     0x77e6f693e03 <stg_enter_info+43>
   0x0000077e6f693df3 <+27>:	cmp    $0x1d,%rcx
   0x0000077e6f693df7 <+31>:	jae    0x77e6f693e09 <stg_enter_info+49>
   0x0000077e6f693df9 <+33>:	mov    0x8(%rax),%rax
   0x0000077e6f693dfd <+37>:	mov    %rax,0x8(%rbp)
   0x0000077e6f693e01 <+41>:	jmp    0x77e6f693ddc <stg_enter_info+4>
   0x0000077e6f693e03 <+43>:	cmp    $0x1b,%rcx
   0x0000077e6f693e07 <+47>:	jae    0x77e6f693df9 <stg_enter_info+33>
   0x0000077e6f693e09 <+49>:	mov    %rbx,%rcx
   0x0000077e6f693e0c <+52>:	mov    %rax,%rbx
   0x0000077e6f693e0f <+55>:	add    $0x10,%rbp
   0x0000077e6f693e13 <+59>:	jmpq   *%rcx
   0x0000077e6f693e15 <+61>:	cmp    $0xf,%rcx
   0x0000077e6f693e19 <+65>:	jb     0x77e6f693e29 <stg_enter_info+81>
   0x0000077e6f693e1b <+67>:	cmp    $0x19,%rcx
   0x0000077e6f693e1f <+71>:	jae    0x77e6f693e2f <stg_enter_info+87>
   0x0000077e6f693e21 <+73>:	cmp    $0x17,%rcx
   0x0000077e6f693e25 <+77>:	jne    0x77e6f693e09 <stg_enter_info+49>
   0x0000077e6f693e27 <+79>:	jmp    0x77e6f693e2f <stg_enter_info+87>
   0x0000077e6f693e29 <+81>:	cmp    $0x8,%rcx
   0x0000077e6f693e2d <+85>:	jb     0x77e6f693e09 <stg_enter_info+49>
   0x0000077e6f693e2f <+87>:	mov    %rax,%rbx
   0x0000077e6f693e32 <+90>:	add    $0x10,%rbp
   0x0000077e6f693e36 <+94>:	jmpq   *0x0(%rbp)
End of assembler dump.
(gdb) p/x $rbx
$1 = 0x77e6f48b7c8
(gdb) x/100bx $rbx
0x77e6f48b7c8 <ZCMain_main_info>:	0x48	0x8d	0x45	0xf0	0x4c	0x39	0xf8	0x72
0x77e6f48b7d0 <ZCMain_main_info+8>:	0x45	0x48	0x83	0xec	0x08	0x4c	0x89	0xe8
0x77e6f48b7d8 <ZCMain_main_info+16>:	0x48	0x89	0xde	0x48	0x89	0xc7	0x31	0xc0
0x77e6f48b7e0 <ZCMain_main_info+24>:	0xe8	0xeb	0x3b	0x20	0x00	0x48	0x83	0xc4
0x77e6f48b7e8 <ZCMain_main_info+32>:	0x08	0x48	0x85	0xc0	0x74	0x26	0x48	0x8d
0x77e6f48b7f0 <ZCMain_main_info+40>:	0x1d	0x13	0xf0	0x20	0x00	0x48	0x89	0x5d
0x77e6f48b7f8 <ZCMain_main_info+48>:	0xf0	0x48	0x89	0x45	0xf8	0x4c	0x8d	0x35
0x77e6f48b800 <ZCMain_main_info+56>:	0x6c	0x9e	0x45	0x00	0x48	0x8d	0x1d	0x25
0x77e6f48b808 <ZCMain_main_info+64>:	0xc9	0x47	0x00	0x48	0x83	0xc5	0xf0	0xe9
0x77e6f48b810 <ZCMain_main_info+72>:	0x8c	0x32	0x21	0x00	0xff	0x23	0x41	0xff
0x77e6f48b818 <ZCMain_main_info+80>:	0x65	0xf0	0xcc	0xcc	0xcc	0xcc	0xcc	0xcc
0x77e6f48b820 <_hs_text_memcpy>:	0x4c	0x8b	0x1d	0x01	0x67	0x21	0x00	0x4c
0x77e6f48b828 <_hs_text_memcpy+8>:	0x33	0x1c	0x24	0x55

Expected behavior

Data gets placed into .rodata and programs run fine when built with --execute-only linker flag. As it stands I will be disabling this feature for all GHC-compiled programs on OpenBSD.

Environment

  • GHC version used: 8.10.7 from bootstrap, but newer versions like 9.2.5 are similarly affected.

Optional:

  • Operating System: OpenBSD
  • System Architecture: amd64
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information