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