blob: 8f5b32d7a988b3c4b563ef3207dd98dc4914c7e8 [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 {
43 static_assert(sizeof(T) < PAGE_SIZE,
44 "WriteProtected only supports contents up to PAGE_SIZE");
45 static_assert(__is_pod(T), "WriteProtected only supports POD contents");
46
47 WriteProtectedContents<T> contents;
48
Peter Collingbourne45f0a3b2019-07-16 11:18:07 -070049 int set_protection(int prot) {
Peter Collingbourne191ecdc2019-08-07 19:06:00 -070050 auto addr = &contents;
Peter Collingbourne45f0a3b2019-07-16 11:18:07 -070051#if __has_feature(hwaddress_sanitizer)
52 // The mprotect system call does not currently untag pointers, so do it
53 // ourselves.
Peter Collingbourne191ecdc2019-08-07 19:06:00 -070054 addr = untag_address(addr);
Peter Collingbourne45f0a3b2019-07-16 11:18:07 -070055#endif
56 return mprotect(reinterpret_cast<void*>(addr), PAGE_SIZE, prot);
57 }
58
Josh Gao93c0f5e2015-10-06 11:08:13 -070059 public:
60 WriteProtected() = default;
Elliott Hughes5e62b342018-10-25 11:00:00 -070061 BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtected);
Josh Gao93c0f5e2015-10-06 11:08:13 -070062
63 void initialize() {
64 // Not strictly necessary, but this will hopefully segfault if we initialize
65 // multiple times by accident.
66 memset(&contents, 0, sizeof(contents));
67
Peter Collingbourne45f0a3b2019-07-16 11:18:07 -070068 if (set_protection(PROT_READ)) {
Christopher Ferris7a3681e2017-04-24 17:48:32 -070069 async_safe_fatal("failed to make WriteProtected nonwritable in initialize");
Josh Gao93c0f5e2015-10-06 11:08:13 -070070 }
71 }
72
73 const T* operator->() {
74 return &contents.value;
75 }
76
77 const T& operator*() {
78 return contents.value;
79 }
80
81 template <typename Mutator>
82 void mutate(Mutator mutator) {
Peter Collingbourne45f0a3b2019-07-16 11:18:07 -070083 if (set_protection(PROT_READ | PROT_WRITE) != 0) {
Christopher Ferris7a3681e2017-04-24 17:48:32 -070084 async_safe_fatal("failed to make WriteProtected writable in mutate: %s",
85 strerror(errno));
Josh Gao93c0f5e2015-10-06 11:08:13 -070086 }
87 mutator(&contents.value);
Peter Collingbourne45f0a3b2019-07-16 11:18:07 -070088 if (set_protection(PROT_READ) != 0) {
Christopher Ferris7a3681e2017-04-24 17:48:32 -070089 async_safe_fatal("failed to make WriteProtected nonwritable in mutate: %s",
90 strerror(errno));
Josh Gao93c0f5e2015-10-06 11:08:13 -070091 }
92 }
93};