1 // Copyright 2023 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 // http://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 //! NP Rust C FFI
16
17 pub mod credentials;
18 pub mod deserialize;
19 pub mod serialize;
20 pub mod v0;
21 pub mod v1;
22
23 use lock_adapter::stdlib::RwLock;
24 use lock_adapter::RwLock as _;
25 use np_ffi_core::common::{CurrentHandleAllocations, InvalidStackDataStructure};
26
27 /// Structure for categorized reasons for why a NP C FFI call may
28 /// be panicking.
29 #[derive(Clone, Copy, Debug)]
30 #[repr(u8)]
31 pub enum PanicReason {
32 /// Some enum cast to a variant failed. Utilized
33 /// for failed enum casts of all enums.
34 ///
35 /// (That is, this is the catch-all panic reason for enum
36 /// casts where there is not a more specific reason
37 /// in some other variant of this enum.)
38 EnumCastFailed = 0,
39 /// The panic handler is used to assert conditions are true to avoid programmer errors.
40 /// If a failed assert condition is hit, this panic handler is invoked with this reason.
41 AssertFailed = 1,
42 /// Error returned if the bit representation of a supposedly-Rust-constructed
43 /// -and-validated type actually doesn't correspond to the format of the
44 /// data structure expected on the Rust side of the boundary, and performing
45 /// further operations on the structure would yield unintended behavior.
46 /// If this kind of error is being raised, the C code must
47 /// be messing with stack-allocated data structures for this library
48 /// in an entirely unexpected way.
49 InvalidStackDataStructure = 2,
50 /// The maximum amount of allocations per type is `u32::MAX`, this panic handler is invoked
51 /// with this reason when this is exceeded. Clients should never need more than 4 Billions
52 /// handles and would certainly run into other issues before reaching that point
53 ExceededMaxHandleAllocations = 3,
54 }
55
56 /// Structure which maintains information about the panic-handler
57 /// for panicking calls in the NP C FFI.
58 struct PanicHandler {
59 /// Optional function-pointer to client-specified panic behavior.
60 handler: Option<unsafe extern "C" fn(PanicReason) -> ()>,
61
62 /// Fuse to prevent setting the panic-handler more than once.
63 /// We do not use the presence/absence of `self.handler` for this,
64 /// since it's possible for the client to explicitly pass "NULL"
65 /// to set the panic-handler to the platform-default (and ensure
66 /// that the panic-handler never changes from the platform-default.)
67 handler_set_by_client: bool,
68 }
69
70 impl PanicHandler {
new() -> Self71 pub(crate) const fn new() -> Self {
72 Self { handler: None, handler_set_by_client: false }
73 }
set_handler( &mut self, handler: Option<unsafe extern "C" fn(PanicReason) -> ()>, ) -> bool74 pub(crate) fn set_handler(
75 &mut self,
76 handler: Option<unsafe extern "C" fn(PanicReason) -> ()>,
77 ) -> bool {
78 // Only allow setting the panic handler once
79 if !self.handler_set_by_client {
80 self.handler = handler;
81 self.handler_set_by_client = true;
82 true
83 } else {
84 false
85 }
86 }
panic(&self, panic_reason: PanicReason) -> !87 pub(crate) fn panic(&self, panic_reason: PanicReason) -> ! {
88 if let Some(handler) = self.handler {
89 unsafe { handler(panic_reason) }
90 }
91 Self::system_handler(panic_reason)
92 }
93
system_handler(panic_reason: PanicReason) -> !94 fn system_handler(panic_reason: PanicReason) -> ! {
95 std::eprintln!("NP FFI Panicked: {:?}", panic_reason);
96 let backtrace = std::backtrace::Backtrace::capture();
97 std::eprintln!("Stack trace: {}", backtrace);
98 std::process::abort()
99 }
100 }
101
102 static PANIC_HANDLER: RwLock<PanicHandler> = RwLock::new(PanicHandler::new());
103
panic(reason: PanicReason) -> !104 pub(crate) fn panic(reason: PanicReason) -> ! {
105 PANIC_HANDLER.read().panic(reason)
106 }
107
panic_if_invalid<T>(value: Result<T, InvalidStackDataStructure>) -> T108 pub(crate) fn panic_if_invalid<T>(value: Result<T, InvalidStackDataStructure>) -> T {
109 match value {
110 Ok(x) => x,
111 Err(_) => panic(PanicReason::InvalidStackDataStructure),
112 }
113 }
114
unwrap<T>(value: Option<T>, panic_reason: PanicReason) -> T115 pub(crate) fn unwrap<T>(value: Option<T>, panic_reason: PanicReason) -> T {
116 match value {
117 Some(x) => x,
118 None => panic(panic_reason),
119 }
120 }
121
122 /// Overrides the global panic handler to be used when NP C FFI calls panic.
123 /// This method will only have an effect on the global panic-handler
124 /// the first time it's called, and this method will return `true`
125 /// to indicate that the panic handler was successfully set.
126 /// All subsequent calls to this method
127 /// will simply ignore the argument and return `false`.
128 ///
129 /// If the passed function pointer is non-null,
130 /// then we will call it upon every panic,
131 /// followed by the default panicking behavior for
132 /// the platform (in the case where the user-specified
133 /// function does not terminate or hang the running process.)
134 ///
135 /// Otherwise, we will resort to the
136 /// default panicking behavior for the system, which
137 /// is a printed stack trace followed by an abort
138 /// when this crate is compiled with `std`,
139 /// but a bare `loop { }` when this crate is compiled without.
140 #[no_mangle]
np_ffi_global_config_panic_handler( handler: Option<unsafe extern "C" fn(PanicReason) -> ()>, ) -> bool141 pub extern "C" fn np_ffi_global_config_panic_handler(
142 handler: Option<unsafe extern "C" fn(PanicReason) -> ()>,
143 ) -> bool {
144 let mut panic_handler = PANIC_HANDLER.write();
145 panic_handler.set_handler(handler)
146 }
147
148 /// Checks the current count of all outstanding handle allocations, useful for debugging,
149 /// logging, and testing
150 #[no_mangle]
np_ffi_global_config_get_current_allocation_count() -> CurrentHandleAllocations151 pub extern "C" fn np_ffi_global_config_get_current_allocation_count() -> CurrentHandleAllocations {
152 np_ffi_core::common::global_config_get_current_allocation_count()
153 }
154
155 /// Sets an override to the number of shards to employ in the NP FFI's
156 /// internal handle-maps, which places an upper bound on the number
157 /// of writing threads which may make progress at any one time
158 /// when concurrently accessing handles of the same type.
159 ///
160 /// By default, this value will be set to 16, or in `std` environments,
161 /// the minimum of 16 and the number of available hardware threads.
162 /// A shard value override of zero will be interpreted the same
163 /// as this default.
164 ///
165 /// Setting this value will have no effect if the handle-maps for the
166 /// API have already begun being used by the client code, and any
167 /// values set will take effect upon the first usage of _any_ non-`np_ffi_global_config_set`
168 /// API call.
169 #[no_mangle]
np_ffi_global_config_set_num_shards(num_shards: u8)170 pub extern "C" fn np_ffi_global_config_set_num_shards(num_shards: u8) {
171 np_ffi_core::common::global_config_set_num_shards(num_shards)
172 }
173