| /* | |
| * Copyright (C) 2024 The Android Open Source Project | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * * Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * * Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in | |
| * the documentation and/or other materials provided with the | |
| * distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| * SUCH DAMAGE. | |
| */ | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <condition_variable> | |
| #include <thread> | |
| #include <dlfcn.h> | |
| #include <inttypes.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include "../mte_utils.h" | |
| #include "CHECK.h" | |
| #if defined(__BIONIC__) && defined(__aarch64__) | |
| enum State { kInit, kThreadStarted, kStackRemapped }; | |
| // We can't use pthread_getattr_np because that uses the rlimit rather than the actual mapping | |
| // bounds. | |
| static void find_main_stack_limits(uintptr_t* low, uintptr_t* high) { | |
| uintptr_t startstack = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)); | |
| // Hunt for the region that contains that address. | |
| FILE* fp = fopen("/proc/self/maps", "re"); | |
| if (fp == nullptr) { | |
| abort(); | |
| } | |
| char line[BUFSIZ]; | |
| while (fgets(line, sizeof(line), fp) != nullptr) { | |
| uintptr_t lo, hi; | |
| if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) { | |
| if (lo <= startstack && startstack <= hi) { | |
| *low = lo; | |
| *high = hi; | |
| fclose(fp); | |
| return; | |
| } | |
| } | |
| } | |
| abort(); | |
| } | |
| template <typename Fn> | |
| unsigned int fault_new_stack_page(uintptr_t low, Fn f) { | |
| uintptr_t new_low; | |
| uintptr_t new_high; | |
| volatile char buf[4096]; | |
| buf[4095] = 1; | |
| find_main_stack_limits(&new_low, &new_high); | |
| if (new_low < low) { | |
| f(); | |
| return new_high; | |
| } | |
| // Useless, but should defeat TCO. | |
| return new_low + fault_new_stack_page(low, f); | |
| } | |
| int main(int argc, char** argv) { | |
| if (argc < 2) { | |
| return 1; | |
| } | |
| const char* path = argv[1]; | |
| CHECK(access(path, F_OK) == 0); // Verify test setup. | |
| CHECK(!is_stack_mte_on()); | |
| std::mutex m; | |
| std::condition_variable cv; | |
| State state = kInit; | |
| bool is_early_thread_mte_on = false; | |
| void* early_thread_mte_tls = nullptr; | |
| std::thread early_th([&] { | |
| { | |
| std::lock_guard lk(m); | |
| state = kThreadStarted; | |
| } | |
| cv.notify_one(); | |
| { | |
| std::unique_lock lk(m); | |
| cv.wait(lk, [&] { return state == kStackRemapped; }); | |
| } | |
| is_early_thread_mte_on = is_stack_mte_on(); | |
| early_thread_mte_tls = mte_tls(); | |
| *reinterpret_cast<uintptr_t*>(early_thread_mte_tls) = 1; | |
| }); | |
| { | |
| std::unique_lock lk(m); | |
| cv.wait(lk, [&] { return state == kThreadStarted; }); | |
| } | |
| void* handle = dlopen(path, RTLD_NOW); | |
| { | |
| std::lock_guard lk(m); | |
| state = kStackRemapped; | |
| } | |
| cv.notify_one(); | |
| CHECK(handle != nullptr); | |
| CHECK(is_stack_mte_on()); | |
| CHECK(mte_tls() != nullptr); | |
| bool new_stack_page_mte_on = false; | |
| uintptr_t low; | |
| uintptr_t high; | |
| find_main_stack_limits(&low, &high); | |
| fault_new_stack_page(low, [&] { new_stack_page_mte_on = is_stack_mte_on(); }); | |
| CHECK(new_stack_page_mte_on); | |
| bool is_late_thread_mte_on = false; | |
| void* late_thread_mte_tls = nullptr; | |
| std::thread late_th([&] { | |
| is_late_thread_mte_on = is_stack_mte_on(); | |
| late_thread_mte_tls = mte_tls(); | |
| *reinterpret_cast<uintptr_t*>(late_thread_mte_tls) = 1; | |
| }); | |
| late_th.join(); | |
| early_th.join(); | |
| CHECK(is_late_thread_mte_on); | |
| CHECK(is_early_thread_mte_on); | |
| CHECK(late_thread_mte_tls != nullptr); | |
| CHECK(early_thread_mte_tls != nullptr); | |
| printf("RAN\n"); | |
| return 0; | |
| } | |
| #else | |
| int main(int, char**) { | |
| return 1; | |
| } | |
| #endif |