Skip to content

Commit 357b0dd

Browse files
tustvoldJochen Parmentier
authored andcommitted
Rust soundness fixes (google#7518)
* Rust soundness fixes * Second pass * Make init_from_table unsafe * Remove SafeSliceAccess * Clippy * Remove create_vector_of_strings * More clippy * Remove deprecated root type accessors * More soundness fixes * Fix EndianScalar for bool * Add TriviallyTransmutable * Add debug assertions * Review comments * Review feedback
1 parent 28f27f7 commit 357b0dd

102 files changed

Lines changed: 2673 additions & 2035 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

rust/flatbuffers/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ no_std = ["core2", "thiserror_core2"]
1717
serialize = ["serde"]
1818

1919
[dependencies]
20-
smallvec = "1.6.1"
2120
bitflags = "1.2.1"
2221
serde = { version = "1.0", optional = true }
2322
thiserror = { version = "1.0.30", optional = true }

rust/flatbuffers/src/array.rs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,18 @@ where
3737
#[allow(clippy::len_without_is_empty)]
3838
#[allow(clippy::from_over_into)] // TODO(caspern): Go from From to Into.
3939
impl<'a, T: 'a, const N: usize> Array<'a, T, N> {
40+
/// # Safety
41+
///
42+
/// buf must be a contiguous array of `T`
43+
///
44+
/// # Panics
45+
///
46+
/// Panics if `buf.len()` is not `size_of::<T>() * N`
4047
#[inline(always)]
41-
pub fn new(buf: &'a [u8]) -> Self {
42-
assert!(size_of::<T>() * N == buf.len());
48+
pub unsafe fn new(buf: &'a [u8]) -> Self {
49+
assert_eq!(size_of::<T>() * N, buf.len());
4350

44-
Array {
45-
0: buf,
46-
1: PhantomData,
47-
}
51+
Array(buf, PhantomData)
4852
}
4953

5054
#[inline(always)]
@@ -61,49 +65,52 @@ impl<'a, T: Follow<'a> + 'a, const N: usize> Array<'a, T, N> {
6165
pub fn get(&self, idx: usize) -> T::Inner {
6266
assert!(idx < N);
6367
let sz = size_of::<T>();
64-
T::follow(self.0, sz * idx)
68+
// Safety:
69+
// self.0 was valid for length `N` on construction and have verified `idx < N`
70+
unsafe { T::follow(self.0, sz * idx) }
6571
}
6672

6773
#[inline(always)]
6874
pub fn iter(&self) -> VectorIter<'a, T> {
69-
VectorIter::from_slice(self.0, self.len())
75+
// Safety:
76+
// self.0 was valid for length N on construction
77+
unsafe { VectorIter::from_slice(self.0, self.len()) }
7078
}
7179
}
7280

73-
impl<'a, T: Follow<'a> + Debug, const N: usize> Into<[T::Inner; N]> for Array<'a, T, N> {
74-
#[inline(always)]
75-
fn into(self) -> [T::Inner; N] {
76-
array_init(|i| self.get(i))
81+
impl<'a, T: Follow<'a> + Debug, const N: usize> From<Array<'a, T, N>> for [T::Inner; N] {
82+
fn from(array: Array<'a, T, N>) -> Self {
83+
array_init(|i| array.get(i))
7784
}
7885
}
7986

80-
// TODO(caspern): Implement some future safe version of SafeSliceAccess.
81-
8287
/// Implement Follow for all possible Arrays that have Follow-able elements.
8388
impl<'a, T: Follow<'a> + 'a, const N: usize> Follow<'a> for Array<'a, T, N> {
8489
type Inner = Array<'a, T, N>;
8590
#[inline(always)]
86-
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
91+
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
8792
Array::new(&buf[loc..loc + N * size_of::<T>()])
8893
}
8994
}
9095

