blob: 2faaf7737847c5ab4876d4889c45c0575598ccf0 [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;
Kalesh Singh2bdf22e2023-08-11 15:15:55 -070033 char padding[max_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);
Kalesh Singh2bdf22e2023-08-11 15:15:55 -070037} __attribute__((aligned(max_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:
Kalesh Singh2bdf22e2023-08-11 15:15:55 -070045 static_assert(sizeof(T) < max_page_size(),
46 "WriteProtected only supports contents up to max_page_size()");
Josh Gao93c0f5e2015-10-06 11:08:13 -070047 static_assert(__is_pod(T), "WriteProtected only supports POD contents");
48
Josh Gao93c0f5e2015-10-06 11:08:13 -070049 WriteProtected() = default;
Elliott Hughes5e62b342018-10-25 11:00:00 -070050 BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtected);
Josh Gao93c0f5e2015-10-06 11:08:13 -070051
52 void initialize() {
53 // Not strictly necessary, but this will hopefully segfault if we initialize
54 // multiple times by accident.
55 memset(&contents, 0, sizeof(contents));
Elliott Hughes1e381a22020-06-08 08:41:27 -070056 set_protection(PROT_READ);
Josh Gao93c0f5e2015-10-06 11:08:13 -070057 }
58
59 const T* operator->() {
60 return &contents.value;
61 }
62
63 const T& operator*() {
64 return contents.value;
65 }
66
67 template <typename Mutator>
68 void mutate(Mutator mutator) {
Elliott Hughes1e381a22020-06-08 08:41:27 -070069 set_protection(PROT_READ | PROT_WRITE);
Josh Gao93c0f5e2015-10-06 11:08:13 -070070 mutator(&contents.value);
Elliott Hughes1e381a22020-06-08 08:41:27 -070071 set_protection(PROT_READ);
72 }
73
74 private:
75 WriteProtectedContents<T> contents;
76
77 void set_protection(int prot) {
78 auto addr = &contents;
79#if __has_feature(hwaddress_sanitizer)
80 // The mprotect system call does not currently untag pointers, so do it
81 // ourselves.
82 addr = untag_address(addr);
83#endif
Kalesh Singh2bdf22e2023-08-11 15:15:55 -070084 if (mprotect(reinterpret_cast<void*>(addr), max_page_size(), prot) == -1) {
Elliott Hughes1e381a22020-06-08 08:41:27 -070085 async_safe_fatal("WriteProtected mprotect %x failed: %s", prot, strerror(errno));
Josh Gao93c0f5e2015-10-06 11:08:13 -070086 }
87 }
88};