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 "asylo/platform/primitives/extent.h"
29 #include "asylo/platform/primitives/primitive_status.h"
30 #include "asylo/platform/primitives/primitives.h"
31 #include "asylo/platform/primitives/util/message.h"
32 #include "asylo/util/asylo_macros.h"
33 #include "asylo/util/status.h"
34 #include "asylo/util/statusor.h"
35 
36 namespace asylo {
37 namespace primitives {
38 
39 /// Loads an enclave.
40 ///
41 /// This template function should be instantiated with an "Enclave Backend"
42 /// parameter exported by a concrete implementation of the "Backend" concept.
43 template <typename Backend, typename... Args>
45  return Backend::Load(std::forward<Args>(args)...);
46 }
47 
48 /// \class ExitHandler untrusted_primitives.h @untrusted_primitives
49 /// Callback structure for dispatching messages from the enclave.
50 struct ExitHandler {
51  using Callback =
54 
55  ExitHandler() : ExitHandler(/*callback=*/nullptr, /*context=*/nullptr) {}
56 
57  /// Initializes an exit handler with a callback.
58  ///
59  /// \param callback The callback this handler uses.
60  explicit ExitHandler(Callback callback)
61  : ExitHandler(callback, /*context=*/nullptr) {}
62 
63  /// Initializes an exit handler with a callback and a context pointer.
64  ///
65  /// \param callback The callback this handler uses.
66  /// \param context A type-erased non-owned pointer that is passed to the
67  /// callback when called. Since an ExitHandler is registered in an
68  /// client-global context, the object should live as long as the client.
69  ExitHandler(Callback callback, void *context)
70  : callback(callback), context(context) {}
71 
72  /// A predicate for whether the callback is initialized.
73  ///
74  /// \returns true if this handler is uninitialized.
75  bool IsNull() const { return callback == nullptr; }
76 
77  /// Implicit bool conversion for null checks.
78  operator bool() const { return IsNull(); }
79 
80  /// Callback function to invoke for this exit.
82 
83  /// Uninterpreted data passed by the runtime to invocations of the handler.
84  void *context;
85 };
86 
87 /// \class Client untrusted_primitives.h @untrusted_primitives
88 /// A reference to an enclave held by untrusted code.
89 ///
90 /// This declares the primitive API exposed to untrusted application code by
91 /// the Asylo runtime. Each Asylo backend is responsible for providing an
92 /// implementation of this interface.
93 /// To support multiple implementations, the interface defines a generic
94 /// "Enclave Backend" concept which every backend must implement. An enclave
95 /// backend is a structure compatible with:
96 ///
97 /// ```
98 /// struct EnclaveBackend {
99 /// // Load an enclave, returning a Client or error status.
100 /// static StatusOr<std::shared_ptr<Client>> Load(...);
101 /// };
102 /// ```
104  public:
105  /// An interface to a provider of enclave exit calls.
107  public:
108  virtual ~ExitCallProvider() = default;
109 
110  /// Registers a callback as the handler routine for an enclave exit point
111  /// `untrusted_selector`.
112  ///
113  /// \param untrusted_selector The identification number an enclave will use
114  /// to select the registered handler, `handler`.
115  /// \param handler The representation of a callable untrusted function.
116  /// \returns If a handler has already been registered for `trusted_selector`
117  /// or if an invalid selector value is passed, returns an error status,
118  /// otherwise Ok.
120  const ExitHandler &handler)
122 
123  /// Finds and invokes an exit handler.
124  ///
125  /// \param untrusted_selector The identification number for the called
126  /// untrusted function.
127  /// \param input A pointer to a MessageReader from which the function
128  /// implementation can read the arguments the enclave wrote.
129  /// \param output A pointer to a MessageWriter to which the function will
130  /// write the function's outputs.
131  /// \param client A pointer to the client that is exiting.
132  /// \returns an error status on failure, otherwise Ok.
137  };
138 
139  /// An RAII wrapper that sets thread-local enclave "current client" reference
140  /// on construction and resets it to the previous value when destroyed.
142  public:
143  /// Constructs a "scope object" to set the current client pointer for the
144  /// lifetime of the object.
145  ///
146  /// \param client An unowned pointer to the new current client.
147  explicit ScopedCurrentClient(Client *client)
148  : saved_client_(Client::current_client_), pid_(getpid()) {
149  current_client_ = client;
150  }
152 
153  ScopedCurrentClient(const ScopedCurrentClient &other) = delete;
154  ScopedCurrentClient &operator=(const ScopedCurrentClient &other) = delete;
155 
156  private:
157  Client *const saved_client_;
158  const pid_t pid_;
159  };
160 
161  virtual ~Client() = default;
162 
163  /// An overridable handler registration method.
164  ///
165  /// This allows backend implementations to register special-purpose exit
166  /// handlers that might only be appropriate to that backend. The default
167  /// implementation registers nothing and returns Ok.
168  ///
169  /// \returns An error on failure, or Ok.
171 
172  /// A predicate for whether the enclave may be entered or will accept
173  /// messages.
174  ///
175  /// \returns True if the enclave has been destroyed, or if it is marked for
176  /// destruction pending the completion of an operation by another thread.
177  virtual bool IsClosed() const = 0;
178 
179  // Marks the enclave for destruction, possibly pending the completion of
180  // operations by concurrent client threads.
181  virtual Status Destroy() = 0;
182 
183  /// A getter for the enclave name.
184  ///
185  /// Enclave names are used for fetching client instances from the enclave
186  /// manager.
187  ///
188  /// \returns The name of the enclave.
189  virtual absl::string_view Name() const { return name_; }
190 
191  /// Stores `this` as the active thread's "current client".
192  ///
193  /// This should only be called if an enclave entry happens without going
194  /// through a regular enclave entry point (like a fork from inside the
195  /// enclave).
196  void SetCurrentClient();
197 
198  /// A static getter for the current client.
199  ///
200  /// \returns A pointer to the active thread's current client.
201  static Client *GetCurrentClient();
202 
203  /// Enters the enclave synchronously at an entry point to trusted code
204  /// designated by `selector`.
205  /// Input `input` is copied into the enclave, which may occur locally inside
206  /// the same address space as the caller or remotely via RPC.
207  /// Conversely, results are copied and returned in 'output'.
208  ///
209  /// \param selector The identification number to select a registered
210  /// handler in the enclave.
211  /// \param input A pointer to a MessageWriter, into which all call inputs must
212  /// be pushed.
213  /// \param output A pointer to a MessageReader from which to read outputs from
214  /// the call.
215  /// \returns A status for the call action, since the call itself may fail.
218 
219  /// Enclave exit callback function shared with the enclave.
220  ///
221  /// \param untrusted_selector The identification number to select a registered
222  /// handler in the current client.
223  /// \param in A pointer to a MessageReader, from which all inputs are read.
224  /// \param out A pointer to a MessageWriter, into which all call outputs are
225  /// written.
226  /// \returns A PrimitiveStatus for the call action, since the call itself may
227  /// fail.
230 
231  /// Accessor to the client's exit call provider.
232  ///
233  /// \returns A mutable pointer to the client's ExitCallProvider.
234  ExitCallProvider *exit_call_provider() { return exit_call_provider_.get(); }
235 
236  protected:
237  /// Constructs a client, reserved for only backend implementations.
238  ///
239  /// \param name The name of the enclave.
240  /// \param exit_call_provider A pointer an ExitCallProvider that the Client
241  /// takes ownership of. The provider is the source of all `ExitHandler`s.
242  Client(const absl::string_view name,
243  std::unique_ptr<ExitCallProvider> exit_call_provider)
245 
246  /// Provides implementation of EnclaveCall.
247  ///
248  /// This method is virtual for backends to override. The public EnclaveCall
249  /// method provides necessary boilerplate around each call to this
250  /// implementation.
251  ///
252  /// \param selector The identification number to select a registered
253  /// handler in the enclave.
254  /// \param input A pointer to a MessageWriter, into which all call inputs must
255  /// be pushed.
256  /// \param output A pointer to a MessageReader from which to read outputs from
257  /// the call.
258  /// \returns A status for the call action, since the call itself may fail.
262 
263  private:
264  // Exit call provider for the enclave.
265  const std::unique_ptr<ExitCallProvider> exit_call_provider_;
266 
267  // Thread-local reference to the enclave that makes exit call.
268  // Can be set by EnclaveCall, enclave loader.
269  static thread_local Client *current_client_;
270 
271  // Enclave name.
272  absl::string_view name_;
273 };
274 
275 } // namespace primitives
276 } // namespace asylo
277 
278 #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:44
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:69
virtual Status Destroy()=0
virtual Status RegisterExitHandlers() ASYLO_MUST_USE_RESULT
An overridable handler registration method.
virtual ~Client()=default
An interface to a provider of enclave exit calls.
Definition: untrusted_primitives.h:106
ScopedCurrentClient & operator=(const ScopedCurrentClient &other)=delete
A reference to an enclave held by untrusted code.
Definition: untrusted_primitives.h:103
ABSL_CONST_INIT const char kStatusMoveAssignmentMsg[]
void * context
Uninterpreted data passed by the runtime to invocations of the handler.
Definition: untrusted_primitives.h:84
Callback structure for dispatching messages from the enclave.
Definition: untrusted_primitives.h:50
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 bool IsClosed() const =0
A predicate for whether the enclave may be entered or will accept messages.
operator bool() const
Implicit bool conversion for null checks.
Definition: untrusted_primitives.h:78
ScopedCurrentClient(Client *client)
Constructs a "scope object" to set the current client pointer for the lifetime of the object...
Definition: untrusted_primitives.h:147
ExitCallProvider * exit_call_provider()
Accessor to the client&#39;s exit call provider.
Definition: untrusted_primitives.h:234
ExitHandler()
Definition: untrusted_primitives.h:55
virtual absl::string_view Name() const
A getter for the enclave name.
Definition: untrusted_primitives.h:189
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:242
ExitHandler(Callback callback)
Initializes an exit handler with a callback.
Definition: untrusted_primitives.h:60
bool IsNull() const
A predicate for whether the callback is initialized.
Definition: untrusted_primitives.h:75
Callback callback
Callback function to invoke for this exit.
Definition: untrusted_primitives.h:81
An RAII wrapper that sets thread-local enclave "current client" reference on construction and resets ...
Definition: untrusted_primitives.h:141
void SetCurrentClient()
Stores this as the active thread&#39;s "current client".
ScopedCurrentClient(const ScopedCurrentClient &other)=delete
Definition: extent.h:29