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() {