1 // This is used when either vtab or modern-sqlite is on. Different methods are
2 // used in each feature. Avoid having to track this for each function. We will
3 // still warn for anything that's not used by either, though.
4 #![cfg_attr(not(feature = "vtab"), allow(dead_code))]
5 use crate::ffi;
6 use std::marker::PhantomData;
7 use std::os::raw::{c_char, c_int};
8 use std::ptr::NonNull;
9 
10 /// A string we own that's allocated on the SQLite heap. Automatically calls
11 /// `sqlite3_free` when dropped, unless `into_raw` (or `into_inner`) is called
12 /// on it. If constructed from a rust string, `sqlite3_malloc` is used.
13 ///
14 /// It has identical representation to a nonnull `*mut c_char`, so you can use
15 /// it transparently as one. It's nonnull, so Option<SqliteMallocString> can be
16 /// used for nullable ones (it's still just one pointer).
17 ///
18 /// Most strings shouldn't use this! Only places where the string needs to be
19 /// freed with `sqlite3_free`. This includes `sqlite3_extended_sql` results,
20 /// some error message pointers... Note that misuse is extremely dangerous!
21 ///
22 /// Note that this is *not* a lossless interface. Incoming strings with internal
23 /// NULs are modified, and outgoing strings which are non-UTF8 are modified.
24 /// This seems unavoidable -- it tries very hard to not panic.
25 #[repr(transparent)]
26 pub(crate) struct SqliteMallocString {
27     ptr: NonNull<c_char>,
28     _boo: PhantomData<Box<[c_char]>>,
29 }
30 // This is owned data for a primitive type, and thus it's safe to implement
31 // these. That said, nothing needs them, and they make things easier to misuse.
32 
33 // unsafe impl Send for SqliteMallocString {}
34 // unsafe impl Sync for SqliteMallocString {}
35 
36 impl SqliteMallocString {
37     /// SAFETY: Caller must be certain that `m` a nul-terminated c string
38     /// allocated by `sqlite3_malloc`, and that SQLite expects us to free it!
39     #[inline]
from_raw_nonnull(ptr: NonNull<c_char>) -> Self40     pub(crate) unsafe fn from_raw_nonnull(ptr: NonNull<c_char>) -> Self {
41         Self {
42             ptr,
43             _boo: PhantomData,
44         }
45     }
46 
47     /// SAFETY: Caller must be certain that `m` a nul-terminated c string
48     /// allocated by `sqlite3_malloc`, and that SQLite expects us to free it!
49     #[inline]
from_raw(ptr: *mut c_char) -> Option<Self>50     pub(crate) unsafe fn from_raw(ptr: *mut c_char) -> Option<Self> {
51         NonNull::new(ptr).map(|p| Self::from_raw_nonnull(p))
52     }
53 
54     /// Get the pointer behind `self`. After this is called, we no longer manage
55     /// it.
56     #[inline]
into_inner(self) -> NonNull<c_char>57     pub(crate) fn into_inner(self) -> NonNull<c_char> {
58         let p = self.ptr;
59         std::mem::forget(self);
60         p
61     }
62 
63     /// Get the pointer behind `self`. After this is called, we no longer manage
64     /// it.
65     #[inline]
into_raw(self) -> *mut c_char66     pub(crate) fn into_raw(self) -> *mut c_char {
67         self.into_inner().as_ptr()
68     }
69 
70     /// Borrow the pointer behind `self`. We still manage it when this function
71     /// returns. If you want to relinquish ownership, use `into_raw`.
72     #[inline]
as_ptr(&self) -> *const c_char73     pub(crate) fn as_ptr(&self) -> *const c_char {
74         self.ptr.as_ptr()
75     }
76 
77     #[inline]
as_cstr(&self) -> &std::ffi::CStr78     pub(crate) fn as_cstr(&self) -> &std::ffi::CStr {
79         unsafe { std::ffi::CStr::from_ptr(self.as_ptr()) }
80     }
81 
82     #[inline]
to_string_lossy(&self) -> std::borrow::Cow<'_, str>83     pub(crate) fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> {
84         self.as_cstr().to_string_lossy()
85     }
86 
87     /// Convert `s` into a SQLite string.
88     ///
89     /// This should almost never be done except for cases like error messages or
90     /// other strings that SQLite frees.
91     ///
92     /// If `s` contains internal NULs, we'll replace them with
93     /// `NUL_REPLACE_CHAR`.
94     ///
95     /// Except for `debug_assert`s which may trigger during testing, this
96     /// function never panics. If we hit integer overflow or the allocation
97     /// fails, we call `handle_alloc_error` which aborts the program after
98     /// calling a global hook.
99     ///
100     /// This means it's safe to use in extern "C" functions even outside of
101     /// `catch_unwind`.
from_str(s: &str) -> Self102     pub(crate) fn from_str(s: &str) -> Self {
103         use std::convert::TryFrom;
104         let s = if s.as_bytes().contains(&0) {
105             std::borrow::Cow::Owned(make_nonnull(s))
106         } else {
107             std::borrow::Cow::Borrowed(s)
108         };
109         debug_assert!(!s.as_bytes().contains(&0));
110         let bytes: &[u8] = s.as_ref().as_bytes();
111         let src_ptr: *const c_char = bytes.as_ptr().cast();
112         let src_len = bytes.len();
113         let maybe_len_plus_1 = s.len().checked_add(1).and_then(|v| c_int::try_from(v).ok());
114         unsafe {
115             let res_ptr = maybe_len_plus_1
116                 .and_then(|len_to_alloc| {
117                     // `>` because we added 1.
118                     debug_assert!(len_to_alloc > 0);
119                     debug_assert_eq!((len_to_alloc - 1) as usize, src_len);
120                     NonNull::new(ffi::sqlite3_malloc(len_to_alloc).cast::<c_char>())
121                 })
122                 .unwrap_or_else(|| {
123                     use std::alloc::{handle_alloc_error, Layout};
124                     // Report via handle_alloc_error so that it can be handled with any
125                     // other allocation errors and properly diagnosed.
126                     //
127                     // This is safe:
128                     // - `align` is never 0
129                     // - `align` is always a power of 2.
130                     // - `size` needs no realignment because it's guaranteed to be aligned
131                     //   (everything is aligned to 1)
132                     // - `size` is also never zero, although this function doesn't actually require
133                     //   it now.
134                     let len = s.len().saturating_add(1).min(isize::MAX as usize);
135                     let layout = Layout::from_size_align_unchecked(len, 1);
136                     // Note: This call does not return.
137                     handle_alloc_error(layout);
138                 });
139             let buf: *mut c_char = res_ptr.as_ptr().cast::<c_char>();
140             src_ptr.copy_to_nonoverlapping(buf, src_len);
141             buf.add(src_len).write(0);
142             debug_assert_eq!(std::ffi::CStr::from_ptr(res_ptr.as_ptr()).to_bytes(), bytes);
143             Self::from_raw_nonnull(res_ptr)
144         }
145     }
146 }
147 
148 const NUL_REPLACE: &str = "␀";
149 
150 #[cold]
make_nonnull(v: &str) -> String151 fn make_nonnull(v: &str) -> String {
152     v.replace('\0', NUL_REPLACE)
153 }
154 
155 impl Drop for SqliteMallocString {
156     #[inline]
drop(&mut self)157     fn drop(&mut self) {
158         unsafe { ffi::sqlite3_free(self.ptr.as_ptr().cast()) };
159     }
160 }
161 
162 impl std::fmt::Debug for SqliteMallocString {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result163     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164         self.to_string_lossy().fmt(f)
165     }
166 }
167 
168 impl std::fmt::Display for SqliteMallocString {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result169     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170         self.to_string_lossy().fmt(f)
171     }
172 }
173 
174 #[cfg(test)]
175 mod test {
176     use super::*;
177     #[test]
test_from_str()178     fn test_from_str() {
179         let to_check = [
180             ("", ""),
181             ("\0", "␀"),
182             ("␀", "␀"),
183             ("\0bar", "␀bar"),
184             ("foo\0bar", "foo␀bar"),
185             ("foo\0", "foo␀"),
186             ("a\0b\0c\0\0d", "a␀b␀c␀␀d"),
187             ("foobar0123", "foobar0123"),
188         ];
189 
190         for &(input, output) in &to_check {
191             let s = SqliteMallocString::from_str(input);
192             assert_eq!(s.to_string_lossy(), output);
193             assert_eq!(s.as_cstr().to_str().unwrap(), output);
194         }
195     }
196 
197     // This will trigger an asan error if into_raw still freed the ptr.
198     #[test]
test_lossy()199     fn test_lossy() {
200         let p = SqliteMallocString::from_str("abcd").into_raw();
201         // Make invalid
202         let s = unsafe {
203             p.cast::<u8>().write(b'\xff');
204             SqliteMallocString::from_raw(p).unwrap()
205         };
206         assert_eq!(s.to_string_lossy().as_ref(), "\u{FFFD}bcd");
207     }
208 
209     // This will trigger an asan error if into_raw still freed the ptr.
210     #[test]
test_into_raw()211     fn test_into_raw() {
212         let mut v = vec![];
213         for i in 0..1000 {
214             v.push(SqliteMallocString::from_str(&i.to_string()).into_raw());
215             v.push(SqliteMallocString::from_str(&format!("abc {i} ��")).into_raw());
216         }
217         unsafe {
218             for (i, s) in v.chunks_mut(2).enumerate() {
219                 let s0 = std::mem::replace(&mut s[0], std::ptr::null_mut());
220                 let s1 = std::mem::replace(&mut s[1], std::ptr::null_mut());
221                 assert_eq!(
222                     std::ffi::CStr::from_ptr(s0).to_str().unwrap(),
223                     &i.to_string()
224                 );
225                 assert_eq!(
226                     std::ffi::CStr::from_ptr(s1).to_str().unwrap(),
227                     &format!("abc {i} ��")
228                 );
229                 let _ = SqliteMallocString::from_raw(s0).unwrap();
230                 let _ = SqliteMallocString::from_raw(s1).unwrap();
231             }
232         }
233     }
234 }
235