linux: add fallback-modes for memfd_create

Bug: chromium:1051354
Change-Id: I5dbbb3b264c09060429db199aa9f046c2f317c48
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2080651
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2020-03-03 09:05:35 -08:00
parent 3c573b54ae
commit 7500e2ef45
6 changed files with 81 additions and 18 deletions

View File

@ -225,7 +225,7 @@ bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(
AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
FileWriter file_writer;
if (!file_writer.OpenMemfd(base::FilePath("/tmp/minidump"))) {
if (!file_writer.OpenMemfd(base::FilePath("minidump"))) {
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed);
return false;
}

View File

@ -399,16 +399,27 @@ FileHandle LoggingOpenFileForWrite(const base::FilePath& path,
FilePermissions permissions);
#if defined(OS_LINUX)
//! \brief Wraps memfd_create(), logging an error if the operation fails.
//! Unlike other file open operations, this doesn't set `O_CLOEXEC`.
//! \brief Opens an in-memory file for input and output.
//!
//! \return The newly opened FileHandle, or an invalid FileHandle on failure.
//! This function first attempts to open the file with `memfd_create()`. If
//! `memfd_create()` isn't supported by the kernel, this function next attempts
//! to open a file using `O_TMPFILE`. If `O_TMPFILE` isn't supported, this
//! function finally falls back to creating a file with a randomized name in
//! `/tmp` and immediately `unlink()`ing it.
//!
//! Unlike other file open operations, this function doesn't set `O_CLOEXEC`.
//!
//! \param name A name associated with the file. This name does not indicate any
//! exact path and may not be used at all, depending on the strategy used to
//! create the file. The name should not contain any '/' characters.
//! \return The newly opened FileHandle, or an invalid FileHandle on failure,
//! with a message logged.
//!
//! \sa ScopedFileHandle
//! \sa LoggingOpenFileForRead
//! \sa LoggingOpenFileForWrite
//! \sa LoggingOpenFileForReadAndWrite
FileHandle LoggingOpenMemFileForWrite(const base::FilePath& path);
FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name);
#endif // OS_LINUX
//! \brief Wraps OpenFileForReadAndWrite(), logging an error if the operation

View File

@ -14,6 +14,7 @@
#include "util/file/file_io.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/mman.h>
@ -26,7 +27,9 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "util/misc/random_string.h"
namespace crashpad {
@ -98,13 +101,6 @@ FileHandle OpenFileForOutput(int rdwr_or_wronly,
flags,
permissions == FilePermissions::kWorldReadable ? 0644 : 0600));
}
#if defined(OS_LINUX)
FileHandle OpenMemFileForOutput(const base::FilePath& path) {
return HANDLE_EINTR(memfd_create(path.value().c_str(), 0));
}
#endif
} // namespace
namespace internal {
@ -157,10 +153,49 @@ FileHandle LoggingOpenFileForWrite(const base::FilePath& path,
}
#if defined(OS_LINUX)
FileHandle LoggingOpenMemFileForWrite(const base::FilePath& path) {
FileHandle fd = OpenMemFileForOutput(path);
PLOG_IF(ERROR, fd < 0) << "memfd_create " << path.value();
return fd;
FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name) {
DCHECK(name.value().find('/') == std::string::npos);
int result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));
if (result >= 0 || errno != ENOSYS) {
PLOG_IF(ERROR, result < 0) << "memfd_create";
return result;
}
const char* tmp = getenv("TMPDIR");
tmp = tmp ? tmp : "/tmp";
result = HANDLE_EINTR(open(tmp, O_RDWR | O_EXCL | O_TMPFILE, 0600));
if (result >= 0 ||
// These are the expected possible error codes indicating that O_TMPFILE
// doesn't have kernel or filesystem support. O_TMPFILE was added in Linux
// 3.11. Experimentation confirms that at least Linux 2.6.29 and Linux
// 3.10 set errno to EISDIR. EOPNOTSUPP is returned when the filesystem
// doesn't support O_TMPFILE. The man pages also mention ENOENT as an
// error code to check, but the language implies it would only occur when
// |tmp| is also an invalid directory. EINVAL is mentioned as a possible
// error code for any invalid values in flags, but O_TMPFILE isn't
// mentioned explicitly in this context and hasn't been observed in
// practice.
(errno != EISDIR && errno != EOPNOTSUPP)) {
PLOG_IF(ERROR, result < 0) << "open";
return result;
}
std::string path = base::StringPrintf("%s/%s.%d.%s",
tmp,
name.value().c_str(),
getpid(),
RandomString().c_str());
result = HANDLE_EINTR(open(path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
if (result < 0) {
PLOG(ERROR) << "open";
return result;
}
if (unlink(path.c_str()) != 0) {
PLOG(WARNING) << "unlink";
}
return result;
}
#endif

View File

@ -473,6 +473,23 @@ TEST(FileIO, LoggingOpenFileForReadAndWrite) {
TestOpenFileForWrite(LoggingOpenFileForReadAndWrite);
}
#if defined(OS_LINUX)
TEST(FileIO, LoggingOpenMemoryFileForReadAndWrite) {
ScopedFileHandle handle(
LoggingOpenMemoryFileForReadAndWrite(base::FilePath("memfile")));
ASSERT_TRUE(handle.is_valid());
static constexpr char kTestData[] = "somedata";
ASSERT_TRUE(LoggingWriteFile(handle.get(), kTestData, sizeof(kTestData)));
ASSERT_EQ(LoggingSeekFile(handle.get(), 0, SEEK_SET), 0);
char buffer[sizeof(kTestData)];
ASSERT_TRUE(LoggingReadFileExactly(handle.get(), buffer, sizeof(buffer)));
EXPECT_EQ(memcmp(buffer, kTestData, sizeof(buffer)), 0);
}
#endif // OS_LINUX
enum class ReadOrWrite : bool {
kRead,
kWrite,

View File

@ -174,7 +174,7 @@ bool FileWriter::Open(const base::FilePath& path,
#if defined(OS_LINUX)
bool FileWriter::OpenMemfd(const base::FilePath& path) {
CHECK(!file_.is_valid());
file_.reset(LoggingOpenMemFileForWrite(path));
file_.reset(LoggingOpenMemoryFileForReadAndWrite(path));
if (!file_.is_valid()) {
return false;
}

View File

@ -132,7 +132,7 @@ class FileWriter : public FileWriterInterface {
FilePermissions permissions);
#if defined(OS_LINUX)
//! \brief Wraps LoggingOpenMemFileForWrite().
//! \brief Wraps LoggingOpenMemoryFileForWrite().
//!
//! \return `true` if the operation succeeded, `false` if it failed, with an
//! error message logged.