Skip to content

Commit 47c4b1d

Browse files
committed
fix: unsoundness in NgxStr conversions
From implementations for NgxStr allowed to create a reference that lives longer than the original object.
1 parent c94b78c commit 47c4b1d

File tree

1 file changed

+39
-10
lines changed

1 file changed

+39
-10
lines changed

src/core/string.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ macro_rules! ngx_string {
2424
/// Representation of a borrowed [Nginx string].
2525
///
2626
/// [Nginx string]: https://nginx.org/en/docs/dev/development_guide.html#string_overview
27+
#[repr(transparent)]
2728
pub struct NgxStr([u_char]);
2829

2930
impl NgxStr {
@@ -37,7 +38,15 @@ impl NgxStr {
3738
/// to range of bytes of at least `len` bytes, whose content remains valid and doesn't
3839
/// change for the lifetime of the returned `NgxStr`.
3940
pub unsafe fn from_ngx_str<'a>(str: ngx_str_t) -> &'a NgxStr {
40-
str.as_bytes().into()
41+
let bytes: &[u8] = str.as_bytes();
42+
&*(bytes as *const [u8] as *const NgxStr)
43+
}
44+
45+
/// Create an [NgxStr] from a borrowed byte slice.
46+
#[inline]
47+
pub fn from_bytes(bytes: &[u8]) -> &Self {
48+
// SAFETY: An `NgxStr` is identical to a `[u8]` slice, given `u_char` is an alias for `u8`
49+
unsafe { &*(bytes as *const [u8] as *const NgxStr) }
4150
}
4251

4352
/// Access the [`NgxStr`] as a byte slice.
@@ -64,16 +73,15 @@ impl NgxStr {
6473
}
6574
}
6675

67-
impl From<&[u8]> for &NgxStr {
68-
fn from(bytes: &[u8]) -> Self {
69-
// SAFETY: An `NgxStr` is identical to a `[u8]` slice, given `u_char` is an alias for `u8`.
70-
unsafe { &*(bytes as *const [u8] as *const NgxStr) }
76+
impl<'a> From<&'a [u8]> for &'a NgxStr {
77+
fn from(bytes: &'a [u8]) -> Self {
78+
NgxStr::from_bytes(bytes)
7179
}
7280
}
7381

74-
impl From<&str> for &NgxStr {
75-
fn from(s: &str) -> Self {
76-
s.as_bytes().into()
82+
impl<'a> From<&'a str> for &'a NgxStr {
83+
fn from(s: &'a str) -> Self {
84+
NgxStr::from_bytes(s.as_bytes())
7785
}
7886
}
7987

@@ -85,7 +93,28 @@ impl AsRef<[u8]> for NgxStr {
8593

8694
impl Default for &NgxStr {
8795
fn default() -> Self {
88-
// SAFETY: The empty `ngx_str_t` is always a valid Nginx string.
89-
unsafe { NgxStr::from_ngx_str(ngx_str_t::default()) }
96+
NgxStr::from_bytes(&[])
97+
}
98+
}
99+
100+
#[cfg(test)]
101+
mod tests {
102+
extern crate alloc;
103+
104+
use alloc::string::ToString;
105+
106+
use super::*;
107+
108+
#[test]
109+
fn test_lifetimes() {
110+
let a: &NgxStr = "Hello World!".into();
111+
112+
let s = "Hello World!".to_string();
113+
let b: &NgxStr = s.as_bytes().into();
114+
115+
// The compiler should detect that s is borrowed and fail.
116+
// drop(s); // ☢️
117+
118+
assert_eq!(a.0, b.0);
90119
}
91120
}

0 commit comments

Comments
 (0)