blob: 746f72a4dbec275fb79cdecbb9925960d796f032 [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"
Josh Gao93c0f5e2015-10-06 11:08:13 -070028
29template <typename T>
30union WriteProtectedContents {
31 T value;
32 char padding[PAGE_SIZE];
33
34 WriteProtectedContents() = default;
Elliott Hughes5e62b342018-10-25 11:00:00 -070035 BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtectedContents);
Josh Gao93c0f5e2015-10-06 11:08:13 -070036} __attribute__((aligned(PAGE_SIZE)));
37
38// Write protected wrapper class that aligns its contents to a page boundary,
39// and sets the memory protection to be non-writable, except when being modified
40// explicitly.
41template <typename T>
42class WriteProtected {
Elliott Hughes1e381a22020-06-08 08:41:27 -070043 public:
Josh Gao93c0f5e2015-10-06 11:08:13 -070044 static_assert(sizeof(T) < PAGE_SIZE,
45 "WriteProtected only supports contents up to PAGE_SIZE");
46 static_assert(__is_pod(T), "WriteProtected only supports POD contents");
47
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.
54 memset(&contents, 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->() {
59 return &contents.value;
60 }
61
62 const T& operator*() {
63 return contents.value;
64 }
65
66 template <typename Mutator>
67 void mutate(Mutator mutator) {
Elliott Hughes1e381a22020-06-08 08:41:27 -070068 set_protection(PROT_READ | PROT_WRITE);
Josh Gao93c0f5e2015-10-06 11:08:13 -070069 mutator(&contents.value);
Elliott Hughes1e381a22020-06-08 08:41:27 -070070 set_protection(PROT_READ);
71 }
72
73 private:
74 WriteProtectedContents<T> contents;
75
76 void set_protection(int prot) {
77 auto addr = &contents;
78#if __has_feature(hwaddress_sanitizer)
79 // The mprotect system call does not currently untag pointers, so do it
80 // ourselves.
81 addr = untag_address(addr);
82#endif
83 if (mprotect(reinterpret_cast<void*>(addr), PAGE_SIZE, prot) == -1) {
84 async_safe_fatal("WriteProtected mprotect %x failed: %s", prot, strerror(errno));
Josh Gao93c0f5e2015-10-06 11:08:13 -070085 }
86 }
87};