[lldb] Fix SIGSEGV in GetPtraceScope() in Procfs.cpp#142224
[lldb] Fix SIGSEGV in GetPtraceScope() in Procfs.cpp#142224
GetPtraceScope() in Procfs.cpp#142224Conversation
|
@llvm/pr-subscribers-lldb Author: None (royitaqi) ChangesSymptomWe have seen SIGSEGV like this: See full stack trace. A similar error (an unchecked Root cause
The intention of the Note that the method FixThe fix is simply remove the TestAdding a new unit test: On a Linux machine which doesn't have Run the unit test with the fix show passed result: Full diff: https://github.com/llvm/llvm-project/pull/142224.diff 2 Files Affected:
diff --git a/lldb/source/Plugins/Process/Linux/Procfs.cpp b/lldb/source/Plugins/Process/Linux/Procfs.cpp
index d3bd396fbaeab..4349a2b1adb9d 100644
--- a/lldb/source/Plugins/Process/Linux/Procfs.cpp
+++ b/lldb/source/Plugins/Process/Linux/Procfs.cpp
@@ -74,7 +74,7 @@ lldb_private::process_linux::GetAvailableLogicalCoreIDs() {
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)
+ 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();
diff --git a/lldb/unittests/Process/Linux/ProcfsTests.cpp b/lldb/unittests/Process/Linux/ProcfsTests.cpp
index e7af1f469c2bf..a795fa4e019c5 100644
--- a/lldb/unittests/Process/Linux/ProcfsTests.cpp
+++ b/lldb/unittests/Process/Linux/ProcfsTests.cpp
@@ -104,7 +104,7 @@ TEST(Perf, RealLogicalCoreIDs) {
ASSERT_GT((int)cpu_ids->size(), 0) << "We must see at least one core";
}
-TEST(Perf, RealPtraceScope) {
+TEST(Perf, RealPtraceScopeWhenExist) {
// We first check we can read /proc/sys/kernel/yama/ptrace_scope
auto buffer_or_error =
errorOrToExpected(getProcFile("sys/kernel/yama/ptrace_scope"));
@@ -120,6 +120,20 @@ TEST(Perf, RealPtraceScope) {
<< "Sensible values of ptrace_scope are between 0 and 3";
}
+TEST(Perf, RealPtraceScopeWhenNotExist) {
+ // We first check we can NOT read /proc/sys/kernel/yama/ptrace_scope
+ auto buffer_or_error =
+ errorOrToExpected(getProcFile("sys/kernel/yama/ptrace_scope"));
+ if (buffer_or_error)
+ GTEST_SKIP() << "In order for this test to run, /proc/sys/kernel/yama/ptrace_scope should not exist";
+ consumeError(buffer_or_error.takeError());
+
+ // At this point we should fail parsing the ptrace_scope value.
+ Expected<int> ptrace_scope = GetPtraceScope();
+ ASSERT_FALSE((bool)ptrace_scope);
+ consumeError(ptrace_scope.takeError());
+}
+
#ifdef LLVM_ENABLE_THREADING
TEST(Support, getProcFile_Tid) {
auto BufferOrError = getProcFile(getpid(), llvm::get_threadid(), "comm");
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
| ErrorOr<std::unique_ptr<MemoryBuffer>> ptrace_scope_file = | ||
| getProcFile("sys/kernel/yama/ptrace_scope"); | ||
| if (!*ptrace_scope_file) | ||
| if (!ptrace_scope_file) |
There was a problem hiding this comment.
We need to do both, !ptrace_scope_file || !*ptrace_scope_file.
The first checks the Error, the second the unique_ptr.
There was a problem hiding this comment.
The 2nd one has to be in a different if statement, because ptrace_scope_file.getError() would have no error.
Adding this in the latest commit.
There was a problem hiding this comment.
I don't think we do. The default for functions returning Expected/ErrorOr<some_ptr<Foo>> is that they return a valid object in the non-error case. If they aren't they should very explicitly document what it means to "successfully return nothing"
There was a problem hiding this comment.
Removing the newly added if per @labath 's comment.
| } | ||
|
|
||
| TEST(Perf, RealPtraceScope) { | ||
| TEST(Perf, RealPtraceScopeWhenExist) { |
There was a problem hiding this comment.
This will probably fail, I don't think kernel/yama is guaranteed to exist on ALL Linux hosts. We should either create it or not.
I think this original code assumed if you ever got an EPERM from ATTACH you would always have this file.
There was a problem hiding this comment.
This will probably fail
I don't think so. Look at line 112. It skips the test if the file doesn't exist.
We should either create it or not.
I am not sure if we want a unit test to create files that may or may not be depended on by other tests, etc.
Given how the original code was written (hardcoded path), the current setup (skip one test or the other) seems to be reasonable. I agree it's not ideal.
There was a problem hiding this comment.
cc @rupprecht (who added the original unit test) in case they have additional thoughts.
There was a problem hiding this comment.
I think it's a bug that I wrote if (!*ptrace_scope_file) instead of if (!ptrace_scope_file) -- I don't think there was anything I had in mind when writing it that way, e.g. if we should be able to assume the ptrace scope file exists & is readable. I'm surprised nobody noticed this bug earlier.
| ErrorOr<std::unique_ptr<MemoryBuffer>> ptrace_scope_file = | ||
| getProcFile("sys/kernel/yama/ptrace_scope"); | ||
| if (!*ptrace_scope_file) | ||
| if (!ptrace_scope_file) |
There was a problem hiding this comment.
I don't think we do. The default for functions returning Expected/ErrorOr<some_ptr<Foo>> is that they return a valid object in the non-error case. If they aren't they should very explicitly document what it means to "successfully return nothing"
|
Updated according to @labath 's comment. @JDevlieghere / @clayborg: Gentle ping - Do you guys want to review, or should I go ahead and land with @labath 's approval? |
| } | ||
|
|
||
| TEST(Perf, RealPtraceScope) { | ||
| TEST(Perf, RealPtraceScopeWhenExist) { |
There was a problem hiding this comment.
I think it's a bug that I wrote if (!*ptrace_scope_file) instead of if (!ptrace_scope_file) -- I don't think there was anything I had in mind when writing it that way, e.g. if we should be able to assume the ptrace scope file exists & is readable. I'm surprised nobody noticed this bug earlier.
Symptom
We have seen SIGSEGV like this:
See full stack trace.
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) has the followingifstatement:The intention of the
ifstatement is to check whether theptrace_scope_fileis anErroror not, and return the error if it is. However, theoperator*ofErrorOrreturns the value that is stored (which is astd::unique_ptr<MemoryBuffer>), so what theifcondition actually do is to check if the unique pointer is non-null.Note that the method
ErrorOr::getStorage()(called byErrorOr::operator *) does assert on whether or notHasErrorhas been set (see ErrorOr.h). However, it seems this wasn't executed, probably because the LLDB was a release build.Fix
The fix is simply remove the
*in the saidifstatement.Test
Adding a new unit test:
RealPtraceScopeWhenNotExist.On a Linux machine which doesn't have
/proc/sys/kernel/yama/ptrace_scope, run the unit test without the fix reproduces this assertion error about an uncheckedError:Run the unit test with the fix show passed result: