Skip to content

Commit 6d40763

Browse files
authored
Merge pull request #757 from memorysafety/ja-nonnull-calloc
pam/securemem: handle null pointer case
2 parents f7ee9bd + cff697e commit 6d40763

File tree

2 files changed

+36
-17
lines changed

2 files changed

+36
-17
lines changed

src/pam/converse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ pub(super) unsafe extern "C" fn converse<C: Converser>(
247247
let response: &mut pam_response = unsafe { &mut *(temp_resp.add(i)) };
248248

249249
if let Some(secbuf) = msg.response {
250-
response.resp = secbuf.leak() as *mut _;
250+
response.resp = secbuf.leak().as_ptr().cast();
251251
}
252252
}
253253

src/pam/securemem.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
//! Routines for "secure" memory operations; i.e. data that we need to send to Linux-PAM and don't
22
//! want any copies to leak (that we would then need to zeroize).
3-
use std::slice;
3+
use std::{
4+
alloc::{self, Layout},
5+
mem,
6+
ptr::NonNull,
7+
slice,
8+
};
49

5-
pub struct PamBuffer(*mut u8);
10+
const SIZE: usize = super::sys::PAM_MAX_RESP_SIZE as usize;
11+
const ALIGN: usize = mem::align_of::<u8>();
612

7-
impl PamBuffer {
8-
const SIZE: usize = super::sys::PAM_MAX_RESP_SIZE as usize;
13+
pub struct PamBuffer(NonNull<[u8; SIZE]>);
14+
15+
fn layout() -> Layout {
16+
// does not panic with the given arguments; also see unit test at the bottom
17+
Layout::from_size_align(SIZE, ALIGN).unwrap()
18+
}
919

20+
impl PamBuffer {
1021
// consume this buffer and return its internal pointer
1122
// (ending the type-level security, but guaranteeing you need unsafe code to access the data)
12-
pub fn leak(self) -> *const u8 {
23+
pub fn leak(self) -> NonNull<u8> {
1324
let result = self.0;
1425
std::mem::forget(self);
1526

16-
result
27+
result.cast()
1728
}
1829

1930
// initialize the buffer with already existing data (otherwise populating it is a bit hairy)
@@ -31,7 +42,12 @@ impl PamBuffer {
3142

3243
impl Default for PamBuffer {
3344
fn default() -> Self {
34-
PamBuffer(unsafe { libc::calloc(1, Self::SIZE) as *mut u8 })
45+
let res = unsafe { libc::calloc(1, SIZE) };
46+
if let Some(nn) = NonNull::new(res) {
47+
PamBuffer(nn.cast())
48+
} else {
49+
alloc::handle_alloc_error(layout())
50+
}
3551
}
3652
}
3753

@@ -40,22 +56,20 @@ impl std::ops::Deref for PamBuffer {
4056

4157
fn deref(&self) -> &[u8] {
4258
// make the slice one less in size to guarantee the existence of a terminating NUL
43-
unsafe { slice::from_raw_parts(self.0, Self::SIZE - 1) }
59+
unsafe { slice::from_raw_parts(self.0.as_ptr().cast(), SIZE - 1) }
4460
}
4561
}
4662

4763
impl std::ops::DerefMut for PamBuffer {
4864
fn deref_mut(&mut self) -> &mut [u8] {
49-
unsafe { slice::from_raw_parts_mut(self.0, Self::SIZE - 1) }
65+
unsafe { slice::from_raw_parts_mut(self.0.as_ptr().cast(), SIZE - 1) }
5066
}
5167
}
5268

5369
impl Drop for PamBuffer {
5470
fn drop(&mut self) {
55-
if !self.0.is_null() {
56-
wipe_memory(unsafe { &mut *(self.0 as *mut [u8; Self::SIZE]) });
57-
unsafe { libc::free(self.0 as *mut _) }
58-
}
71+
wipe_memory(unsafe { self.0.as_mut() });
72+
unsafe { libc::free(self.0.as_ptr().cast()) }
5973
}
6074
}
6175

@@ -82,9 +96,9 @@ mod test {
8296
let test = |text: &str| unsafe {
8397
let buf = PamBuffer::new(text.to_string().as_bytes_mut());
8498
assert_eq!(&buf[..text.len()], text.as_bytes());
85-
let ptr = buf.leak();
86-
let result = crate::cutils::string_from_ptr(ptr as *mut _);
87-
libc::free(ptr as *mut libc::c_void);
99+
let nn = buf.leak();
100+
let result = crate::cutils::string_from_ptr(nn.as_ptr().cast());
101+
libc::free(nn.as_ptr().cast());
88102
result
89103
};
90104
assert_eq!(test(""), "");
@@ -100,4 +114,9 @@ mod test {
100114
assert!(fix[3..].iter().all(|&x| x == 0));
101115
std::mem::drop(fix);
102116
}
117+
118+
#[test]
119+
fn layout_does_not_panic() {
120+
let _ = super::layout();
121+
}
103122
}

0 commit comments

Comments
 (0)