Skip to content

Commit

Permalink
Fix MIDIPacketNext to not trigger Rust's UB check (#18)
Browse files Browse the repository at this point in the history
This should fix #17.

It also simplifies MIDIEventPacketNext, which doesn't need the platform-specific distinction, as the size is 4-byte aligned anyways (see also https://github.com/phracker/MacOSX-SDKs/blob/041600eda65c6a668f66cb7d56b7d1da3e8bcc93/MacOSX11.3.sdk/System/Library/Frameworks/CoreMIDI.framework/Versions/A/Headers/MIDIServices.h#L2387).
  • Loading branch information
Boddlnagg authored Jun 11, 2024
1 parent dd8df3b commit c2ff432
Showing 1 changed file with 16 additions and 11 deletions.
27 changes: 16 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,32 @@ include!("generated.rs");
pub unsafe fn MIDIPacketNext(pkt: *const MIDIPacket) -> *const MIDIPacket {
// Get pointer to potentially unaligned data without triggering undefined behavior
// addr_of does not require creating an intermediate reference to unaligned data.
// See also the definition of `MIDIPacketNext` in the official SDK MIDIServices.h
let ptr = ptr::addr_of!((*pkt).data) as *const u8;
let offset = (*pkt).length as isize;
let ptr_length = ptr::addr_of!((*pkt).length) as *const u16;
if cfg!(any(target_arch = "arm", target_arch = "aarch64")) {
// MIDIPacket must be 4-byte aligned on ARM
// MIDIPacket must be 4-byte aligned on ARM, so we need to calculate an aligned offset.
// We do not need `read_unaligned` for the length, because the length will never
// be unaligned, and `read_unaligned` would lead to less efficient machine code.
let offset = ptr_length.read() as isize;
((ptr.offset(offset + 3) as usize) & !(3usize)) as *const MIDIPacket
} else {
// MIDIPacket is unaligned on non-ARM, so reading the length requires `read_unaligned`
// to not trigger Rust's UB check (although unaligned reads are harmless on Intel
// and `read_unaligned` will generate the same machine code as `read`).
let offset = ptr_length.read_unaligned() as isize;
ptr.offset(offset) as *const MIDIPacket
}
}

#[inline]
pub unsafe fn MIDIEventPacketNext(pkt: *const MIDIEventPacket) -> *const MIDIEventPacket {
// Get pointer to potentially unaligned data without triggering undefined behavior
// addr_of does not require creating an intermediate reference to unaligned data.
// Each EventPacket's size is a multiple of 4 bytes, so no special care
// needs to be taken when reading the data (except the timeStamp, which is not 8-byte aligned).
// See also the definition of `MIDIEventPacketNext` in the official SDK MIDIServices.h
let ptr = ptr::addr_of!((*pkt).words) as *const u8;
let offset = (((*pkt).wordCount as usize) * mem::size_of::<u32>()) as isize;
if cfg!(any(target_arch = "arm", target_arch = "aarch64")) {
// MIDIEventPacket must be 4-byte aligned on ARM
((ptr.offset(offset + 3) as usize) & !(3usize)) as *const MIDIEventPacket
} else {
ptr.offset(offset) as *const MIDIEventPacket
}
ptr.offset(offset) as *const MIDIEventPacket
}

#[allow(dead_code)]
Expand Down Expand Up @@ -96,7 +100,8 @@ mod tests {
);

let second_packet = MIDIPacketNext(first_packet);
let len = (*second_packet).length as usize;
let ptr_length = ptr::addr_of!((*second_packet).length) as *const u16;
let len = ptr_length.read_unaligned() as usize;
assert_eq!(
&(*second_packet).data[0..len],
&[0x90, 0x41, 0x7f]
Expand Down

0 comments on commit c2ff432

Please sign in to comment.