Skip to content

Commit e4db77d

Browse files
authored
Merge pull request #942 from gcmoreira/mountinfo_fixes
Mountinfo fixes
2 parents 778df1a + 7516346 commit e4db77d

File tree

5 files changed

+458
-135
lines changed

5 files changed

+458
-135
lines changed

volatility3/framework/plugins/linux/iomem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def parse_resource(
6666
name = utility.pointer_to_string(resource.name, 128)
6767
except exceptions.InvalidAddressException:
6868
vollog.warning(
69-
"Unable to follow pointer to name for resource object at {resource_offset:#x}, "
69+
f"Unable to follow pointer to name for resource object at {resource_offset:#x}, "
7070
"replaced with UnreadableValue"
7171
)
7272
name = renderers.UnreadableValue()

volatility3/framework/plugins/linux/mountinfo.py

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
from volatility3.framework import renderers, interfaces
1010
from volatility3.framework.configuration import requirements
1111
from volatility3.framework.interfaces import plugins
12+
from volatility3.framework.symbols import linux
1213
from volatility3.plugins.linux import pslist
1314

15+
1416
vollog = logging.getLogger(__name__)
1517

1618
MountInfoData = namedtuple(
@@ -48,6 +50,9 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
4850
requirements.PluginRequirement(
4951
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
5052
),
53+
requirements.VersionRequirement(
54+
name="linuxutils", component=linux.LinuxUtilities, version=(2, 1, 0)
55+
),
5156
requirements.ListRequirement(
5257
name="pids",
5358
description="Filter on specific process IDs.",
@@ -71,37 +76,6 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
7176
),
7277
]
7378

74-
@classmethod
75-
def _do_get_path(cls, mnt, fs_root) -> Union[None, str]:
76-
"""It mimics the Linux kernel prepend_path function."""
77-
vfsmnt = mnt.mnt
78-
dentry = vfsmnt.get_mnt_root()
79-
80-
path_reversed = []
81-
while dentry != fs_root.dentry or vfsmnt.vol.offset != fs_root.mnt:
82-
if dentry == vfsmnt.get_mnt_root() or dentry.is_root():
83-
parent = mnt.get_mnt_parent().dereference()
84-
# Escaped?
85-
if dentry != vfsmnt.get_mnt_root():
86-
return None
87-
88-
# Global root?
89-
if mnt.vol.offset != parent.vol.offset:
90-
dentry = mnt.get_mnt_mountpoint()
91-
mnt = parent
92-
vfsmnt = mnt.mnt
93-
continue
94-
95-
return None
96-
97-
parent = dentry.d_parent
98-
dname = dentry.d_name.name_as_str()
99-
path_reversed.append(dname.strip("/"))
100-
dentry = parent
101-
102-
path = "/" + "/".join(reversed(path_reversed))
103-
return path
104-
10579
@classmethod
10680
def get_mountinfo(
10781
cls, mnt, task
@@ -115,8 +89,8 @@ def get_mountinfo(
11589
if not mnt_root:
11690
return None
11791

118-
path_root = cls._do_get_path(mnt, task.fs.root)
119-
if path_root is None:
92+
path_root = linux.LinuxUtilities.get_path_mnt(task, mnt)
93+
if not path_root:
12094
return None
12195

12296
mnt_root_path = mnt_root.path()
@@ -170,9 +144,11 @@ def get_mountinfo(
170144
)
171145

172146
def _get_tasks_mountpoints(
173-
self, tasks: Iterable[interfaces.objects.ObjectInterface], per_namespace: bool
147+
self,
148+
tasks: Iterable[interfaces.objects.ObjectInterface],
149+
filtered_by_pids: bool,
174150
):
175-
seen_namespaces = set()
151+
seen_mountpoints = set()
176152
for task in tasks:
177153
if not (
178154
task
@@ -181,30 +157,48 @@ def _get_tasks_mountpoints(
181157
and task.nsproxy
182158
and task.nsproxy.mnt_ns
183159
):
184-
# This task doesn't have all the information required
160+
# This task doesn't have all the information required.
161+
# It should be a kernel < 2.6.30
185162
continue
186163

187164
mnt_namespace = task.nsproxy.mnt_ns
188-
mnt_ns_id = mnt_namespace.get_inode()
189-
190-
if per_namespace:
191-
if mnt_ns_id in seen_namespaces:
192-
continue
193-
else:
194-
seen_namespaces.add(mnt_ns_id)
165+
try:
166+
mnt_ns_id = mnt_namespace.get_inode()
167+
except AttributeError:
168+
mnt_ns_id = renderers.NotAvailableValue()
195169

196170
for mount in mnt_namespace.get_mount_points():
171+
# When PIDs are filtered, it makes sense that the user want to
172+
# see each of those processes mount points. So we don't filter
173+
# by mount id in this case.
174+
if not filtered_by_pids:
175+
mnt_id = int(mount.mnt_id)
176+
if mnt_id in seen_mountpoints:
177+
continue
178+
else:
179+
seen_mountpoints.add(mnt_id)
180+
197181
yield task, mount, mnt_ns_id
198182

199183
def _generator(
200184
self,
201185
tasks: Iterable[interfaces.objects.ObjectInterface],
202186
mnt_ns_ids: List[int],
203187
mount_format: bool,
204-
per_namespace: bool,
188+
filtered_by_pids: bool,
205189
) -> Iterable[Tuple[int, Tuple]]:
206-
for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, per_namespace):
207-
if mnt_ns_ids and mnt_ns_id not in mnt_ns_ids:
190+
show_filter_warning = False
191+
for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(
192+
tasks, filtered_by_pids
193+
):
194+
if mnt_ns_ids and isinstance(mnt_ns_id, renderers.NotAvailableValue):
195+
show_filter_warning = True
196+
197+
if (
198+
not isinstance(mnt_ns_id, renderers.NotAvailableValue)
199+
and mnt_ns_ids
200+
and mnt_ns_id not in mnt_ns_ids
201+
):
208202
continue
209203

210204
mnt_info = self.get_mountinfo(mnt, task)
@@ -242,12 +236,17 @@ def _generator(
242236
]
243237

244238
fields_values = [mnt_ns_id]
245-
if not per_namespace:
239+
if filtered_by_pids:
246240
fields_values.append(task.pid)
247241
fields_values.extend(extra_fields_values)
248242

249243
yield (0, fields_values)
250244

245+
if show_filter_warning:
246+
vollog.warning(
247+
"Could not filter by mount namespace id. This field is not available in this kernel."
248+
)
249+
251250
def run(self):
252251
pids = self.config.get("pids")
253252
mount_ns_ids = self.config.get("mntns")
@@ -263,9 +262,9 @@ def run(self):
263262
# to displays the mountpoints per namespace.
264263
if pids:
265264
columns.append(("PID", int))
266-
per_namespace = False
265+
filtered_by_pids = True
267266
else:
268-
per_namespace = True
267+
filtered_by_pids = False
269268

270269
if self.config.get("mount-format"):
271270
extra_columns = [
@@ -292,5 +291,6 @@ def run(self):
292291
columns.extend(extra_columns)
293292

294293
return renderers.TreeGrid(
295-
columns, self._generator(tasks, mount_ns_ids, mount_format, per_namespace)
294+
columns,
295+
self._generator(tasks, mount_ns_ids, mount_format, filtered_by_pids),
296296
)

volatility3/framework/plugins/linux/sockstat.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ def __init__(self, vmlinux, task):
2828
self._vmlinux = vmlinux
2929
self._task = task
3030

31-
netns_id = task.nsproxy.net_ns.get_inode()
31+
try:
32+
netns_id = task.nsproxy.net_ns.get_inode()
33+
except AttributeError:
34+
netns_id = NotAvailableValue()
35+
3236
self._netdevices = self._build_network_devices_map(netns_id)
3337

3438
self._sock_family_handlers = {
@@ -61,7 +65,10 @@ def _build_network_devices_map(self, netns_id: int) -> Dict:
6165
self._vmlinux.symbol_table_name + constants.BANG + "net_device"
6266
)
6367
for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"):
64-
if net.get_inode() != netns_id:
68+
if (
69+
isinstance(netns_id, NotAvailableValue)
70+
or net.get_inode() != netns_id
71+
):
6572
continue
6673
dev_name = utility.array_to_string(net_dev.name)
6774
netdevices_map[net_dev.ifindex] = dev_name
@@ -143,19 +150,32 @@ def _extract_socket_filter_info(
143150
return
144151

145152
bpfprog = sock_filter.prog
146-
if bpfprog.type == 0:
147-
# BPF_PROG_TYPE_UNSPEC = 0
153+
154+
BPF_PROG_TYPE_UNSPEC = 0 # cBPF filter
155+
try:
156+
bpfprog_type = bpfprog.get_type()
157+
if bpfprog_type == BPF_PROG_TYPE_UNSPEC:
158+
return # cBPF filter
159+
except AttributeError:
160+
# kernel < 3.18.140, it's a cBPF filter
161+
return
162+
163+
BPF_PROG_TYPE_SOCKET_FILTER = 1 # eBPF filter
164+
if bpfprog_type != BPF_PROG_TYPE_SOCKET_FILTER:
165+
socket_filter["bpf_filter_type"] = f"UNK({bpfprog_type})"
166+
vollog.warning(f"Unexpected BPF type {bpfprog_type} for a socket")
148167
return
149168

150169
socket_filter["bpf_filter_type"] = "eBPF"
151170
if not bpfprog.has_member("aux") or not bpfprog.aux:
152-
return
171+
return # kernel < 3.18.140
153172
bpfprog_aux = bpfprog.aux
173+
154174
if bpfprog_aux.has_member("id"):
155-
# `id` member was added to `bpf_prog_aux` in kernels 4.13
175+
# `id` member was added to `bpf_prog_aux` in kernels 4.13.16
156176
socket_filter["bpf_filter_id"] = str(bpfprog_aux.id)
157177
if bpfprog_aux.has_member("name"):
158-
# `name` was added to `bpf_prog_aux` in kernels 4.15
178+
# `name` was added to `bpf_prog_aux` in kernels 4.15.18
159179
bpfprog_name = utility.array_to_string(bpfprog_aux.name)
160180
if bpfprog_name:
161181
socket_filter["bpf_filter_name"] = bpfprog_name
@@ -227,14 +247,22 @@ def _netlink_sock(
227247
if netlink_sock.groups:
228248
groups_bitmap = netlink_sock.groups.dereference()
229249
src_addr = f"groups:0x{groups_bitmap:08x}"
230-
src_port = netlink_sock.portid
250+
251+
try:
252+
# Kernel >= 3.7.10
253+
src_port = netlink_sock.get_portid()
254+
except AttributeError:
255+
src_port = NotAvailableValue()
231256

232257
dst_addr = f"group:0x{netlink_sock.dst_group:08x}"
233258
module = netlink_sock.module
234259
if module and module.name:
235260
module_name_str = utility.array_to_string(module.name)
236261
dst_addr = f"{dst_addr},lkm:{module_name_str}"
237-
dst_port = netlink_sock.dst_portid
262+
try:
263+
dst_port = netlink_sock.get_dst_portid()
264+
except AttributeError:
265+
dst_port = NotAvailableValue()
238266

239267
state = netlink_sock.get_state()
240268

@@ -518,7 +546,11 @@ def list_sockets(
518546
protocol = child_sock.get_protocol()
519547

520548
net = task.nsproxy.net_ns
521-
netns_id = net.get_inode()
549+
try:
550+
netns_id = net.get_inode()
551+
except AttributeError:
552+
netns_id = NotAvailableValue()
553+
522554
yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields
523555

524556
def _format_fields(self, sock_stat, protocol):

0 commit comments

Comments
 (0)