-
-
Notifications
You must be signed in to change notification settings - Fork 385
Use mmap for faster warn_missing_py_init #2950
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
Changes from all commits
b425f56
102529d
d3b54bc
038aaad
a408987
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -9,7 +9,7 @@ use fs_err::{self as fs, File}; | |||
| use normpath::PathExt; | ||||
| use std::collections::HashMap; | ||||
| use std::env; | ||||
| use std::io::{BufReader, Read}; | ||||
| use std::io::BufReader; | ||||
| use std::path::{Path, PathBuf}; | ||||
| use std::process::{Command, Stdio}; | ||||
| use std::str; | ||||
|
|
@@ -753,11 +753,13 @@ fn compile_target( | |||
| #[instrument(skip_all)] | ||||
| pub fn warn_missing_py_init(artifact: &Path, module_name: &str) -> Result<()> { | ||||
| let py_init = format!("PyInit_{module_name}"); | ||||
| let mut fd = File::open(artifact)?; | ||||
| let mut buffer = Vec::new(); | ||||
| fd.read_to_end(&mut buffer)?; | ||||
| let fd = File::open(artifact)?; | ||||
| // SAFETY: The caller stages (moves/copies) the artifact into a private | ||||
| // directory before invoking this function, so no concurrent process | ||||
| // (e.g. cargo / rust-analyzer) can modify it while we have it mapped. | ||||
| let mmap = unsafe { memmap2::Mmap::map(&fd).context("mmap failed")? }; | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that sound, does it guarantee non of the rust invariants are being violated? If so, can you write a safety comment on why it it safe to do?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @konstin Please see https://docs.rs/memmap2/0.9.9/memmap2/struct.Mmap.html#safety. I'm not exactly sure what kind of safety comment you'd be looking for. It is technically inherently 'unsound' by the Rust abstract model if someone edits the file in the file system at the same time. But
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm afraid I'm not comfortable with using mmap like that in maturin. For context, the only production usage of
As long as we can't guarantee that the artifact is not actually modified by some means, we risk UB, and that's a risk that's not worth it for this performance win.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @konstin Which risks are you worried about? 'UB' is not a risk in of itself, it's a binary property of a program that states the abstract model of Rust was violated, a risk needs an actual attacker model, consequence and probability of exploitation. Mmap is already widely used in the Rust compiler: https://github.com/search?q=repo%3Arust-lang%2Frust%20mmap&type=code. I'd really like to know which risks in particular you're worried about that are unacceptable for maturin but are apparently acceptable for the Rust compiler.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know which measure or soundness considerations the rust-lang/rust repo has around mmap, but the documentation you linked at https://docs.rs/memmap2/0.9.9/memmap2/struct.Mmap.html#safety is very clear that unless we guarantee that the underlying file doesn't change, this is unsound. I assume those mmap calls ensure this, but here I don't see that we've ensured that (yet). With rust being safe by default, using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @konstin "Measure of soundness" is the wrong measure. The API is fundamentally unsound. You can not prevent someone from mutating the file. There also is no alternative without re-writing the entire parser to be I'm perfectly aware of how Rust works with unsafe. You are however holding me to an impossible standard. There is no safety comment I could possibly write containing the contract we uphold, because we can not possibly guarantee that the contract is upheld. This is no different in the Rust compiler. However, would you be satisfied with the following logic? // SAFETY: technically this is unsound if the file gets modified while we hold the
// immutable &[u8] slice. However, considering we're reading a dynamic library
// with executable code intended to be run, should a malicious agent have write
// access to it, arbitrary code execution is already trivially achieved. So there
// is no reasonable attacker model where this would lead to exploits that are
// otherwise impossible.
let mmap = unsafe { memmap2::Mmap::map(&fd).context("mmap failed")? };
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant more in the sense of, can we ensure that this is not a file that changes underneath the mmap if you do another
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No, you can not in general. And should this be a problem (it's not, really), you're already in the woods because you call maturin/src/auditwheel/repair.rs Line 15 in e7922ab
which does... you guessed it, I must re-iterate that I feel you're holding me to an impossibly high standard for this PR, considering your tool already does the exact same strategy I proposed elsewhere (as well as your dependencies in the Rust compiler, probably others). |
||||
| let mut found = false; | ||||
| match goblin::Object::parse(&buffer)? { | ||||
| match goblin::Object::parse(&mmap)? { | ||||
| goblin::Object::Elf(elf) => { | ||||
| for dyn_sym in elf.dynsyms.iter() { | ||||
| if py_init == elf.dynstrtab[dyn_sym.st_name] { | ||||
|
|
||||
Uh oh!
There was an error while loading. Please reload this page.