keystone
keystone copied to clipboard
Rust binding give out random values
Running on linux x86_64 with version crates.io version 0.9.0. When I assemble something for x86_64, the Rust keystone package returns inconsistent and seemingly random values. For example, if I try to assemble a single "nop", it gives out a single byte value between 0x00 and 0xF0 (a multiple of 0x10, so the right-most hex character is always 0). Haven't really checked if this is fixed on the current version that's not on crates.io.
Currently 0.9.0 is indeed currently the latest version on crates.io so that's a problem. I was able to reproduce the described issue with the following code:
use ::keystone::{
Arch,
keystone_const::*,
Keystone,
};
fn main ()
{
let keystone = Keystone::new(
Arch::X86,
Mode::MODE_32,
).unwrap();
for _ in 0 .. 1_000 {
let bytes = keystone.asm("nop".into(), 0).unwrap().bytes;
assert_eq!(bytes, [0x90]);
}
}
Their current version 0.10.0 seems to fix the issue, at least for a single threaded scenario. So you can fix this by using the following config in your Cargo.toml:
[dependencies.keystone]
git = "https://github.com/keystone-engine/keystone"
rev = "c4de98f71f05f356817f5e91fee0d509c7b0b440" # latest master as of 2019/10/16
while waiting for the official release.
In a multi-threaded scenario, however, the API is at least buggy, and I even suspect it to be unsound:
all the functions taking a Keystone instance to perform FFI operations just take it by &self, i.e., by shared reference. Moreover, since FFI raw pointers are represented as size_t = usize from the Rust side, the Sync trait was not auto-unimplemented as it should have; thus leading to Keystone : Sync. Combined with the &self API, this means that FFI functions can be called by multiple threads in parallel using the same Keystone instance, and I doubt that such thing is sound.
For instance, the following program panic!s when using the currently latest revision of master (c4de98f71f05f356817f5e91fee0d509c7b0b440) :
use ::keystone::{
Arch,
Mode,
Keystone,
};
fn main ()
{
let keystone = Keystone::new(
Arch::X86,
Mode::MODE_32,
).unwrap();
::crossbeam::thread::scope(|scope| {
scope.spawn(|_| {
loop {
let bytes = keystone.asm("nop".into(), 0).unwrap().bytes;
assert_eq!(bytes, [0x90]);
}
});
loop {
let bytes = keystone.asm("nop".into(), 0).unwrap().bytes;
assert_eq!(bytes, [0x90]);
}
}).unwrap();
}