diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -45,6 +45,7 @@ #include "lldb/Utility/StringExtractor.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/Errno.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" @@ -214,6 +215,43 @@ return error; } +static llvm::Error AddPtraceScopeNote(llvm::Error original_error) { + Expected ptrace_scope = GetPtraceScope(); + if (auto E = ptrace_scope.takeError()) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "error reading value of ptrace_scope: {0}", E); + + // The original error is probably more interesting than not being able to + // read or interpret ptrace_scope. + return original_error; + } + + // We only have suggestions to provide for 1-3. + switch (*ptrace_scope) { + case 1: + case 2: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is %d, which can cause ptrace to " + "fail to attach to a running process. To fix this, run:\n" + "\tsudo sysctl -w kernel.yama.ptrace_scope=0\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt.", + *ptrace_scope); + case 3: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is 3, which will cause ptrace to " + "fail to attach to a running process. This value cannot be changed " + "without rebooting.\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt."); + case 0: + default: + return original_error; + } +} + // Public Static Methods llvm::Expected> @@ -352,6 +390,11 @@ it = tids_to_attach.erase(it); continue; } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a different + // error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } return status.ToError(); } diff --git a/lldb/source/Plugins/Process/Linux/Procfs.h b/lldb/source/Plugins/Process/Linux/Procfs.h --- a/lldb/source/Plugins/Process/Linux/Procfs.h +++ b/lldb/source/Plugins/Process/Linux/Procfs.h @@ -28,5 +28,11 @@ /// if errors didn't happen. llvm::Expected> GetAvailableLogicalCoreIDs(); +/// \return +/// The current value of /proc/sys/kernel/yama/ptrace_scope, parsed as an +/// integer, or an error if the proc file cannot be read or has non-integer +/// contents. +llvm::Expected GetPtraceScope(); + } // namespace process_linux } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Linux/Procfs.cpp b/lldb/source/Plugins/Process/Linux/Procfs.cpp --- a/lldb/source/Plugins/Process/Linux/Procfs.cpp +++ b/lldb/source/Plugins/Process/Linux/Procfs.cpp @@ -8,6 +8,7 @@ #include "Procfs.h" #include "lldb/Host/linux/Support.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Threading.h" #include @@ -68,3 +69,18 @@ } return *logical_cores_ids; } + +llvm::Expected lldb_private::process_linux::GetPtraceScope() { + ErrorOr> 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; +} diff --git a/lldb/unittests/Process/Linux/ProcfsTests.cpp b/lldb/unittests/Process/Linux/ProcfsTests.cpp --- a/lldb/unittests/Process/Linux/ProcfsTests.cpp +++ b/lldb/unittests/Process/Linux/ProcfsTests.cpp @@ -102,3 +102,19 @@ ASSERT_TRUE((bool)cpu_ids); ASSERT_GT((int)cpu_ids->size(), 0) << "We must see at least one core"; } + +TEST(Perf, RealPtraceScope) { + // We first check we can read /proc/sys/kernel/yama/ptrace_scope + auto buffer_or_error = + errorOrToExpected(getProcFile("sys/kernel/yama/ptrace_scope")); + if (!buffer_or_error) + GTEST_SKIP() << toString(buffer_or_error.takeError()); + + // At this point we shouldn't fail parsing the ptrace_scope value. + Expected ptrace_scope = GetPtraceScope(); + ASSERT_TRUE((bool)ptrace_scope) << ptrace_scope.takeError(); + ASSERT_GE(*ptrace_scope, 0) + << "Sensible values of ptrace_scope are between 0 and 3"; + ASSERT_LE(*ptrace_scope, 3) + << "Sensible values of ptrace_scope are between 0 and 3"; +}