Skip to content

Commit 6db44e5

Browse files
committed
[Linux] Add kernel.yama.ptrace_scope note when ptrace fails to attach.
A common reason for LLDB failing to attach to an already-running process on Linux is the Yama security module: https://www.kernel.org/doc/Documentation/security/Yama.txt. This patch adds an explaination and suggested fix when it detects that case happening. This was previously proposed in D106226, but hasn't been updated in a while. The last request was to put the check in a target-specific location, which is the approach this patch takes. I believe Yama only exists on Linux, so it's put in that package. This has no end-to-end test because I'm not sure how to set `ptrace_scope` in a test environment -- if there are suggestions on how to do that, I'd be happy to add it. (Also, setting it to `3` is comically irreversible). I tested this locally. Reviewed By: DavidSpickett, labath Differential Revision: https://reviews.llvm.org/D144904
1 parent 9679075 commit 6db44e5

File tree

4 files changed

+81
-0
lines changed

4 files changed

+81
-0
lines changed

lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "lldb/Utility/StringExtractor.h"
4646
#include "llvm/ADT/ScopeExit.h"
4747
#include "llvm/Support/Errno.h"
48+
#include "llvm/Support/Error.h"
4849
#include "llvm/Support/FileSystem.h"
4950
#include "llvm/Support/Threading.h"
5051

@@ -214,6 +215,43 @@ static Status EnsureFDFlags(int fd, int flags) {
214215
return error;
215216
}
216217

218+
static llvm::Error AddPtraceScopeNote(llvm::Error original_error) {
219+
Expected<int> ptrace_scope = GetPtraceScope();
220+
if (auto E = ptrace_scope.takeError()) {
221+
Log *log = GetLog(POSIXLog::Process);
222+
LLDB_LOG(log, "error reading value of ptrace_scope: {0}", E);
223+
224+
// The original error is probably more interesting than not being able to
225+
// read or interpret ptrace_scope.
226+
return original_error;
227+
}
228+
229+
// We only have suggestions to provide for 1-3.
230+
switch (*ptrace_scope) {
231+
case 1:
232+
case 2:
233+
return llvm::createStringError(
234+
std::error_code(errno, std::generic_category()),
235+
"The current value of ptrace_scope is %d, which can cause ptrace to "
236+
"fail to attach to a running process. To fix this, run:\n"
237+
"\tsudo sysctl -w kernel.yama.ptrace_scope=0\n"
238+
"For more information, see: "
239+
"https://www.kernel.org/doc/Documentation/security/Yama.txt.",
240+
*ptrace_scope);
241+
case 3:
242+
return llvm::createStringError(
243+
std::error_code(errno, std::generic_category()),
244+
"The current value of ptrace_scope is 3, which will cause ptrace to "
245+
"fail to attach to a running process. This value cannot be changed "
246+
"without rebooting.\n"
247+
"For more information, see: "
248+
"https://www.kernel.org/doc/Documentation/security/Yama.txt.");
249+
case 0:
250+
default:
251+
return original_error;
252+
}
253+
}
254+
217255
// Public Static Methods
218256

219257
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
@@ -352,6 +390,11 @@ llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) {
352390
it = tids_to_attach.erase(it);
353391
continue;
354392
}
393+
if (status.GetError() == EPERM) {
394+
// Depending on the value of ptrace_scope, we can return a different
395+
// error that suggests how to fix it.
396+
return AddPtraceScopeNote(status.ToError());
397+
}
355398
return status.ToError();
356399
}
357400

lldb/source/Plugins/Process/Linux/Procfs.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "Procfs.h"
1010
#include "lldb/Host/linux/Support.h"
11+
#include "llvm/Support/Error.h"
1112
#include "llvm/Support/MemoryBuffer.h"
1213
#include "llvm/Support/Threading.h"
1314
#include <optional>
@@ -68,3 +69,18 @@ lldb_private::process_linux::GetAvailableLogicalCoreIDs() {
6869
}
6970
return *logical_cores_ids;
7071
}
72+
73+
llvm::Expected<int> lldb_private::process_linux::GetPtraceScope() {
74+
ErrorOr<std::unique_ptr<MemoryBuffer>> ptrace_scope_file =
75+
getProcFile("sys/kernel/yama/ptrace_scope");
76+
if (!*ptrace_scope_file)
77+
return errorCodeToError(ptrace_scope_file.getError());
78+
// The contents should be something like "1\n". Trim it so we get "1".
79+
StringRef buffer = (*ptrace_scope_file)->getBuffer().trim();
80+
int ptrace_scope_value;
81+
if (buffer.getAsInteger(10, ptrace_scope_value)) {
82+
return createStringError(inconvertibleErrorCode(),
83+
"Invalid ptrace_scope value: '%s'", buffer.data());
84+
}
85+
return ptrace_scope_value;
86+
}

lldb/source/Plugins/Process/Linux/Procfs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,11 @@ GetAvailableLogicalCoreIDs(llvm::StringRef cpuinfo);
2828
/// if errors didn't happen.
2929
llvm::Expected<llvm::ArrayRef<lldb::cpu_id_t>> GetAvailableLogicalCoreIDs();
3030

31+
/// \return
32+
/// The current value of /proc/sys/kernel/yama/ptrace_scope, parsed as an
33+
/// integer, or an error if the proc file cannot be read or has non-integer
34+
/// contents.
35+
llvm::Expected<int> GetPtraceScope();
36+
3137
} // namespace process_linux
3238
} // namespace lldb_private

lldb/unittests/Process/Linux/ProcfsTests.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,19 @@ TEST(Perf, RealLogicalCoreIDs) {
102102
ASSERT_TRUE((bool)cpu_ids);
103103
ASSERT_GT((int)cpu_ids->size(), 0) << "We must see at least one core";
104104
}
105+
106+
TEST(Perf, RealPtraceScope) {
107+
// We first check we can read /proc/sys/kernel/yama/ptrace_scope
108+
auto buffer_or_error =
109+
errorOrToExpected(getProcFile("sys/kernel/yama/ptrace_scope"));
110+
if (!buffer_or_error)
111+
GTEST_SKIP() << toString(buffer_or_error.takeError());
112+
113+
// At this point we shouldn't fail parsing the ptrace_scope value.
114+
Expected<int> ptrace_scope = GetPtraceScope();
115+
ASSERT_TRUE((bool)ptrace_scope) << ptrace_scope.takeError();
116+
ASSERT_GE(*ptrace_scope, 0)
117+
<< "Sensible values of ptrace_scope are between 0 and 3";
118+
ASSERT_LE(*ptrace_scope, 3)
119+
<< "Sensible values of ptrace_scope are between 0 and 3";
120+
}

0 commit comments

Comments
 (0)