diff --git a/librustls/src/acceptor.rs b/librustls/src/acceptor.rs index add6d200..72b159e8 100644 --- a/librustls/src/acceptor.rs +++ b/librustls/src/acceptor.rs @@ -367,7 +367,7 @@ impl rustls_accepted { None => return Default::default(), }; match alpn_iter.nth(i) { - Some(slice_bytes) => slice_bytes.into(), + Some(slice_bytes) => unsafe { rustls_slice_bytes::from(slice_bytes).into_static() }, None => rustls_slice_bytes::default(), } } @@ -539,6 +539,7 @@ mod tests { use crate::certificate::rustls_certified_key; use crate::client::{rustls_client_config, rustls_client_config_builder}; + use crate::ffi::try_slice; use crate::server::rustls_server_config_builder; use crate::verifier::rustls_server_cert_verifier; @@ -574,7 +575,7 @@ mod tests { out_n: *mut size_t, ) -> rustls_io_result { let vecdeq: *mut VecDeque = userdata as *mut _; - let buf = slice::from_raw_parts(buf, n); + let buf = try_slice!(buf, n); (*vecdeq).extend(buf); *out_n = n; rustls_io_result(0) @@ -765,7 +766,7 @@ mod tests { // Sort to ensure consistent comparison signature_schemes.sort(); - #[cfg_attr(not(feature = "aws-lc-rs"), allow(unused_mut))] + #[cfg_attr(not(feature = "aws-lc-rs"), expect(unused_mut))] let mut expected_schemes = vec![ SignatureScheme::RSA_PKCS1_SHA256, SignatureScheme::ECDSA_NISTP256_SHA256, diff --git a/librustls/src/certificate.rs b/librustls/src/certificate.rs index de032efb..696f21c6 100644 --- a/librustls/src/certificate.rs +++ b/librustls/src/certificate.rs @@ -1,7 +1,5 @@ -use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::{NonNull, null}; -use std::slice; use libc::{c_char, size_t}; use rustls::RootCertStore; @@ -12,9 +10,9 @@ use rustls::sign::CertifiedKey; use crate::crypto_provider::{self, rustls_signing_key}; use crate::error::{map_error, rustls_result}; use crate::ffi::{ - Castable, OwnershipArc, OwnershipBox, OwnershipRef, free_arc, free_box, set_arc_mut_ptr, - to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, try_mut_from_ptr, try_ref_from_ptr, - try_ref_from_ptr_ptr, try_slice, try_take, + Castable, OwnershipArc, OwnershipBox, OwnershipRef, bound_ref, free_arc, free_box, + set_arc_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, try_cstr, + try_mut_from_ptr, try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, try_take, }; use crate::panic::ffi_panic_boundary; use crate::rslice::rustls_slice_bytes; @@ -229,19 +227,11 @@ impl rustls_certified_key { cloned_key_out: *mut *const rustls_certified_key, ) -> rustls_result { ffi_panic_boundary! { - let cloned_key_out = unsafe { - match cloned_key_out.as_mut() { - Some(c) => c, - None => return rustls_result::NullParameter, - } - }; + let cloned_key_out = try_ref_from_ptr_ptr!(cloned_key_out); let certified_key = try_ref_from_ptr!(certified_key); let mut new_key = certified_key.clone(); - new_key.ocsp = match NonNull::new(ocsp_response.cast_mut()) { - Some(ocsp_response) => { - let ocsp_slice = unsafe { ocsp_response.as_ref() }; - Some(Vec::from(try_slice!(ocsp_slice.data, ocsp_slice.len))) - } + new_key.ocsp = match unsafe { bound_ref(&ocsp_response) } { + Some(ocsp) => Some(Vec::from(try_slice!(ocsp.data, ocsp.len))), None => None, }; *cloned_key_out = to_arc_const_ptr(new_key); @@ -386,11 +376,7 @@ impl rustls_root_cert_store_builder { Some(b) => b, }; - let Some(filename) = NonNull::new(filename.cast_mut()) else { - return rustls_result::NullParameter; - }; - let filename = unsafe { CStr::from_ptr(filename.as_ptr()) }; - + let filename = try_cstr!(filename); let filename = filename.to_bytes(); let filename = match std::str::from_utf8(filename) { Ok(s) => s, diff --git a/librustls/src/client.rs b/librustls/src/client.rs index c6951c2c..bb559f8a 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -1,7 +1,4 @@ -use std::ffi::CStr; use std::fmt::{Debug, Formatter}; -use std::ptr::NonNull; -use std::slice; use std::sync::Arc; use libc::{c_char, size_t}; @@ -20,8 +17,8 @@ use crate::crypto_provider::{self, rustls_crypto_provider, rustls_hpke}; use crate::error::{self, map_error, rustls_result}; use crate::ffi::{ Castable, OwnershipArc, OwnershipBox, free_arc, free_box, set_arc_mut_ptr, set_boxed_mut_ptr, - to_boxed_mut_ptr, try_box_from_ptr, try_clone_arc, try_mut_from_ptr, try_mut_from_ptr_ptr, - try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, + to_boxed_mut_ptr, try_box_from_ptr, try_clone_arc, try_cstr, try_mut_from_ptr, + try_mut_from_ptr_ptr, try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, }; use crate::keylog::{CallbackKeyLog, rustls_keylog_log_callback, rustls_keylog_will_log_callback}; use crate::panic::ffi_panic_boundary; @@ -621,7 +618,6 @@ impl rustls_client_config_builder { /// /// server_name can contain a hostname, an IPv4 address in textual form, or an /// IPv6 address in textual form. -#[allow(non_camel_case_types)] #[repr(C)] pub struct rustls_verify_server_cert_params<'a> { pub end_entity_cert_der: rustls_slice_bytes<'a>, @@ -633,7 +629,7 @@ pub struct rustls_verify_server_cert_params<'a> { /// User-provided input to a custom certificate verifier callback. /// /// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`. -#[allow(non_camel_case_types)] +#[expect(non_camel_case_types)] pub type rustls_verify_server_cert_user_data = *mut libc::c_void; /// A callback that is invoked to verify a server certificate. @@ -641,7 +637,7 @@ pub type rustls_verify_server_cert_user_data = *mut libc::c_void; // > Option c_int> is a correct way to represent a // > nullable function pointer using the C ABI (corresponding to the C type int (*)(int)). // So we use Option<...> here. This is the type that is passed from C code. -#[allow(non_camel_case_types)] +#[expect(non_camel_case_types)] pub type rustls_verify_server_cert_callback = Option< unsafe extern "C" fn( userdata: rustls_verify_server_cert_user_data, @@ -810,6 +806,8 @@ impl rustls_client_config { /// point at a valid `rustls_connection`. The caller now owns the `rustls_connection` /// and must call `rustls_connection_free` when done with it. /// + /// This function copies the contents of `server_name`. + /// /// Uses the `rustls_client_config` to determine ALPN protocol support. Prefer /// `rustls_client_connection_new_alpn` to customize this per-connection. /// @@ -845,6 +843,8 @@ impl rustls_client_config { /// point at a valid `rustls_connection`. The caller now owns the `rustls_connection` /// and must call `rustls_connection_free` when done with it. /// + /// This function copies the contents of `server_name`. + /// /// If this returns an error code, the memory pointed to by `conn_out` remains /// unchanged. /// @@ -890,16 +890,14 @@ impl rustls_client_config { alpn_protocols: Vec>, conn_out: *mut *mut rustls_connection, ) -> rustls_result { - let Some(server_name) = NonNull::new(server_name.cast_mut()) else { - return rustls_result::NullParameter; - }; - let server_name = unsafe { CStr::from_ptr(server_name.as_ptr()) }; + let server_name = try_cstr!(server_name); let Ok(server_name) = server_name.to_str() else { return rustls_result::InvalidDnsNameError; }; - let Ok(server_name) = server_name.try_into() else { + let Ok(server_name) = ServerName::try_from(server_name) else { return rustls_result::InvalidDnsNameError; }; + let server_name = server_name.to_owned(); set_boxed_mut_ptr( try_mut_from_ptr_ptr!(conn_out), diff --git a/librustls/src/connection.rs b/librustls/src/connection.rs index 26c77a15..c9d34dff 100644 --- a/librustls/src/connection.rs +++ b/librustls/src/connection.rs @@ -1,6 +1,6 @@ use std::io::{ErrorKind, Read, Write}; -use std::{ffi::c_void, ptr::NonNull, ptr::null}; -use std::{ptr::null_mut, slice}; +use std::ptr::{NonNull, null_mut}; +use std::{ffi::c_void, ptr::null}; use libc::{EINVAL, EIO, size_t}; use rustls::CipherSuite::TLS_NULL_WITH_NULL_NULL; @@ -46,7 +46,6 @@ impl Connection { } } - #[allow(dead_code)] pub(crate) fn as_client(&self) -> Option<&ClientConnection> { match &self.conn { rustls::Connection::Client(c) => Some(c), @@ -61,7 +60,7 @@ impl Connection { } } - #[allow(dead_code)] + #[expect(dead_code)] pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientConnection> { match &mut self.conn { rustls::Connection::Client(c) => Some(c), @@ -69,7 +68,7 @@ impl Connection { } } - #[allow(dead_code)] + #[expect(dead_code)] pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerConnection> { match &mut self.conn { rustls::Connection::Server(s) => Some(s), @@ -564,16 +563,13 @@ impl rustls_connection { ) -> rustls_result { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); - let Some(buf) = NonNull::new(buf) else { - return rustls_result::NullParameter; - }; let Some(out_n) = NonNull::new(out_n) else { return rustls_result::NullParameter; }; // Safety: the memory pointed at by buf must be initialized // (required by documentation of this function). - let read_buf = try_slice_mut!(buf.as_ptr(), count); + let read_buf = try_slice_mut!(buf, count); let n_read = match conn.reader().read(read_buf) { Ok(n) => n, @@ -615,11 +611,10 @@ impl rustls_connection { ) -> rustls_result { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); - let (Some(buf), Some(out_n)) = (NonNull::new(buf), NonNull::new(out_n)) else { + let Some(out_n) = NonNull::new(out_n) else { return rustls_result::NullParameter; }; - let mut read_buf: std::io::BorrowedBuf<'_, u8> = - try_slice_mut!(buf.as_ptr(), count).into(); + let mut read_buf: std::io::BorrowedBuf<'_, u8> = try_slice_mut!(buf, count).into(); let n_read = match conn.reader().read_buf(read_buf.unfilled()) { Ok(()) => read_buf.filled().len(), diff --git a/librustls/src/crypto_provider.rs b/librustls/src/crypto_provider.rs index b45ecd08..48f5e500 100644 --- a/librustls/src/crypto_provider.rs +++ b/librustls/src/crypto_provider.rs @@ -1,4 +1,3 @@ -use std::slice; use std::sync::Arc; use libc::size_t; @@ -531,7 +530,7 @@ impl Hpke { pub(crate) fn grease_public_key( &self, provider: &CryptoProvider, - ) -> Option<(&dyn hpke::Hpke, hpke::HpkePublicKey)> { + ) -> Option<(&'static dyn hpke::Hpke, hpke::HpkePublicKey)> { let num_suites = self.suites.len(); if num_suites == 0 { return None; diff --git a/librustls/src/error.rs b/librustls/src/error.rs index a6918f7d..22a43ab6 100644 --- a/librustls/src/error.rs +++ b/librustls/src/error.rs @@ -42,7 +42,6 @@ macro_rules! ResultFromU32 { } /// Numeric error codes returned from rustls-ffi API functions. -#[allow(dead_code)] #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, ResultFromU32!)] pub enum rustls_result { diff --git a/librustls/src/ffi.rs b/librustls/src/ffi.rs index 48fd2641..aef89c36 100644 --- a/librustls/src/ffi.rs +++ b/librustls/src/ffi.rs @@ -1,6 +1,7 @@ -use std::mem; +use std::ffi::{CStr, c_char}; use std::ptr::NonNull; use std::sync::Arc; +use std::{mem, slice}; /// Used to mark that pointer to a [`Castable`]'s underlying `Castable::RustType` is provided /// to C code as a pointer to a `Box`. @@ -217,11 +218,14 @@ where /// /// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has /// specified a cast type equal to [`OwnershipBox`]. -pub(crate) fn try_from_mut<'a, C>(from: *mut C) -> Option<&'a mut C::RustType> +/// +/// The result borrows `from`, so it cannot outlive the local holding the pointer. +#[allow(clippy::mut_from_ref)] +pub(crate) fn try_from_mut(from: &*mut C) -> Option<&mut C::RustType> where C: Castable, { - unsafe { cast_mut_ptr(from).as_mut() } + unsafe { cast_mut_ptr(*from).as_mut() } } /// If the provided pointer to a [`Castable`] is non-null, convert it to a mutable reference using @@ -229,7 +233,7 @@ where /// (`false`, `0`, `NULL`) based on the context. See [`try_from_mut`] for more information. macro_rules! try_mut_from_ptr { ( $var:ident ) => { - match $crate::ffi::try_from_mut($var) { + match $crate::ffi::try_from_mut(&$var) { Some(c) => c, None => return $crate::panic::NullParameterOrDefault::value(), } @@ -242,8 +246,11 @@ pub(crate) use try_mut_from_ptr; /// the mutable pointer to the [`Castable::RustType`]. /// /// Does nothing, returning `None`, when passed `NULL`. -pub(crate) fn try_from_mut_mut<'a, C: Castable>(from: *mut *mut C) -> Option<&'a mut *mut C> { - let mut from = NonNull::new(from)?; +/// +/// The result borrows `from`, so it cannot outlive the local holding the pointer. +#[allow(clippy::mut_from_ref)] +pub(crate) fn try_from_mut_mut(from: &*mut *mut C) -> Option<&mut *mut C> { + let mut from = NonNull::new(*from)?; unsafe { Some(from.as_mut()) } } @@ -253,7 +260,7 @@ pub(crate) fn try_from_mut_mut<'a, C: Castable>(from: *mut *mut C) -> Option<&'a /// context. See [`try_from_mut_mut`] for more information. macro_rules! try_mut_from_ptr_ptr { ( $var:ident ) => { - match $crate::ffi::try_from_mut_mut($var) { + match $crate::ffi::try_from_mut_mut(&$var) { Some(c) => c, None => return $crate::panic::NullParameterOrDefault::value(), } @@ -268,11 +275,13 @@ pub(crate) use try_mut_from_ptr_ptr; /// Does nothing, returning `None` when passed `NULL`. Can be used with `Castable`'s that /// specify a cast type of [`OwnershipArc`] as well as `Castable`'s that specify /// a cast type of [`OwnershipBox`]. -pub(crate) fn try_from<'a, C, O>(from: *const C) -> Option<&'a C::RustType> +/// +/// The result borrows `from`, so it cannot outlive the local holding the pointer. +pub(crate) fn try_from(from: &*const C) -> Option<&C::RustType> where C: Castable, { - unsafe { cast_const_ptr(from).as_ref() } + unsafe { cast_const_ptr(*from).as_ref() } } /// If the provided pointer to a [`Castable`] is non-null, convert it to a reference using @@ -282,7 +291,7 @@ where /// See [`try_from`] for more information. macro_rules! try_ref_from_ptr { ( $var:ident ) => { - match $crate::ffi::try_from($var) { + match $crate::ffi::try_from(&$var) { Some(c) => c, None => return $crate::panic::NullParameterOrDefault::value(), } @@ -295,11 +304,14 @@ pub(crate) use try_ref_from_ptr; /// const pointer to the underlying [`Castable::RustType`]. /// /// Does nothing, returning `None` when passed `NULL`. -pub(crate) fn try_from_ptr<'a, C>(from: *mut *const C) -> Option<&'a mut *const C> +/// +/// The result borrows `from`, so it cannot outlive the local holding the pointer. +#[allow(clippy::mut_from_ref)] +pub(crate) fn try_from_ptr(from: &*mut *const C) -> Option<&mut *const C> where C: Castable, { - let mut from = NonNull::new(from)?; + let mut from = NonNull::new(*from)?; unsafe { Some(from.as_mut()) } } @@ -311,7 +323,7 @@ where /// See [`try_from_ptr`] for more information. macro_rules! try_ref_from_ptr_ptr { ( $var:ident ) => { - match $crate::ffi::try_from_ptr($var) { + match $crate::ffi::try_from_ptr(&$var) { Some(c) => c, None => return $crate::panic::NullParameterOrDefault::value(), } @@ -364,8 +376,8 @@ pub(crate) use try_box_from_ptr; macro_rules! try_slice { ( $ptr:expr, $count:expr ) => { - match ::std::ptr::NonNull::new($ptr.cast_mut()) { - Some(ptr) => unsafe { slice::from_raw_parts(ptr.as_ptr().cast_const(), $count) }, + match unsafe { $crate::ffi::bound_slice(&$ptr, $count) } { + Some(s) => s, None => return $crate::panic::NullParameterOrDefault::value(), } }; @@ -375,8 +387,8 @@ pub(crate) use try_slice; macro_rules! try_slice_mut { ( $ptr:expr, $count:expr ) => { - match ::std::ptr::NonNull::new($ptr) { - Some(ptr) => unsafe { slice::from_raw_parts_mut(ptr.as_ptr(), $count) }, + match unsafe { $crate::ffi::bound_slice_mut(&$ptr, $count) } { + Some(s) => s, None => return $crate::panic::NullParameterOrDefault::value(), } }; @@ -407,3 +419,62 @@ macro_rules! try_take { } pub(crate) use try_take; + +/// If `ptr` is non-null, dereference it; otherwise return `None`. The result borrows +/// `ptr`, so it cannot outlive the local holding the pointer. +/// +/// # Safety +/// +/// If `*ptr` is non-null, it must be aligned and point to a valid `T`. +pub(crate) unsafe fn bound_ref(ptr: &*const T) -> Option<&T> { + let ptr = NonNull::new(ptr.cast_mut())?; + unsafe { Some(ptr.as_ref()) } +} + +/// Like [`slice::from_raw_parts`], but returns `None` if `ptr` is `NULL`, and the +/// result borrows `ptr`, so it cannot outlive the local holding the pointer. +/// +/// # Safety +/// +/// As for [`slice::from_raw_parts`], if `ptr` is non-null. +pub(crate) unsafe fn bound_slice(ptr: &*const T, len: usize) -> Option<&[T]> { + let ptr = NonNull::new(ptr.cast_mut())?; + unsafe { Some(slice::from_raw_parts(ptr.as_ptr().cast_const(), len)) } +} + +/// Like [`slice::from_raw_parts_mut`], but returns `None` if `ptr` is `NULL`, and the +/// result borrows `ptr`, so it cannot outlive the local holding the pointer. +/// +/// # Safety +/// +/// As for [`slice::from_raw_parts_mut`], if `ptr` is non-null. +#[expect(clippy::mut_from_ref)] +pub(crate) unsafe fn bound_slice_mut(ptr: &*mut T, len: usize) -> Option<&mut [T]> { + let ptr = NonNull::new(*ptr)?; + unsafe { Some(slice::from_raw_parts_mut(ptr.as_ptr(), len)) } +} + +/// Like [`CStr::from_ptr`], but returns `None` if `ptr` is `NULL`, and the result +/// borrows `ptr`, so it cannot outlive the local holding the pointer. +/// +/// # Safety +/// +/// As for [`CStr::from_ptr`], if `ptr` is non-null. +pub(crate) unsafe fn bound_cstr(ptr: &*const c_char) -> Option<&CStr> { + let ptr = NonNull::new(ptr.cast_mut())?; + unsafe { Some(CStr::from_ptr(ptr.as_ptr())) } +} + +/// If the provided C string pointer is non-null, convert it to a `&CStr` using +/// [`bound_cstr`]. Otherwise, return [`rustls_result::NullParameter`], or an appropriate +/// default (`false`, `0`, `NULL`) based on the context. +macro_rules! try_cstr { + ( $var:ident ) => { + match unsafe { $crate::ffi::bound_cstr(&$var) } { + Some(s) => s, + None => return $crate::panic::NullParameterOrDefault::value(), + } + }; +} + +pub(crate) use try_cstr; diff --git a/librustls/src/lib.rs b/librustls/src/lib.rs index 5960ef77..5b2efb40 100644 --- a/librustls/src/lib.rs +++ b/librustls/src/lib.rs @@ -1,10 +1,6 @@ #![crate_type = "staticlib"] -#![allow(non_camel_case_types)] -// TODO(XXX): Remove `renamed_and_removed_lints` once stable renames `thread_local_initializer_can_be_made_const` -#![allow(renamed_and_removed_lints)] -#![allow(clippy::not_unsafe_ptr_arg_deref)] -// TODO(#333): Fix this clippy warning. -#![allow(clippy::arc_with_non_send_sync)] +#![expect(non_camel_case_types)] +#![expect(clippy::not_unsafe_ptr_arg_deref)] #![cfg_attr(feature = "read_buf", feature(read_buf))] #![cfg_attr(feature = "read_buf", feature(core_io_borrowed_buf))] diff --git a/librustls/src/log.rs b/librustls/src/log.rs index 7968acde..67b7e53e 100644 --- a/librustls/src/log.rs +++ b/librustls/src/log.rs @@ -72,6 +72,6 @@ pub struct rustls_log_params<'a> { } /// A callback that is invoked for messages logged by rustls. -#[allow(non_camel_case_types)] +#[expect(non_camel_case_types)] pub type rustls_log_callback = Option; diff --git a/librustls/src/rslice.rs b/librustls/src/rslice.rs index 0c4b49ed..1154e6e6 100644 --- a/librustls/src/rslice.rs +++ b/librustls/src/rslice.rs @@ -22,6 +22,13 @@ pub struct rustls_slice_bytes<'a> { phantom: PhantomData<&'a [u8]>, } +impl rustls_slice_bytes<'_> { + /// Erase the lifetime; see `rustls_str::into_static` for the safety contract. + pub(crate) unsafe fn into_static(self) -> rustls_slice_bytes<'static> { + unsafe { std::mem::transmute(self) } + } +} + impl<'a> Default for rustls_slice_bytes<'a> { fn default() -> rustls_slice_bytes<'a> { Self { diff --git a/librustls/src/rustls.h b/librustls/src/rustls.h index 08175870..61625c49 100644 --- a/librustls/src/rustls.h +++ b/librustls/src/rustls.h @@ -1652,6 +1652,8 @@ void rustls_client_config_free(const struct rustls_client_config *config); * point at a valid `rustls_connection`. The caller now owns the `rustls_connection` * and must call `rustls_connection_free` when done with it. * + * This function copies the contents of `server_name`. + * * Uses the `rustls_client_config` to determine ALPN protocol support. Prefer * `rustls_client_connection_new_alpn` to customize this per-connection. * @@ -1677,6 +1679,8 @@ rustls_result rustls_client_connection_new(const struct rustls_client_config *co * point at a valid `rustls_connection`. The caller now owns the `rustls_connection` * and must call `rustls_connection_free` when done with it. * + * This function copies the contents of `server_name`. + * * If this returns an error code, the memory pointed to by `conn_out` remains * unchanged. * diff --git a/librustls/src/server.rs b/librustls/src/server.rs index 376ed083..0201dc21 100644 --- a/librustls/src/server.rs +++ b/librustls/src/server.rs @@ -1,7 +1,5 @@ use std::ffi::c_void; use std::fmt::{Debug, Formatter}; -use std::ptr::NonNull; -use std::slice; use std::sync::Arc; use libc::size_t; @@ -483,11 +481,8 @@ impl rustls_server_config { conn_out: *mut *mut rustls_connection, ) -> rustls_result { ffi_panic_boundary! { - let Some(mut conn_out) = NonNull::new(conn_out) else { - return rustls_result::NullParameter; - }; let config = try_clone_arc!(config); - let conn_out = unsafe { conn_out.as_mut() }; + let conn_out = try_mut_from_ptr_ptr!(conn_out); let server_connection = match ServerConnection::new(config) { Ok(sc) => sc, @@ -772,16 +767,12 @@ pub extern "C" fn rustls_client_hello_select_certified_key( hello.signature_schemes.data, hello.signature_schemes.len )); - let Some(out_key) = NonNull::new(out_key) else { - return rustls_result::NullParameter; - }; + let out_key = try_ref_from_ptr_ptr!(out_key); let keys_ptrs = try_slice!(certified_keys, certified_keys_len); for &key_ptr in keys_ptrs { let key_ref = try_ref_from_ptr!(key_ptr); if key_ref.key.choose_scheme(&schemes).is_some() { - unsafe { - out_key.write(key_ptr); - } + *out_key = key_ptr; return rustls_result::Ok; } } diff --git a/librustls/src/userdata.rs b/librustls/src/userdata.rs index 017f6c58..63ee98f6 100644 --- a/librustls/src/userdata.rs +++ b/librustls/src/userdata.rs @@ -15,9 +15,7 @@ use crate::log::rustls_log_callback; // Rust code, we model these thread locals as a stack, so we can always // restore the previous version. thread_local! { - // TODO(XXX): Remove 'thread_local_initializer_can_be_made_const' in the future - // once stable has renamed. - #[allow(clippy::thread_local_initializer_can_be_made_const, clippy::missing_const_for_thread_local)] + #[expect(clippy::missing_const_for_thread_local)] pub(crate) static USERDATA: RefCell> = RefCell::new(Vec::new()); } diff --git a/librustls/src/verifier.rs b/librustls/src/verifier.rs index 0922386c..b9a32002 100644 --- a/librustls/src/verifier.rs +++ b/librustls/src/verifier.rs @@ -1,4 +1,3 @@ -use std::slice; use std::sync::Arc; use libc::size_t; diff --git a/librustls/tests/client.c b/librustls/tests/client.c index 1d1ad1b1..4c17e182 100644 --- a/librustls/tests/client.c +++ b/librustls/tests/client.c @@ -645,9 +645,17 @@ demo_client_connect(const demo_client_request_options *options) LOG_SIMPLE("socket connected"); demo_conn->sockfd = sockfd; - // Construct the rustls request with the client config. + // Construct the rustls request with the client config. Use a temporary copy + // of the hostname to ensure the connection does not retain the caller's + // server_name buffer after rustls_client_connection_new returns. + char *server_name = strdup(options->hostname); + if(server_name == NULL) { + perror("client: server_name strdup"); + goto cleanup; + } const rustls_result rr = rustls_client_connection_new( - options->tls_config, options->hostname, &demo_conn->rconn); + options->tls_config, server_name, &demo_conn->rconn); + free(server_name); if(rr != RUSTLS_RESULT_OK) { print_error("client_connection_new", rr); goto cleanup; diff --git a/librustls/tests/client_server.rs b/librustls/tests/client_server.rs index e726b303..4fd641f6 100644 --- a/librustls/tests/client_server.rs +++ b/librustls/tests/client_server.rs @@ -4,6 +4,7 @@ use std::{env, panic, thread}; const HOST: &str = "localhost"; const PORT: &str = "8443"; +const VALGRIND_ERROR_EXIT_CODE: &str = "--error-exitcode=1"; #[test] #[ignore] // This test requires the client & server binaries be present. @@ -256,7 +257,7 @@ impl ClientTest { None => (client_binary.as_str(), args), Some(valgrind) => ( valgrind.as_str(), - [vec![client_binary.as_str()], args].concat(), + [vec![VALGRIND_ERROR_EXIT_CODE, client_binary.as_str()], args].concat(), ), }; let result = Command::new(program) @@ -289,7 +290,7 @@ impl ServerOptions { None => (server_binary.as_str(), args), Some(valgrind) => ( valgrind.as_str(), - [vec![server_binary.as_str()], args].concat(), + [vec![VALGRIND_ERROR_EXIT_CODE, server_binary.as_str()], args].concat(), ), }; Command::new(program) diff --git a/website/static/api.json b/website/static/api.json index 07d29620..8282c5a3 100644 --- a/website/static/api.json +++ b/website/static/api.json @@ -604,7 +604,7 @@ }, { "anchor": "rustls-client-connection-new", - "comment": "Create a new client [`rustls_connection`](#rustls-connection).\n\n If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to\n point at a valid [`rustls_connection`](#rustls-connection). The caller now owns the [`rustls_connection`](#rustls-connection)\n and must call [`rustls_connection_free`](#rustls-connection-free) when done with it.\n\n Uses the [`rustls_client_config`](#rustls-client-config) to determine ALPN protocol support. Prefer\n [`rustls_client_connection_new_alpn`](#rustls-client-connection-new-alpn) to customize this per-connection.\n\n If this returns an error code, the memory pointed to by `conn_out` remains\n unchanged.\n\n The `server_name` parameter can contain a hostname or an IP address in\n textual form (IPv4 or IPv6). This function will return an error if it\n cannot be parsed as one of those types.", + "comment": "Create a new client [`rustls_connection`](#rustls-connection).\n\n If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to\n point at a valid [`rustls_connection`](#rustls-connection). The caller now owns the [`rustls_connection`](#rustls-connection)\n and must call [`rustls_connection_free`](#rustls-connection-free) when done with it.\n\n This function copies the contents of `server_name`.\n\n Uses the [`rustls_client_config`](#rustls-client-config) to determine ALPN protocol support. Prefer\n [`rustls_client_connection_new_alpn`](#rustls-client-connection-new-alpn) to customize this per-connection.\n\n If this returns an error code, the memory pointed to by `conn_out` remains\n unchanged.\n\n The `server_name` parameter can contain a hostname or an IP address in\n textual form (IPv4 or IPv6). This function will return an error if it\n cannot be parsed as one of those types.", "feature": null, "deprecation": null, "name": "rustls_client_connection_new", @@ -612,7 +612,7 @@ }, { "anchor": "rustls-client-connection-new-alpn", - "comment": "Create a new client [`rustls_connection`](#rustls-connection) with custom ALPN protocols.\n\n Operates the same as [`rustls_client_connection_new`](#rustls-client-connection-new), but allows specifying\n custom per-connection ALPN protocols instead of inheriting ALPN protocols\n from the `rustls_clinet_config`.\n\n If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to\n point at a valid [`rustls_connection`](#rustls-connection). The caller now owns the [`rustls_connection`](#rustls-connection)\n and must call [`rustls_connection_free`](#rustls-connection-free) when done with it.\n\n If this returns an error code, the memory pointed to by `conn_out` remains\n unchanged.\n\n The `server_name` parameter can contain a hostname or an IP address in\n textual form (IPv4 or IPv6). This function will return an error if it\n cannot be parsed as one of those types.\n\n `alpn_protocols` must point to a buffer of [`rustls_slice_bytes`](#rustls-slice-bytes) (built by the caller)\n with `alpn_protocols_len` elements. Each element of the buffer must be a [`rustls_slice_bytes`](#rustls-slice-bytes)\n whose data field points to a single ALPN protocol ID. This function makes a copy of the\n data in `alpn_protocols` and does not retain any pointers, so the caller can free the\n pointed-to memory after calling.\n\n Standard ALPN protocol IDs are defined at\n .", + "comment": "Create a new client [`rustls_connection`](#rustls-connection) with custom ALPN protocols.\n\n Operates the same as [`rustls_client_connection_new`](#rustls-client-connection-new), but allows specifying\n custom per-connection ALPN protocols instead of inheriting ALPN protocols\n from the `rustls_clinet_config`.\n\n If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to\n point at a valid [`rustls_connection`](#rustls-connection). The caller now owns the [`rustls_connection`](#rustls-connection)\n and must call [`rustls_connection_free`](#rustls-connection-free) when done with it.\n\n This function copies the contents of `server_name`.\n\n If this returns an error code, the memory pointed to by `conn_out` remains\n unchanged.\n\n The `server_name` parameter can contain a hostname or an IP address in\n textual form (IPv4 or IPv6). This function will return an error if it\n cannot be parsed as one of those types.\n\n `alpn_protocols` must point to a buffer of [`rustls_slice_bytes`](#rustls-slice-bytes) (built by the caller)\n with `alpn_protocols_len` elements. Each element of the buffer must be a [`rustls_slice_bytes`](#rustls-slice-bytes)\n whose data field points to a single ALPN protocol ID. This function makes a copy of the\n data in `alpn_protocols` and does not retain any pointers, so the caller can free the\n pointed-to memory after calling.\n\n Standard ALPN protocol IDs are defined at\n .", "feature": null, "deprecation": null, "name": "rustls_client_connection_new_alpn",