worker: better error handling for memory maps

Previously, we would bail out of using memory maps if we could detect
ahead of time that opening a memory map would fail. The only case we
checked was whether the file size was 0 or not.

This is actually insufficient. The mmap call can return ENODEV errors
when a file doesn't support memory maps. This is the case for new files
exposed by Linux, for example,
/sys/devices/system/cpu/vulnerabilities/meltdown.

We fix this by checking the actual error codes returned by the mmap call.
If ENODEV (or EOVERFLOW) is returned, then we fall back to regular `read`
calls. If any other error occurs, we report it to the user.

Fixes #760
This commit is contained in:
Andrew Gallant 2018-01-31 19:20:36 -05:00
parent 0fedaa7d28
commit 93943793c3
No known key found for this signature in database
GPG Key ID: B2E3A4923F8B0D44
2 changed files with 29 additions and 2 deletions

View File

@ -9,6 +9,7 @@ extern crate grep;
extern crate ignore; extern crate ignore;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate libc;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate memchr; extern crate memchr;
@ -339,7 +340,6 @@ fn eprint_nothing_searched() {
// https://github.com/BurntSushi/ripgrep/issues/200. // https://github.com/BurntSushi/ripgrep/issues/200.
#[cfg(unix)] #[cfg(unix)]
fn reset_sigpipe() { fn reset_sigpipe() {
extern crate libc;
unsafe { unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL); libc::signal(libc::SIGPIPE, libc::SIG_DFL);
} }

View File

@ -310,7 +310,10 @@ impl Worker {
// regular read calls. // regular read calls.
return self.search(printer, path, file); return self.search(printer, path, file);
} }
let mmap = unsafe { Mmap::map(file)? }; let mmap = match self.mmap(file)? {
None => return self.search(printer, path, file),
Some(mmap) => mmap,
};
let buf = &*mmap; let buf = &*mmap;
if buf.len() >= 3 && Encoding::for_bom(buf).is_some() { if buf.len() >= 3 && Encoding::for_bom(buf).is_some() {
// If we have a UTF-16 bom in our memory map, then we need to fall // If we have a UTF-16 bom in our memory map, then we need to fall
@ -330,4 +333,28 @@ impl Worker {
.text(self.opts.text) .text(self.opts.text)
.run()) .run())
} }
#[cfg(not(unix))]
fn mmap(&self, file: &File) -> Result<Option<Mmap>> {
Ok(Some(mmap_readonly(file)?))
}
#[cfg(unix)]
fn mmap(&self, file: &File) -> Result<Option<Mmap>> {
use libc::{ENODEV, EOVERFLOW};
let err = match mmap_readonly(file) {
Ok(mmap) => return Ok(Some(mmap)),
Err(err) => err,
};
let code = err.raw_os_error();
if code == Some(ENODEV) || code == Some(EOVERFLOW) {
return Ok(None);
}
Err(From::from(err))
}
}
fn mmap_readonly(file: &File) -> io::Result<Mmap> {
unsafe { Mmap::map(file) }
} }