Skip to content

Commit

Permalink
Merge pull request #1652 from volatilityfoundation/fix_pstree_infinit…
Browse files Browse the repository at this point in the history
…e_loop

Add complete smear protection to linux.pstree
  • Loading branch information
ikelos authored Mar 7, 2025
2 parents 0a4e182 + 1daf62a commit 21bcdf9
Showing 1 changed file with 43 additions and 3 deletions.
46 changes: 43 additions & 3 deletions volatility3/framework/plugins/linux/pstree.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#

import logging

from volatility3.framework import interfaces, renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.renderers import format_hints
from volatility3.plugins.linux import pslist

vollog = logging.getLogger(__name__)


class PsTree(interfaces.plugins.PluginInterface):
"""Plugin for listing processes in a tree based on their parent process ID."""
Expand Down Expand Up @@ -52,19 +56,41 @@ def find_level(self, pid: int) -> None:
Args:
pid: PID to find the level in the hierarchy
"""
seen = set([pid])
seen_ppids = set()
seen_offsets = set()

level = 0
proc = self._tasks.get(pid)
while proc and proc.get_parent_pid() not in seen:

while proc:
# we don't want swapper in the tree
if proc.pid == 0:
break

if proc.is_thread_group_leader:
parent_pid = proc.get_parent_pid()
else:
parent_pid = proc.tgid

if parent_pid in seen_ppids or proc.vol.offset in seen_offsets:
break

# only pid 1 (init/systemd) or 2 (kthreadd) should have swapper as a parent
# any other process with a ppid of 0 is smeared or terminated
if parent_pid == 0 and proc.pid > 2:
vollog.debug(
"Smeared process with parent PID of 0 and PID greater than 2 ({proc.pid}) is being skipped."
)
break

seen_ppids.add(parent_pid)
seen_offsets.add(proc.vol.offset)

child_list = self._children.setdefault(parent_pid, set())
child_list.add(proc.pid)

proc = self._tasks.get(parent_pid)

level += 1

self._levels[pid] = level
Expand Down Expand Up @@ -110,12 +136,26 @@ def yield_processes(pid):
)
yield (self._levels[task_fields.user_tid] - 1, fields)

seen_children = set()

for child_pid in sorted(self._children.get(task_fields.user_tid, [])):
if child_pid in seen_children:
break
seen_children.add(child_pid)

yield from yield_processes(child_pid)

seen_processes = set()

for pid, level in self._levels.items():
if level == 1:
yield from yield_processes(pid)
for fields in yield_processes(pid):
pid = fields[1]
if pid in seen_processes:
break
seen_processes.add(pid)

yield fields

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

0 comments on commit 21bcdf9

Please sign in to comment.