Mercurial > crates > nonstick
comparison src/libpam/memory.rs @ 78:002adfb98c5c
Rename files, reorder structs, remove annoying BorrowedBinaryData type.
This is basically a cleanup change. Also it adds tests.
- Renames the files with Questions and Answers to question and answer.
- Reorders the structs in those files to put the important ones first.
- Removes the BorrowedBinaryData type. It was a bad idea all along.
Instead, we just use (&[u8], u8).
- Adds some tests because I just can't help myself.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Sun, 08 Jun 2025 03:48:40 -0400 |
| parents | 351bdc13005e |
| children | 2128123b9406 |
comparison
equal
deleted
inserted
replaced
| 77:351bdc13005e | 78:002adfb98c5c |
|---|---|
| 1 //! Things for dealing with memory. | 1 //! Things for dealing with memory. |
| 2 | 2 |
| 3 use crate::conv::BorrowedBinaryData; | |
| 4 use crate::Result; | 3 use crate::Result; |
| 5 use crate::{BinaryData, ErrorCode}; | 4 use crate::{BinaryData, ErrorCode}; |
| 6 use std::ffi::{c_char, c_void, CStr, CString}; | 5 use std::ffi::{c_char, CStr, CString}; |
| 7 use std::marker::{PhantomData, PhantomPinned}; | 6 use std::marker::{PhantomData, PhantomPinned}; |
| 8 use std::{ptr, slice}; | 7 use std::{ptr, slice}; |
| 8 | |
| 9 /// Allocates `count` elements to hold `T`. | |
| 10 #[inline] | |
| 11 pub fn calloc<T>(count: usize) -> *mut T { | |
| 12 // SAFETY: it's always safe to allocate! Leaking memory is fun! | |
| 13 unsafe { libc::calloc(count, size_of::<T>()) }.cast() | |
| 14 } | |
| 15 | |
| 16 /// Wrapper for [`libc::free`] to make debugging calls/frees easier. | |
| 17 /// | |
| 18 /// # Safety | |
| 19 /// | |
| 20 /// If you double-free, it's all your fault. | |
| 21 #[inline] | |
| 22 pub unsafe fn free<T>(p: *mut T) { | |
| 23 libc::free(p.cast()) | |
| 24 } | |
| 9 | 25 |
| 10 /// Makes whatever it's in not [`Send`], [`Sync`], or [`Unpin`]. | 26 /// Makes whatever it's in not [`Send`], [`Sync`], or [`Unpin`]. |
| 11 #[repr(C)] | 27 #[repr(C)] |
| 12 #[derive(Debug)] | 28 #[derive(Debug)] |
| 13 pub struct Immovable(pub PhantomData<(*mut u8, PhantomPinned)>); | 29 pub struct Immovable(pub PhantomData<(*mut u8, PhantomPinned)>); |
| 60 Ok(ret) | 76 Ok(ret) |
| 61 } | 77 } |
| 62 | 78 |
| 63 /// Allocates a string with the given contents on the C heap. | 79 /// Allocates a string with the given contents on the C heap. |
| 64 /// | 80 /// |
| 65 /// This is like [`CString::new`](std::ffi::CString::new), but: | 81 /// This is like [`CString::new`], but: |
| 66 /// | 82 /// |
| 67 /// - it allocates data on the C heap with [`libc::malloc`]. | 83 /// - it allocates data on the C heap with [`libc::malloc`]. |
| 68 /// - it doesn't take ownership of the data passed in. | 84 /// - it doesn't take ownership of the data passed in. |
| 69 pub fn malloc_str(text: &str) -> Result<*mut c_char> { | 85 pub fn malloc_str(text: &str) -> Result<*mut c_char> { |
| 70 let data = text.as_bytes(); | 86 let data = text.as_bytes(); |
| 71 if data.contains(&0) { | 87 if data.contains(&0) { |
| 72 return Err(ErrorCode::ConversationError); | 88 return Err(ErrorCode::ConversationError); |
| 73 } | 89 } |
| 90 let data_alloc: *mut c_char = calloc(data.len() + 1); | |
| 91 // SAFETY: we just allocated this and we have enough room. | |
| 74 unsafe { | 92 unsafe { |
| 75 let data_alloc = libc::calloc(data.len() + 1, 1); | 93 libc::memcpy(data_alloc.cast(), data.as_ptr().cast(), data.len()); |
| 76 libc::memcpy(data_alloc, data.as_ptr().cast(), data.len()); | 94 } |
| 77 Ok(data_alloc.cast()) | 95 Ok(data_alloc) |
| 78 } | |
| 79 } | 96 } |
| 80 | 97 |
| 81 /// Writes zeroes over the contents of a C string. | 98 /// Writes zeroes over the contents of a C string. |
| 82 /// | 99 /// |
| 83 /// This won't overwrite a null pointer. | 100 /// This won't overwrite a null pointer. |
| 84 /// | 101 /// |
| 85 /// # Safety | 102 /// # Safety |
| 86 /// | 103 /// |
| 87 /// It's up to you to provide a valid C string. | 104 /// It's up to you to provide a valid C string. |
| 88 pub unsafe fn zero_c_string(cstr: *mut c_void) { | 105 pub unsafe fn zero_c_string(cstr: *mut c_char) { |
| 89 if !cstr.is_null() { | 106 if !cstr.is_null() { |
| 90 libc::memset(cstr, 0, libc::strlen(cstr.cast())); | 107 libc::memset(cstr.cast(), 0, libc::strlen(cstr.cast())); |
| 91 } | 108 } |
| 92 } | 109 } |
| 93 | 110 |
| 94 /// Binary data used in requests and responses. | 111 /// Binary data used in requests and responses. |
| 95 /// | 112 /// |
| 108 _marker: Immovable, | 125 _marker: Immovable, |
| 109 } | 126 } |
| 110 | 127 |
| 111 impl CBinaryData { | 128 impl CBinaryData { |
| 112 /// Copies the given data to a new BinaryData on the heap. | 129 /// Copies the given data to a new BinaryData on the heap. |
| 113 pub fn alloc(source: &[u8], data_type: u8) -> Result<*mut CBinaryData> { | 130 pub fn alloc((data, data_type): (&[u8], u8)) -> Result<*mut CBinaryData> { |
| 114 let buffer_size = | 131 let buffer_size = |
| 115 u32::try_from(source.len() + 5).map_err(|_| ErrorCode::ConversationError)?; | 132 u32::try_from(data.len() + 5).map_err(|_| ErrorCode::ConversationError)?; |
| 116 // SAFETY: We're only allocating here. | 133 // SAFETY: We're only allocating here. |
| 117 let data = unsafe { | 134 let dest = unsafe { |
| 118 let dest_buffer: *mut CBinaryData = libc::malloc(buffer_size as usize).cast(); | 135 let dest_buffer: *mut CBinaryData = calloc::<u8>(buffer_size as usize).cast(); |
| 119 let data = &mut *dest_buffer; | 136 let dest = &mut *dest_buffer; |
| 120 data.total_length = buffer_size.to_be_bytes(); | 137 dest.total_length = buffer_size.to_be_bytes(); |
| 121 data.data_type = data_type; | 138 dest.data_type = data_type; |
| 122 let dest = data.data.as_mut_ptr(); | 139 let dest = dest.data.as_mut_ptr(); |
| 123 libc::memcpy(dest.cast(), source.as_ptr().cast(), source.len()); | 140 libc::memcpy(dest.cast(), data.as_ptr().cast(), data.len()); |
| 124 dest_buffer | 141 dest_buffer |
| 125 }; | 142 }; |
| 126 Ok(data) | 143 Ok(dest) |
| 127 } | 144 } |
| 128 | 145 |
| 129 fn length(&self) -> usize { | 146 fn length(&self) -> usize { |
| 130 u32::from_be_bytes(self.total_length).saturating_sub(5) as usize | 147 u32::from_be_bytes(self.total_length).saturating_sub(5) as usize |
| 131 } | 148 } |
| 139 self.data_type = 0; | 156 self.data_type = 0; |
| 140 self.total_length = [0; 4]; | 157 self.total_length = [0; 4]; |
| 141 } | 158 } |
| 142 } | 159 } |
| 143 | 160 |
| 144 impl<'a> From<&'a CBinaryData> for BorrowedBinaryData<'a> { | 161 impl<'a> From<&'a CBinaryData> for (&'a[u8], u8) { |
| 145 fn from(value: &'a CBinaryData) -> Self { | 162 fn from(value: &'a CBinaryData) -> Self { |
| 146 BorrowedBinaryData::new( | 163 (unsafe { slice::from_raw_parts(value.data.as_ptr(), value.length()) }, |
| 147 unsafe { slice::from_raw_parts(value.data.as_ptr(), value.length()) }, | 164 value.data_type ) |
| 148 value.data_type, | |
| 149 ) | |
| 150 } | 165 } |
| 151 } | 166 } |
| 152 | 167 |
| 153 impl From<Option<&'_ CBinaryData>> for BinaryData { | 168 impl From<Option<&'_ CBinaryData>> for BinaryData { |
| 154 fn from(value: Option<&CBinaryData>) -> Self { | 169 fn from(value: Option<&CBinaryData>) -> Self { |
| 155 value.map(BorrowedBinaryData::from).map(Into::into).unwrap_or_default() | 170 // This is a dumb trick but I like it because it is simply the presence |
| 171 // of `.map(|(x, y)| (x, y))` in the middle of this that gives | |
| 172 // type inference the hint it needs to make this work. | |
| 173 value | |
| 174 .map(Into::into) | |
| 175 .map(|(data, data_type)| (data, data_type)) | |
| 176 .map(Into::into) | |
| 177 .unwrap_or_default() | |
| 156 } | 178 } |
| 157 } | 179 } |
| 158 | 180 |
| 159 #[cfg(test)] | 181 #[cfg(test)] |
| 160 mod tests { | 182 mod tests { |
| 161 use super::{copy_pam_string, malloc_str, option_cstr, prompt_ptr, zero_c_string}; | 183 use super::{free, ErrorCode, CString, copy_pam_string, malloc_str, option_cstr, prompt_ptr, zero_c_string}; |
| 162 use crate::ErrorCode; | |
| 163 use std::ffi::CString; | |
| 164 #[test] | 184 #[test] |
| 165 fn test_strings() { | 185 fn test_strings() { |
| 166 let str = malloc_str("hello there").unwrap(); | 186 let str = malloc_str("hello there").unwrap(); |
| 167 malloc_str("hell\0 there").unwrap_err(); | 187 malloc_str("hell\0 there").unwrap_err(); |
| 168 unsafe { | 188 unsafe { |
| 169 let copied = copy_pam_string(str.cast()).unwrap(); | 189 let copied = copy_pam_string(str).unwrap(); |
| 170 assert_eq!("hello there", copied); | 190 assert_eq!("hello there", copied); |
| 171 zero_c_string(str.cast()); | 191 zero_c_string(str); |
| 172 let idx_three = str.add(3).as_mut().unwrap(); | 192 let idx_three = str.add(3).as_mut().unwrap(); |
| 173 *idx_three = 0x80u8 as i8; | 193 *idx_three = 0x80u8 as i8; |
| 174 let zeroed = copy_pam_string(str.cast()).unwrap(); | 194 let zeroed = copy_pam_string(str).unwrap(); |
| 175 assert!(zeroed.is_empty()); | 195 assert!(zeroed.is_empty()); |
| 176 libc::free(str.cast()); | 196 free(str); |
| 177 } | 197 } |
| 178 } | 198 } |
| 179 | 199 |
| 180 #[test] | 200 #[test] |
| 181 fn test_option_str() { | 201 fn test_option_str() { |
