PCRE2Project / pcre2

PCRE2 development is now based here.
Other
910 stars 191 forks source link

JIT is violating Darwin W^X if not targeting macOS #212

Open Torrekie opened 1 year ago

Torrekie commented 1 year ago

On iOS there's no pthread_jit* API provided by Apple, resulting PROT_WRITE and PROT_EXEC has been set at same time, causing KERN_PROTECTION_FAILURE

Date/Time:           2023-02-27 15:52:08.6193 +0800
Launch Time:         2023-02-27 15:52:08.2011 +0800
OS Version:          iPhone OS 14.5.1 (18E212)
Release Type:        User
Baseband Version:    n/a
Report Version:      104

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Subtype: KERN_PROTECTION_FAILURE at 0x0000000a1b2fc008
VM Region Info: 0xa1b2fc008 is in 0xa1b2fc000-0xa1b30c000;  bytes after start: 8  bytes before end: 65527
      REGION TYPE                 START - END      [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      unused shlib __TEXT      210000000-217598000 [117.6M] r--/r-- SM=COW  ... this process
      GAP OF 0x803d64000 BYTES
--->  VM_ALLOCATE              a1b2fc000-a1b30c000 [   64K] rwx/rwx SM=PRV  
      GAP OF 0x5a4cf4000 BYTES
      commpage (reserved)      fc0000000-1000000000 [  1.0G] ---/--- SM=NUL  ...(unallocated)

Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libpcre2-8.0.dylib                  0x0000000100cfe700 0x100ce4000 + 108288
1   libpcre2-8.0.dylib                  0x0000000100cfe6a4 0x100ce4000 + 108196
2   libpcre2-8.0.dylib                  0x0000000100cfe578 0x100ce4000 + 107896
3   pcre2_jit_test                      0x0000000100cc1050 0x100cbc000 + 20560
4   libdyld.dylib                       0x000000019ed26cf8 0x19ed25000 + 7416
zherczeg commented 1 year ago

As far as I heard no jit works on ios, it is better to disable it.

Torrekie commented 1 year ago

After several patching I could avoid that SIGBUS using METHOD1, but then resulting all pcre2_jit_tests failed

--- a/src/sljit/sljitExecAllocator.c  1645780892.000000000
+++ b/src/sljit/sljitExecAllocator.c      1677493457.634072731
@@ -158,7 +158,62 @@ static SLJIT_INLINE void apple_update_wx
 }
 #endif /* SLJIT_CONFIG_X86 */
 #else /* !TARGET_OS_OSX */
+#define _COMM_PAGE_START_ADDRESS        (0x0000000FFFFFC000ULL) /* In TTBR0 */
+#define _COMM_PAGE_APRR_SUPPORT         (_COMM_PAGE_START_ADDRESS + 0x10C)
+#define _COMM_PAGE_APPR_WRITE_ENABLE    (_COMM_PAGE_START_ADDRESS + 0x110)
+#define _COMM_PAGE_APRR_WRITE_DISABLE   (_COMM_PAGE_START_ADDRESS + 0x118)
+
 #define SLJIT_MAP_JIT  (MAP_JIT)
