From e6077eba1d238b89a9ad67cf876ea22880ecd7a2 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 12 Mar 2024 10:44:45 -0700 Subject: [PATCH 1/4] feat: support unaligned allocations from the pool Use unaligned allocations for strings. --- examples/httporigdst.rs | 4 ++-- nginx-sys/src/lib.rs | 4 ++-- src/core/pool.rs | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/examples/httporigdst.rs b/examples/httporigdst.rs index 3592d4a7..37a9bfbc 100644 --- a/examples/httporigdst.rs +++ b/examples/httporigdst.rs @@ -28,7 +28,7 @@ impl Default for NgxHttpOrigDstCtx { impl NgxHttpOrigDstCtx { pub fn save(&mut self, addr: &str, port: in_port_t, pool: &mut core::Pool) -> core::Status { - let addr_data = pool.alloc(addr.len()); + let addr_data = pool.alloc_unaligned(addr.len()); if addr_data.is_null() { return core::Status::NGX_ERROR; } @@ -37,7 +37,7 @@ impl NgxHttpOrigDstCtx { self.orig_dst_addr.data = addr_data as *mut u8; let port_str = port.to_string(); - let port_data = pool.alloc(port_str.len()); + let port_data = pool.alloc_unaligned(port_str.len()); if port_data.is_null() { return core::Status::NGX_ERROR; } diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs index 9b4fcefd..9611cb92 100644 --- a/nginx-sys/src/lib.rs +++ b/nginx-sys/src/lib.rs @@ -57,7 +57,7 @@ pub use bindings::*; /// * `data` - The string slice to convert to a raw pointer. /// /// # Safety -/// This function is marked as unsafe because it involves raw pointer manipulation and direct memory allocation using `ngx_palloc`. +/// This function is marked as unsafe because it involves raw pointer manipulation and direct memory allocation using `ngx_pnalloc`. /// /// # Returns /// A raw pointer (`*mut u_char`) to the allocated memory containing the converted string data. @@ -69,7 +69,7 @@ pub use bindings::*; /// let ptr = str_to_uchar(pool, data); /// ``` pub unsafe fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char { - let ptr: *mut u_char = ngx_palloc(pool, data.len() as _) as _; + let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _; copy_nonoverlapping(data.as_ptr(), ptr, data.len()); ptr } diff --git a/src/core/pool.rs b/src/core/pool.rs index 388a96ad..92c2e04d 100644 --- a/src/core/pool.rs +++ b/src/core/pool.rs @@ -85,6 +85,7 @@ impl Pool { } /// Allocates memory from the pool of the specified size. + /// The resulting pointer is aligned to a platform word size. /// /// Returns a raw pointer to the allocated memory. pub fn alloc(&mut self, size: usize) -> *mut c_void { @@ -92,6 +93,7 @@ impl Pool { } /// Allocates memory for a type from the pool. + /// The resulting pointer is aligned to a platform word size. /// /// Returns a typed pointer to the allocated memory. pub fn alloc_type(&mut self) -> *mut T { @@ -99,6 +101,7 @@ impl Pool { } /// Allocates zeroed memory from the pool of the specified size. + /// The resulting pointer is aligned to a platform word size. /// /// Returns a raw pointer to the allocated memory. pub fn calloc(&mut self, size: usize) -> *mut c_void { @@ -106,12 +109,27 @@ impl Pool { } /// Allocates zeroed memory for a type from the pool. + /// The resulting pointer is aligned to a platform word size. /// /// Returns a typed pointer to the allocated memory. pub fn calloc_type(&mut self) -> *mut T { self.calloc(mem::size_of::()) as *mut T } + /// Allocates unaligned memory from the pool of the specified size. + /// + /// Returns a raw pointer to the allocated memory. + pub fn alloc_unaligned(&mut self, size: usize) -> *mut c_void { + unsafe { ngx_pnalloc(self.0, size) } + } + + /// Allocates unaligned memory for a type from the pool. + /// + /// Returns a typed pointer to the allocated memory. + pub fn alloc_type_unaligned(&mut self) -> *mut T { + self.alloc_unaligned(mem::size_of::()) as *mut T + } + /// Allocates memory for a value of a specified type and adds a cleanup handler to the memory pool. /// /// Returns a typed pointer to the allocated memory if successful, or a null pointer if allocation or cleanup handler addition fails. From b898b81f2a5bcda680ea7fe00a0b9d17aa80dc46 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 15 Jul 2024 13:12:16 -0700 Subject: [PATCH 2/4] fix(sys): crash when getting `&str` from empty `ngx_str_t` A zero-length `ngx_str_t` is still valid, but the data pointer is not guaranteed to be valid or initialized in that case. --- nginx-sys/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs index 9611cb92..2b61ccb1 100644 --- a/nginx-sys/src/lib.rs +++ b/nginx-sys/src/lib.rs @@ -75,19 +75,50 @@ pub unsafe fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char { } impl ngx_str_t { + /// Returns the contents of this `ngx_str_t` as a byte slice. + /// + /// The returned slice will **not** contain the optional nul terminator that `ngx_str_t.data` + /// may have. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + if self.is_empty() { + &[] + } else { + // SAFETY: `ngx_str_t` with non-zero len must contain a valid correctly aligned pointer + unsafe { slice::from_raw_parts(self.data, self.len) } + } + } + + /// Returns the contents of this `ngx_str_t` as a mutable byte slice. + #[inline] + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + if self.is_empty() { + &mut [] + } else { + // SAFETY: `ngx_str_t` with non-zero len must contain a valid correctly aligned pointer + unsafe { slice::from_raw_parts_mut(self.data, self.len) } + } + } + + /// Returns `true` if the string has a length of 0. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + /// Convert the nginx string to a string slice (`&str`). /// /// # Safety /// This function is marked as unsafe because it involves raw pointer manipulation. /// It assumes that the underlying `data` pointer is valid and points to a valid UTF-8 encoded string. /// + /// # Panics + /// This function panics if the `ngx_str_t` is not valid UTF-8. + /// /// # Returns /// A string slice (`&str`) representing the nginx string. pub fn to_str(&self) -> &str { - unsafe { - let slice = slice::from_raw_parts(self.data, self.len); - return std::str::from_utf8(slice).unwrap(); - } + std::str::from_utf8(self.as_bytes()).unwrap() } /// Create an `ngx_str_t` instance from a `String`. From c4c7b691bc8ffc672d4fcc99ade03275a466b3c1 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 15 Jul 2024 17:14:23 -0700 Subject: [PATCH 3/4] feat: allow creating `ngx_str_t` from a byte slice There's no guarantee we'll be working with a valid UTF-8 data. --- nginx-sys/src/lib.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs index 2b61ccb1..4e5537eb 100644 --- a/nginx-sys/src/lib.rs +++ b/nginx-sys/src/lib.rs @@ -49,6 +49,20 @@ mod bindings { #[doc(no_inline)] pub use bindings::*; +/// Convert a byte slice to a raw pointer (`*mut u_char`) allocated in the given nginx memory pool. +/// +/// # Safety +/// +/// The caller must provide a valid pointer to the memory pool. +pub unsafe fn bytes_to_uchar(pool: *mut ngx_pool_t, data: &[u8]) -> Option<*mut u_char> { + let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _; + if ptr.is_null() { + return None; + } + copy_nonoverlapping(data.as_ptr(), ptr, data.len()); + Some(ptr) +} + /// Convert a string slice (`&str`) to a raw pointer (`*mut u_char`) allocated in the given nginx memory pool. /// /// # Arguments @@ -70,6 +84,7 @@ pub use bindings::*; /// ``` pub unsafe fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char { let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _; + debug_assert!(!ptr.is_null()); copy_nonoverlapping(data.as_ptr(), ptr, data.len()); ptr } @@ -121,6 +136,15 @@ impl ngx_str_t { std::str::from_utf8(self.as_bytes()).unwrap() } + /// Create an `ngx_str_t` instance from a byte slice. + /// + /// # Safety + /// + /// The caller must provide a valid pointer to a memory pool. + pub unsafe fn from_bytes(pool: *mut ngx_pool_t, src: &[u8]) -> Option { + bytes_to_uchar(pool, src).map(|data| Self { data, len: src.len() }) + } + /// Create an `ngx_str_t` instance from a `String`. /// /// # Arguments From cb7e21dd9186d6bb78586bf0a4a70a98611e3301 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 21 Oct 2024 13:48:27 -0700 Subject: [PATCH 4/4] fix: drop unnecessary casts from usize to size_t `size_t` is transformed to `usize` by default since bindgen 0.61. As we require much newer version of bindgen, the explicit casts are no longer necessary. --- nginx-sys/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs index 4e5537eb..d56a3aa2 100644 --- a/nginx-sys/src/lib.rs +++ b/nginx-sys/src/lib.rs @@ -162,7 +162,7 @@ impl ngx_str_t { pub unsafe fn from_string(pool: *mut ngx_pool_t, data: String) -> Self { ngx_str_t { data: str_to_uchar(pool, data.as_str()), - len: data.len() as _, + len: data.len(), } } @@ -183,7 +183,7 @@ impl ngx_str_t { pub unsafe fn from_str(pool: *mut ngx_pool_t, data: &str) -> Self { ngx_str_t { data: str_to_uchar(pool, data), - len: data.len() as _, + len: data.len(), } } } @@ -254,9 +254,9 @@ pub unsafe fn add_to_ngx_table( } table.as_mut().map(|table| { table.hash = 1; - table.key.len = key.len() as _; + table.key.len = key.len(); table.key.data = str_to_uchar(pool, key); - table.value.len = value.len() as _; + table.value.len = value.len(); table.value.data = str_to_uchar(pool, value); table.lowcase_key = str_to_uchar(pool, String::from(key).to_ascii_lowercase().as_str()); })