rust-secure-code / safety-dance

Auditing crates for unsafe code which can be safely replaced
Apache License 2.0
537 stars 10 forks source link

Audit libgoblin #8

Open Shnatsel opened 5 years ago

Shnatsel commented 5 years ago

https://crates.io/crates/goblin

Parser for binary formats: ELF, Mach-O, PE. 750 downloads/day. High-risk due to being a binary parser and potentially exposed to untrusted input.

At a glance the ELF module contains unnecessary unsafe code. It should be possible to rewrite it safely without losing performance.

nico-abram commented 5 years ago

There seems to be quite a bit of unsafe in here.

Some low hanging fruit seems to be in https://github.com/m4b/goblin/blob/master/src/pe/data_directories.rs#L38-L97 . I believe these unsafe get_unchecked's to be completely unnecessary. In my experience (At least in small samples in godbolt), rust seems to elide the bounds checks when indexing a constant when using arrays with a length longer than that constant (Example: https://godbolt.org/z/tHdEs4 ). Anecdotically, someone was talking about get_unchecked being slower on debug than the regular indexing because it didn't inline the call or some such the other day in the rust community discord iirc.

danielhenrymantilla commented 5 years ago

To be honest, I don't think debug performance matters that much (when it does, one can adjust the opt-level for the [profile.dev]).

On the other hand, the code you just linked could be far more resiliant (e.g., to code refactoring) if the lets became const, which would allow appending static assertions for the bound checks.


On the other hand, all these unsafe blocks without // # Safety annotations are indeed worrisome: the least thing to do is to add such annotations justifying their soundness (if any).

icefoxen commented 5 years ago

Sheesh, that's some weird Rust code. Very C-ish in style, though I guess they really don't want to copy things if they can avoid it. Looking through the ELF code, at least, most of the unsafety is hazardous but reasonable -- A lot of it is unsafe impl plain::Plain for Whatever {}, which provides methods via the plain crate allowing casting to and from structs that have the correct layout -- these impl's seem to always be on structs with #[repr(C)] that match ELF header structure layouts. There's also a number of implementations of from_raw_parts(), which on its own are (mostly?) harmless and just call slice::from_raw_parts().

More dubious things in src/elf/*.rs: