nix-rust / nix

Rust friendly bindings to *nix APIs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Calling an ioctl_none! with unflushed stdout leads to uninitialized memory access.

rvvvr opened this issue · comments

Calling an ioctl defined by ioctl_none! has a chance to access some memory it shouldn't when preparations have been made to push a buffer to stdout (specifically the length of the buffer) without flushing. For example:

// example from kvm bindings I've been writing. vcpu,run() is a pure wrapper around the KVM_RUN ioctl, with that very ioctl being the first function it calls.
print!("abcdefghijklmnop");
vcpu.run().unwrap();

strace output from the above example:

ioctl(5, KVM_RUN, 0x10) = -1 EINVAL (Invalid Argument) //same length as that string above...

but, when explicitly flushing the buffer...

print!("abcdefghijklmnop");
std::io::stdout().flush().unwrap();
vcpu.run().unwrap();

strace output now:

write(1, "abcdefghijklmnop", 16abcdefghijklmnop) = 16
ioctl(5, KVM_RUN, 0) = 0

this is explicitly a problem when emulating, for example, uart serial communication with KVM, as it's pretty common practice to throw out characters 1 by 1 as you receive them. this should be (in theory) as simple to fix as explicitly passing 0 to ioctl calls in ioctl_none*! macros.

"passing 0 to ioctl calls" for what argument? Have you done cargo expand to look at the generated code?

I have! The macro right now expands out to a call to ioctl leaving the first vararg unspecified. This (as I understand it) is incorrect, given that glibc will look for one vararg whether or not it should, and leads to grabbing some arbitrary value off of the stack as that third argument.

The docs say that KVM_RUN has no arguments. But the only example I can find with its use provides an argument of zero. Maybe you need to define it with ioctl_write_int_bad! and provide that argument when you call it.
https://github.com/rminnich/vmtool/blob/31cf14e56896aee4546a63e3bc7b911174362430/kvm-cpu.c#L34