Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Don't panic in neqo_crypto::init() #1775

Merged
merged 15 commits into from
Mar 28, 2024
3 changes: 2 additions & 1 deletion neqo-bin/src/bin/client/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub enum ClientError {
IoError(io::Error),
QlogError,
TransportError(neqo_transport::Error),
InternalError,
}

impl From<io::Error> for ClientError {
Expand Down Expand Up @@ -451,7 +452,7 @@ async fn main() -> Res<()> {
neqo_common::log::init(Some(args.verbose.log_level_filter()));
args.update_for_tests();

init();
init().map_err(|_| ClientError::InternalError)?;

let urls_by_origin = args
.urls
Expand Down
2 changes: 1 addition & 1 deletion neqo-bin/src/bin/server/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ async fn main() -> Result<(), io::Error> {
neqo_common::log::init(Some(args.verbose.log_level_filter()));
assert!(!args.key.is_empty(), "Need at least one key");

init_db(args.db.clone());
init_db(args.db.clone()).map_err(|_| io::Error::other("init_db failure"))?;

if let Some(testcase) = args.shared.qns_test.as_ref() {
if args.shared.quic_parameters.quic_version.is_empty() {
Expand Down
54 changes: 30 additions & 24 deletions neqo-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl Drop for NssLoaded {
}
}

static INITIALIZED: OnceLock<NssLoaded> = OnceLock::new();
static INITIALIZED: OnceLock<Res<NssLoaded>> = OnceLock::new();

fn already_initialized() -> bool {
unsafe { nss::NSS_IsInitialized() != 0 }
Expand All @@ -107,24 +107,27 @@ fn version_check() {
/// Initialize NSS. This only executes the initialization routines once, so if there is any chance
/// that
///
/// # Panics
/// # Errors
///
/// When NSS initialization fails.
pub fn init() {
pub fn init() -> Res<()> {
// Set time zero.
time::init();
_ = INITIALIZED.get_or_init(|| {
let res = INITIALIZED.get_or_init(|| {
version_check();
if already_initialized() {
return NssLoaded::External;
return Ok(NssLoaded::External);
}

secstatus_to_res(unsafe { nss::NSS_NoDB_Init(null()) }).expect("NSS_NoDB_Init failed");
secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })
.expect("NSS_SetDomesticPolicy failed");
secstatus_to_res(unsafe { nss::NSS_NoDB_Init(null()) })?;
secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })?;

NssLoaded::NoDb
Ok(NssLoaded::NoDb)
});
match res {
Ok(_) => Ok(()),
Err(e) => Err(e.clone()),
}
}

/// This enables SSLTRACE by calling a simple, harmless function to trigger its
Expand All @@ -141,22 +144,24 @@ fn enable_ssl_trace() {

/// Initialize with a database.
///
/// # Panics
/// # Errors
///
/// If NSS cannot be initialized.
pub fn init_db<P: Into<PathBuf>>(dir: P) {
pub fn init_db<P: Into<PathBuf>>(dir: P) -> Res<()> {
time::init();
_ = INITIALIZED.get_or_init(|| {
let res = INITIALIZED.get_or_init(|| {
version_check();
if already_initialized() {
return NssLoaded::External;
return Ok(NssLoaded::External);
}

let path = dir.into();
assert!(path.is_dir());
let pathstr = path.to_str().expect("path converts to string").to_string();
let dircstr = CString::new(pathstr).unwrap();
let empty = CString::new("").unwrap();
if !path.is_dir() {
return Err(Error::InternalError);
}
let pathstr = path.to_str().ok_or(Error::InternalError)?;
let dircstr = CString::new(pathstr)?;
let empty = CString::new("")?;
secstatus_to_res(unsafe {
nss::NSS_Initialize(
dircstr.as_ptr(),
Expand All @@ -165,21 +170,22 @@ pub fn init_db<P: Into<PathBuf>>(dir: P) {
nss::SECMOD_DB.as_ptr().cast(),
nss::NSS_INIT_READONLY,
)
})
.expect("NSS_Initialize failed");
})?;

secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })
.expect("NSS_SetDomesticPolicy failed");
secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })?;
secstatus_to_res(unsafe {
ssl::SSL_ConfigServerSessionIDCache(1024, 0, 0, dircstr.as_ptr())
})
.expect("SSL_ConfigServerSessionIDCache failed");
})?;

#[cfg(debug_assertions)]
enable_ssl_trace();

NssLoaded::Db
Ok(NssLoaded::Db)
});
match res {
Ok(_) => Ok(()),
Err(e) => Err(e.clone()),
}
}

/// # Panics
Expand Down
2 changes: 1 addition & 1 deletion neqo-crypto/tests/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn init_nodb() {
#[cfg(not(nss_nodb))]
#[test]
fn init_withdb() {
init_db(::test_fixture::NSS_DB_PATH);
let _ = init_db(::test_fixture::NSS_DB_PATH);
assert_initialized();
unsafe {
assert!(nss::NSS_IsInitialized() != 0);
Expand Down
4 changes: 2 additions & 2 deletions neqo-crypto/tests/selfencrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use neqo_crypto::{

#[test]
fn se_create() {
init();
let _ = init();
SelfEncrypt::new(TLS_VERSION_1_3, TLS_AES_128_GCM_SHA256).expect("constructor works");
}

const PLAINTEXT: &[u8] = b"PLAINTEXT";
const AAD: &[u8] = b"AAD";

fn sealed() -> (SelfEncrypt, Vec<u8>) {
init();
let _ = init();
let se = SelfEncrypt::new(TLS_VERSION_1_3, TLS_AES_128_GCM_SHA256).unwrap();
let sealed = se.seal(AAD, PLAINTEXT).expect("sealing works");
(se, sealed)
Expand Down
2 changes: 1 addition & 1 deletion test-fixture/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub const NSS_DB_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/db");
/// fixture function that depends on setup. Other functions in the fixture
/// that depend on this setup call the function for you.
pub fn fixture_init() {
init_db(NSS_DB_PATH);
let _ = init_db(NSS_DB_PATH);
}

// This needs to be > 2ms to avoid it being rounded to zero.
Expand Down