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}