+#ifdef METHOD1
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
+                       apple_update_wx_flags(enable_exec)
+#warning Using https://siguza.github.io/APRR/
+static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec)
+{
+    uint8_t aprr_support = *(volatile uint8_t *)_COMM_PAGE_APRR_SUPPORT;
+    if (aprr_support == 0 || aprr_support > 3) {
+        return;
+    } else if (aprr_support == 1) {
+        __asm__ __volatile__ (
+            "mov x0, %0\n"
+            "ldr x0, [x0]\n"
+            "msr S3_4_c15_c2_7, x0\n"
+            "isb sy\n"
+            :: "r" (enable_exec ? _COMM_PAGE_APRR_WRITE_DISABLE
+                            : _COMM_PAGE_APPR_WRITE_ENABLE)
+            : "memory", "x0"
+        );
+    } else {
+        __asm__ __volatile__ (
+            "mov x0, %0\n"
+            "ldr x0, [x0]\n"
+            "msr S3_6_c15_c1_5, x0\n"
+            "isb sy\n"
+            :: "r" (enable_exec ? _COMM_PAGE_APRR_WRITE_DISABLE
+                            : _COMM_PAGE_APPR_WRITE_ENABLE)
+            : "memory", "x0"
+        );
+    }
+}
+#elif defined(METHOD2)
+#warning Using mprotect
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
+                       update_wx_flags(from, to, enable_exec)
+static SLJIT_INLINE void update_wx_flags(void *from, void *to, int enable_exec)
+{
+       sljit_uw page_mask = (sljit_uw)get_page_alignment();
+       sljit_uw start = (sljit_uw)from;
+       sljit_uw end = (sljit_uw)to;
+       int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
+
+       SLJIT_ASSERT(start < end);
+
+       start &= ~page_mask;
+       end = (end + page_mask) & ~page_mask;
+
+       mprotect((void*)start, end - start, prot);
+}
+#endif
 #endif /* TARGET_OS_OSX */
 #endif /* __APPLE__ && MAP_JIT */
 #ifndef SLJIT_UPDATE_WX_FLAGS
iPad:/buildroot/pcre2-10.42 root# .libs/pcre2_jit_test | grep "%"
Successful test ratio: 0% (674 failed)
Invalid UTF8 successful test ratio: 0% (129 failed)
Invalid UTF16 successful test ratio: 0% (39 failed)
Invalid UTF32 successful test ratio: 0% (26 failed)
zherczeg commented 1 year ago

The last thing I heard, no jit is allowed in iOS, because it hurts monetizing the platform. I also heard jit works on jailbroken devices, but that is special case.

Torrekie commented 1 year ago

basically, there have JIT support on iOS with dynamic-codesigning entitlement enabled (which also enabled for Safari.app) and I am pretty sure there used to have iOS JIT support during PCRE 8.x. Actually partial JIT tests passed after my modification but still meeting some weird errors on others. I believe it can work properly if we are able to handle W^X in a better way (https://github.com/qemu/qemu/commit/36195a781c1c7237935b45c30a91686c07a10474 this fork using the exactly same method that I mentioned above to replace previous pthread_jit_write_protect_np and it do have working JIT on both macOS/iOS)

zherczeg commented 1 year ago

Sounds interesting. Unfortunately I cannot help since I have no access to any Apple device, but somebody else might know something. Maybe in some apple dev forums.

Torrekie commented 6 months ago

Retried today and have to say defining SLJIT_WX_EXECUTABLE_ALLOCATOR and make non-macOS Darwin targets to use POSIX implementation is now having a working JIT outside of sandbox. But this macro doesn’t seems configurable by autoconf script.

so the thing is:

JIT (SLJIT_WX_EXECUTABLE_ALLOCATOR defined, unsandboxed)

iPad:/buildroot/pcre2-10.43 root# ./pcre2_jit_test 
Running JIT regression tests
  target CPU of SLJIT compiler: ARM-64 64bit (little endian + unaligned)
  in  8 bit mode with UTF-8  enabled:
  in 16 bit mode with UTF-16 enabled:
  in 32 bit mode with UTF-32 enabled:
............................................................
............................................................
............................................................
............................................................
............................................................
............................................................
............................................................
............................................................
............................................................
............................................................
............................................................
............................
All JIT regression tests are successfully passed.

Running invalid-utf8 JIT regression tests
............................................................
............................................................
............

All invalid UTF8 JIT regression tests are successfully passed.

Running invalid-utf16 JIT regression tests
.......................................

All invalid UTF16 JIT regression tests are successfully passed.

Running invalid-utf32 JIT regression tests
..........................

All invalid UTF32 JIT regression tests are successfully passed.