Skip to content

Commit

Permalink
Fixing property parsing issue on Rust 1.78+ (#129)
Browse files Browse the repository at this point in the history
* adding safety checks to support calls to from_raw_parts()

* adding temp debug prints

* more triaging

* temporarily removing alignment check for strings

* trying to use Vec<u16> to fix pointer alignment weirdness

* cleanup
  • Loading branch information
matterpreter authored Jun 4, 2024
1 parent 8602d9c commit 73137e7
Showing 1 changed file with 34 additions and 6 deletions.
40 changes: 34 additions & 6 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,17 +371,38 @@ macro_rules! impl_try_parse_primitive_array {
match prop_slice.property.info {
PropertyInfo::Array { .. } => {
// TODO: Check In and Out type and do a better type checking

// This property type has not been tested yet as I don't have a
// provider that uses it. It's possible that the buffer is not
// aligned correctly, which would cause this to fail.
let size = std::mem::size_of::<$T>();
let align = std::mem::align_of::<$T>();

if prop_slice.buffer.len() % size != 0 {
return Err(ParserError::LengthMismatch);
}

let count = prop_slice.buffer.len() / size;

if prop_slice.buffer.as_ptr() as usize % align != 0 {
return Err(ParserError::PropertyError(
"buffer alignment mismatch".into()
));
}

if size.checked_mul(count).is_none() || (size * count) > isize::MAX as usize {
return Err(ParserError::PropertyError(
"size overflow".into()
));
}

let slice = unsafe {
std::slice::from_raw_parts(
prop_slice.buffer.as_ptr() as *const $T,
count,
)
};

Ok(slice)
}
_ => Err(ParserError::InvalidType),
Expand Down Expand Up @@ -447,19 +468,26 @@ impl private::TryParse<String> for Parser<'_, '_> {
));
}

let mut wide = unsafe {
std::slice::from_raw_parts(
prop_slice.buffer.as_ptr() as *const u16,
prop_slice.buffer.len() / 2,
)
};
// std::slice::from_raw_parts requires a pointer to be aligned, but we can't
// guarantee that the buffer is aligned. In testing, I found that the buffer
// is in fact never aligned appropriately, so a cheap workaround is to copy
// the buffer into a new Vec<u16> and use that as the source for the slice
// until we can find a better solution.
let mut aligned_buffer = Vec::with_capacity(prop_slice.buffer.len() / 2);
for chunk in prop_slice.buffer.chunks_exact(2) {
let part = u16::from_ne_bytes([chunk[0], chunk[1]]);
aligned_buffer.push(part);
}

let mut wide = aligned_buffer.as_slice();

match wide.last() {
// remove the null terminator from the slice
Some(c) if c == &0 => wide = &wide[..wide.len() - 1],
_ => (),
}

// Decode UTF-16 to String
Ok(widestring::decode_utf16_lossy(wide.iter().copied()).collect::<String>())
}
TdhInType::InTypeAnsiString => {
Expand Down

0 comments on commit 73137e7

Please sign in to comment.