From 4afc5387b8d05e72e16e7f36c83ac9280fd80ba2 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Wed, 17 Jul 2019 14:56:30 -0300 Subject: [PATCH 1/3] fix(luajit) allow regular pointers to be used as lightuserdata in ARM64 This patch allows for regular pointers to be used as lightuserdata in ARM64. The source of the problem is that ARM64 has 48 bits of addressable memory in userspace (256 GiB), but LuaJIT only supports 47 bits for lightuserdata (128 GiB). This means that half of the memory is not addressable. This is what the ARM64 user space memory layout looks like on Linux: ``` / 0000000000000000 1111111111111111 1111111111111111 1111111111111111 | | 0000000000000000 1111111111111111 1111111111111111 1111111111111110 V stack 64G| ... | 0000000000000000 1100000000000000 0000000000000000 0000000000000001 \ 0000000000000000 1100000000000000 0000000000000000 0000000000000000 / 0000000000000000 1011111111111111 1111111111111111 1111111111111111 | 0000000000000000 1011111111111111 1111111111111111 1111111111111110 64G| ... | 0000000000000000 1000000000000000 0000000000000000 0000000000000001 \ 0000000000000000 1000000000000000 0000000000000000 0000000000000000 / 0000000000000000 0111111111111111 1111111111111111 1111111111111111 | 0000000000000000 0111111111111111 1111111111111111 1111111111111110 64G| ... | 0000000000000000 0100000000000000 0000000000000000 0000000000000001 \ 0000000000000000 0100000000000000 0000000000000000 0000000000000000 / 0000000000000000 0011111111111111 1111111111111111 1111111111111111 | 0000000000000000 0011111111111111 1111111111111111 1111111111111110 64G| ... | 0000000000000000 0000000000000000 0000000000000000 0000000000000001 ^ heap \ 0000000000000000 0000000000000000 0000000000000000 0000000000000000 | ``` Notice that the stack grows downwards from the top (this is also the area where static variables are allocated), and the heap grows upwards from the bottom. By losing bit 48, that means that only the bottom 128 GiB are addressable, meaning that all pointers to the stack and to static variables are invalid, and cause a "bad light userdata pointer" error. This patch modifies ARM64 support so that instead of losing access to the top 128 GiB, we lose access of the _middle_ 128 GiB, allowing for heap and stack pointers to be valid, up to 64 GiB each. This greatly restores compatibility with third-party Lua modules, most of which do not expect over 64 GiB of addressable space but _do_ expect to be able to store lightuserdata of static variables: it allows, for example, lua-cjson 2.1.0.6 to run unmodified with LuaJIT on ARM64 (because it stores lightuserdata pointing to static variables). This patch does this safely: LuaJIT's original check for bad pointers is still in place. How this patch works: LuaJIT does a check and produces a "bad light userdata pointer" if any bit from bits 48 to 64 are set. On input (`lua_pushlightuserdata`), this patch checks bits 47 and 48: * 00 - do nothing, it will be a LuaJIT-accepted value * 01 - convert it to 11, so that LuaJIT rejects the pointer * 10 - do nothing, LuaJIT will reject the pointer * 11 - convert it to 01, so that LuaJIT accepts the pointer In other words, if bit 47 is set, flip bit 48. On output (`lua_touserdata`), this patch checks bit 47, and if it is set, it sets bit 48 as well. --- ...-2.1-20190507_03-arm64-lightuserdata.patch | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch diff --git a/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch b/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch new file mode 100644 index 0000000..9c85875 --- /dev/null +++ b/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch @@ -0,0 +1,108 @@ +This patch allows for regular pointers to be used as lightuserdata in ARM64. + +The source of the problem is that ARM64 has 48 bits of addressable memory in +userspace (256 GiB), but LuaJIT only supports 47 bits for lightuserdata (128 +GiB). This means that half of the memory is not addressable. + +This is what the ARM64 user space memory layout looks like on Linux: + + / 0000000000000000 1111111111111111 1111111111111111 1111111111111111 | + | 0000000000000000 1111111111111111 1111111111111111 1111111111111110 V stack + 64G| ... + | 0000000000000000 1100000000000000 0000000000000000 0000000000000001 + \ 0000000000000000 1100000000000000 0000000000000000 0000000000000000 + / 0000000000000000 1011111111111111 1111111111111111 1111111111111111 + | 0000000000000000 1011111111111111 1111111111111111 1111111111111110 + 64G| ... + | 0000000000000000 1000000000000000 0000000000000000 0000000000000001 + \ 0000000000000000 1000000000000000 0000000000000000 0000000000000000 + / 0000000000000000 0111111111111111 1111111111111111 1111111111111111 + | 0000000000000000 0111111111111111 1111111111111111 1111111111111110 + 64G| ... + | 0000000000000000 0100000000000000 0000000000000000 0000000000000001 + \ 0000000000000000 0100000000000000 0000000000000000 0000000000000000 + / 0000000000000000 0011111111111111 1111111111111111 1111111111111111 + | 0000000000000000 0011111111111111 1111111111111111 1111111111111110 + 64G| ... + | 0000000000000000 0000000000000000 0000000000000000 0000000000000001 ^ heap + \ 0000000000000000 0000000000000000 0000000000000000 0000000000000000 | + +Notice that the stack grows downwards from the top, and the heap grows upwards +from the bottom. By losing bit 48, that means that only the bottom 128 GiB +are addressable, meaning that all pointers to the stack and to static variables +are invalid, and cause a "bad light userdata pointer" error. + +This patch modifies ARM64 support so that instead of losing access to the top +128 GiB, we lose access of the _middle_ 128 GiB, allowing for heap and stack +pointers to be valid, up to 64 GiB each. This greatly restores compatibility +with third-party Lua modules, most of which do not expect over 64 GiB of +addressable space but _do_ expect to be able to store lightuserdata of static +variables: it allows, for example, lua-cjson 2.1.0.6 to run unmodified with +LuaJIT on ARM64 (because it stores lightuserdata pointing to static +variables). + +How this patch works: + +LuaJIT does a check and produces a "bad light userdata pointer" if any bit +from bits 48 to 64 are set. + +On input (`lua_pushlightuserdata`), this patch checks bits 47 and 48: + +* 00 - do nothing, it will be a LuaJIT-accepted value +* 01 - convert it to 11, so that LuaJIT rejects the pointer +* 10 - do nothing, LuaJIT will reject the pointer +* 11 - convert it to 01, so that LuaJIT accepts the pointer + +In other words, if bit 47 is set, flip bit 48. + +On output (`lua_touserdata`), this patch checks bit 47, and if it is +set, it sets bit 48 as well. + +Signed-off-by: Hisham Muhammad + +diff -ur a/LuaJIT-2.1-20190507/src/lj_api.c b/LuaJIT-2.1-20190507/src/lj_api.c +--- a/LuaJIT-2.1-20190507/src/lj_api.c 2019-05-02 17:58:14.000000000 -0300 ++++ b/LuaJIT-2.1-20190507/src/lj_api.c 2019-07-17 11:48:35.894843435 -0300 +@@ -594,9 +594,14 @@ + cTValue *o = index2adr(L, idx); + if (tvisudata(o)) + return uddata(udataV(o)); +- else if (tvislightud(o)) +- return lightudV(o); +- else ++ else if (tvislightud(o)) { ++ void *p = lightudV(o); ++#if LJ_TARGET_ARM64 ++ if ((uintptr_t) p >> 46) ++ p = (void*) ((uintptr_t) (p) | (1UL << 47)); ++#endif ++ return p; ++ } else + return NULL; + } + +@@ -696,6 +701,11 @@ + + LUA_API void lua_pushlightuserdata(lua_State *L, void *p) + { ++#if LJ_TARGET_ARM64 ++ if ((uintptr_t) p & (1UL << 46)) { ++ p = (void*) ((uintptr_t) p ^ (1UL << 47)); ++ } ++#endif + setlightudV(L->top, checklightudptr(L, p)); + incr_top(L); + } +--- a/LuaJIT-2.1-20190507/src/lj_cconv.c 2019-05-02 17:58:14.000000000 -0300 ++++ b/LuaJIT-2.1-20190507/src/lj_cconv.c 2019-07-22 13:29:19.007491869 -0300 +@@ -612,6 +612,10 @@ + tmpptr = *(void **)tmpptr; + } else if (tvislightud(o)) { + tmpptr = lightudV(o); ++#if LJ_TARGET_ARM64 ++ if ((uintptr_t) tmpptr >> 46) ++ tmpptr = (void*) ((uintptr_t) (tmpptr) | (1UL << 47)); ++#endif + } else if (tvisfunc(o)) { + void *p = lj_ccallback_new(cts, d, funcV(o)); + if (p) { From 432b19cc8fab849a5508f3f3b406235b3f219cb7 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Fri, 9 Aug 2019 11:09:14 -0300 Subject: [PATCH 2/3] fix(lua-cjson) revert lightuserdata pointer masking in 64-bit builds --- .../lua-cjson-2.1.0.7-01-no-arm64-mask.patch | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch diff --git a/patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch b/patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch new file mode 100644 index 0000000..4437c62 --- /dev/null +++ b/patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch @@ -0,0 +1,20 @@ +This is a minimal patch to revert the lightuserdata pointer masking +for 64-bit builds done in the lua-cjson fork included in OpenResty 1.15.8.1. + +diff -ur a/lua-cjson-2.1.0.7/lua_cjson.c b/lua-cjson-2.1.0.7/lua_cjson.c +--- a/lua-cjson-2.1.0.7/lua_cjson.c 2018-12-07 07:17:38.000000000 -0200 ++++ b/lua-cjson-2.1.0.7/lua_cjson.c 2019-08-09 11:05:03.192973418 -0300 +@@ -93,13 +93,7 @@ + #define strcasecmp _stricmp + #endif + +-#if LONG_MAX > ((1UL << 31) - 1) +-#define json_lightudata_mask(ludata) \ +- ((void *) ((uintptr_t) (ludata) & ((1UL << 47) - 1))) +- +-#else + #define json_lightudata_mask(ludata) (ludata) +-#endif + + static const char * const *json_empty_array; + static const char * const *json_array; From 2a5773e31f9cbf4df2a5596d2bb55d7d33f6e944 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Fri, 9 Aug 2019 17:21:07 -0300 Subject: [PATCH 3/3] [tmp] take 2 --- ...-2.1-20190507_03-arm64-lightuserdata.patch | 111 +++++++++--------- .../lua-cjson-2.1.0.7-01-no-arm64-mask.patch | 20 ---- 2 files changed, 58 insertions(+), 73 deletions(-) delete mode 100644 patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch diff --git a/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch b/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch index 9c85875..f61a955 100644 --- a/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch +++ b/patches/1.15.8.1/LuaJIT-2.1-20190507_03-arm64-lightuserdata.patch @@ -6,26 +6,30 @@ GiB). This means that half of the memory is not addressable. This is what the ARM64 user space memory layout looks like on Linux: - / 0000000000000000 1111111111111111 1111111111111111 1111111111111111 | - | 0000000000000000 1111111111111111 1111111111111111 1111111111111110 V stack - 64G| ... - | 0000000000000000 1100000000000000 0000000000000000 0000000000000001 - \ 0000000000000000 1100000000000000 0000000000000000 0000000000000000 - / 0000000000000000 1011111111111111 1111111111111111 1111111111111111 - | 0000000000000000 1011111111111111 1111111111111111 1111111111111110 - 64G| ... - | 0000000000000000 1000000000000000 0000000000000000 0000000000000001 - \ 0000000000000000 1000000000000000 0000000000000000 0000000000000000 - / 0000000000000000 0111111111111111 1111111111111111 1111111111111111 - | 0000000000000000 0111111111111111 1111111111111111 1111111111111110 - 64G| ... - | 0000000000000000 0100000000000000 0000000000000000 0000000000000001 - \ 0000000000000000 0100000000000000 0000000000000000 0000000000000000 - / 0000000000000000 0011111111111111 1111111111111111 1111111111111111 - | 0000000000000000 0011111111111111 1111111111111111 1111111111111110 - 64G| ... - | 0000000000000000 0000000000000000 0000000000000000 0000000000000001 ^ heap - \ 0000000000000000 0000000000000000 0000000000000000 0000000000000000 | + / 0000000000000000 1111111111111111 1111111111111111 1111111111111111 | +A 32G| ... | + \ 0000000000000000 1110000000000000 0000000000000000 0000000000000000 V stack + / 0000000000000000 1101111111111111 1111111111111111 1111111111111111 +B 32G| ... + \ 0000000000000000 1100000000000000 0000000000000000 0000000000000000 + / 0000000000000000 1011111111111111 1111111111111111 1111111111111111 +C 32G| ... + \ 0000000000000000 1010000000000000 0000000000000000 0000000000000000 + / 0000000000000000 1001111111111111 1111111111111111 1111111111111111 +D 32G| ... + \ 0000000000000000 1000000000000000 0000000000000000 0000000000000000 + / 0000000000000000 0111111111111111 1111111111111111 1111111111111111 +E 32G| ... + \ 0000000000000000 0110000000000000 0000000000000000 0000000000000000 + / 0000000000000000 0101111111111111 1111111111111111 1111111111111111 +F 32G| ... + \ 0000000000000000 0100000000000000 0000000000000000 0000000000000000 + / 0000000000000000 0011111111111111 1111111111111111 1111111111111111 +G 32G| ... + \ 0000000000000000 0010000000000000 0000000000000000 0000000000000000 + / 0000000000000000 0001111111111111 1111111111111111 1111111111111111 ^ heap +H 32G| ... + \ 0000000000000000 0000000000000000 0000000000000000 0000000000000000 Notice that the stack grows downwards from the top, and the heap grows upwards from the bottom. By losing bit 48, that means that only the bottom 128 GiB @@ -33,37 +37,29 @@ are addressable, meaning that all pointers to the stack and to static variables are invalid, and cause a "bad light userdata pointer" error. This patch modifies ARM64 support so that instead of losing access to the top -128 GiB, we lose access of the _middle_ 128 GiB, allowing for heap and stack -pointers to be valid, up to 64 GiB each. This greatly restores compatibility -with third-party Lua modules, most of which do not expect over 64 GiB of -addressable space but _do_ expect to be able to store lightuserdata of static -variables: it allows, for example, lua-cjson 2.1.0.6 to run unmodified with -LuaJIT on ARM64 (because it stores lightuserdata pointing to static -variables). +128 GiB, we lose access to sections in the _middle_, allowing for normal +heap and stack pointers to be valid. -How this patch works: +This greatly restores compatibility with third-party Lua modules, most of +which do not expect over 64 GiB of addressable space but _do_ expect to be +able to store lightuserdata of static variables: it allows, for example, +lua-cjson 2.1.0.6 to run unmodified with LuaJIT on ARM64 (because it stores +lightuserdata pointing to static variables). -LuaJIT does a check and produces a "bad light userdata pointer" if any bit -from bits 48 to 64 are set. - -On input (`lua_pushlightuserdata`), this patch checks bits 47 and 48: - -* 00 - do nothing, it will be a LuaJIT-accepted value -* 01 - convert it to 11, so that LuaJIT rejects the pointer -* 10 - do nothing, LuaJIT will reject the pointer -* 11 - convert it to 01, so that LuaJIT accepts the pointer - -In other words, if bit 47 is set, flip bit 48. - -On output (`lua_touserdata`), this patch checks bit 47, and if it is -set, it sets bit 48 as well. +The original version of this patch discarded the exact middle section of +the address space (i.e. sections A B for stack and G H for heap were valid, +C D E F were invalid). However, existing libraries for LuaJIT mitigate this +problem by masking bit 48 in stack pointers, resulting in addresses in the +E section. For this reason, the revised version of this patch discards +sections B C D F, providing 32G for stack addresses (A), 64G for heap address +(G H) and 32G for masked stack addresses (E). Signed-off-by: Hisham Muhammad diff -ur a/LuaJIT-2.1-20190507/src/lj_api.c b/LuaJIT-2.1-20190507/src/lj_api.c --- a/LuaJIT-2.1-20190507/src/lj_api.c 2019-05-02 17:58:14.000000000 -0300 -+++ b/LuaJIT-2.1-20190507/src/lj_api.c 2019-07-17 11:48:35.894843435 -0300 -@@ -594,9 +594,14 @@ ++++ b/LuaJIT-2.1-20190507/src/lj_api.c 2019-08-09 15:36:18.435306003 -0300 +@@ -594,9 +594,17 @@ cTValue *o = index2adr(L, idx); if (tvisudata(o)) return uddata(udataV(o)); @@ -73,35 +69,44 @@ diff -ur a/LuaJIT-2.1-20190507/src/lj_api.c b/LuaJIT-2.1-20190507/src/lj_api.c + else if (tvislightud(o)) { + void *p = lightudV(o); +#if LJ_TARGET_ARM64 -+ if ((uintptr_t) p >> 46) -+ p = (void*) ((uintptr_t) (p) | (1UL << 47)); ++ switch ((uintptr_t) p & (7UL << 45)) { ++ case (7UL << 45): /* 111 -> 010 */ ++ case (2UL << 45): /* 010 -> 111 */ ++ p = (void*) ((uintptr_t) p ^ (5UL << 45)); ++ } +#endif + return p; + } else return NULL; } -@@ -696,6 +701,11 @@ +@@ -696,6 +704,13 @@ LUA_API void lua_pushlightuserdata(lua_State *L, void *p) { +#if LJ_TARGET_ARM64 -+ if ((uintptr_t) p & (1UL << 46)) { -+ p = (void*) ((uintptr_t) p ^ (1UL << 47)); ++ switch ((uintptr_t) p & (7UL << 45)) { ++ case (7UL << 45): /* 111 -> 010 */ ++ case (2UL << 45): /* 010 -> 111 */ ++ p = (void*) ((uintptr_t) p ^ (5UL << 45)); + } +#endif setlightudV(L->top, checklightudptr(L, p)); incr_top(L); } +diff -ur a/LuaJIT-2.1-20190507/src/lj_cconv.c b/LuaJIT-2.1-20190507/src/lj_cconv.c --- a/LuaJIT-2.1-20190507/src/lj_cconv.c 2019-05-02 17:58:14.000000000 -0300 -+++ b/LuaJIT-2.1-20190507/src/lj_cconv.c 2019-07-22 13:29:19.007491869 -0300 -@@ -612,6 +612,10 @@ ++++ b/LuaJIT-2.1-20190507/src/lj_cconv.c 2019-08-09 15:38:14.356301250 -0300 +@@ -612,6 +612,13 @@ tmpptr = *(void **)tmpptr; } else if (tvislightud(o)) { tmpptr = lightudV(o); +#if LJ_TARGET_ARM64 -+ if ((uintptr_t) tmpptr >> 46) -+ tmpptr = (void*) ((uintptr_t) (tmpptr) | (1UL << 47)); ++ switch ((uintptr_t) tmpptr & (7UL << 45)) { ++ case (7UL << 45): /* 111 -> 010 */ ++ case (2UL << 45): /* 010 -> 111 */ ++ tmpptr = (void*) ((uintptr_t) tmpptr ^ (5UL << 45)); ++ } +#endif } else if (tvisfunc(o)) { void *p = lj_ccallback_new(cts, d, funcV(o)); diff --git a/patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch b/patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch deleted file mode 100644 index 4437c62..0000000 --- a/patches/1.15.8.1/lua-cjson-2.1.0.7-01-no-arm64-mask.patch +++ /dev/null @@ -1,20 +0,0 @@ -This is a minimal patch to revert the lightuserdata pointer masking -for 64-bit builds done in the lua-cjson fork included in OpenResty 1.15.8.1. - -diff -ur a/lua-cjson-2.1.0.7/lua_cjson.c b/lua-cjson-2.1.0.7/lua_cjson.c ---- a/lua-cjson-2.1.0.7/lua_cjson.c 2018-12-07 07:17:38.000000000 -0200 -+++ b/lua-cjson-2.1.0.7/lua_cjson.c 2019-08-09 11:05:03.192973418 -0300 -@@ -93,13 +93,7 @@ - #define strcasecmp _stricmp - #endif - --#if LONG_MAX > ((1UL << 31) - 1) --#define json_lightudata_mask(ludata) \ -- ((void *) ((uintptr_t) (ludata) & ((1UL << 47) - 1))) -- --#else - #define json_lightudata_mask(ludata) (ludata) --#endif - - static const char * const *json_empty_array; - static const char * const *json_array;