Skip to content

Commit 21bcdf9

Browse files
authored
Merge pull request #1652 from volatilityfoundation/fix_pstree_infinite_loop
Add complete smear protection to linux.pstree
2 parents 0a4e182 + 1daf62a commit 21bcdf9

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

volatility3/framework/plugins/linux/pstree.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33
#
44

5+
import logging
6+
57
from volatility3.framework import interfaces, renderers
68
from volatility3.framework.configuration import requirements
79
from volatility3.framework.renderers import format_hints
810
from volatility3.plugins.linux import pslist
911

12+
vollog = logging.getLogger(__name__)
13+
1014

1115
class PsTree(interfaces.plugins.PluginInterface):
1216
"""Plugin for listing processes in a tree based on their parent process ID."""
@@ -52,19 +56,41 @@ def find_level(self, pid: int) -> None:
5256
Args:
5357
pid: PID to find the level in the hierarchy
5458
"""
55-
seen = set([pid])
59+
seen_ppids = set()
60+
seen_offsets = set()
61+
5662
level = 0
5763
proc = self._tasks.get(pid)
58-
while proc and proc.get_parent_pid() not in seen:
64+
65+
while proc:
66+
# we don't want swapper in the tree
67+
if proc.pid == 0:
68+
break
69+
5970
if proc.is_thread_group_leader:
6071
parent_pid = proc.get_parent_pid()
6172
else:
6273
parent_pid = proc.tgid
6374

75+
if parent_pid in seen_ppids or proc.vol.offset in seen_offsets:
76+
break
77+
78+
# only pid 1 (init/systemd) or 2 (kthreadd) should have swapper as a parent
79+
# any other process with a ppid of 0 is smeared or terminated
80+
if parent_pid == 0 and proc.pid > 2:
81+
vollog.debug(
82+
"Smeared process with parent PID of 0 and PID greater than 2 ({proc.pid}) is being skipped."
83+
)
84+
break
85+
86+
seen_ppids.add(parent_pid)
87+
seen_offsets.add(proc.vol.offset)
88+
6489
child_list = self._children.setdefault(parent_pid, set())
6590
child_list.add(proc.pid)
6691

6792
proc = self._tasks.get(parent_pid)
93+
6894
level += 1
6995

7096
self._levels[pid] = level
@@ -110,12 +136,26 @@ def yield_processes(pid):
110136
)
111137
yield (self._levels[task_fields.user_tid] - 1, fields)
112138

139+
seen_children = set()
140+
113141
for child_pid in sorted(self._children.get(task_fields.user_tid, [])):
142+
if child_pid in seen_children:
143+
break
144+
seen_children.add(child_pid)
145+
114146
yield from yield_processes(child_pid)
115147

148+
seen_processes = set()
149+
116150
for pid, level in self._levels.items():
117151
if level == 1:
118-
yield from yield_processes(pid)
152+
for fields in yield_processes(pid):
153+
pid = fields[1]
154+
if pid in seen_processes:
155+
break
156+
seen_processes.add(pid)
157+
158+
yield fields
119159

120160
def run(self):
121161
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))

0 commit comments

Comments
 (0)