Mercurial > crates > nonstick
comparison src/libpam/answer.rs @ 139:33b9622ed6d2
Remove redundant memory management in nonstick::libpam; fix UB.
- Uses the libpam-sys-helpers BinaryPayload / OwnedBinaryPayload structs
to handle memory management and parsing for Linux-PAM binary messages.
- Gets rid of the (technically) undefined behavior in PtrPtrVec
due to pointer provenance.
- Don't check for malloc failing. It won't, even if it does.
- Formatting/cleanups/etc.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Thu, 03 Jul 2025 23:57:49 -0400 |
parents | 80c07e5ab22f |
children | ebb71a412b58 |
comparison
equal
deleted
inserted
replaced
138:999bf07efbcb | 139:33b9622ed6d2 |
---|---|
1 //! Types used to communicate data from the application to the module. | 1 //! Types used to communicate data from the application to the module. |
2 | 2 |
3 use crate::libpam::conversation::OwnedExchange; | 3 use crate::libpam::conversation::OwnedExchange; |
4 use crate::libpam::memory; | 4 use crate::libpam::memory; |
5 use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString, Immovable}; | 5 use crate::libpam::memory::{CHeapBox, CHeapPayload, CHeapString, Immovable}; |
6 use crate::{ErrorCode, Result}; | 6 use crate::{ErrorCode, Result}; |
7 use libpam_sys_helpers::memory::BinaryPayload; | |
7 use std::ffi::{c_int, c_void, CStr}; | 8 use std::ffi::{c_int, c_void, CStr}; |
8 use std::mem::ManuallyDrop; | 9 use std::mem::ManuallyDrop; |
9 use std::ops::{Deref, DerefMut}; | 10 use std::ops::{Deref, DerefMut}; |
10 use std::ptr::NonNull; | 11 use std::ptr::NonNull; |
11 use std::{iter, ptr, slice}; | 12 use std::{iter, ptr, slice}; |
21 | 22 |
22 impl Answers { | 23 impl Answers { |
23 /// Builds an Answers out of the given answered Message Q&As. | 24 /// Builds an Answers out of the given answered Message Q&As. |
24 pub fn build(value: Vec<OwnedExchange>) -> Result<Self> { | 25 pub fn build(value: Vec<OwnedExchange>) -> Result<Self> { |
25 let mut outputs = Self { | 26 let mut outputs = Self { |
26 base: memory::calloc(value.len())?, | 27 base: memory::calloc(value.len()), |
27 count: value.len(), | 28 count: value.len(), |
28 }; | 29 }; |
29 // Even if we fail during this process, we still end up freeing | 30 // Even if we fail during this process, we still end up freeing |
30 // all allocated answer memory. | 31 // all allocated answer memory. |
31 for (input, output) in iter::zip(value, outputs.iter_mut()) { | 32 for (input, output) in iter::zip(value, outputs.iter_mut()) { |
191 /// The `data_type` is a tag you can use for whatever. | 192 /// The `data_type` is a tag you can use for whatever. |
192 /// It is passed through PAM unchanged. | 193 /// It is passed through PAM unchanged. |
193 /// | 194 /// |
194 /// The referenced data is copied to the C heap. | 195 /// The referenced data is copied to the C heap. |
195 /// We do not take ownership of the original data. | 196 /// We do not take ownership of the original data. |
196 pub fn fill(dest: &mut Answer, data_and_type: (&[u8], u8)) -> Result<()> { | 197 pub fn fill(dest: &mut Answer, (data, type_): (&[u8], u8)) -> Result<()> { |
197 let allocated = CBinaryData::alloc(data_and_type)?; | 198 let payload = CHeapPayload::new(data, type_).map_err(|_| ErrorCode::BufferError)?; |
198 let _ = dest.data.replace(unsafe { CHeapBox::cast(allocated) }); | 199 let _ = dest |
200 .data | |
201 .replace(unsafe { CHeapBox::cast(payload.into_inner()) }); | |
199 Ok(()) | 202 Ok(()) |
200 } | 203 } |
201 | 204 |
202 /// Gets the binary data in this answer. | 205 /// Gets the binary data in this answer. |
203 pub fn data(&self) -> Option<NonNull<CBinaryData>> { | 206 pub fn contents(&self) -> Option<(&[u8], u8)> { |
204 // SAFETY: We either got this data from PAM or allocated it ourselves. | 207 // SAFETY: We either got this data from PAM or allocated it ourselves. |
205 // Either way, we trust that it is either valid data or null. | 208 // Either way, we trust that it is either valid data or null. |
206 self.0 | 209 self.0 |
207 .data | 210 .data |
208 .as_ref() | 211 .as_ref() |
209 .map(CHeapBox::as_ptr) | 212 .map(|data| unsafe { BinaryPayload::contents(CHeapBox::as_ptr(data).cast().as_ptr()) }) |
210 .map(NonNull::cast) | |
211 } | 213 } |
212 | 214 |
213 /// Zeroes out the answer data, frees it, and points our data to `null`. | 215 /// Zeroes out the answer data, frees it, and points our data to `null`. |
214 /// | 216 /// |
215 /// When this `BinaryAnswer` is part of an [`Answers`], | 217 /// When this `BinaryAnswer` is part of an [`Answers`], |
216 /// this is optional (since that will perform the `free`), | 218 /// this is optional (since that will perform the `free`), |
217 /// but it will clear potentially sensitive data. | 219 /// but it will clear potentially sensitive data. |
218 pub fn zero_contents(&mut self) { | 220 pub fn zero_contents(&mut self) { |
219 // SAFETY: We know that our data pointer is either valid or null. | 221 // SAFETY: We know that our data pointer is either valid or null. |
220 // Once we're done, it's null and the answer is safe. | 222 if let Some(data) = self.0.data.as_mut() { |
221 unsafe { | 223 unsafe { |
222 if let Some(ptr) = self.0.data.as_ref() { | 224 let total = BinaryPayload::total_bytes(CHeapBox::as_ptr(data).cast().as_ref()); |
223 CBinaryData::zero_contents(CHeapBox::as_ptr(ptr).cast()) | 225 let data: &mut [u8] = |
226 slice::from_raw_parts_mut(CHeapBox::as_raw_ptr(data).cast(), total); | |
227 data.fill(0) | |
224 } | 228 } |
225 } | 229 } |
226 } | 230 } |
227 } | 231 } |
228 | 232 |
288 ), | 292 ), |
289 ]); | 293 ]); |
290 | 294 |
291 if let [bin, radio] = &mut answers[..] { | 295 if let [bin, radio] = &mut answers[..] { |
292 let up = unsafe { BinaryAnswer::upcast(bin) }; | 296 let up = unsafe { BinaryAnswer::upcast(bin) }; |
293 assert_eq!(BinaryData::from((&[1, 2, 3][..], 99)), unsafe { | 297 assert_eq!((&[1, 2, 3][..], 99), up.contents().unwrap()); |
294 CBinaryData::as_binary_data(up.data().unwrap()) | |
295 }); | |
296 up.zero_contents(); | 298 up.zero_contents(); |
297 assert_eq!(BinaryData::default(), unsafe { | 299 assert_eq!((&[][..], 0), up.contents().unwrap()); |
298 CBinaryData::as_binary_data(up.data().unwrap()) | |
299 }); | |
300 | 300 |
301 assert_text_answer("beep boop", radio); | 301 assert_text_answer("beep boop", radio); |
302 } else { | 302 } else { |
303 panic!("received wrong size {len}!", len = answers.len()) | 303 panic!("received wrong size {len}!", len = answers.len()) |
304 } | 304 } |
321 use crate::conv::BinaryData; | 321 use crate::conv::BinaryData; |
322 let mut answer: CHeapBox<Answer> = CHeapBox::default(); | 322 let mut answer: CHeapBox<Answer> = CHeapBox::default(); |
323 let real_data = BinaryData::new([1, 2, 3, 4, 5, 6, 7, 8], 9); | 323 let real_data = BinaryData::new([1, 2, 3, 4, 5, 6, 7, 8], 9); |
324 BinaryAnswer::fill(&mut answer, (&real_data).into()).expect("alloc should succeed"); | 324 BinaryAnswer::fill(&mut answer, (&real_data).into()).expect("alloc should succeed"); |
325 let bin_answer = unsafe { BinaryAnswer::upcast(&mut answer) }; | 325 let bin_answer = unsafe { BinaryAnswer::upcast(&mut answer) }; |
326 assert_eq!(real_data, unsafe { | 326 assert_eq!(real_data, bin_answer.contents().unwrap().into()); |
327 CBinaryData::as_binary_data(bin_answer.data().unwrap()) | |
328 }); | |
329 } | 327 } |
330 | 328 |
331 #[test] | 329 #[test] |
332 #[ignore] | 330 #[ignore] |
333 fn test_binary_answer_too_big() { | 331 fn test_binary_answer_too_big() { |