aboutsummaryrefslogtreecommitdiff
path: root/src/lj_mcode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lj_mcode.c')
-rw-r--r--src/lj_mcode.c255
1 files changed, 158 insertions, 97 deletions
diff --git a/src/lj_mcode.c b/src/lj_mcode.c
index 2b8ac2df..c3032f4e 100644
--- a/src/lj_mcode.c
+++ b/src/lj_mcode.c
@@ -63,31 +63,46 @@ void lj_mcode_sync(void *start, void *end)
63 63
64#if LJ_HASJIT 64#if LJ_HASJIT
65 65
66#if LUAJIT_SECURITY_MCODE != 0
67/* Protection twiddling failed. Probably due to kernel security. */
68static LJ_NORET LJ_NOINLINE void mcode_protfail(jit_State *J)
69{
70 lua_CFunction panic = J2G(J)->panic;
71 if (panic) {
72 lua_State *L = J->L;
73 setstrV(L, L->top++, lj_err_str(L, LJ_ERR_JITPROT));
74 panic(L);
75 }
76 exit(EXIT_FAILURE);
77}
78#endif
79
66#if LJ_TARGET_WINDOWS 80#if LJ_TARGET_WINDOWS
67 81
68#define MCPROT_RW PAGE_READWRITE 82#define MCPROT_RW PAGE_READWRITE
69#define MCPROT_RX PAGE_EXECUTE_READ 83#define MCPROT_RX PAGE_EXECUTE_READ
70#define MCPROT_RWX PAGE_EXECUTE_READWRITE 84#define MCPROT_RWX PAGE_EXECUTE_READWRITE
71 85
72static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, DWORD prot) 86static void *mcode_alloc_at(uintptr_t hint, size_t sz, DWORD prot)
73{ 87{
74 void *p = LJ_WIN_VALLOC((void *)hint, sz, 88 return LJ_WIN_VALLOC((void *)hint, sz,
75 MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot); 89 MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot);
76 if (!p && !hint)
77 lj_trace_err(J, LJ_TRERR_MCODEAL);
78 return p;
79} 90}
80 91
81static void mcode_free(jit_State *J, void *p, size_t sz) 92static void mcode_free(void *p, size_t sz)
82{ 93{
83 UNUSED(J); UNUSED(sz); 94 UNUSED(sz);
84 VirtualFree(p, 0, MEM_RELEASE); 95 VirtualFree(p, 0, MEM_RELEASE);
85} 96}
86 97
87static int mcode_setprot(void *p, size_t sz, DWORD prot) 98static void mcode_setprot(jit_State *J, void *p, size_t sz, DWORD prot)
88{ 99{
100#if LUAJIT_SECURITY_MCODE != 0
89 DWORD oprot; 101 DWORD oprot;
90 return !LJ_WIN_VPROTECT(p, sz, prot, &oprot); 102 if (!LJ_WIN_VPROTECT(p, sz, prot, &oprot)) mcode_protfail(J);
103#else
104 UNUSED(J); UNUSED(p); UNUSED(sz); UNUSED(prot);
105#endif
91} 106}
92 107
93#elif LJ_TARGET_POSIX 108#elif LJ_TARGET_POSIX
@@ -117,33 +132,33 @@ static int mcode_setprot(void *p, size_t sz, DWORD prot)
117#define MCPROT_CREATE 0 132#define MCPROT_CREATE 0
118#endif 133#endif
119 134
120static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, int prot) 135static void *mcode_alloc_at(uintptr_t hint, size_t sz, int prot)
121{ 136{
122 void *p = mmap((void *)hint, sz, prot|MCPROT_CREATE, MAP_PRIVATE|MAP_ANONYMOUS|MCMAP_CREATE, -1, 0); 137 void *p = mmap((void *)hint, sz, prot|MCPROT_CREATE, MAP_PRIVATE|MAP_ANONYMOUS|MCMAP_CREATE, -1, 0);
123 if (p == MAP_FAILED) { 138 if (p == MAP_FAILED) return NULL;
124 if (!hint) lj_trace_err(J, LJ_TRERR_MCODEAL);
125 p = NULL;
126#if MCMAP_CREATE 139#if MCMAP_CREATE
127 } else { 140 pthread_jit_write_protect_np(0);
128 pthread_jit_write_protect_np(0);
129#endif 141#endif
130 }
131 return p; 142 return p;
132} 143}
133 144
134static void mcode_free(jit_State *J, void *p, size_t sz) 145static void mcode_free(void *p, size_t sz)
135{ 146{
136 UNUSED(J);
137 munmap(p, sz); 147 munmap(p, sz);
138} 148}
139 149
140static int mcode_setprot(void *p, size_t sz, int prot) 150static void mcode_setprot(jit_State *J, void *p, size_t sz, int prot)
141{ 151{
152#if LUAJIT_SECURITY_MCODE != 0
142#if MCMAP_CREATE 153#if MCMAP_CREATE
154 UNUSED(J); UNUSED(p); UNUSED(sz);
143 pthread_jit_write_protect_np((prot & PROT_EXEC)); 155 pthread_jit_write_protect_np((prot & PROT_EXEC));
144 return 0; 156 return 0;
145#else 157#else
146 return mprotect(p, sz, prot); 158 if (mprotect(p, sz, prot)) mcode_protfail(J);
159#endif
160#else
161 UNUSED(J); UNUSED(p); UNUSED(sz); UNUSED(prot);
147#endif 162#endif
148} 163}
149 164
@@ -153,6 +168,49 @@ static int mcode_setprot(void *p, size_t sz, int prot)
153 168
154#endif 169#endif
155 170
171#ifdef LUAJIT_MCODE_TEST
172/* Test wrapper for mcode allocation. DO NOT ENABLE in production! Try:
173** LUAJIT_MCODE_TEST=hhhhhhhhhhhhhhhh luajit -jv main.lua
174** LUAJIT_MCODE_TEST=F luajit -jv main.lua
175*/
176static void *mcode_alloc_at_TEST(jit_State *J, uintptr_t hint, size_t sz, int prot)
177{
178 static int test_ofs = 0;
179 static const char *test_str;
180 if (!test_str) {
181 test_str = getenv("LUAJIT_MCODE_TEST");
182 if (!test_str) test_str = "";
183 }
184 switch (test_str[test_ofs]) {
185 case 'a': /* OK for one allocation. */
186 test_ofs++;
187 /* fallthrough */
188 case '\0': /* EOS: OK for any further allocations. */
189 break;
190 case 'h': /* Ignore one hint. */
191 test_ofs++;
192 /* fallthrough */
193 case 'H': /* Ignore any further hints. */
194 hint = 0u;
195 break;
196 case 'r': /* Randomize one hint. */
197 test_ofs++;
198 /* fallthrough */
199 case 'R': /* Randomize any further hints. */
200 hint = lj_prng_u64(&J2G(J)->prng) & ~(uintptr_t)0xffffu;
201 hint &= ((uintptr_t)1 << (LJ_64 ? 47 : 31)) - 1;
202 break;
203 case 'f': /* Fail one allocation. */
204 test_ofs++;
205 /* fallthrough */
206 default: /* 'F' or unknown: Fail any further allocations. */
207 return NULL;
208 }
209 return mcode_alloc_at(hint, sz, prot);
210}
211#define mcode_alloc_at(hint, sz, prot) mcode_alloc_at_TEST(J, hint, sz, prot)
212#endif
213
156/* -- MCode area protection ----------------------------------------------- */ 214/* -- MCode area protection ----------------------------------------------- */
157 215
158#if LUAJIT_SECURITY_MCODE == 0 216#if LUAJIT_SECURITY_MCODE == 0
@@ -174,7 +232,7 @@ static int mcode_setprot(void *p, size_t sz, int prot)
174 232
175static void mcode_protect(jit_State *J, int prot) 233static void mcode_protect(jit_State *J, int prot)
176{ 234{
177 UNUSED(J); UNUSED(prot); UNUSED(mcode_setprot); 235 UNUSED(J); UNUSED(prot);
178} 236}
179 237
180#else 238#else
@@ -190,24 +248,11 @@ static void mcode_protect(jit_State *J, int prot)
190#define MCPROT_GEN MCPROT_RW 248#define MCPROT_GEN MCPROT_RW
191#define MCPROT_RUN MCPROT_RX 249#define MCPROT_RUN MCPROT_RX
192 250
193/* Protection twiddling failed. Probably due to kernel security. */
194static LJ_NORET LJ_NOINLINE void mcode_protfail(jit_State *J)
195{
196 lua_CFunction panic = J2G(J)->panic;
197 if (panic) {
198 lua_State *L = J->L;
199 setstrV(L, L->top++, lj_err_str(L, LJ_ERR_JITPROT));
200 panic(L);
201 }
202 exit(EXIT_FAILURE);
203}
204
205/* Change protection of MCode area. */ 251/* Change protection of MCode area. */
206static void mcode_protect(jit_State *J, int prot) 252static void mcode_protect(jit_State *J, int prot)
207{ 253{
208 if (J->mcprot != prot) { 254 if (J->mcprot != prot) {
209 if (LJ_UNLIKELY(mcode_setprot(J->mcarea, J->szmcarea, prot))) 255 mcode_setprot(J, J->mcarea, J->szmcarea, prot);
210 mcode_protfail(J);
211 J->mcprot = prot; 256 J->mcprot = prot;
212 } 257 }
213} 258}
@@ -216,47 +261,74 @@ static void mcode_protect(jit_State *J, int prot)
216 261
217/* -- MCode area allocation ----------------------------------------------- */ 262/* -- MCode area allocation ----------------------------------------------- */
218 263
219#if LJ_64
220#define mcode_validptr(p) (p)
221#else
222#define mcode_validptr(p) ((p) && (uintptr_t)(p) < 0xffff0000)
223#endif
224
225#ifdef LJ_TARGET_JUMPRANGE 264#ifdef LJ_TARGET_JUMPRANGE
226 265
227/* Get memory within relative jump distance of our code in 64 bit mode. */ 266#define MCODE_RANGE64 ((1u << LJ_TARGET_JUMPRANGE) - 0x10000u)
228static void *mcode_alloc(jit_State *J, size_t sz) 267
268/* Set a memory range for mcode allocation with addr in the middle. */
269static void mcode_setrange(jit_State *J, uintptr_t addr)
229{ 270{
230 /* Target an address in the static assembler code (64K aligned).
231 ** Try addresses within a distance of target-range/2+1MB..target+range/2-1MB.
232 ** Use half the jump range so every address in the range can reach any other.
233 */
234#if LJ_TARGET_MIPS 271#if LJ_TARGET_MIPS
235 /* Use the middle of the 256MB-aligned region. */ 272 /* Use the whole 256MB-aligned region. */
236 uintptr_t target = ((uintptr_t)(void *)lj_vm_exit_handler & 273 J->mcmin = addr & ~(uintptr_t)((1u << LJ_TARGET_JUMPRANGE) - 1);
237 ~(uintptr_t)0x0fffffffu) + 0x08000000u; 274 J->mcmax = J->mcmin + (1u << LJ_TARGET_JUMPRANGE);
238#else 275#else
239 uintptr_t target = (uintptr_t)(void *)lj_vm_exit_handler & ~(uintptr_t)0xffff; 276 /* Every address in the 64KB-aligned range should be able to reach
277 ** any other, so MCODE_RANGE64 is only half the (signed) branch range.
278 */
279 J->mcmin = (addr - (MCODE_RANGE64 >> 1) + 0xffffu) & ~(uintptr_t)0xffffu;
280 J->mcmax = J->mcmin + MCODE_RANGE64;
240#endif 281#endif
241 const uintptr_t range = (1u << (LJ_TARGET_JUMPRANGE-1)) - (1u << 21); 282 /* Avoid wrap-around and the 64KB corners. */
242 /* First try a contiguous area below the last one. */ 283 if (addr < J->mcmin || !J->mcmin) J->mcmin = 0x10000u;
243 uintptr_t hint = J->mcarea ? (uintptr_t)J->mcarea - sz : 0; 284 if (addr > J->mcmax) J->mcmax = ~(uintptr_t)0xffffu;
244 int i; 285}
245 /* Limit probing iterations, depending on the available pool size. */ 286
246 for (i = 0; i < LJ_TARGET_JUMPRANGE; i++) { 287/* Check if an address is in range of the mcode allocation range. */
247 if (mcode_validptr(hint)) { 288static LJ_AINLINE int mcode_inrange(jit_State *J, uintptr_t addr, size_t sz)
248 void *p = mcode_alloc_at(J, hint, sz, MCPROT_GEN); 289{
249 290 /* Take care of unsigned wrap-around of addr + sz, too. */
250 if (mcode_validptr(p) && 291 return addr >= J->mcmin && addr + sz >= J->mcmin && addr + sz <= J->mcmax;
251 ((uintptr_t)p + sz - target < range || target - (uintptr_t)p < range)) 292}
252 return p; 293
253 if (p) mcode_free(J, p, sz); /* Free badly placed area. */ 294/* Get memory within a specific jump range in 64 bit mode. */
254 } 295static void *mcode_alloc(jit_State *J, size_t sz)
255 /* Next try probing 64K-aligned pseudo-random addresses. */ 296{
297 uintptr_t hint;
298 int i = 0, j;
299 if (!J->mcmin) /* Place initial range near the interpreter code. */
300 mcode_setrange(J, (uintptr_t)(void *)lj_vm_exit_handler);
301 else if (!J->mcmax) /* Switch to a new range (already flushed). */
302 goto newrange;
303 /* First try a contiguous area below the last one (if in range). */
304 hint = (uintptr_t)J->mcarea - sz;
305 if (!mcode_inrange(J, hint, sz)) /* Also takes care of NULL J->mcarea. */
306 goto probe;
307 for (; i < 16; i++) {
308 void *p = mcode_alloc_at(hint, sz, MCPROT_GEN);
309 if (mcode_inrange(J, (uintptr_t)p, sz))
310 return p; /* Success. */
311 else if (p)
312 mcode_free(p, sz); /* Free badly placed area. */
313 probe:
314 /* Next try probing 64KB-aligned pseudo-random addresses. */
315 j = 0;
256 do { 316 do {
257 hint = lj_prng_u64(&J2G(J)->prng) & ((1u<<LJ_TARGET_JUMPRANGE)-0x10000); 317 hint = J->mcmin + (lj_prng_u64(&J2G(J)->prng) & MCODE_RANGE64);
258 } while (!(hint + sz < range+range)); 318 if (++j > 15) goto fail;
259 hint = target + hint - range; 319 } while (!mcode_inrange(J, hint, sz));
320 }
321fail:
322 if (!J->mcarea) { /* Switch to a new range now. */
323 void *p;
324 newrange:
325 p = mcode_alloc_at(0, sz, MCPROT_GEN);
326 if (p) {
327 mcode_setrange(J, (uintptr_t)p + (sz >> 1));
328 return p; /* Success. */
329 }
330 } else {
331 J->mcmax = 0; /* Switch to a new range after the flush. */
260 } 332 }
261 lj_trace_err(J, LJ_TRERR_MCODEAL); /* Give up. OS probably ignores hints? */ 333 lj_trace_err(J, LJ_TRERR_MCODEAL); /* Give up. OS probably ignores hints? */
262 return NULL; 334 return NULL;
@@ -269,15 +341,13 @@ static void *mcode_alloc(jit_State *J, size_t sz)
269{ 341{
270#if defined(__OpenBSD__) || defined(__NetBSD__) || LJ_TARGET_UWP 342#if defined(__OpenBSD__) || defined(__NetBSD__) || LJ_TARGET_UWP
271 /* Allow better executable memory allocation for OpenBSD W^X mode. */ 343 /* Allow better executable memory allocation for OpenBSD W^X mode. */
272 void *p = mcode_alloc_at(J, 0, sz, MCPROT_RUN); 344 void *p = mcode_alloc_at(0, sz, MCPROT_RUN);
273 if (p && mcode_setprot(p, sz, MCPROT_GEN)) { 345 if (p) mcode_setprot(J, p, sz, MCPROT_GEN);
274 mcode_free(J, p, sz);
275 return NULL;
276 }
277 return p;
278#else 346#else
279 return mcode_alloc_at(J, 0, sz, MCPROT_GEN); 347 void *p = mcode_alloc_at(0, sz, MCPROT_GEN);
280#endif 348#endif
349 if (!p) lj_trace_err(J, LJ_TRERR_MCODEAL);
350 return p;
281} 351}
282 352
283#endif 353#endif
@@ -289,7 +359,6 @@ static void mcode_allocarea(jit_State *J)
289{ 359{
290 MCode *oldarea = J->mcarea; 360 MCode *oldarea = J->mcarea;
291 size_t sz = (size_t)J->param[JIT_P_sizemcode] << 10; 361 size_t sz = (size_t)J->param[JIT_P_sizemcode] << 10;
292 sz = (sz + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1);
293 J->mcarea = (MCode *)mcode_alloc(J, sz); 362 J->mcarea = (MCode *)mcode_alloc(J, sz);
294 J->szmcarea = sz; 363 J->szmcarea = sz;
295 J->mcprot = MCPROT_GEN; 364 J->mcprot = MCPROT_GEN;
@@ -311,7 +380,7 @@ void lj_mcode_free(jit_State *J)
311 MCode *next = ((MCLink *)mc)->next; 380 MCode *next = ((MCLink *)mc)->next;
312 size_t sz = ((MCLink *)mc)->size; 381 size_t sz = ((MCLink *)mc)->size;
313 lj_err_deregister_mcode(mc, sz, (uint8_t *)mc + sizeof(MCLink)); 382 lj_err_deregister_mcode(mc, sz, (uint8_t *)mc + sizeof(MCLink));
314 mcode_free(J, mc, sz); 383 mcode_free(mc, sz);
315 mc = next; 384 mc = next;
316 } 385 }
317} 386}
@@ -347,32 +416,25 @@ void lj_mcode_abort(jit_State *J)
347MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish) 416MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish)
348{ 417{
349 if (finish) { 418 if (finish) {
350#if LUAJIT_SECURITY_MCODE
351 if (J->mcarea == ptr) 419 if (J->mcarea == ptr)
352 mcode_protect(J, MCPROT_RUN); 420 mcode_protect(J, MCPROT_RUN);
353 else if (LJ_UNLIKELY(mcode_setprot(ptr, ((MCLink *)ptr)->size, MCPROT_RUN))) 421 else
354 mcode_protfail(J); 422 mcode_setprot(J, ptr, ((MCLink *)ptr)->size, MCPROT_RUN);
355#endif
356 return NULL; 423 return NULL;
357 } else { 424 } else {
358 MCode *mc = J->mcarea; 425 uintptr_t base = (uintptr_t)J->mcarea, addr = (uintptr_t)ptr;
359 /* Try current area first to use the protection cache. */ 426 /* Try current area first to use the protection cache. */
360 if (ptr >= mc && ptr < (MCode *)((char *)mc + J->szmcarea)) { 427 if (addr >= base && addr < base + J->szmcarea) {
361#if LUAJIT_SECURITY_MCODE
362 mcode_protect(J, MCPROT_GEN); 428 mcode_protect(J, MCPROT_GEN);
363#endif 429 return (MCode *)base;
364 return mc;
365 } 430 }
366 /* Otherwise search through the list of MCode areas. */ 431 /* Otherwise search through the list of MCode areas. */
367 for (;;) { 432 for (;;) {
368 mc = ((MCLink *)mc)->next; 433 base = (uintptr_t)(((MCLink *)base)->next);
369 lj_assertJ(mc != NULL, "broken MCode area chain"); 434 lj_assertJ(base != 0, "broken MCode area chain");
370 if (ptr >= mc && ptr < (MCode *)((char *)mc + ((MCLink *)mc)->size)) { 435 if (addr >= base && addr < base + ((MCLink *)base)->size) {
371#if LUAJIT_SECURITY_MCODE 436 mcode_setprot(J, (MCode *)base, ((MCLink *)base)->size, MCPROT_GEN);
372 if (LJ_UNLIKELY(mcode_setprot(mc, ((MCLink *)mc)->size, MCPROT_GEN))) 437 return (MCode *)base;
373 mcode_protfail(J);
374#endif
375 return mc;
376 } 438 }
377 } 439 }
378 } 440 }
@@ -384,7 +446,6 @@ void lj_mcode_limiterr(jit_State *J, size_t need)
384 size_t sizemcode, maxmcode; 446 size_t sizemcode, maxmcode;
385 lj_mcode_abort(J); 447 lj_mcode_abort(J);
386 sizemcode = (size_t)J->param[JIT_P_sizemcode] << 10; 448 sizemcode = (size_t)J->param[JIT_P_sizemcode] << 10;
387 sizemcode = (sizemcode + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1);
388 maxmcode = (size_t)J->param[JIT_P_maxmcode] << 10; 449 maxmcode = (size_t)J->param[JIT_P_maxmcode] << 10;
389 if (need * sizeof(MCode) > sizemcode) 450 if (need * sizeof(MCode) > sizemcode)
390 lj_trace_err(J, LJ_TRERR_MCODEOV); /* Too long for any area. */ 451 lj_trace_err(J, LJ_TRERR_MCODEOV); /* Too long for any area. */