1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SANDBOXED_API_TRANSACTION_H_ 16 #define SANDBOXED_API_TRANSACTION_H_ 17 18 #include <ctime> 19 #include <functional> 20 #include <memory> 21 #include <utility> 22 23 #include "absl/base/attributes.h" 24 #include "absl/log/check.h" 25 #include "absl/log/log.h" 26 #include "absl/status/status.h" 27 #include "absl/strings/str_cat.h" 28 #include "absl/time/time.h" 29 #include "sandboxed_api/sandbox.h" 30 31 #define TRANSACTION_FAIL_IF_NOT(x, y) \ 32 if (!(x)) { \ 33 return absl::FailedPreconditionError(y); \ 34 } 35 36 namespace sapi { 37 38 // The Transaction class allows to perform operations in the sandboxee, 39 // repeating them if necessary (if the sandboxing, or IPC failed). 40 // 41 // We provide two different implementations of transactions: 42 // 1) Single function transactions - They consist out of a single function 43 // Main() that will be invoked as body of the transaction. For this, 44 // inherit from the Transaction class and implement Main(). 45 // 2) Function pointer based transactions - The BasicTransaction class accepts 46 // functions that take a sandbox object (along with arbitrary other 47 // parameters) and return a status. This way no custom implementation of a 48 // Transaction class is required. 49 // 50 // Additionally both methods support Init() and Finish() functions. 51 // Init() will be called after the sandbox has been set up. 52 // Finish() will be called when the transaction object goes out of scope. 53 class TransactionBase { 54 public: 55 TransactionBase(const TransactionBase&) = delete; 56 TransactionBase& operator=(const TransactionBase&) = delete; 57 58 virtual ~TransactionBase(); 59 60 // Getter/Setter for retry_count_. retry_count()61 int retry_count() const { return retry_count_; } set_retry_count(int value)62 void set_retry_count(int value) { 63 CHECK_GE(value, 0); 64 retry_count_ = value; 65 } 66 67 // Getter/Setter for time_limit_. GetTimeLimit()68 time_t GetTimeLimit() const { return time_limit_; } SetTimeLimit(time_t time_limit)69 void SetTimeLimit(time_t time_limit) { time_limit_ = time_limit; } SetTimeLimit(absl::Duration time_limit)70 void SetTimeLimit(absl::Duration time_limit) { 71 time_limit_ = absl::ToTimeT(absl::UnixEpoch() + time_limit); 72 } 73 IsInitialized()74 bool IsInitialized() const { return initialized_; } 75 76 // Getter for the sandbox_. sandbox()77 Sandbox* sandbox() const { return sandbox_.get(); } 78 79 // Restarts the sandbox. 80 // WARNING: This will invalidate any references to the remote process, make 81 // sure you don't keep any vars or FDs to the remote process when 82 // calling this. Restart()83 absl::Status Restart() { 84 if (initialized_) { 85 Finish().IgnoreError(); 86 initialized_ = false; 87 } 88 return sandbox_->Restart(true); 89 } 90 91 protected: TransactionBase(std::unique_ptr<Sandbox> sandbox)92 explicit TransactionBase(std::unique_ptr<Sandbox> sandbox) 93 : time_limit_(absl::ToTimeT(absl::UnixEpoch() + kDefaultTimeLimit)), 94 sandbox_(std::move(sandbox)) {} 95 96 // Runs the main (retrying) transaction loop. 97 absl::Status RunTransactionLoop(const std::function<absl::Status()>& f); 98 99 private: 100 // Number of default transaction execution re-tries, in case of failures. 101 static constexpr int kDefaultRetryCount = 1; 102 103 // Wall-time limit for a single transaction execution (60 s.). 104 static constexpr absl::Duration kDefaultTimeLimit = absl::Seconds(60); 105 106 // Executes a single function in the sandbox, used in the main transaction 107 // loop. Asserts that the sandbox has been set up and Init() was called. 108 absl::Status RunTransactionFunctionInSandbox( 109 const std::function<absl::Status()>& f); 110 111 // Initialization routine of the sandboxed process that will be called only 112 // once upon sandboxee startup. Init()113 virtual absl::Status Init() { return absl::OkStatus(); } 114 115 // End routine for the sandboxee that gets calls when the transaction is 116 // destroyed/restarted to clean up resources. Finish()117 virtual absl::Status Finish() { return absl::OkStatus(); } 118 119 // Number of tries this transaction will be re-executed until it succeeds. 120 int retry_count_ = kDefaultRetryCount; 121 122 // Time (wall-time) limit for a single Run() call (in seconds). 0 means: no 123 // wall-time limit. 124 time_t time_limit_; 125 126 // Has Init() finished with success? 127 bool initialized_ = false; 128 129 // The main sapi::Sandbox object. 130 std::unique_ptr<Sandbox> sandbox_; 131 }; 132 133 // Regular style transactions, based on inheriting. 134 class Transaction : public TransactionBase { 135 public: 136 Transaction(const Transaction&) = delete; 137 Transaction& operator=(const Transaction&) = delete; 138 using TransactionBase::TransactionBase; 139 140 // Run the transaction. Run()141 absl::Status Run() { 142 return RunTransactionLoop([this] { return Main(); }); 143 } 144 145 protected: 146 // The main sandboxee routine: Can be called multiple times. Main()147 virtual absl::Status Main() { return absl::OkStatus(); } 148 }; 149 150 // Callback style transactions: 151 class BasicTransaction final : public TransactionBase { 152 private: 153 using InitFunction = std::function<absl::Status(Sandbox*)>; 154 using FinishFunction = std::function<absl::Status(Sandbox*)>; 155 156 public: BasicTransaction(std::unique_ptr<Sandbox> sandbox)157 explicit BasicTransaction(std::unique_ptr<Sandbox> sandbox) 158 : TransactionBase(std::move(sandbox)), 159 init_function_(nullptr), 160 finish_function_(nullptr) {} 161 162 template <typename F> BasicTransaction(std::unique_ptr<Sandbox> sandbox,F init_function)163 BasicTransaction(std::unique_ptr<Sandbox> sandbox, F init_function) 164 : TransactionBase(std::move(sandbox)), 165 init_function_(static_cast<InitFunction>(init_function)), 166 finish_function_(nullptr) {} 167 168 template <typename F, typename G> BasicTransaction(std::unique_ptr<Sandbox> sandbox,F init_function,G fini_function)169 BasicTransaction(std::unique_ptr<Sandbox> sandbox, F init_function, 170 G fini_function) 171 : TransactionBase(std::move(sandbox)), 172 init_function_(static_cast<InitFunction>(init_function)), 173 finish_function_(static_cast<FinishFunction>(fini_function)) {} 174 175 // Run any function as body of the transaction that matches our expectations 176 // (that is: Returning a Status and accepting a Sandbox object as first 177 // parameter). 178 template <typename T, typename... Args> Run(T func,Args &&...args)179 absl::Status Run(T func, Args&&... args) { 180 return RunTransactionLoop( 181 [&] { return func(sandbox(), std::forward<Args>(args)...); }); 182 } 183 184 private: 185 InitFunction init_function_; 186 FinishFunction finish_function_; 187 Init()188 absl::Status Init() final { 189 return init_function_ ? init_function_(sandbox()) : absl::OkStatus(); 190 } 191 Finish()192 absl::Status Finish() final { 193 return finish_function_ ? finish_function_(sandbox()) : absl::OkStatus(); 194 } 195 }; 196 197 } // namespace sapi 198 199 #endif // SANDBOXED_API_TRANSACTION_H_ 200