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