1 /* 2 * Copyright (C) 2020 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 #pragma once 17 18 // 19 // This file defines low-level primitives for handling SIGBUS signals that get 20 // generated when accessing memory mapped files on IncFS and hitting a missing 21 // page. These primitives provide a way for a process to not crash, but require 22 // careful attention to use properly. 23 // 24 // A slightly safer alternative is in "access.h", you probably want it unless 25 // the performance is so critical that a single function call overhead is 26 // unacceptable. 27 // 28 // Macros: 29 // SCOPED_SIGBUS_HANDLER(code) 30 // SCOPED_SIGBUS_HANDLER_CONDITIONAL(condition, code) 31 // 32 // Both macros set up a thread-local handler for SIGBUS, and make the (code) 33 // passed as a parameter run when it happens; _CONDITIONAL version only sets the 34 // handler up if the (condition) is true, making it easier to write generic 35 // code. (code) runs in a context of a current function. Macros work via a 36 // setjmp/longjmp machinery, and those result in the following pitfalls: 37 // 38 // - Last action of the (code) MUST BE a return from the function. Current 39 // state is too badly corrupted to continue execution. 40 // -- Macro checks for code that failed to return and kills the process. 41 // - C++ destructors of the objects created since the macro DO NOT RUN. (code) 42 // has to manually clean up all of those. 43 // -- this means ALL allocated memory, locked mutexes, created temp files, 44 // etc. 45 // -- what this really means is it's better to not do anything that needs 46 // cleanup in the code protected with the macro 47 // - Signal handler jumps out of any level of a nested function call straight 48 // into (code); stack unwinding doesn't happen in a regular way - only the SP 49 // gets reset. Nested functions have even stronger restrictions: no destructor 50 // will run in them. 51 // -- under no circumstance one may call a user-supplied callback in a code 52 // protected with these macros. It's a recipe for a huge disaster. 53 // - If some other code overrides the signal handler, protections cease to 54 // work. Unfortunately, this is just the way signal handling works in Linux. 55 // 56 // Usage: 57 // 58 // In a function that will access mapped memory, as close to the access point as 59 // possible: 60 // 61 // int foo(char* mapped, long size) { 62 // ... 63 // SCOPED_SIGBUS_HANDLER(return -1); 64 // for (...) { 65 // <access |mapped|> 66 // } 67 // return 0; 68 // } 69 // 70 // If you need to perform some non-trivial cleanup: 71 // 72 // ... 73 // int fd = -1; 74 // SCOPED_SIGBUS_HANDLER({ 75 // if (fd >= 0) close(fd); 76 // return -1; 77 // }); 78 // ... 79 // <access |mapped|> 80 // fd = open(...); 81 // <access |mapped|> 82 // ... 83 // 84 // Cleanup: 85 // Pay attention when releasing the allocated resources - some functions may appear 86 // to do that while in reality they aren't; use the function(s) from incfs_support/util.h to 87 // do it safely, e.g.: 88 // 89 // std::vector<int> numbers; 90 // SCOPED_SIGBUS_HANDLER({ 91 // incfs::util::clearAndFree(numbers); 92 // // putting 'numbers.clear()' here instead would leak it as vector doesn't free its 93 // // capacity on that call, and SIGBUS may prevent its destructor from running. 94 // return -1; 95 // }); 96 97 // Performance: each macro sets up a couple of thread-local variables and calls 98 // a single syscall, 99 // setjmp(). The only serious consideration is to not call it on each 100 // iteration of a tight loop, as syscalls aren't particularly instant. See 101 // "incfs-support-benchmark" project for more details. 102 // 103 104 #include <sys/types.h> 105 106 #if !defined(__BIONIC__) || INCFS_SUPPORT_DISABLED 107 108 // IncFS signal handling isn't needed anywhere but on Android as of now; 109 // or if we're told it's not desired. 110 #define SCOPED_SIGBUS_HANDLER(code) 111 #define SCOPED_SIGBUS_HANDLER_CONDITIONAL(condition, code) 112 113 #else 114 115 #ifndef LOG_TAG 116 #define LOG_TAG "incfs:hardening" 117 #endif 118 119 #include <log/log.h> 120 #include <setjmp.h> 121 #include <signal.h> 122 #include <string.h> 123 124 namespace incfs { 125 126 struct JmpBufState final { 127 jmp_buf buf; 128 bool armed = false; 129 130 JmpBufState() = default; JmpBufStatefinal131 JmpBufState(const JmpBufState& other) { 132 if (other.armed) { 133 memcpy(&buf, &other.buf, sizeof(buf)); 134 armed = true; 135 } 136 } 137 138 JmpBufState& operator=(const JmpBufState& other) { 139 if (&other != this) { 140 if (other.armed) { 141 memcpy(&buf, &other.buf, sizeof(buf)); 142 armed = true; 143 } else { 144 armed = false; 145 } 146 } 147 return *this; 148 } 149 }; 150 151 class ScopedJmpBuf final { 152 public: ScopedJmpBuf(const JmpBufState & prev)153 ScopedJmpBuf(const JmpBufState& prev) : mPrev(prev) {} 154 ~ScopedJmpBuf(); 155 156 ScopedJmpBuf(const ScopedJmpBuf&) = delete; 157 158 private: 159 const JmpBufState& mPrev; 160 }; 161 162 #define SCOPED_SIGBUS_HANDLER_CONDITIONAL(condition, code) \ 163 (void)incfs::SignalHandler::instance(); \ 164 auto& tlsBuf_macro = incfs::SignalHandler::mJmpBuf; \ 165 incfs::JmpBufState oldBuf_macro = tlsBuf_macro; \ 166 const bool condition_macro_val = (condition); \ 167 if (condition_macro_val && setjmp(tlsBuf_macro.buf) != 0) { \ 168 ALOGI("%s: handling SIGBUS at line %d", __func__, __LINE__); \ 169 tlsBuf_macro = oldBuf_macro; \ 170 do { \ 171 code; \ 172 } while (0); \ 173 LOG_ALWAYS_FATAL("%s(): signal handler was supposed to return", __func__); \ 174 } \ 175 tlsBuf_macro.armed |= (condition_macro_val); \ 176 incfs::ScopedJmpBuf oldBufRestore_macro(oldBuf_macro) 177 178 #define SCOPED_SIGBUS_HANDLER(code) \ 179 SCOPED_SIGBUS_HANDLER_CONDITIONAL(true, code) 180 181 class SignalHandler final { 182 public: 183 static SignalHandler& instance(); 184 185 private: 186 SignalHandler(); 187 inline static struct sigaction mOldSigaction = {}; 188 189 static void handler(int sig, siginfo_t* info, void* ucontext); 190 191 public: 192 inline static thread_local JmpBufState mJmpBuf = {}; 193 }; 194 195 } // namespace incfs 196 197 #endif 198