mountpoint_s3_crt/
lib.rs

1#![deny(
2    missing_debug_implementations,
3    missing_docs,
4    clippy::undocumented_unsafe_blocks,
5    rustdoc::bare_urls,
6    rustdoc::broken_intra_doc_links
7)]
8
9//! Rust bindings for the AWS Common Runtime.
10
11use mountpoint_s3_crt_sys::*;
12
13pub mod auth;
14pub mod checksums;
15pub mod common;
16pub mod http;
17pub mod io;
18pub mod s3;
19
20use std::ptr::NonNull;
21use std::{ffi::OsStr, os::unix::prelude::OsStrExt};
22
23use crate::common::error::Error;
24
25pub(crate) mod private {
26    /// Seals a trait to prevent clients from implementing it for their own types, since this trait
27    /// is only accessible to this crate.
28    pub trait Sealed {}
29}
30
31pub(crate) trait ToAwsByteCursor {
32    /// SAFETY: the user *must not* mutate the bytes pointed at by this cursor
33    /// Also, the user must be careful that the aws_byte_cursor does not outlive self. When passing
34    /// the aws_byte_cursor to the CRT, make sure that self will live as long as the CRT might
35    /// continue to use that buffer.
36    unsafe fn as_aws_byte_cursor(&self) -> aws_byte_cursor;
37}
38
39impl<S: AsRef<OsStr>> ToAwsByteCursor for S {
40    unsafe fn as_aws_byte_cursor(&self) -> aws_byte_cursor {
41        // SAFETY: See comment on [ToAwsByteCursor::as_aws_byte_cursor].
42        unsafe { self.as_ref().as_bytes().as_aws_byte_cursor() }
43    }
44}
45
46impl ToAwsByteCursor for [u8] {
47    /// SAFETY: See comment on [ToAwsByteCursor::as_aws_byte_cursor].
48    unsafe fn as_aws_byte_cursor(&self) -> aws_byte_cursor {
49        aws_byte_cursor {
50            ptr: self.as_ptr() as *mut _,
51            len: self.len(),
52        }
53    }
54}
55
56/// View an aws_byte_cursor as a slice of bytes.
57/// SAFETY: This function is unsafe because it makes a reference from the raw pointers
58/// inside the aws_byte_cursor. The caller must ensure that the returned slice does not outlive
59/// the bytes pointed to by the cursor, for example, by copying the bytes out.
60pub(crate) unsafe fn aws_byte_cursor_as_slice<'a>(cursor: &aws_byte_cursor) -> &'a [u8] {
61    if cursor.ptr.is_null() {
62        // If the pointer is null, the length must be 0 and we return an empty slice
63        assert_eq!(cursor.len, 0, "length must be 0 for null cursors");
64        &[]
65    } else {
66        // SAFETY: from_raw_parts can't be used on null pointers, even if the length is 0. So we handle
67        // that as a special case above.
68        unsafe { std::slice::from_raw_parts(cursor.ptr, cursor.len) }
69    }
70}
71
72/// Translate the common "return a null pointer on failure" pattern into Results that pull the last
73/// error from the CRT.
74pub(crate) trait CrtError: Sized {
75    type Return;
76
77    /// # Safety
78    /// This must only be used immediately on a pointer returned from the CRT, with no other
79    /// CRT code being run beforehand on the same thread, or else it will return the wrong error.
80    /// In particular, there should not be any `await` point between the return from the CRT
81    /// function and the invocation of this method.
82    unsafe fn ok_or_last_error(self) -> Result<Self::Return, Error>;
83}
84
85impl<T> CrtError for *const T {
86    type Return = NonNull<T>;
87
88    /// # Safety
89    /// This reads a thread local, so the caller must ensure no other CRT code has run on
90    /// the same thread since the error was last set, otherwise the result will be the wrong error.
91    unsafe fn ok_or_last_error(self) -> Result<Self::Return, Error> {
92        NonNull::new(self as *mut T).ok_or_else(|| {
93            // SAFETY: Must be guaranteed by the caller.
94            unsafe { Error::last_error() }
95        })
96    }
97}
98
99impl<T> CrtError for *mut T {
100    type Return = NonNull<T>;
101
102    /// # Safety
103    /// This reads a thread local, so the caller must ensure no other CRT code has run on
104    /// the same thread since the error was last set, otherwise the result will be the wrong error.
105    unsafe fn ok_or_last_error(self) -> Result<Self::Return, Error> {
106        NonNull::new(self).ok_or_else(|| {
107            // SAFETY: Must be guaranteed by the caller.
108            unsafe { Error::last_error() }
109        })
110    }
111}
112
113/// Some CRT functions return an int that is either AWS_OP_SUCCESS or AWS_OP_ERR, and the caller
114/// should use last_error to find out what happened. This simplifies that pattern.
115impl CrtError for i32 {
116    type Return = ();
117
118    /// # Safety
119    /// This reads a thread local, so the caller must ensure no other CRT code has run on
120    /// the same thread since the error was last set, otherwise the result will be the wrong error.
121    unsafe fn ok_or_last_error(self) -> Result<Self::Return, Error> {
122        match self {
123            AWS_OP_SUCCESS => Ok(()),
124            // SAFETY: Must be guaranteed by the caller.
125            AWS_OP_ERR => Err(unsafe { Error::last_error() }),
126            // This case shouldn't happen if used correctly since we should use this on functions
127            // that only return SUCCESS or ERR. But if it does happen, we can attempt to convert the
128            // error code directly, which may or may not work (but at least the Error won't be swallowed).
129            n => Err(common::error::Error::from(n)),
130        }
131    }
132}
133
134#[cfg(test)]
135mod test {
136    use crate::common::rust_log_adapter::RustLogAdapter;
137
138    /// Enable tracing when running unit tests.
139    #[ctor::ctor]
140    fn init_tracing_subscriber() {
141        RustLogAdapter::try_init().expect("unable to install CRT log adapter");
142        tracing_subscriber::fmt::init();
143    }
144
145    #[ctor::ctor]
146    fn init_crt() {
147        crate::io::io_library_init(&crate::common::allocator::Allocator::default());
148        crate::s3::s3_library_init(&crate::common::allocator::Allocator::default());
149    }
150
151    /// Validate that ASan is working across both Rust and the CRT by intentionally provoking a
152    /// use-after-free that crosses the boundary: the allocation is created and freed by Rust, but
153    /// accessed by the CRT. Ignored by default, and run only by ASan in CI.
154    #[test]
155    #[ignore]
156    fn test_asan_working() {
157        use mountpoint_s3_crt_sys::{aws_byte_cursor, aws_byte_cursor_is_valid};
158
159        let heap_cursor = Box::new(aws_byte_cursor {
160            ptr: std::ptr::null_mut(),
161            len: 0,
162        });
163        let heap_ptr = &*heap_cursor as *const aws_byte_cursor as *mut _;
164        drop(heap_cursor);
165
166        // SAFETY: This code isn't safe; it's supposed to test that ASan can catch use-after-free
167        // bugs. `heap_ptr` points to a freed allocation, so this causes a use-after-free that ASan
168        // should catch.
169        let _ = unsafe { aws_byte_cursor_is_valid(heap_ptr) };
170    }
171}