Asylo
untrusted_primitives.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2018 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_PLATFORM_PRIMITIVES_UNTRUSTED_PRIMITIVES_H_
20 #define ASYLO_PLATFORM_PRIMITIVES_UNTRUSTED_PRIMITIVES_H_
21 
22 #include <unistd.h>
23 
24 #include <cstdint>
25 #include <memory>
26 #include <utility>
27 
28 #include "absl/base/thread_annotations.h"
29 #include "asylo/platform/primitives/extent.h"
30 #include "asylo/platform/primitives/primitive_status.h"
31 #include "asylo/platform/primitives/primitives.h"
32 #include "asylo/platform/primitives/util/message.h"
33 #include "asylo/util/asylo_macros.h"
34 #include "asylo/util/mutex_guarded.h"
35 #include "asylo/util/status.h"
36 #include "asylo/util/statusor.h"
37 
38 namespace asylo {
39 namespace primitives {
40 
41 /// Loads an enclave.
42 ///
43 /// This template function should be instantiated with an "Enclave Backend"
44 /// parameter exported by a concrete implementation of the "Backend" concept.
45 template <typename Backend, typename... Args>
46 StatusOr<std::shared_ptr<class Client>> LoadEnclave(Args &&... args) {
47  return Backend::Load(std::forward<Args>(args)...);
48 }
49 
50 /// \class ExitHandler untrusted_primitives.h @untrusted_primitives
51 /// Callback structure for dispatching messages from the enclave.
52 struct ExitHandler {
53  using Callback =
56 
57  ExitHandler() : ExitHandler(/*callback=*/nullptr, /*context=*/nullptr) {}
58 
59  /// Initializes an exit handler with a callback.
60  ///
61  /// \param callback The callback this handler uses.
62  explicit ExitHandler(Callback callback)
63  : ExitHandler(callback, /*context=*/nullptr) {}
64 
65  /// Initializes an exit handler with a callback and a context pointer.
66  ///
67  /// \param callback The callback this handler uses.
68  /// \param context A type-erased non-owned pointer that is passed to the
69  /// callback when called. Since an ExitHandler is registered in an
70  /// client-global context, the object should live as long as the client.
71  ExitHandler(Callback callback, void *context)
72  : callback(callback), context(context) {}
73 
74  /// A predicate for whether the callback is initialized.
75  ///
76  /// \returns true if this handler is uninitialized.
77  bool IsNull() const { return callback == nullptr; }
78 
79  /// Implicit bool conversion for null checks.
80  operator bool() const { return IsNull(); }
81 
82  /// Callback function to invoke for this exit.
84 
85  /// Uninterpreted data passed by the runtime to invocations of the handler.
86  void *context;
87 };
88 
89 /// \class Client untrusted_primitives.h @untrusted_primitives
90 /// A reference to an enclave held by untrusted code.
91 ///
92 /// This declares the primitive API exposed to untrusted application code by
93 /// the Asylo runtime. Each Asylo backend is responsible for providing an
94 /// implementation of this interface.
95 /// To support multiple implementations, the interface defines a generic
96 /// "Enclave Backend" concept which every backend must implement. An enclave
97 /// backend is a structure compatible with:
98 ///
99 /// ```
100 /// struct EnclaveBackend {
101 /// // Load an enclave, returning a Client or error status.
102 /// static StatusOr<std::shared_ptr<Client>> Load(...);
103 /// };
104 /// ```
105 class Client : public std::enable_shared_from_this<Client> {
106  public:
107  /// An interface to a provider of enclave exit calls.
109  public:
110  virtual ~ExitCallProvider() = default;
111 
112  /// Registers a callback as the handler routine for an enclave exit point
113  /// `untrusted_selector`.
114  ///
115  /// \param untrusted_selector The identification number an enclave will use
116  /// to select the registered handler, `handler`.
117  /// \param handler The representation of a callable untrusted function.
118  /// \returns If a handler has already been registered for `trusted_selector`
119  /// or if an invalid selector value is passed, returns an error status,
120  /// otherwise Ok.
122  const ExitHandler &handler)
124 
125  /// Finds and invokes an exit handler.
126  ///
127  /// \param untrusted_selector The identification number for the called
128  /// untrusted function.
129  /// \param input A pointer to a MessageReader from which the function
130  /// implementation can read the arguments the enclave wrote.
131  /// \param output A pointer to a MessageWriter to which the function will
132  /// write the function's outputs.
133  /// \param client A pointer to the client that is exiting.
134  /// \returns an error status on failure, otherwise Ok.
139  };
140 
141  /// An RAII wrapper that sets thread-local enclave "current client" reference
142  /// on construction and resets it to the previous value when destroyed.
144  public:
145  /// Constructs a "scope object" to set the current client pointer for the
146  /// lifetime of the object.
147  ///
148  /// \param client An unowned pointer to the new current client.
149  explicit ScopedCurrentClient(Client *client)
150  : saved_client_(Client::current_client_), pid_(getpid()) {
151  current_client_ = client;
152  }
154 
155  ScopedCurrentClient(const ScopedCurrentClient &other) = delete;
156  ScopedCurrentClient &operator=(const ScopedCurrentClient &other) = delete;
157 
158  private:
159  Client *const saved_client_;
160  const pid_t pid_;
161  };
162 
163  virtual ~Client() { ReleaseMemory(); }
164 
165  /// An overridable handler registration method.
166  ///
167  /// This allows backend implementations to register special-purpose exit
168  /// handlers that might only be appropriate to that backend. The default
169  /// implementation registers nothing and returns Ok.
170  ///
171  /// \returns An error on failure, or Ok.
172  virtual Status RegisterExitHandlers() ASYLO_MUST_USE_RESULT;
173 
174  /// A predicate for whether the enclave may be entered or will accept
175  /// messages.
176  ///
177  /// \returns True if the enclave has been destroyed, or if it is marked for
178  /// destruction pending the completion of an operation by another thread.
179  virtual bool IsClosed() const = 0;
180 
181  // Marks the enclave for destruction, possibly pending the completion of
182  // operations by concurrent client threads.
183  virtual Status Destroy() = 0;
184 
185  /// A getter for the enclave name.
186  ///
187  /// Enclave names are used for fetching client instances from the enclave
188  /// manager.
189  ///
190  /// \returns The name of the enclave.
191  virtual absl::string_view Name() const { return name_; }
192 
193  /// Stores `this` as the active thread's "current client".
194  ///
195  /// This should only be called if an enclave entry happens without going
196  /// through a regular enclave entry point (like a fork from inside the
197  /// enclave).
198  void SetCurrentClient();
199 
200  /// A static getter for the current client.
201  ///
202  /// \returns A pointer to the active thread's current client.
203  static Client *GetCurrentClient();
204 
205  /// Enters the enclave synchronously at an entry point to trusted code
206  /// designated by `selector`.
207  /// Input `input` is copied into the enclave, which may occur locally inside
208  /// the same address space as the caller or remotely via RPC.
209  /// Conversely, results are copied and returned in 'output'.
210  ///
211  /// \param selector The identification number to select a registered
212  /// handler in the enclave.
213  /// \param input A pointer to a MessageWriter, into which all call inputs must
214  /// be pushed.
215  /// \param output A pointer to a MessageReader from which to read outputs from
216  /// the call.
217  /// \returns A status for the call action, since the call itself may fail.
219  MessageReader *output) ASYLO_MUST_USE_RESULT;
220 
221  /// Enclave exit callback function shared with the enclave.
222  ///
223  /// \param untrusted_selector The identification number to select a registered
224  /// handler in the current client.
225  /// \param in A pointer to a MessageReader, from which all inputs are read.
226  /// \param out A pointer to a MessageWriter, into which all call outputs are
227  /// written.
228  /// \returns A PrimitiveStatus for the call action, since the call itself may
229  /// fail.
232 
233  /// Accessor to the client's exit call provider.
234  ///
235  /// \returns A mutable pointer to the client's ExitCallProvider.
236  ExitCallProvider *exit_call_provider() { return exit_call_provider_.get(); }
237 
238  /// Register memory to be freed upon enclave destruction.
239  ///
240  /// \param mem A pointer to be passed to free on enclave exit.
241  virtual void RegisterMemory(void *mem) {
242  memory_to_free_.Lock()->emplace_back(mem);
243  }
244 
245  /// Frees enclave resources registered to the client.
246  virtual void ReleaseMemory() {
247  auto to_free = memory_to_free_.Lock();
248  for (auto ptr : *to_free) {
249  free(ptr);
250  }
251  to_free->clear();
252  }
253 
254  protected:
255  /// Constructs a client, reserved for only backend implementations.
256  ///
257  /// \param name The name of the enclave.
258  /// \param exit_call_provider A pointer an ExitCallProvider that the Client
259  /// takes ownership of. The provider is the source of all `ExitHandler`s.
260  Client(const absl::string_view name,
261  std::unique_ptr<ExitCallProvider> exit_call_provider)
263  name_(name) {}
264 
265  /// Provides implementation of EnclaveCall.
266  ///
267  /// This method is virtual for backends to override. The public EnclaveCall
268  /// method provides necessary boilerplate around each call to this
269  /// implementation.
270  ///
271  /// \param selector The identification number to select a registered
272  /// handler in the enclave.
273  /// \param input A pointer to a MessageWriter, into which all call inputs must
274  /// be pushed.
275  /// \param output A pointer to a MessageReader from which to read outputs from
276  /// the call.
277  /// \returns A status for the call action, since the call itself may fail.
280  ASYLO_MUST_USE_RESULT = 0;
281 
282  private:
283  // Exit call provider for the enclave.
284  const std::unique_ptr<ExitCallProvider> exit_call_provider_;
285 
286  // Thread-local reference to the enclave that makes exit call.
287  // Can be set by EnclaveCall, enclave loader.
288  static thread_local Client *current_client_;
289 
290  // Enclave name.
291  absl::string_view name_;
292 
293  // A collection of memory to free upon enclave exit.
294  MutexGuarded<std::vector<void *>> memory_to_free_;
295 };
296 
297 } // namespace primitives
298 } // namespace asylo
299 
300 #endif // ASYLO_PLATFORM_PRIMITIVES_UNTRUSTED_PRIMITIVES_H_
Status EnclaveCall(uint64_t selector, MessageWriter *input, MessageReader *output) ASYLO_MUST_USE_RESULT
Enters the enclave synchronously at an entry point to trusted code designated by selector.
virtual Status RegisterExitHandler(uint64_t untrusted_selector, const ExitHandler &handler) ASYLO_MUST_USE_RESULT=0
Registers a callback as the handler routine for an enclave exit point untrusted_selector.
StatusOr< std::shared_ptr< class Client > > LoadEnclave(Args &&... args)
Loads an enclave.
Definition: untrusted_primitives.h:46
static PrimitiveStatus ExitCallback(uint64_t untrusted_selector, MessageReader *in, MessageWriter *out)
Enclave exit callback function shared with the enclave.
virtual Status EnclaveCallInternal(uint64_t selector, MessageWriter *input, MessageReader *output) ASYLO_MUST_USE_RESULT=0
Provides implementation of EnclaveCall.
ExitHandler(Callback callback, void *context)
Initializes an exit handler with a callback and a context pointer.
Definition: untrusted_primitives.h:71
virtual Status Destroy()=0
virtual Status RegisterExitHandlers() ASYLO_MUST_USE_RESULT
An overridable handler registration method.
An interface to a provider of enclave exit calls.
Definition: untrusted_primitives.h:108
ScopedCurrentClient & operator=(const ScopedCurrentClient &other)=delete
ABSL_CONST_INIT const char kStatusMoveAssignmentMsg[]
void * context
Uninterpreted data passed by the runtime to invocations of the handler.
Definition: untrusted_primitives.h:86
virtual void RegisterMemory(void *mem)
Register memory to be freed upon enclave destruction.
Definition: untrusted_primitives.h:241
Callback structure for dispatching messages from the enclave.
Definition: untrusted_primitives.h:52
virtual Status InvokeExitHandler(uint64_t untrusted_selector, MessageReader *input, MessageWriter *output, Client *client) ASYLO_MUST_USE_RESULT=0
Finds and invokes an exit handler.
static Client * GetCurrentClient()
A static getter for the current client.
virtual ~Client()
Definition: untrusted_primitives.h:163
virtual bool IsClosed() const =0
A predicate for whether the enclave may be entered or will accept messages.
virtual void ReleaseMemory()
Frees enclave resources registered to the client.
Definition: untrusted_primitives.h:246
operator bool() const
Implicit bool conversion for null checks.
Definition: untrusted_primitives.h:80
ScopedCurrentClient(Client *client)
Constructs a "scope object" to set the current client pointer for the lifetime of the object...
Definition: untrusted_primitives.h:149
ExitCallProvider * exit_call_provider()
Accessor to the client&#39;s exit call provider.
Definition: untrusted_primitives.h:236
ExitHandler()
Definition: untrusted_primitives.h:57
virtual absl::string_view Name() const
A getter for the enclave name.
Definition: untrusted_primitives.h:191
Client(const absl::string_view name, std::unique_ptr< ExitCallProvider > exit_call_provider)
Constructs a client, reserved for only backend implementations.
Definition: untrusted_primitives.h:260
ExitHandler(Callback callback)
Initializes an exit handler with a callback.
Definition: untrusted_primitives.h:62
bool IsNull() const
A predicate for whether the callback is initialized.
Definition: untrusted_primitives.h:77
Callback callback
Callback function to invoke for this exit.
Definition: untrusted_primitives.h:83
An RAII wrapper that sets thread-local enclave "current client" reference on construction and resets ...
Definition: untrusted_primitives.h:143
void SetCurrentClient()
Stores this as the active thread&#39;s "current client".
ScopedCurrentClient(const ScopedCurrentClient &other)=delete
Definition: extent.h:29