Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions folly/debugging/symbolizer/Elf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@
namespace folly {
namespace symbolizer {

namespace detail {

folly::StringPiece getNullTerminatedPathComponent(
folly::StringPiece section) noexcept {
auto const* begin = section.data();
auto const* nul =
static_cast<const char*>(memchr(begin, '\0', section.size()));
if (nul == nullptr || nul == begin) {
return {};
}
auto const nameLen = static_cast<size_t>(nul - begin);
if (memchr(begin, '/', nameLen) != nullptr ||
memchr(begin, '\\', nameLen) != nullptr) {
return {};
}
return folly::StringPiece(begin, nameLen);
}

} // namespace detail

ElfFile::ElfFile() noexcept
: fd_(-1),
file_(static_cast<char*>(MAP_FAILED)),
Expand Down Expand Up @@ -144,15 +164,21 @@ ElfFile::OpenResult ElfFile::openAndFollow(

// The section starts with the filename, with any leading directory
// components removed, followed by a zero byte.
auto debugFileName = getSectionBody(*debuginfo);
auto debugFileLen = strlen(debugFileName.begin());
if (dirlen + debugFileLen >= PATH_MAX) {
auto debugFileName =
detail::getNullTerminatedPathComponent(getSectionBody(*debuginfo));
if (debugFileName.empty()) {
return result;
}

auto debugFileLen = debugFileName.size();
if (dirlen + debugFileLen + 1 > PATH_MAX) {
return result;
}

char linkname[PATH_MAX];
memcpy(linkname, name, dirlen);
memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1);
memcpy(linkname + dirlen, debugFileName.data(), debugFileLen);
linkname[dirlen + debugFileLen] = '\0';
reset();
result = openNoThrow(linkname, options);
if (result == kSuccess) {
Expand Down
10 changes: 10 additions & 0 deletions folly/debugging/symbolizer/Elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@
namespace folly {
namespace symbolizer {

namespace detail {

// Extract and validate a filename-like C-string from section data, returning
// empty on malformed input (missing null terminator, empty name, or directory
// separator in the prefix).
folly::StringPiece getNullTerminatedPathComponent(
folly::StringPiece section) noexcept;

} // namespace detail

#if defined(ElfW)
#define FOLLY_ELF_ELFW(name) ElfW(name)
#elif defined(__FreeBSD__)
Expand Down
21 changes: 21 additions & 0 deletions folly/debugging/symbolizer/test/ElfTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,27 @@ TEST_F(ElfTest, FailToOpenLargeFilename) {
EXPECT_EQ(ElfFile::kSuccess, elfFile->openNoThrow(kDefaultElf));
}

TEST(TestDebugLinkParsing, ParsesNullTerminatedName) {
std::string raw("debug.bin\0ignored", 17);
auto const parsed = folly::symbolizer::detail::getNullTerminatedPathComponent(
folly::StringPiece(raw.data(), raw.size()));
EXPECT_EQ("debug.bin", parsed);
}

TEST(TestDebugLinkParsing, RejectsMissingNullTerminator) {
std::string raw("debug.bin", 9);
auto const parsed = folly::symbolizer::detail::getNullTerminatedPathComponent(
folly::StringPiece(raw.data(), raw.size()));
EXPECT_TRUE(parsed.empty());
}

TEST(TestDebugLinkParsing, RejectsDirectoryTraversalLikeNames) {
std::string raw("../debug.bin\0", 13);
auto const parsed = folly::symbolizer::detail::getNullTerminatedPathComponent(
folly::StringPiece(raw.data(), raw.size()));
EXPECT_TRUE(parsed.empty());
}

TEST(TestGetNoteGnuBuildId, SimpleElf) {
auto const file =
folly::test::find_resource("folly/debugging/symbolizer/test/simple_elf");
Expand Down
Loading