bitwiseworks / libcx

kLIBC Extension Library
GNU Lesser General Public License v2.1
11 stars 1 forks source link

Preserve MAP_PRIVATE mappngs after fork #88

Open dmik opened 3 years ago

dmik commented 3 years ago

Currently, only MAP_SHARED mappings are preserved after fork but it appears that all mapping should be preserved, including MAP_PRIVATE ones (per POSIX):

MAP_SHARED and MAP_PRIVATE describe the disposition of write references to the memory object. If MAP_SHARED is specified, write references shall change the underlying object. If MAP_PRIVATE is specified, modifications to the mapped data by the calling process shall be visible only to the calling process and shall not change the underlying object. It is unspecified whether modifications to the underlying object done after the MAP_PRIVATE mapping is established are visible through the MAP_PRIVATE mapping. Either MAP_SHARED or MAP_PRIVATE can be specified, but not both. The mapping type is retained across fork().

There is at least one program that assumes that: GnuPG. Calling

gpg --full-gen-key

when libgcrypt is built with mmap support results in this crash:

______________________________________________________________________

 Exception Report - created 2021/01/14 21:43:28
______________________________________________________________________

 OS2/eCS Version:  2.45
 # of Processors:  4
 Physical Memory:  3260 mb
 Virt Addr Limit:  3072 mb
 Exceptq Version:  7.11.3-shl (Jul  5 2016)

______________________________________________________________________

 Exception C0000005 - Access Violation
______________________________________________________________________

 Process:  C:\USR\BIN\GPG.EXE (11/10/2020 13:33:03 625,451)
 PID:      A5A5 (42405)
 TID:      01 (1)
 Priority: 200

 Filename: C:\USR\LIB\GCRYPT20.DLL (10/07/2020 19:41:23 523,979)
 Address:  005B:1D278C74 (0001:00008C74)
 Cause:    Attempted to write to 21030000
           (unallocated memory)

______________________________________________________________________

 Failing Instruction
______________________________________________________________________

 1D278C6C  ADD EDX, EDX                     (01d2)
 1D278C6E  ADD EDX, EDX                     (01d2)
 1D278C70  ADD EDX, EDX                     (01d2)
 1D278C72  ADD EDX, EAX                     (01c2)
 1D278C74 >MOV DWORD [EAX], 0xffffffff      (c700 ffffffff)
 1D278C7A  MOV DWORD [EAX+0x4], 0xffffffff  (c740 04 ffffffff)
 1D278C81  ADD EAX, 0x8                     (83c0 08)
 1D278C84  CMP EAX, EDX                     (39d0)

______________________________________________________________________

 Registers
______________________________________________________________________

 EAX : 21030000   EBX  : 17D133F0   ECX : 21030000   EDX  : 21040000
 ESI : 00000000   EDI  : 00010000
 ESP : 001FF140   EBP  : 001FF168   EIP : 1D278C74   EFLG : 00010206
 CS  : 005B       CSLIM: FFFFFFFF   SS  : 0053       SSLIM: FFFFFFFF

 EAX : unallocated memory
 EBX : read/write memory at 0002:000033F0 in GCRYPT20
 ECX : unallocated memory
 EDX : unallocated memory
 ESI : not a valid address
 EDI : read/exec  memory at 0001:00000000 in GPG

______________________________________________________________________

 Stack Info for Thread 01
______________________________________________________________________

   Size       Base        ESP         Max         Top
 00100000   00200000 -> 001FF140 -> 001FC000 -> 00100000

______________________________________________________________________

 Call Stack
______________________________________________________________________

   EBP     Address    Module     Obj:Offset    Nearest Public Symbol
 --------  ---------  --------  -------------  -----------------------
 Trap  ->  1D278C74   GCRYPT20  0001:00008C74  secmem.c#844 __gcry_secmem_term + 12F 0001:00008B45 (secmem.c)

 001FF168  1D2732D3   GCRYPT20  0001:000032D3  global.c#465 __gcry_vcontrol + 219 0001:000030BA (global.c)

  Offset Name                 Type                         Hex Value
  ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ
   8     cmd                  0x227                        19
   12    arg_ptr              pointer to 8 bit unsigned    1FF1C4

 001FF198  1D270118   GCRYPT20  0001:00000118  gpg-error.h#947 _gcry_control + 18 0001:00000100 (visibility.c)

  Offset Name                 Type                         Hex Value
  ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ
   8     cmd                  0x212                        19

 001FF1B8  000EB726   GPG       0001:000DB726  exechelp-posix.c#866 _gnupg_spawn_process_detached + 66 0001:000DB6C0 (libcommon_a-exechelp-posix.obj)

  Offset Name                 Type                         Hex Value
  ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ
   8     pgmname              8 bit unsigned               20061120
   12    argv                 0x0                          1FF228
   16    envp                 0x0                          0

 001FF1D8  000D3893   GPG       0001:000C3893  asshelp.c#482 _start_new_gpg_agent + 3C3 0001:000C34D0 (libcommon_a-asshelp.obj)
dmik commented 3 years ago

Looking at https://github.com/bitwiseworks/gnupg-os2/blob/a8a4ff072648159e2f4d3c8581aa8c4c2e17201a/common/exechelp-posix.c#L838 it's pretty much obvious what's going on:

Since the memory is not actually inherited, it is unallocated in the child and a crash occurs.

Implementing this is not too difficult but requires some time.

  1. We should use LIBCn DosAllocMemEx(OBJ_FORK) in mmap.c when allocating memory for MAP_PRIVATE to have the respective memory block be automatically "cloned" in the forked child by the LIBCn fork machinery.
  2. We should enhance forkCompletion in mmap.c to recreate mmap structures in the forked child the same way we do it for MAP_SHARED mappings.