Skip to content

Commit

Permalink
fix(Rust): Fix memory errors caused by casting (#1372)
Browse files Browse the repository at this point in the history
In the stable version of Rust, the `from_raw_parts_mut` API does not
check for memory alignment, but in the 1.78.0-nightly version, alignment
checking has been added to the preconditions. To ensure memory safety,
we have replaced direct memory writes with the `LittleEndian::write_xx`
methods from the appropriate library. While this may introduce
additional overhead for memory checks, robustness is the priority before
releasing the software.
  • Loading branch information
theweipeng authored Feb 14, 2024
1 parent 1d345bb commit a6986ec
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 79 deletions.
166 changes: 88 additions & 78 deletions rust/fury/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,14 @@
// specific language governing permissions and limitations
// under the License.

use std::{
mem, ptr,
slice::{from_raw_parts, from_raw_parts_mut},
};

use byteorder::{ByteOrder, LittleEndian};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};

#[derive(Default)]
pub struct Writer {
bf: Vec<u8>,
reserved: usize,
}

macro_rules! write_num {
($name: ident, $ty: tt) => {
pub fn $name(&mut self, v: $ty) {
let c = self.cast::<$ty>(mem::size_of::<$ty>());
c[0] = v;
self.move_next(mem::size_of::<$ty>());
}
};
}

