blob: 0dd6d33dfdbada16b174723d3d093894d65679f4 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! NP Rust C FFI
pub mod credentials;
pub mod deserialize;
use lock_adapter::std::RwLock;
use lock_adapter::RwLock as _;
/// Structure for categorized reasons for why a NP C FFI call may
/// be panicking.
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum PanicReason {
/// Some enum cast to a variant failed. Utilized
/// for failed enum casts of all enums.
///
/// (That is, this is the catch-all panic reason for enum
/// casts where there is not a more specific reason
/// in some other variant of this enum.)
EnumCastFailed = 0,
/// The panic handler is used to assert conditions are true to avoid programmer errors.
/// If a failed assert condition is hit, this panic handler is invoked with this reason.
AssertFailed = 1,
/// Error returned if action bits inside of a V0Actions struct are invalid. If the struct was
/// created by this deserializer, the bits will always be valid, they are only invalid if
/// a user reaches in and changes them to something invalid.
InvalidActionBits = 2,
}
/// Structure which maintains information about the panic-handler
/// for panicking calls in the NP C FFI.
struct PanicHandler {
/// Optional function-pointer to client-specified panic behavior.
handler: Option<unsafe extern "C" fn(PanicReason) -> ()>,
/// Fuse to prevent setting the panic-handler more than once.
/// We do not use the presence/absence of `self.handler` for this,
/// since it's possible for the client to explicitly pass "NULL"
/// to set the panic-handler to the platform-default (and ensure
/// that the panic-handler never changes from the platform-default.)
handler_set_by_client: bool,
}
impl PanicHandler {
pub(crate) const fn new() -> Self {
Self { handler: None, handler_set_by_client: false }
}
pub(crate) fn set_handler(
&mut self,
handler: Option<unsafe extern "C" fn(PanicReason) -> ()>,
) -> bool {
// Only allow setting the panic handler once
if !self.handler_set_by_client {
self.handler = handler;
self.handler_set_by_client = true;
true
} else {
false
}
}
pub(crate) fn panic(&self, panic_reason: PanicReason) -> ! {
if let Some(handler) = self.handler {
unsafe { handler(panic_reason) }
}
Self::system_handler(panic_reason)
}
fn system_handler(panic_reason: PanicReason) -> ! {
std::eprintln!("NP FFI Panicked: {:?}", panic_reason);
let backtrace = std::backtrace::Backtrace::capture();
std::eprintln!("Stack trace: {}", backtrace);
std::process::abort()
}
}
//TODO: use a std library RwLock if we have that available, spin is only needed for no_std and
// won't be as performant
static PANIC_HANDLER: RwLock<PanicHandler> = RwLock::new(PanicHandler::new());
pub(crate) fn panic(reason: PanicReason) -> ! {
PANIC_HANDLER.read().panic(reason)
}
pub(crate) fn unwrap<T>(value: Option<T>, panic_reason: PanicReason) -> T {
match value {
Some(x) => x,
None => panic(panic_reason),
}
}
/// Overrides the global panic handler to be used when NP C FFI calls panic.
/// This method will only have an effect on the global panic-handler
/// the first time it's called, and this method will return `true`
/// to indicate that the panic handler was successfully set.
/// All subsequent calls to this method
/// will simply ignore the argument and return `false`.
///
/// If the passed function pointer is non-null,
/// then we will call it upon every panic,
/// followed by the default panicking behavior for
/// the platform (in the case where the user-specified
/// function does not terminate or hang the running process.)
///
/// Otherwise, we will resort to the
/// default panicking behavior for the system, which
/// is a printed stack trace followed by an abort
/// when this crate is compiled with `std`,
/// but a bare `loop { }` when this crate is compiled without.
#[no_mangle]
pub extern "C" fn np_ffi_global_config_panic_handler(
handler: Option<unsafe extern "C" fn(PanicReason) -> ()>,
) -> bool {
let mut panic_handler = PANIC_HANDLER.write();
panic_handler.set_handler(handler)
}
/// Sets an override to the number of shards to employ in the NP FFI's
/// internal handle-maps, which places an upper bound on the number
/// of writing threads which may make progress at any one time
/// when concurrently accessing handles of the same type.
///
/// By default, this value will be set to 16, or in `std` environments,
/// the minimum of 16 and the number of available hardware threads.
/// A shard value override of zero will be interpreted the same
/// as this default.
///
/// Setting this value will have no effect if the handle-maps for the
/// API have already begun being used by the client code, and any
/// values set will take effect upon the first usage of _any_ non-`np_ffi_global_config_set`
/// API call.
#[no_mangle]
pub extern "C" fn np_ffi_global_config_set_num_shards(num_shards: u8) {
np_ffi_core::common::global_config_set_num_shards(num_shards)
}
/// Sets the maximum number of active handles to credential books
/// which may be active at any one time.
/// Default value: Max value.
/// Max value: `u32::MAX - 1`.
///
/// Useful for bounding the maximum memory used by the client application
/// on credential books in constrained-memory environments.
///
/// Setting this value will have no effect if the handle-maps for the
/// API have already begun being used by the client code, and any
/// values set will take effect upon the first usage of any API
/// call utilizing credential books.
#[no_mangle]
pub extern "C" fn np_ffi_global_config_set_max_num_credential_books(max_num_credential_books: u32) {
np_ffi_core::common::global_config_set_max_num_credential_books(max_num_credential_books)
}
/// Sets the maximum number of active handles to deserialized v0
/// advertisements which may be active at any one time.
///
/// Useful for bounding the maximum memory used by the client application
/// on v0 advertisements in constrained-memory environments.
///
/// Default value: Max value.
/// Max value: `u32::MAX - 1`.
///
/// Setting this value will have no effect if the handle-maps for the
/// API have already begun being used by the client code, and any
/// values set will take effect upon the first usage of any API
/// call which references or returns a deserialized V0 advertisement.
#[no_mangle]
pub extern "C" fn np_ffi_global_config_set_max_num_deserialized_v0_advertisements(
max_num_deserialized_v0_advertisements: u32,
) {
np_ffi_core::common::global_config_set_max_num_deserialized_v0_advertisements(
max_num_deserialized_v0_advertisements,
)
}
/// Sets the maximum number of active handles to deserialized v1
/// advertisements which may be active at any one time.
///
/// Useful for bounding the maximum memory used by the client application
/// on v1 advertisements in constrained-memory environments.
///
/// Default value: Max value.
/// Max value: `u32::MAX - 1`.
///
/// Setting this value will have no effect if the handle-maps for the
/// API have already begun being used by the client code, and any
/// values set will take effect upon the first usage of any API
/// call which references or returns a deserialized V1 advertisement.
#[no_mangle]
pub extern "C" fn np_ffi_global_config_set_max_num_deserialized_v1_advertisements(
max_num_deserialized_v1_advertisements: u32,
) {
np_ffi_core::common::global_config_set_max_num_deserialized_v1_advertisements(
max_num_deserialized_v1_advertisements,
)
}