/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "platform/bionic/macros.h" #include "platform/bionic/page.h" template union WriteProtectedContents { T value; char padding[max_android_page_size()]; WriteProtectedContents() = default; BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtectedContents); } __attribute__((aligned(max_android_page_size()))); // Write protected wrapper class that aligns its contents to a page boundary, // and sets the memory protection to be non-writable, except when being modified // explicitly. template class WriteProtected { public: static_assert(sizeof(T) < max_android_page_size(), "WriteProtected only supports contents up to max_android_page_size()"); WriteProtected() = default; BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtected); void initialize() { // Not strictly necessary, but this will hopefully segfault if we initialize // multiple times by accident. memset(contents_addr(), 0, sizeof(contents)); set_protection(PROT_READ); } const T* operator->() { return &contents_addr()->value; } const T& operator*() { return contents_addr()->value; } template void mutate(Mutator mutator) { set_protection(PROT_READ | PROT_WRITE); mutator(&contents_addr()->value); set_protection(PROT_READ); } private: WriteProtectedContents contents; WriteProtectedContents* contents_addr() { auto addr = &contents; // Hide the fact that we're returning the address of contents from the compiler. // Otherwise it may generate code assuming alignment of 64KB even though the // variable is only guaranteed to have 4KB alignment. __asm__ __volatile__("" : "+r"(addr)); return addr; } void set_protection(int prot) { auto addr = contents_addr(); #if __has_feature(hwaddress_sanitizer) // The mprotect system call does not currently untag pointers, so do it // ourselves. addr = untag_address(addr); #endif if (mprotect(reinterpret_cast(addr), max_android_page_size(), prot) == -1) { async_safe_fatal("WriteProtected mprotect %x failed: %s", prot, strerror(errno)); } } };