impl Writer {
pub fn dump(&self) -> Vec<u8> {
self.bf.clone()
Expand All @@ -47,48 +32,55 @@ impl Writer {
self.bf.len()
}

fn move_next(&mut self, additional: usize) {
unsafe { self.bf.set_len(self.bf.len() + additional) }
}
fn ptr(&mut self) -> *mut u8 {
unsafe {
let t = self.bf.as_mut_ptr();
t.add(self.bf.len())
}
}

fn cast<T>(&mut self, len: usize) -> &mut [T] {
unsafe { from_raw_parts_mut(self.ptr() as *mut T, len) }
}

pub fn reserve(&mut self, additional: usize) {
self.reserved += additional;
if self.bf.capacity() < self.reserved {
self.bf.reserve(self.reserved);
}
}

write_num!(u8, u8);
write_num!(u16, u16);
write_num!(u32, u32);
write_num!(u64, u64);
write_num!(i8, i8);
write_num!(i16, i16);
write_num!(i32, i32);
write_num!(i64, i64);
pub fn u8(&mut self, value: u8) {
self.bf.write_u8(value).unwrap();
}

pub fn i8(&mut self, value: i8) {
self.bf.write_i8(value).unwrap();
}

pub fn u16(&mut self, value: u16) {
self.bf.write_u16::<LittleEndian>(value).unwrap();
}

pub fn i16(&mut self, value: i16) {
self.bf.write_i16::<LittleEndian>(value).unwrap();
}

pub fn u32(&mut self, value: u32) {
self.bf.write_u32::<LittleEndian>(value).unwrap();
}

pub fn skip(&mut self, len: usize) {
self.move_next(len);
self.bf.resize(self.bf.len() + len, 0);
}

pub fn i32(&mut self, value: i32) {
self.bf.write_i32::<LittleEndian>(value).unwrap();
}

pub fn f32(&mut self, value: f32) {
LittleEndian::write_f32(self.cast::<u8>(4), value);
self.move_next(4);
self.bf.write_f32::<LittleEndian>(value).unwrap();
}

pub fn i64(&mut self, value: i64) {
self.bf.write_i64::<LittleEndian>(value).unwrap();
}

pub fn f64(&mut self, value: f64) {
LittleEndian::write_f64(self.cast::<u8>(8), value);
self.move_next(8);
self.bf.write_f64::<LittleEndian>(value).unwrap();
}

pub fn u64(&mut self, value: u64) {
self.bf.write_u64::<LittleEndian>(value).unwrap();
}

pub fn var_int32(&mut self, value: i32) {
Expand Down Expand Up @@ -121,10 +113,7 @@ impl Writer {

pub fn bytes(&mut self, v: &[u8]) {
self.reserve(v.len());
unsafe {
ptr::copy_nonoverlapping(v.as_ptr(), self.ptr(), v.len());
}
self.move_next(v.len());
self.bf.extend_from_slice(v);
}

pub fn set_bytes(&mut self, offset: usize, data: &[u8]) {
Expand All @@ -140,17 +129,6 @@ pub struct Reader<'de> {
cursor: usize,
}

macro_rules! read_num {
($name: ident, $ty: tt) => {
pub fn $name(&mut self) -> $ty {
let c = self.cast::<$ty>(mem::size_of::<$ty>());
let result = c[0];
self.move_next(mem::size_of::<$ty>());
result
}
};
}

impl<'bf> Reader<'bf> {
pub fn new(bf: &[u8]) -> Reader {
Reader { bf, cursor: 0 }
Expand All @@ -160,34 +138,66 @@ impl<'bf> Reader<'bf> {
self.cursor += additional;
}

fn ptr(&self) -> *const u8 {
unsafe {
let t = self.bf.as_ptr();
t.add(self.cursor)
}
fn slice_after_cursor(&self) -> &[u8] {
&self.bf[self.cursor..self.bf.len()]
}

pub fn u8(&mut self) -> u8 {
let result = self.bf[self.cursor];
self.move_next(1);
result
}

pub fn i8(&mut self) -> i8 {
let result = self.bf[self.cursor];
self.move_next(1);
result as i8
}

pub fn u16(&mut self) -> u16 {
let result = LittleEndian::read_u16(self.slice_after_cursor());
self.move_next(2);
result
}

pub fn i16(&mut self) -> i16 {
let result = LittleEndian::read_i16(self.slice_after_cursor());
self.move_next(2);
result as i16
}

pub fn u32(&mut self) -> u32 {
let result = LittleEndian::read_u32(self.slice_after_cursor());
self.move_next(4);
result
}

pub fn i32(&mut self) -> i32 {
let result = LittleEndian::read_i32(self.slice_after_cursor());
self.move_next(4);
result as i32
}

fn cast<T>(&self, len: usize) -> &[T] {
unsafe { from_raw_parts(self.ptr() as *const T, len) }
pub fn u64(&mut self) -> u64 {
let result = LittleEndian::read_u64(self.slice_after_cursor());
self.move_next(8);
result
}

read_num!(u8, u8);
read_num!(u16, u16);
read_num!(u32, u32);
read_num!(u64, u64);
read_num!(i8, i8);
read_num!(i16, i16);
read_num!(i32, i32);
read_num!(i64, i64);
pub fn i64(&mut self) -> i64 {
let result = LittleEndian::read_i64(self.slice_after_cursor());
self.move_next(8);
result as i64
}

pub fn f32(&mut self) -> f32 {
let result = LittleEndian::read_f32(self.cast::<u8>(4));
let result = LittleEndian::read_f32(self.slice_after_cursor());
self.move_next(4);
result
}

pub fn f64(&mut self) -> f64 {
let result = LittleEndian::read_f64(self.cast::<u8>(8));
let result = LittleEndian::read_f64(self.slice_after_cursor());
self.move_next(8);
result
}
Expand All @@ -214,9 +224,9 @@ impl<'bf> Reader<'bf> {
result
}

pub fn string(&mut self, len: u32) -> String {
let result = String::from_utf8_lossy(self.cast::<u8>(len as usize)).to_string();
self.move_next(len as usize);
pub fn string(&mut self, len: usize) -> String {
let result = String::from_utf8_lossy(&self.bf[self.cursor..self.cursor + len]).to_string();
self.move_next(len);
result
}

Expand Down
2 changes: 1 addition & 1 deletion rust/fury/src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl_num_deserialize_and_pritimive_vec!(f64, f64);
impl Deserialize for String {
fn read(deserializer: &mut DeserializerState) -> Result<Self, Error> {
let len = deserializer.reader.var_int32();
Ok(deserializer.reader.string(len as u32))
Ok(deserializer.reader.string(len as usize))
}
}

Expand Down

0 comments on commit a6986ec

Please sign in to comment.