I've noticed the following pattern is used in allocating a bz_stream object from Rust within src/mem.rs:
let mut raw = Box::new(mem::zeroed());
assert_eq!(
ffi::BZ2_bzCompressInit(&mut *raw, lvl.level() as c_int, 0, work_factor as c_int),
0
);
Then, the unique borrow created by &mut *raw is used to create a cyclic data structure between the initialized bz_stream and its EState member, state. For example, in BZ2_bzCompressInit in bzlib.c, you have the following:
int BZ_API(BZ2_bzCompressInit)
( bz_stream* strm,
... )
{
...
EState* s;
...
s->strm = strm;
...
strm->state = s;
...
}
Every time the allocated bz_stream object is passed from Rust to C, it's done with &mut *raw. In Stacked Borrows, it seems like this would invalidate the SharedRW permission associated with the original pointer stored by s->strm = strm;. In Tree Borrows, it would initially transition the permission for s->strm to being Reserved the next time a &mut *raw is passed, so reading through it would still be allowed.
But, the root bz_stream allocation is both read and written to through its state member, so this would still lead to violations of the model eventually. For instance, in copy_input_until_stop (also in bzlib.c), you have both reads and writes from the EState pointer s to its parent bz_stream:
ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) );
s->strm->next_in++;
It seems like the fix for this would be to convert the Box into a raw pointer with Box::into_raw and pass this raw pointer directly to the FFI. Then, it would be re-wrapped it for deallocation in the implementation of Drop for Stream. However, I'm totally unsure if this would be a situation where violating the Stacked Borrows/Tree Borrows models would be problematic. What do you think?
I've noticed the following pattern is used in allocating a
bz_streamobject from Rust within src/mem.rs:Then, the unique borrow created by
&mut *rawis used to create a cyclic data structure between the initializedbz_streamand itsEStatemember,state. For example, inBZ2_bzCompressInitin bzlib.c, you have the following:Every time the allocated
bz_streamobject is passed from Rust to C, it's done with&mut *raw. In Stacked Borrows, it seems like this would invalidate the SharedRW permission associated with the original pointer stored bys->strm = strm;. In Tree Borrows, it would initially transition the permission fors->strmto beingReservedthe next time a&mut *rawis passed, so reading through it would still be allowed.But, the root
bz_streamallocation is both read and written to through itsstatemember, so this would still lead to violations of the model eventually. For instance, incopy_input_until_stop(also in bzlib.c), you have both reads and writes from theEStatepointersto its parentbz_stream:It seems like the fix for this would be to convert the
Boxinto a raw pointer withBox::into_rawand pass this raw pointer directly to the FFI. Then, it would be re-wrapped it for deallocation in the implementation ofDropforStream. However, I'm totally unsure if this would be a situation where violating the Stacked Borrows/Tree Borrows models would be problematic. What do you think?