Skip to content

Commit a40929d

Browse files
committed
[lldb] Fix cross-platform kills
This patch fixes an amusing bug where a Platform::Kill operation would happily terminate a proces on a completely different platform, as long as they have the same process ID. This was due to the fact that the implementation was iterating through all known (debugged) processes in order terminate them directly. This patch just deletes that logic, and makes everything go through the OS process termination APIs. While it would be possible to fix the logic to check for a platform match, it seemed to me that the implementation was being too smart for its own good -- accessing random Process objects without knowing anything about their state is risky at best. Going through the os ensures we avoid any races. I also "upgrade" the termination signal to a SIGKILL to ensure the process really dies after this operation. Differential Revision: https://reviews.llvm.org/D113184
1 parent 8cc2de6 commit a40929d

File tree

6 files changed

+64
-16
lines changed

6 files changed

+64
-16
lines changed

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ def terminate(self):
422422
def poll(self):
423423
return self._proc.poll()
424424

425+
def wait(self, timeout=None):
426+
return self._proc.wait(timeout)
427+
425428

426429
class _RemoteProcess(_BaseProcess):
427430

lldb/source/Target/Platform.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,25 +1044,11 @@ Status Platform::KillProcess(const lldb::pid_t pid) {
10441044
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
10451045
LLDB_LOGF(log, "Platform::%s, pid %" PRIu64, __FUNCTION__, pid);
10461046

1047-
// Try to find a process plugin to handle this Kill request. If we can't,
1048-
// fall back to the default OS implementation.
1049-
size_t num_debuggers = Debugger::GetNumDebuggers();
1050-
for (size_t didx = 0; didx < num_debuggers; ++didx) {
1051-
DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx);
1052-
lldb_private::TargetList &targets = debugger->GetTargetList();
1053-
for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) {
1054-
ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP();
1055-
if (process->GetID() == pid)
1056-
return process->Destroy(true);
1057-
}
1058-
}
1059-
10601047
if (!IsHost()) {
10611048
return Status(
1062-
"base lldb_private::Platform class can't kill remote processes unless "
1063-
"they are controlled by a process plugin");
1049+
"base lldb_private::Platform class can't kill remote processes");
10641050
}
1065-
Host::Kill(pid, SIGTERM);
1051+
Host::Kill(pid, SIGKILL);
10661052
return Status();
10671053
}
10681054

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include Makefile.rules
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import lldb
2+
import time
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test.decorators import *
5+
from gdbclientutils import *
6+
7+
class TestPlatformKill(GDBRemoteTestBase):
8+
9+
@skipIfRemote
10+
def test_kill_different_platform(self):
11+
"""Test connecting to a remote linux platform"""
12+
13+
self.build(dictionary={"CXX_SOURCES":"sleep.cpp"})
14+
host_process = self.spawnSubprocess(self.getBuildArtifact())
15+
16+
# Create a fake remote process with the same PID as host_process
17+
class MyResponder(MockGDBServerResponder):
18+
def __init__(self):
19+
MockGDBServerResponder.__init__(self)
20+
self.got_kill = False
21+
22+
def qC(self):
23+
return "QC%x"%host_process.pid
24+
25+
def k(self):
26+
self.got_kill = True
27+
return "X09"
28+
29+
self.server.responder = MyResponder()
30+
31+
error = lldb.SBError()
32+
target = self.dbg.CreateTarget("", "x86_64-pc-linux", "remote-linux",
33+
False, error)
34+
self.assertSuccess(error)
35+
process = self.connect(target)
36+
self.assertEqual(process.GetProcessID(), host_process.pid)
37+
38+
host_platform = lldb.SBPlatform("host")
39+
self.assertSuccess(host_platform.Kill(host_process.pid))
40+
41+
# Host dies, remote process lives.
42+
self.assertFalse(self.server.responder.got_kill)
43+
self.assertIsNotNone(host_process.wait(timeout=10))
44+
45+
# Now kill the remote one as well
46+
self.assertSuccess(process.Kill())
47+
self.assertTrue(self.server.responder.got_kill)

lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ def respond(self, packet):
203203
if packet.startswith("qRegisterInfo"):
204204
regnum = int(packet[len("qRegisterInfo"):], 16)
205205
return self.qRegisterInfo(regnum)
206+
if packet == "k":
207+
return self.k()
206208

207209
return self.other(packet)
208210

@@ -331,6 +333,9 @@ def QEnvironmentHexEncoded(self, packet):
331333
def qRegisterInfo(self, num):
332334
return ""
333335

336+
def k(self):
337+
return ""
338+
334339
"""
335340
Raised when we receive a packet for which there is no default action.
336341
Override the responder class to implement behavior suitable for the test at
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <thread>
2+
3+
int main() {
4+
std::this_thread::sleep_for(std::chrono::minutes(1));
5+
return 0;
6+
}

0 commit comments

Comments
 (0)