mac: Tolerate the new flavor of weird cl_kernels modules on 10.14

OpenCL modules that appeared as “cl_kernels” since 10.7 now show up in
10.14 as ad-hoc signed modules at
/private/var/db/CVMS/cvmsCodeSignObjXXXXXXXXXXXXXXXX (16 random
characters). The modules are unlinked from the filesystem once loaded.

Bug: crashpad:243
Change-Id: I00fdd1311d4e6cd4c9224ef54ac990ac1afb849c
Reviewed-on: https://chromium-review.googlesource.com/1142027
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Mark Mentovai 2018-07-18 11:16:21 -04:00 committed by Commit Bot
parent fb0f7ca8d7
commit 03abd1bb34
3 changed files with 92 additions and 20 deletions

View File

@ -15,6 +15,7 @@
#include "snapshot/mac/mach_o_image_segment_reader.h"
#include <mach-o/loader.h>
#include <string.h>
#include <utility>
@ -35,6 +36,37 @@ std::string SizeLimitedCString(const char* c_string, size_t max_length) {
} // namespace
bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
const std::string& module_name,
bool* has_timestamp) {
if (mach_o_file_type != MH_BUNDLE) {
return false;
}
if (module_name == "cl_kernels") {
if (MacOSXMinorVersion() >= 10) {
if (has_timestamp) {
*has_timestamp = false;
}
return true;
}
return false;
}
static const char kCvmsObjectPathPrefix[] =
"/private/var/db/CVMS/cvmsCodeSignObj";
if (module_name.compare(
0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 &&
MacOSXMinorVersion() >= 14) {
if (has_timestamp) {
*has_timestamp = true;
}
return true;
}
return false;
}
MachOImageSegmentReader::MachOImageSegmentReader()
: segment_command_(),
sections_(),
@ -121,21 +153,13 @@ bool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader,
load_command_info.c_str());
// cl_kernels modules (for OpenCL) arent ld output, and theyre formatted
// incorrectly on OS X 10.10 and later. They have a single __TEXT segment,
// but one of the sections within it claims to belong to the __LD segment.
// This mismatch shouldnt happen. This errant section also has the
// S_ATTR_DEBUG flag set, which shouldnt happen unless all of the other
// sections in the segment also have this bit set (they dont). These odd
// sections are reminiscent of unwind information stored in MH_OBJECT
// images, although cl_kernels images claim to be MH_BUNDLE. Because at
// least one cl_kernels module will commonly be found in a process, and
// sometimes more will be, tolerate this quirk.
// incorrectly on OS X 10.10 and later. Because at least one cl_kernels
// module will commonly be found in a process, and sometimes more will be,
// tolerate this quirk.
//
// https://openradar.appspot.com/20239912
if (section_segment_name != segment_name &&
!(file_type == MH_BUNDLE &&
module_name == "cl_kernels" &&
MacOSXMinorVersion() >= 10 &&
!(IsMalformedCLKernelsModule(file_type, module_name, nullptr) &&
segment_name == SEG_TEXT &&
section_segment_name == "__LD" &&
section_name == "__compact_unwind" &&

View File

@ -29,6 +29,41 @@
namespace crashpad {
//! \brief Determines whether a module appears to be a malformed OpenCL
//! `cl_kernels` module based on its name and Mach-O file type.
//!
//! `cl_kernels` modules require special handling because theyre malformed on
//! OS X 10.10 and later. A `cl_kernels` module always has Mach-O type
//! `MH_BUNDLE` and is named `"cl_kernels"` until macOS 10.14, and
//! `"/private/var/db/CVMS/cvmsCodeSignObj"` plus 16 random characters on macOS
//! 10.14.
//!
//! Malformed `cl_kernels` modules have a single `__TEXT` segment, but one of
//! the sections within it claims to belong to the `__LD` segment. This mismatch
//! shouldnt happen. This errant section also has the `S_ATTR_DEBUG` flag set,
//! which shouldnt happen unless all of the other sections in the segment also
//! have this bit set (they dont). These odd sections are reminiscent of unwind
//! information stored in `MH_OBJECT` images, although `cl_kernels` images claim
//! to be `MH_BUNDLE`.
//!
//! This function is exposed for testing purposes only.
//!
//! \param[in] mach_o_file_type The Mach-O type of the module being examined.
//! \param[in] module_name The pathname that `dyld` reported having loaded the
//! module from.
//! \param[out] has_timestamp Optional, may be `nullptr`. If provided, and the
//! module is a maformed `cl_kernels` module, this will be set to `true` if
//! the module was loaded from the filesystem (as is the case when loaded
//! from the CVMS directory) and is expected to have a timestamp, and
//! `false` otherwise. Note that even when loaded from the filesystem, these
//! modules are unlinked from the filesystem after loading.
//!
//! \return `true` if the module appears to be a malformed `cl_kernels` module
//! based on the provided information, `false` otherwise.
bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
const std::string& module_name,
bool* has_timestamp);
//! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O
//! images mapped into another process.
//!

View File

@ -32,6 +32,7 @@
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "snapshot/mac/mach_o_image_reader.h"
#include "snapshot/mac/mach_o_image_segment_reader.h"
#include "test/errors.h"
#include "test/mac/dyld.h"
#include "test/mac/mach_errors.h"
@ -663,14 +664,20 @@ TEST(ProcessReaderMac, SelfModules) {
modules[index].reader->Address(),
FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index)));
bool expect_timestamp;
if (index == 0) {
// dyld didnt load the main executable, so it couldnt record its
// timestamp, and it is reported as 0.
EXPECT_EQ(modules[index].timestamp, 0);
} else if (modules[index].reader->FileType() == MH_BUNDLE &&
modules[index].name == "cl_kernels") {
// cl_kernels doesnt exist as a file.
EXPECT_EQ(modules[index].timestamp, 0);
} else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(),
modules[index].name,
&expect_timestamp)) {
// cl_kernels doesnt exist as a file, but may still have a timestamp.
if (!expect_timestamp) {
EXPECT_EQ(modules[index].timestamp, 0);
} else {
EXPECT_NE(modules[index].timestamp, 0);
}
found_cl_kernels = true;
} else {
// Hope that the module didnt change on disk.
@ -747,14 +754,20 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
ASSERT_TRUE(modules[index].reader);
EXPECT_EQ(modules[index].reader->Address(), expect_address);
bool expect_timestamp;
if (index == 0 || index == modules.size() - 1) {
// dyld didnt load the main executable or itself, so it couldnt record
// these timestamps, and they are reported as 0.
EXPECT_EQ(modules[index].timestamp, 0);
} else if (modules[index].reader->FileType() == MH_BUNDLE &&
modules[index].name == "cl_kernels") {
// cl_kernels doesnt exist as a file.
EXPECT_EQ(modules[index].timestamp, 0);
} else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(),
modules[index].name,
&expect_timestamp)) {
// cl_kernels doesnt exist as a file, but may still have a timestamp.
if (!expect_timestamp) {
EXPECT_EQ(modules[index].timestamp, 0);
} else {
EXPECT_NE(modules[index].timestamp, 0);
}
found_cl_kernels = true;
} else {
// Hope that the module didnt change on disk.