Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unsoundness in PdInfo::as_struct() #18

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions libosdp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ where
trampoline::<F>
}

fn cp_setup(info: Vec<libosdp_sys::osdp_pd_info_t>) -> Result<*mut c_void> {
let ctx = unsafe { libosdp_sys::osdp_cp_setup(info.len() as i32, info.as_ptr()) };
fn cp_setup(info: Vec<crate::OsdpPdInfoHandle>) -> Result<*mut c_void> {
let ctx = unsafe { libosdp_sys::osdp_cp_setup(info.len() as i32, info.as_ptr() as *const _) };
if ctx.is_null() {
Err(OsdpError::Setup)
} else {
Expand All @@ -74,12 +74,11 @@ unsafe impl Send for ControlPanel {}

impl ControlPanel {
/// Create a new CP context for the list of PDs described by the [`PdInfo`] vector.
pub fn new(mut pd_info: Vec<PdInfo>) -> Result<Self> {
pub fn new(pd_info: Vec<PdInfo>) -> Result<Self> {
if pd_info.len() > 126 {
return Err(OsdpError::PdInfo("max PD count exceeded"));
}
let info: Vec<libosdp_sys::osdp_pd_info_t> =
pd_info.iter_mut().map(|i| i.as_struct()).collect();
let info: Vec<crate::OsdpPdInfoHandle> = pd_info.into_iter().map(|i| i.into()).collect();
unsafe { libosdp_sys::osdp_set_log_callback(Some(log_handler)) };
Ok(Self {
ctx: cp_setup(info)?,
Expand Down
5 changes: 3 additions & 2 deletions libosdp/src/pd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ where
trampoline::<F>
}

fn pd_setup(mut info: PdInfo) -> Result<*mut c_void> {
let ctx = unsafe { libosdp_sys::osdp_pd_setup(&info.as_struct()) };
fn pd_setup(info: PdInfo) -> Result<*mut c_void> {
let info: crate::OsdpPdInfoHandle = info.into();
let ctx = unsafe { libosdp_sys::osdp_pd_setup(&*info) };
if ctx.is_null() {
Err(OsdpError::Setup)
} else {
Expand Down
84 changes: 67 additions & 17 deletions libosdp/src/pdinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
//
// SPDX-License-Identifier: Apache-2.0

use core::ops::Deref;

use alloc::ffi::CString;

use crate::{Channel, OsdpError, OsdpFlag, PdCapability, PdId};
use crate::{Box, Channel, OsdpError, OsdpFlag, PdCapability, PdId};

/// OSDP PD Information. This struct is used to describe a PD to LibOSDP
#[derive(Debug, Default)]
Expand Down Expand Up @@ -247,24 +249,72 @@ impl PdInfoBuilder {
}
}

impl PdInfo {
/// Get a C-repr struct for PdInfo that LibOSDP can operate on.
pub fn as_struct(&mut self) -> libosdp_sys::osdp_pd_info_t {
let scbk;
if let Some(key) = self.scbk.as_mut() {
scbk = key.as_mut_ptr();
#[repr(transparent)]
pub(crate) struct OsdpPdInfoHandle(pub libosdp_sys::osdp_pd_info_t);

impl Deref for OsdpPdInfoHandle {
type Target = libosdp_sys::osdp_pd_info_t;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<PdInfo> for OsdpPdInfoHandle {
fn from(info: PdInfo) -> OsdpPdInfoHandle {
let scbk = if let Some(key) = info.scbk {
Box::into_raw(Box::new(key)) as *mut _
} else {
scbk = std::ptr::null_mut::<u8>();
}
libosdp_sys::osdp_pd_info_t {
name: self.name.as_ptr(),
baud_rate: self.baud_rate,
address: self.address,
flags: self.flags.bits() as i32,
id: self.id.into(),
cap: self.cap.as_ptr(),
channel: self.channel.take().unwrap().into(),
core::ptr::null_mut::<u8>()
};
let cap = if !info.cap.is_empty() {
let mut cap = info.cap.clone();
cap.reserve(1);
cap.push(libosdp_sys::osdp_pd_cap {
function_code: -1i8 as u8,
compliance_level: 0,
num_items: 0,
});
Box::into_raw(cap.into_boxed_slice()) as *mut _
} else {
core::ptr::null_mut::<libosdp_sys::osdp_pd_cap>()
};
OsdpPdInfoHandle(libosdp_sys::osdp_pd_info_t {
name: info.name.clone().into_raw(),
baud_rate: info.baud_rate,
address: info.address,
flags: info.flags.bits() as i32,
id: info.id.into(),
cap: cap as *mut _,
channel: info.channel.unwrap().into(),
scbk,
})
}
}

impl Drop for OsdpPdInfoHandle {
fn drop(&mut self) {
unsafe {
let info = self.0;
// LibOSDP versions <= 3.0.6 do not copy the name, so this is only valid for >= 3.0.7
if !info.name.is_null() {
drop(CString::from_raw(info.name as *mut _));
}
if !info.cap.is_null() {
let mut cap = info.cap as *mut libosdp_sys::osdp_pd_cap;
while (*cap).function_code != -1i8 as u8 {
cap = cap.add(1);
}
let len = (cap.offset_from(info.cap) + 1) as usize;
drop(Vec::from_raw_parts(
info.cap as *mut libosdp_sys::osdp_pd_cap,
len,
len,
));
}
if !info.scbk.is_null() {
drop(Box::from_raw(info.scbk as *mut [u8; 16]));
}
}
}
}