blob: f269125467d43d5737985c3066c5931cf221d13a [file] [log] [blame]
Josh Gao93c0f5e2015-10-06 11:08:13 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Elliott Hughes5e62b342018-10-25 11:00:00 -070017#pragma once
Josh Gao93c0f5e2015-10-06 11:08:13 -070018
19#include <errno.h>
20#include <string.h>
21#include <sys/cdefs.h>
22#include <sys/mman.h>
23#include <sys/user.h>
24
Christopher Ferris7a3681e2017-04-24 17:48:32 -070025#include <async_safe/log.h>
26
Josh Gao4956c372019-12-19 16:35:51 -080027#include "platform/bionic/macros.h"
Kalesh Singh2bdf22e2023-08-11 15:15:55 -070028#include "platform/bionic/page.h"
Josh Gao93c0f5e2015-10-06 11:08:13 -070029
30template <typename T>
31union WriteProtectedContents {
32 T value;
Steven Moreland84012302024-04-27 01:17:21 +000033 char padding[max_android_page_size()];
Josh Gao93c0f5e2015-10-06 11:08:13 -070034
35 WriteProtectedContents() = default;
Elliott Hughes5e62b342018-10-25 11:00:00 -070036 BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtectedContents);
Steven Moreland84012302024-04-27 01:17:21 +000037} __attribute__((aligned(max_android_page_size())));
Josh Gao93c0f5e2015-10-06 11:08:13 -070038
39// Write protected wrapper class that aligns its contents to a page boundary,
40// and sets the memory protection to be non-writable, except when being modified
41// explicitly.
42template <typename T>
43class WriteProtected {
Elliott Hughes1e381a22020-06-08 08:41:27 -070044 public:
Steven Moreland84012302024-04-27 01:17:21 +000045 static_assert(sizeof(T) < max_android_page_size(),
46 "WriteProtected only supports contents up to max_android_page_size()");
Josh Gao93c0f5e2015-10-06 11:08:13 -070047
Josh Gao93c0f5e2015-10-06 11:08:13 -070048 WriteProtected() = default;
Elliott Hughes5e62b342018-10-25 11:00:00 -070049 BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtected);
Josh Gao93c0f5e2015-10-06 11:08:13 -070050
51 void initialize() {
52 // Not strictly necessary, but this will hopefully segfault if we initialize
53 // multiple times by accident.
Peter Collingbourne8bd83d82024-04-03 19:28:06 -070054 memset(contents_addr(), 0, sizeof(contents));
Elliott Hughes1e381a22020-06-08 08:41:27 -070055 set_protection(PROT_READ);
Josh Gao93c0f5e2015-10-06 11:08:13 -070056 }
57
58 const T* operator->() {
Peter Collingbourne8bd83d82024-04-03 19:28:06 -070059 return &contents_addr()->value;
Josh Gao93c0f5e2015-10-06 11:08:13 -070060 }
61
62 const T& operator*() {
Peter Collingbourne8bd83d82024-04-03 19:28:06 -070063 return contents_addr()->value;
Josh Gao93c0f5e2015-10-06 11:08:13 -070064 }
65
66 template <typename Mutator>
67 void mutate(Mutator mutator) {
Elliott Hughes1e381a22020-06-08 08:41:27 -070068 set_protection(PROT_READ | PROT_WRITE);
Peter Collingbourne8bd83d82024-04-03 19:28:06 -070069 mutator(&contents_addr()->value);
Elliott Hughes1e381a22020-06-08 08:41:27 -070070 set_protection(PROT_READ);
71 }
72
73 private:
74 WriteProtectedContents<T> contents;
75
Peter Collingbourne8bd83d82024-04-03 19:28:06 -070076 WriteProtectedContents<T>* contents_addr() {
Elliott Hughes1e381a22020-06-08 08:41:27 -070077 auto addr = &contents;
Peter Collingbourne8bd83d82024-04-03 19:28:06 -070078 // Hide the fact that we're returning the address of contents from the compiler.
79 // Otherwise it may generate code assuming alignment of 64KB even though the
80 // variable is only guaranteed to have 4KB alignment.
81 __asm__ __volatile__("" : "+r"(addr));
82 return addr;
83 }
84
85 void set_protection(int prot) {
86 auto addr = contents_addr();
Elliott Hughes1e381a22020-06-08 08:41:27 -070087#if __has_feature(hwaddress_sanitizer)
88 // The mprotect system call does not currently untag pointers, so do it
89 // ourselves.
90 addr = untag_address(addr);
91#endif
Steven Moreland84012302024-04-27 01:17:21 +000092 if (mprotect(reinterpret_cast<void*>(addr), max_android_page_size(), prot) == -1) {
Elliott Hughes1e381a22020-06-08 08:41:27 -070093 async_safe_fatal("WriteProtected mprotect %x failed: %s", prot, strerror(errno));
Josh Gao93c0f5e2015-10-06 11:08:13 -070094 }
95 }
96};