royitaqi 8e8da88d46
[lldb] Fix SIGSEGV in GetPtraceScope() in Procfs.cpp (#142224)
# Symptom

We have seen SIGSEGV like this:
```
* thread #1, name = 'lldb-server', stop reason = SIGSEGV
    frame #0: 0x00007f39e529c993 libc.so.6`__pthread_kill_internal(signo=11, threadid=<unavailable>) at pthread_kill.c:46:37
    ...
  * frame #5: 0x000056027c94fe48 lldb-server`lldb_private::process_linux::GetPtraceScope() + 72
    frame #6: 0x000056027c92f94f lldb-server`lldb_private::process_linux::NativeProcessLinux::Attach(int) + 1087
    ...
```
See [full stack trace](https://pastebin.com/X0d6QhYj).

This happens on Linux where LLDB doesn't have access to
`/proc/sys/kernel/yama/ptrace_scope`.

A similar error (an unchecked `Error`) can be reproduced by running the
newly added unit test without the fix. See the "Test" section below.


# Root cause

`GetPtraceScope()`
([code](328f40f408/lldb/source/Plugins/Process/Linux/Procfs.cpp (L77)))
has the following `if` statement:
```
llvm::Expected<int> lldb_private::process_linux::GetPtraceScope() {
  ErrorOr<std::unique_ptr<MemoryBuffer>> ptrace_scope_file =
      getProcFile("sys/kernel/yama/ptrace_scope");
  if (!*ptrace_scope_file)
    return errorCodeToError(ptrace_scope_file.getError());
  ...
}
```

The intention of the `if` statement is to check whether the
`ptrace_scope_file` is an `Error` or not, and return the error if it is.
However, the `operator*` of `ErrorOr` returns the value that is stored
(which is a `std::unique_ptr<MemoryBuffer>`), so what the `if` condition
actually do is to check if the unique pointer is non-null.

Note that the method `ErrorOr::getStorage()` ([called
by](328f40f408/llvm/include/llvm/Support/ErrorOr.h (L162-L164))
`ErrorOr::operator *`) **does** assert on whether or not `HasError` has
been set (see
[ErrorOr.h](328f40f408/llvm/include/llvm/Support/ErrorOr.h (L235-L243))).
However, it seems this wasn't executed, probably because the LLDB was a
release build.

# Fix

The fix is simply remove the `*` in the said `if` statement.
2025-06-02 10:43:58 -07:00

88 lines
3.0 KiB
C++

//===-- Procfs.cpp --------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Procfs.h"
#include "lldb/Host/posix/Support.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Threading.h"
#include <optional>
using namespace lldb;
using namespace lldb_private;
using namespace process_linux;
using namespace llvm;
Expected<ArrayRef<uint8_t>> lldb_private::process_linux::GetProcfsCpuInfo() {
static ErrorOr<std::unique_ptr<MemoryBuffer>> cpu_info_or_err =
getProcFile("cpuinfo");
if (!*cpu_info_or_err)
cpu_info_or_err.getError();
MemoryBuffer &buffer = **cpu_info_or_err;
return arrayRefFromStringRef(buffer.getBuffer());
}
Expected<std::vector<cpu_id_t>>
lldb_private::process_linux::GetAvailableLogicalCoreIDs(StringRef cpuinfo) {
SmallVector<StringRef, 8> lines;
cpuinfo.split(lines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
std::vector<cpu_id_t> logical_cores;
for (StringRef line : lines) {
std::pair<StringRef, StringRef> key_value = line.split(':');
auto key = key_value.first.trim();
auto val = key_value.second.trim();
if (key == "processor") {
cpu_id_t processor;
if (val.getAsInteger(10, processor))
return createStringError(
inconvertibleErrorCode(),
"Failed parsing the /proc/cpuinfo line entry: %s", line.data());
logical_cores.push_back(processor);
}
}
return logical_cores;
}
llvm::Expected<llvm::ArrayRef<cpu_id_t>>
lldb_private::process_linux::GetAvailableLogicalCoreIDs() {
static std::optional<std::vector<cpu_id_t>> logical_cores_ids;
if (!logical_cores_ids) {
// We find the actual list of core ids by parsing /proc/cpuinfo
Expected<ArrayRef<uint8_t>> cpuinfo = GetProcfsCpuInfo();
if (!cpuinfo)
return cpuinfo.takeError();
Expected<std::vector<cpu_id_t>> cpu_ids = GetAvailableLogicalCoreIDs(
StringRef(reinterpret_cast<const char *>(cpuinfo->data())));
if (!cpu_ids)
return cpu_ids.takeError();
logical_cores_ids.emplace(std::move(*cpu_ids));
}
return *logical_cores_ids;
}
llvm::Expected<int> lldb_private::process_linux::GetPtraceScope() {
ErrorOr<std::unique_ptr<MemoryBuffer>> ptrace_scope_file =
getProcFile("sys/kernel/yama/ptrace_scope");
if (!ptrace_scope_file)
return errorCodeToError(ptrace_scope_file.getError());
// The contents should be something like "1\n". Trim it so we get "1".
StringRef buffer = (*ptrace_scope_file)->getBuffer().trim();
int ptrace_scope_value;
if (buffer.getAsInteger(10, ptrace_scope_value)) {
return createStringError(inconvertibleErrorCode(),
"Invalid ptrace_scope value: '%s'", buffer.data());
}
return ptrace_scope_value;
}