-
Notifications
You must be signed in to change notification settings - Fork 80
Remove lifetime of Capstone, and adjust self mutablity for disasm #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… immutable reference to self
…present the fact that this is a eager iterator
Codecov Report
@@ Coverage Diff @@
## master #49 +/- ##
==========================================
+ Coverage 93.85% 94.07% +0.21%
==========================================
Files 15 15
Lines 2019 2094 +75
==========================================
+ Hits 1895 1970 +75
Misses 124 124
Continue to review full report at Codecov.
|
@@ -157,7 +154,7 @@ impl<'cs> Capstone<'cs> { | |||
|
|||
/// Disassemble all instructions in buffer | |||
pub fn disasm_all<'a>( | |||
&mut self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried about removing the mut
requirement for self
on the disasm()
methods since the cs_disasm()
function mutates the underlying handle:
https://github.com/capstone-rust/capstone-sys/blob/master/capstone/cs.c#L646-L650
However, maybe this is safe to do since:
- Capstone is not
Send
orSync
- Capstone is not
Clone
(so the handle should be unique to a givenCapstone
instance)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is like a UnsafeCell hidden behind FFI... So if we can prove that it is not possible to have two disasm
call in progress simultaneously, we should be safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the other hand, if this turn out not safe, then the csh
method will need to accept &mut self
, as the semantic requires csh
works like a *mut c_void
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To ensure safety, we need to prove that is is not possible to perform two operation that mutate the underlying handle simultaneously.
If it is not safe, we need to change csh
method to accept &mut self
, because it returns something that semantically equal to *mut c_void
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As Send
or Sync
is not an issue, unwinding is become the main focus as this is another way to run code simutaneously (i.e. mutation in one piece of code panic, and unwind calls another piece of mutating code).
So your unsafe advanture will arrive the next stop: unwind safety. The following is from Nomicon:
Rust's unwinding strategy is not specified to be fundamentally compatible with any other language's unwinding. As such, unwinding into Rust from another language, or unwinding into another language from Rust is Undefined Behavior. You must absolutely catch any panics at the FFI boundary! What you do at that point is up to you, but something must be done. If you fail to do this, at best, your application will crash and burn. At worst, your application won't crash and burn, and will proceed with completely clobbered state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, unfortunately there is no easy solutions, you need to be ready to create a wrapper in C and making sure all exceptions are handled in the wrapper. Or, if you are sure the C function is already exception free, you can just trust it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this library, the Rust code calls into the C code; the C code never calls into the Rust (or any other language).
From that point of view, we should also be safe (since C has no exceptions or stack unwinding).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like that Capstone
is also not UnwindSafe
, because it contains *mut _
, this is a good thing as this only prevents you to return a Capstone
from catch_unwind
, and this is the safety garantee that the underline handle can never being called simultaneously.
@@ -157,7 +154,7 @@ impl<'cs> Capstone<'cs> { | |||
|
|||
/// Disassemble all instructions in buffer | |||
pub fn disasm_all<'a>( | |||
&mut self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To ensure safety, we need to prove that is is not possible to perform two operation that mutate the underlying handle simultaneously.
If it is not safe, we need to change csh
method to accept &mut self
, because it returns something that semantically equal to *mut c_void
.
@@ -157,7 +154,7 @@ impl<'cs> Capstone<'cs> { | |||
|
|||
/// Disassemble all instructions in buffer | |||
pub fn disasm_all<'a>( | |||
&mut self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As Send
or Sync
is not an issue, unwinding is become the main focus as this is another way to run code simutaneously (i.e. mutation in one piece of code panic, and unwind calls another piece of mutating code).
So your unsafe advanture will arrive the next stop: unwind safety. The following is from Nomicon:
Rust's unwinding strategy is not specified to be fundamentally compatible with any other language's unwinding. As such, unwinding into Rust from another language, or unwinding into another language from Rust is Undefined Behavior. You must absolutely catch any panics at the FFI boundary! What you do at that point is up to you, but something must be done. If you fail to do this, at best, your application will crash and burn. At worst, your application won't crash and burn, and will proceed with completely clobbered state.
@@ -157,7 +154,7 @@ impl<'cs> Capstone<'cs> { | |||
|
|||
/// Disassemble all instructions in buffer | |||
pub fn disasm_all<'a>( | |||
&mut self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, unfortunately there is no easy solutions, you need to be ready to create a wrapper in C and making sure all exceptions are handled in the wrapper. Or, if you are sure the C function is already exception free, you can just trust it.
I think that remove the @earthengine thanks for your help! |
Discussed in the following post:
https://www.reddit.com/r/rust/comments/9hwr5r/a_rust_ffi_adventure_in_unsafety/
Conclusion:
Capstone
to ensureInstructions
's lifetime constrainted.disasm
methods might not actually needself
to be mutable, as it only access immutable methods.disasm
methods uses unbound lifetime, which is not desired here. Bound toself
instead. However, as the semantic is eager evaluation, there is no need to also bound the code slice.disasm
is no longer mutatingCapstone
.This change compiles all tests, but I didn't run the tests, please confirm with your own test before merge.