91-
pub fn emplace_scalar_array<T: EndianScalar, const N: usize>(
96+
/// Place an array of EndianScalar into the provided mutable byte slice. Performs
97+
/// endian conversion, if necessary.
98+
/// # Safety
99+
/// Caller must ensure `s.len() >= size_of::<[T; N]>()`
100+
pub unsafe fn emplace_scalar_array<T: EndianScalar, const N: usize>(
92101
buf: &mut [u8],
93102
loc: usize,
94103
src: &[T; N],
95104
) {
96105
let mut buf_ptr = buf[loc..].as_mut_ptr();
97106
for item in src.iter() {
98107
let item_le = item.to_little_endian();
99-
unsafe {
100-
core::ptr::copy_nonoverlapping(
101-
&item_le as *const T as *const u8,
102-
buf_ptr,
103-
size_of::<T>(),
104-
);
105-
buf_ptr = buf_ptr.add(size_of::<T>());
106-
}
108+
core::ptr::copy_nonoverlapping(
109+
&item_le as *const T::Scalar as *const u8,
110+
buf_ptr,
111+
size_of::<T::Scalar>(),
112+
);
113+
buf_ptr = buf_ptr.add(size_of::<T::Scalar>());
107114
}
108115
}
109116

@@ -124,6 +131,8 @@ where
124131
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
125132
let mut ptr_i = array.as_mut_ptr() as *mut T;
126133

134+
// Safety:
135+
// array is aligned by T, and has length N
127136
unsafe {
128137
for i in 0..N {
129138
let value_i = initializer(i);
@@ -134,7 +143,7 @@ where
134143
}
135144
}
136145

137-
#[cfg(feature="serialize")]
146+
#[cfg(feature = "serialize")]
138147
impl<'a, T: 'a, const N: usize> serde::ser::Serialize for Array<'a, T, N>
139148
where
140149
T: 'a + Follow<'a>,

rust/flatbuffers/src/builder.rs

Lines changed: 65 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,22 @@
1414
* limitations under the License.
1515
*/
1616

17-
extern crate smallvec;
18-
17+
#[cfg(feature = "no_std")]
18+
use alloc::{vec, vec::Vec};
1919
use core::cmp::max;
2020
use core::iter::{DoubleEndedIterator, ExactSizeIterator};
2121
use core::marker::PhantomData;
2222
use core::ptr::write_bytes;
23-
use core::slice::from_raw_parts;
24-
#[cfg(feature = "no_std")]
25-
use alloc::{vec, vec::Vec};
2623

27-
use crate::endian_scalar::{emplace_scalar, read_scalar_at};
24+
use crate::endian_scalar::emplace_scalar;
2825
use crate::primitives::*;
2926
use crate::push::{Push, PushAlignment};
27+
use crate::read_scalar;
3028
use crate::table::Table;
31-
use crate::vector::{SafeSliceAccess, Vector};
29+
use crate::vector::Vector;
3230
use crate::vtable::{field_index_to_field_offset, VTable};
3331
use crate::vtable_writer::VTableWriter;
3432

35-
pub const N_SMALLVEC_STRING_VECTOR_CAPACITY: usize = 16;
36-
3733
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3834
struct FieldLoc {
3935
off: UOffsetT,
@@ -121,6 +117,8 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
121117
{
122118
let to_clear = self.owned_buf.len() - self.head;
123119
let ptr = (&mut self.owned_buf[self.head..]).as_mut_ptr();
120+
// Safety:
121+
// Verified ptr is valid for `to_clear` above
124122
unsafe {
125123
write_bytes(ptr, 0, to_clear);
126124
}
@@ -153,7 +151,9 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
153151
self.make_space(sz);
154152
{
155153
let (dst, rest) = (&mut self.owned_buf[self.head..]).split_at_mut(sz);
156-
x.push(dst, rest);
154+
// Safety:
155+
// Called make_space above
156+
unsafe { x.push(dst, rest.len()) };
157157
}
158158
WIPOffset::new(self.used_space() as UOffsetT)
159159
}
@@ -309,73 +309,32 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
309309
WIPOffset::new(self.used_space() as UOffsetT)
310310
}
311311

312-
/// Create a vector by memcpy'ing. This is much faster than calling
313-
/// `create_vector`, but the underlying type must be represented as
314-
/// little-endian on the host machine. This property is encoded in the
315-
/// type system through the SafeSliceAccess trait. The following types are
316-
/// always safe, on any platform: bool, u8, i8, and any
317-
/// FlatBuffers-generated struct.
318-
#[inline]
319-
pub fn create_vector_direct<'a: 'b, 'b, T: SafeSliceAccess + Push + Sized + 'b>(
320-
&'a mut self,
321-
items: &'b [T],
322-
) -> WIPOffset<Vector<'fbb, T>> {
323-
self.assert_not_nested(
324-
"create_vector_direct can not be called when a table or vector is under construction",
325-
);
326-
let elem_size = T::size();
327-
self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
328-
329-
let bytes = {
330-
let ptr = items.as_ptr() as *const T as *const u8;
331-
unsafe { from_raw_parts(ptr, items.len() * elem_size) }
332-
};
333-
self.push_bytes_unprefixed(bytes);
334-
self.push(items.len() as UOffsetT);
335-
336-
WIPOffset::new(self.used_space() as UOffsetT)
337-
}
338-
339-
/// Create a vector of strings.
340-
///
341-
/// Speed-sensitive users may wish to reduce memory usage by creating the
342-
/// vector manually: use `start_vector`, `push`, and `end_vector`.
343-
#[inline]
344-
pub fn create_vector_of_strings<'a, 'b>(
345-
&'a mut self,
346-
xs: &'b [&'b str],
347-
) -> WIPOffset<Vector<'fbb, ForwardsUOffset<&'fbb str>>> {
348-
self.assert_not_nested("create_vector_of_strings can not be called when a table or vector is under construction");
349-
// internally, smallvec can be a stack-allocated or heap-allocated vector:
350-
// if xs.len() > N_SMALLVEC_STRING_VECTOR_CAPACITY then it will overflow to the heap.
351-
let mut offsets: smallvec::SmallVec<[WIPOffset<&str>; N_SMALLVEC_STRING_VECTOR_CAPACITY]> =
352-
smallvec::SmallVec::with_capacity(xs.len());
353-
unsafe {
354-
offsets.set_len(xs.len());
355-
}
356-
357-
// note that this happens in reverse, because the buffer is built back-to-front:
358-
for (i, &s) in xs.iter().enumerate().rev() {
359-
let o = self.create_string(s);
360-
offsets[i] = o;
361-
}
362-
self.create_vector(&offsets[..])
363-
}
364-
365312
/// Create a vector of Push-able objects.
366313
///
367314
/// Speed-sensitive users may wish to reduce memory usage by creating the
368315
/// vector manually: use `start_vector`, `push`, and `end_vector`.
369316
#[inline]
370-
pub fn create_vector<'a: 'b, 'b, T: Push + Copy + 'b>(
317+
pub fn create_vector<'a: 'b, 'b, T: Push + 'b>(
371318
&'a mut self,
372319
items: &'b [T],
373320
) -> WIPOffset<Vector<'fbb, T::Output>> {
374321
let elem_size = T::size();
375-
self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
376-
for i in (0..items.len()).rev() {
377-
self.push(items[i]);
322+
let slice_size = items.len() * elem_size;
323+
self.align(slice_size, T::alignment().max_of(SIZE_UOFFSET));
324+
self.ensure_capacity(slice_size + UOffsetT::size());
325+
326+
self.head -= slice_size;
327+
let mut written_len = self.owned_buf.len() - self.head;
328+
329+
let buf = &mut self.owned_buf[self.head..self.head + slice_size];
330+
for (item, out) in items.iter().zip(buf.chunks_exact_mut(elem_size)) {
331+
written_len -= elem_size;
332+
333+
// Safety:
334+
// Called ensure_capacity and aligned to T above
335+
unsafe { item.push(out, written_len) };
378336
}
337+
379338
WIPOffset::new(self.push::<UOffsetT>(items.len() as UOffsetT).value())
380339
}
381340

@@ -384,17 +343,18 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
384343
/// Speed-sensitive users may wish to reduce memory usage by creating the
385344
/// vector manually: use `start_vector`, `push`, and `end_vector`.
386345
#[inline]
387-
pub fn create_vector_from_iter<T: Push + Copy>(
346+
pub fn create_vector_from_iter<T: Push>(
388347
&mut self,
389348
items: impl ExactSizeIterator<Item = T> + DoubleEndedIterator,
390349
) -> WIPOffset<Vector<'fbb, T::Output>> {
391350
let elem_size = T::size();
392-
let len = items.len();
393-
self.align(len * elem_size, T::alignment().max_of(SIZE_UOFFSET));
351+
self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
352+
let mut actual = 0;
394353
for item in items.rev() {
395354
self.push(item);
355+
actual += 1;
396356
}
397-
WIPOffset::new(self.push::<UOffsetT>(len as UOffsetT).value())
357+
WIPOffset::new(self.push::<UOffsetT>(actual).value())
398358
}
399359

400360
/// Set whether default values are stored.
@@ -443,7 +403,15 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
443403
assert_msg_name: &'static str,
444404
) {
445405
let idx = self.used_space() - tab_revloc.value() as usize;
446-
let tab = Table::new(&self.owned_buf[self.head..], idx);
406+
407+
// Safety:
408+
// The value of TableFinishedWIPOffset is the offset from the end of owned_buf
409+
// to an SOffsetT pointing to a valid VTable
410+
//
411+
// `self.owned_buf.len() = self.used_space() + self.head`
412+
// `self.owned_buf.len() - tab_revloc = self.used_space() - tab_revloc + self.head`
413+
// `self.owned_buf.len() - tab_revloc = idx + self.head`
414+
let tab = unsafe { Table::new(&self.owned_buf[self.head..], idx) };
447415
let o = tab.vtable().get(slot_byte_loc) as usize;
448416
assert!(o != 0, "missing required field {}", assert_msg_name);
449417
}
@@ -560,11 +528,15 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
560528
}
561529
}
562530
let new_vt_bytes = &self.owned_buf[vt_start_pos..vt_end_pos];
563-
let found = self.written_vtable_revpos.binary_search_by(|old_vtable_revpos: &UOffsetT| {
564-
let old_vtable_pos = self.owned_buf.len() - *old_vtable_revpos as usize;
565-
let old_vtable = VTable::init(&self.owned_buf, old_vtable_pos);
566-
new_vt_bytes.cmp(old_vtable.as_bytes())
567-
});
531+
let found = self
532+
.written_vtable_revpos
533+
.binary_search_by(|old_vtable_revpos: &UOffsetT| {
534+
let old_vtable_pos = self.owned_buf.len() - *old_vtable_revpos as usize;
535+
// Safety:
536+
// Already written vtables are valid by construction
537+
let old_vtable = unsafe { VTable::init(&self.owned_buf, old_vtable_pos) };
538+
new_vt_bytes.cmp(old_vtable.as_bytes())
539+
});
568540
let final_vtable_revpos = match found {
569541
Ok(i) => {
570542
// The new vtable is a duplicate so clear it.
@@ -581,12 +553,22 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
581553
};
582554
// Write signed offset from table to its vtable.
583555
let table_pos = self.owned_buf.len() - object_revloc_to_vtable.value() as usize;
584-
let tmp_soffset_to_vt = unsafe { read_scalar_at::<UOffsetT>(&self.owned_buf, table_pos) };
585-
debug_assert_eq!(tmp_soffset_to_vt, 0xF0F0_F0F0);
556+
if cfg!(debug_assertions) {
557+
// Safety:
558+
// Verified slice length
559+
let tmp_soffset_to_vt = unsafe {
560+
read_scalar::<UOffsetT>(&self.owned_buf[table_pos..table_pos + SIZE_UOFFSET])
561+
};
562+
assert_eq!(tmp_soffset_to_vt, 0xF0F0_F0F0);
563+
}
564+
565+
let buf = &mut self.owned_buf[table_pos..table_pos + SIZE_SOFFSET];
566+
// Safety:
567+
// Verified length of buf above
586568
unsafe {
587569
emplace_scalar::<SOffsetT>(
588-
&mut self.owned_buf[table_pos..table_pos + SIZE_SOFFSET],
589-
final_vtable_revpos as SOffsetT - object_revloc_to_vtable.value() as SOffsetT
570+
buf,
571+
final_vtable_revpos as SOffsetT - object_revloc_to_vtable.value() as SOffsetT,
590572
);
591573
}
592574

@@ -624,6 +606,8 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
624606
// finally, zero out the old end data.
625607
{
626608
let ptr = (&mut self.owned_buf[..middle]).as_mut_ptr();
609+
// Safety:
610+
// ptr is byte aligned and of length middle
627611
unsafe {
628612
write_bytes(ptr, 0, middle);
629613
}

0 commit comments

Comments
 (0)