art
Revision | 39e967c05572b2072367ec40304763c0976755f6 (tree) |
---|---|
Zeit | 2017-08-11 19:39:45 |
Autor | Andreas Gampe <agampe@goog...> |
Commiter | Chih-Wei Huang |
ART: Change main-thread thread paging scheme
Modify the code that ensures we can install a stack guard page into
the main thread.
A recent kernel change means that our previous approach of using a
free pointer does not work. It is important to actually extend the
stack properly. For portability, use a function with a large stack
frame (suggested by and adapted from hboehm).
Bug: 62952017
Test: m
Test: m test-art-host
Test: Device boots (x86_64 emulator)
Test: Device boots (bullhead)
Change-Id: Ic2a0c3d6d05a1ea9f655329d147b46949e1b9db3
@@ -546,27 +546,40 @@ void Thread::InstallImplicitProtection() { | ||
546 | 546 | // |
547 | 547 | // We map in the stack by reading every page from the stack bottom (highest address) |
548 | 548 | // to the stack top. (We then madvise this away.) This must be done by reading from the |
549 | - // current stack pointer downwards. Any access more than a page below the current SP | |
550 | - // might cause a segv. | |
551 | - // TODO: This comment may be out of date. It seems possible to speed this up. As | |
552 | - // this is normally done once in the zygote on startup, ignore for now. | |
549 | + // current stack pointer downwards. | |
553 | 550 | // |
554 | - // AddressSanitizer does not like the part of this functions that reads every stack page. | |
555 | - // Looks a lot like an out-of-bounds access. | |
551 | + // Accesses too far below the current machine register corresponding to the stack pointer (e.g., | |
552 | + // ESP on x86[-32], SP on ARM) might cause a SIGSEGV (at least on x86 with newer kernels). We | |
553 | + // thus have to move the stack pointer. We do this portably by using a recursive function with a | |
554 | + // large stack frame size. | |
556 | 555 | |
557 | - // (Defensively) first remove the protection on the protected region as will want to read | |
556 | + // (Defensively) first remove the protection on the protected region as we'll want to read | |
558 | 557 | // and write it. Ignore errors. |
559 | 558 | UnprotectStack(); |
560 | 559 | |
561 | 560 | VLOG(threads) << "Need to map in stack for thread at " << std::hex << |
562 | 561 | static_cast<void*>(pregion); |
563 | 562 | |
564 | - // Read every page from the high address to the low. | |
565 | - volatile uint8_t dont_optimize_this; | |
566 | - UNUSED(dont_optimize_this); | |
567 | - for (uint8_t* p = stack_top; p >= pregion; p -= kPageSize) { | |
568 | - dont_optimize_this = *p; | |
569 | - } | |
563 | + struct RecurseDownStack { | |
564 | + // This function has an intentionally large stack size. | |
565 | +#pragma GCC diagnostic push | |
566 | +#pragma GCC diagnostic ignored "-Wframe-larger-than=" | |
567 | + NO_INLINE | |
568 | + static void Touch(uintptr_t target) { | |
569 | + volatile size_t zero = 0; | |
570 | + // Use a large local volatile array to ensure a large frame size. Do not use anything close | |
571 | + // to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but | |
572 | + // there is no pragma support for this. | |
573 | + volatile char space[kPageSize - 256]; | |
574 | + char sink ATTRIBUTE_UNUSED = space[zero]; | |
575 | + if (reinterpret_cast<uintptr_t>(space) >= target + kPageSize) { | |
576 | + Touch(target); | |
577 | + } | |
578 | + zero *= 2; // Try to avoid tail recursion. | |
579 | + } | |
580 | +#pragma GCC diagnostic pop | |
581 | + }; | |
582 | + RecurseDownStack::Touch(reinterpret_cast<uintptr_t>(pregion)); | |
570 | 583 | |
571 | 584 | VLOG(threads) << "(again) installing stack protected region at " << std::hex << |
572 | 585 | static_cast<void*>(pregion) << " to " << |