blob: d186602d49e1959f8e03c2e901be996143524f5e [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Omar Sandovale822a812016-10-16 12:31:32 -07002#
3# mountsnoop Trace mount() and umount syscalls.
4# For Linux, uses BCC, eBPF. Embedded C.
5#
6# USAGE: mountsnoop [-h]
7#
8# Copyright (c) 2016 Facebook, Inc.
9# Licensed under the Apache License, Version 2.0 (the "License")
10#
11# 14-Oct-2016 Omar Sandoval Created this.
12
13from __future__ import print_function
14import argparse
15import bcc
Francis Laniel4eb9cee2021-08-22 20:23:23 +020016from bcc.containers import filter_by_containers
Omar Sandovale822a812016-10-16 12:31:32 -070017import ctypes
18import errno
19import functools
20import sys
21
22
23bpf_text = r"""
24#include <uapi/linux/ptrace.h>
25#include <linux/sched.h>
26
27#include <linux/nsproxy.h>
28#include <linux/ns_common.h>
29
30/*
31 * XXX: struct mnt_namespace is defined in fs/mount.h, which is private to the
32 * VFS and not installed in any kernel-devel packages. So, let's duplicate the
33 * important part of the definition. There are actually more members in the
34 * real struct, but we don't need them, and they're more likely to change.
Francis Laniel4eb9cee2021-08-22 20:23:23 +020035 *
36 * To add support for --selector option, we need to call filter_by_containers().
37 * But this function adds code which defines struct mnt_namespace.
38 * To avoid having this structure twice, we define MNT_NAMESPACE_DEFINED in
39 * filter_by_containers(), then here we check if macro is already defined before
40 * adding struct definition.
Omar Sandovale822a812016-10-16 12:31:32 -070041 */
Francis Laniel4eb9cee2021-08-22 20:23:23 +020042#ifndef MNT_NAMESPACE_DEFINED
Omar Sandovale822a812016-10-16 12:31:32 -070043struct mnt_namespace {
sum12332e2ea2021-07-20 16:47:48 +020044 // This field was removed in https://github.com/torvalds/linux/commit/1a7b8969e664d6af328f00fe6eb7aabd61a71d13
45 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
Omar Sandovale822a812016-10-16 12:31:32 -070046 atomic_t count;
sum12332e2ea2021-07-20 16:47:48 +020047 #endif
Omar Sandovale822a812016-10-16 12:31:32 -070048 struct ns_common ns;
49};
Francis Laniel4eb9cee2021-08-22 20:23:23 +020050#endif /* !MNT_NAMESPACE_DEFINED */
Omar Sandovale822a812016-10-16 12:31:32 -070051
52/*
53 * XXX: this could really use first-class string support in BPF. target is a
54 * NUL-terminated path up to PATH_MAX in length. source and type are
55 * NUL-terminated strings up to PAGE_SIZE in length. data is a weird case: it's
56 * almost always a NUL-terminated string, but for some filesystems (e.g., older
57 * NFS variants), it's a binary structure with plenty of NUL bytes, so the
58 * kernel always copies up to PAGE_SIZE bytes, stopping when it hits a fault.
59 *
60 * The best we can do with the existing BPF helpers is to copy as much of each
61 * argument as we can. Our stack space is limited, and we need to leave some
62 * headroom for the rest of the function, so this should be a decent value.
63 */
64#define MAX_STR_LEN 412
65
66enum event_type {
67 EVENT_MOUNT,
68 EVENT_MOUNT_SOURCE,
69 EVENT_MOUNT_TARGET,
70 EVENT_MOUNT_TYPE,
71 EVENT_MOUNT_DATA,
72 EVENT_MOUNT_RET,
73 EVENT_UMOUNT,
74 EVENT_UMOUNT_TARGET,
75 EVENT_UMOUNT_RET,
76};
77
78struct data_t {
79 enum event_type type;
80 pid_t pid, tgid;
81 union {
82 /* EVENT_MOUNT, EVENT_UMOUNT */
83 struct {
84 /* current->nsproxy->mnt_ns->ns.inum */
85 unsigned int mnt_ns;
86 char comm[TASK_COMM_LEN];
Wen Yang24645012021-04-21 16:21:56 +080087 char pcomm[TASK_COMM_LEN];
88 pid_t ppid;
Omar Sandovale822a812016-10-16 12:31:32 -070089 unsigned long flags;
90 } enter;
91 /*
92 * EVENT_MOUNT_SOURCE, EVENT_MOUNT_TARGET, EVENT_MOUNT_TYPE,
93 * EVENT_MOUNT_DATA, EVENT_UMOUNT_TARGET
94 */
95 char str[MAX_STR_LEN];
96 /* EVENT_MOUNT_RET, EVENT_UMOUNT_RET */
97 int retval;
98 };
99};
100
101BPF_PERF_OUTPUT(events);
102
yonghong-song2da34262018-06-13 06:12:22 -0700103int syscall__mount(struct pt_regs *ctx, char __user *source,
Omar Sandovale822a812016-10-16 12:31:32 -0700104 char __user *target, char __user *type,
Yonghong Song6ee245b2019-11-16 22:47:07 -0800105 unsigned long flags, char __user *data)
Omar Sandovale822a812016-10-16 12:31:32 -0700106{
Omar Sandovale822a812016-10-16 12:31:32 -0700107 struct data_t event = {};
108 struct task_struct *task;
109 struct nsproxy *nsproxy;
110 struct mnt_namespace *mnt_ns;
111
Francis Laniel4eb9cee2021-08-22 20:23:23 +0200112 if (container_should_be_filtered()) {
113 return 0;
114 }
115
Omar Sandovale822a812016-10-16 12:31:32 -0700116 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
117 event.tgid = bpf_get_current_pid_tgid() >> 32;
118
119 event.type = EVENT_MOUNT;
120 bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
121 event.enter.flags = flags;
122 task = (struct task_struct *)bpf_get_current_task();
Wen Yang24645012021-04-21 16:21:56 +0800123 event.enter.ppid = task->real_parent->tgid;
124 bpf_probe_read_kernel_str(&event.enter.pcomm, TASK_COMM_LEN, task->real_parent->comm);
Paul Chaignon719e1002017-08-06 14:33:20 +0200125 nsproxy = task->nsproxy;
126 mnt_ns = nsproxy->mnt_ns;
127 event.enter.mnt_ns = mnt_ns->ns.inum;
Omar Sandovale822a812016-10-16 12:31:32 -0700128 events.perf_submit(ctx, &event, sizeof(event));
129
130 event.type = EVENT_MOUNT_SOURCE;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900131 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500132 bpf_probe_read_user(event.str, sizeof(event.str), source);
Omar Sandovale822a812016-10-16 12:31:32 -0700133 events.perf_submit(ctx, &event, sizeof(event));
134
135 event.type = EVENT_MOUNT_TARGET;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900136 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500137 bpf_probe_read_user(event.str, sizeof(event.str), target);
Omar Sandovale822a812016-10-16 12:31:32 -0700138 events.perf_submit(ctx, &event, sizeof(event));
139
140 event.type = EVENT_MOUNT_TYPE;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900141 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500142 bpf_probe_read_user(event.str, sizeof(event.str), type);
Omar Sandovale822a812016-10-16 12:31:32 -0700143 events.perf_submit(ctx, &event, sizeof(event));
144
145 event.type = EVENT_MOUNT_DATA;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900146 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500147 bpf_probe_read_user(event.str, sizeof(event.str), data);
Omar Sandovale822a812016-10-16 12:31:32 -0700148 events.perf_submit(ctx, &event, sizeof(event));
149
150 return 0;
151}
152
Yonghong Song64335692018-04-25 00:40:13 -0700153int do_ret_sys_mount(struct pt_regs *ctx)
Omar Sandovale822a812016-10-16 12:31:32 -0700154{
155 struct data_t event = {};
156
157 event.type = EVENT_MOUNT_RET;
158 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
159 event.tgid = bpf_get_current_pid_tgid() >> 32;
160 event.retval = PT_REGS_RC(ctx);
161 events.perf_submit(ctx, &event, sizeof(event));
162
163 return 0;
164}
165
yonghong-song2da34262018-06-13 06:12:22 -0700166int syscall__umount(struct pt_regs *ctx, char __user *target, int flags)
Omar Sandovale822a812016-10-16 12:31:32 -0700167{
168 struct data_t event = {};
169 struct task_struct *task;
170 struct nsproxy *nsproxy;
171 struct mnt_namespace *mnt_ns;
172
Francis Laniel4eb9cee2021-08-22 20:23:23 +0200173 if (container_should_be_filtered()) {
174 return 0;
175 }
176
Omar Sandovale822a812016-10-16 12:31:32 -0700177 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
178 event.tgid = bpf_get_current_pid_tgid() >> 32;
179
180 event.type = EVENT_UMOUNT;
181 bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
182 event.enter.flags = flags;
183 task = (struct task_struct *)bpf_get_current_task();
Wen Yang24645012021-04-21 16:21:56 +0800184 event.enter.ppid = task->real_parent->tgid;
185 bpf_probe_read_kernel_str(&event.enter.pcomm, TASK_COMM_LEN, task->real_parent->comm);
Paul Chaignon719e1002017-08-06 14:33:20 +0200186 nsproxy = task->nsproxy;
187 mnt_ns = nsproxy->mnt_ns;
188 event.enter.mnt_ns = mnt_ns->ns.inum;
Omar Sandovale822a812016-10-16 12:31:32 -0700189 events.perf_submit(ctx, &event, sizeof(event));
190
191 event.type = EVENT_UMOUNT_TARGET;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900192 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500193 bpf_probe_read_user(event.str, sizeof(event.str), target);
Omar Sandovale822a812016-10-16 12:31:32 -0700194 events.perf_submit(ctx, &event, sizeof(event));
195
196 return 0;
197}
198
Yonghong Song64335692018-04-25 00:40:13 -0700199int do_ret_sys_umount(struct pt_regs *ctx)
Omar Sandovale822a812016-10-16 12:31:32 -0700200{
201 struct data_t event = {};
202
203 event.type = EVENT_UMOUNT_RET;
204 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
205 event.tgid = bpf_get_current_pid_tgid() >> 32;
206 event.retval = PT_REGS_RC(ctx);
207 events.perf_submit(ctx, &event, sizeof(event));
208
209 return 0;
210}
211"""
212
213# sys/mount.h
214MS_MGC_VAL = 0xc0ed0000
215MS_MGC_MSK = 0xffff0000
216MOUNT_FLAGS = [
217 ('MS_RDONLY', 1),
218 ('MS_NOSUID', 2),
219 ('MS_NODEV', 4),
220 ('MS_NOEXEC', 8),
221 ('MS_SYNCHRONOUS', 16),
222 ('MS_REMOUNT', 32),
223 ('MS_MANDLOCK', 64),
224 ('MS_DIRSYNC', 128),
225 ('MS_NOATIME', 1024),
226 ('MS_NODIRATIME', 2048),
227 ('MS_BIND', 4096),
228 ('MS_MOVE', 8192),
229 ('MS_REC', 16384),
230 ('MS_SILENT', 32768),
231 ('MS_POSIXACL', 1 << 16),
232 ('MS_UNBINDABLE', 1 << 17),
233 ('MS_PRIVATE', 1 << 18),
234 ('MS_SLAVE', 1 << 19),
235 ('MS_SHARED', 1 << 20),
236 ('MS_RELATIME', 1 << 21),
237 ('MS_KERNMOUNT', 1 << 22),
238 ('MS_I_VERSION', 1 << 23),
239 ('MS_STRICTATIME', 1 << 24),
240 ('MS_LAZYTIME', 1 << 25),
241 ('MS_ACTIVE', 1 << 30),
242 ('MS_NOUSER', 1 << 31),
243]
244UMOUNT_FLAGS = [
245 ('MNT_FORCE', 1),
246 ('MNT_DETACH', 2),
247 ('MNT_EXPIRE', 4),
248 ('UMOUNT_NOFOLLOW', 8),
249]
250
251
252TASK_COMM_LEN = 16 # linux/sched.h
253MAX_STR_LEN = 412
254
255
256class EventType(object):
257 EVENT_MOUNT = 0
258 EVENT_MOUNT_SOURCE = 1
259 EVENT_MOUNT_TARGET = 2
260 EVENT_MOUNT_TYPE = 3
261 EVENT_MOUNT_DATA = 4
262 EVENT_MOUNT_RET = 5
263 EVENT_UMOUNT = 6
264 EVENT_UMOUNT_TARGET = 7
265 EVENT_UMOUNT_RET = 8
266
267
268class EnterData(ctypes.Structure):
269 _fields_ = [
270 ('mnt_ns', ctypes.c_uint),
271 ('comm', ctypes.c_char * TASK_COMM_LEN),
Wen Yang24645012021-04-21 16:21:56 +0800272 ('pcomm', ctypes.c_char * TASK_COMM_LEN),
273 ('ppid', ctypes.c_uint),
Omar Sandovale822a812016-10-16 12:31:32 -0700274 ('flags', ctypes.c_ulong),
275 ]
276
277
278class DataUnion(ctypes.Union):
279 _fields_ = [
280 ('enter', EnterData),
281 ('str', ctypes.c_char * MAX_STR_LEN),
282 ('retval', ctypes.c_int),
283 ]
284
285
286class Event(ctypes.Structure):
287 _fields_ = [
288 ('type', ctypes.c_uint),
289 ('pid', ctypes.c_uint),
290 ('tgid', ctypes.c_uint),
291 ('union', DataUnion),
292 ]
293
294
295def _decode_flags(flags, flag_list):
296 str_flags = []
297 for flag, bit in flag_list:
298 if flags & bit:
299 str_flags.append(flag)
300 flags &= ~bit
301 if flags or not str_flags:
302 str_flags.append('0x{:x}'.format(flags))
303 return str_flags
304
305
306def decode_flags(flags, flag_list):
307 return '|'.join(_decode_flags(flags, flag_list))
308
309
310def decode_mount_flags(flags):
311 str_flags = []
312 if flags & MS_MGC_MSK == MS_MGC_VAL:
313 flags &= ~MS_MGC_MSK
314 str_flags.append('MS_MGC_VAL')
315 str_flags.extend(_decode_flags(flags, MOUNT_FLAGS))
316 return '|'.join(str_flags)
317
318
319def decode_umount_flags(flags):
320 return decode_flags(flags, UMOUNT_FLAGS)
321
322
323def decode_errno(retval):
324 try:
325 return '-' + errno.errorcode[-retval]
326 except KeyError:
327 return str(retval)
328
329
330_escape_chars = {
331 ord('\a'): '\\a',
332 ord('\b'): '\\b',
333 ord('\t'): '\\t',
334 ord('\n'): '\\n',
335 ord('\v'): '\\v',
336 ord('\f'): '\\f',
337 ord('\r'): '\\r',
338 ord('"'): '\\"',
339 ord('\\'): '\\\\',
340}
341
342
343def escape_character(c):
344 try:
345 return _escape_chars[c]
346 except KeyError:
347 if 0x20 <= c <= 0x7e:
348 return chr(c)
349 else:
350 return '\\x{:02x}'.format(c)
351
352
353if sys.version_info.major < 3:
354 def decode_mount_string(s):
355 return '"{}"'.format(''.join(escape_character(ord(c)) for c in s))
356else:
357 def decode_mount_string(s):
358 return '"{}"'.format(''.join(escape_character(c) for c in s))
359
360
Wen Yang24645012021-04-21 16:21:56 +0800361def print_event(mounts, umounts, parent, cpu, data, size):
Omar Sandovale822a812016-10-16 12:31:32 -0700362 event = ctypes.cast(data, ctypes.POINTER(Event)).contents
363
364 try:
365 if event.type == EventType.EVENT_MOUNT:
366 mounts[event.pid] = {
367 'pid': event.pid,
368 'tgid': event.tgid,
369 'mnt_ns': event.union.enter.mnt_ns,
370 'comm': event.union.enter.comm,
371 'flags': event.union.enter.flags,
Wen Yang24645012021-04-21 16:21:56 +0800372 'ppid': event.union.enter.ppid,
373 'pcomm': event.union.enter.pcomm,
Omar Sandovale822a812016-10-16 12:31:32 -0700374 }
375 elif event.type == EventType.EVENT_MOUNT_SOURCE:
376 mounts[event.pid]['source'] = event.union.str
377 elif event.type == EventType.EVENT_MOUNT_TARGET:
378 mounts[event.pid]['target'] = event.union.str
379 elif event.type == EventType.EVENT_MOUNT_TYPE:
380 mounts[event.pid]['type'] = event.union.str
381 elif event.type == EventType.EVENT_MOUNT_DATA:
382 # XXX: data is not always a NUL-terminated string
383 mounts[event.pid]['data'] = event.union.str
384 elif event.type == EventType.EVENT_UMOUNT:
385 umounts[event.pid] = {
386 'pid': event.pid,
387 'tgid': event.tgid,
388 'mnt_ns': event.union.enter.mnt_ns,
389 'comm': event.union.enter.comm,
390 'flags': event.union.enter.flags,
Wen Yang24645012021-04-21 16:21:56 +0800391 'ppid': event.union.enter.ppid,
392 'pcomm': event.union.enter.pcomm,
Omar Sandovale822a812016-10-16 12:31:32 -0700393 }
394 elif event.type == EventType.EVENT_UMOUNT_TARGET:
395 umounts[event.pid]['target'] = event.union.str
396 elif (event.type == EventType.EVENT_MOUNT_RET or
397 event.type == EventType.EVENT_UMOUNT_RET):
398 if event.type == EventType.EVENT_MOUNT_RET:
399 syscall = mounts.pop(event.pid)
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300400 call = ('mount({source}, {target}, {type}, {flags}, {data}) ' +
401 '= {retval}').format(
Omar Sandovale822a812016-10-16 12:31:32 -0700402 source=decode_mount_string(syscall['source']),
403 target=decode_mount_string(syscall['target']),
404 type=decode_mount_string(syscall['type']),
405 flags=decode_mount_flags(syscall['flags']),
406 data=decode_mount_string(syscall['data']),
407 retval=decode_errno(event.union.retval))
408 else:
409 syscall = umounts.pop(event.pid)
410 call = 'umount({target}, {flags}) = {retval}'.format(
411 target=decode_mount_string(syscall['target']),
412 flags=decode_umount_flags(syscall['flags']),
413 retval=decode_errno(event.union.retval))
Wen Yang24645012021-04-21 16:21:56 +0800414 if parent:
415 print('{:16} {:<7} {:<7} {:16} {:<7} {:<11} {}'.format(
416 syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'],
417 syscall['pid'], syscall['pcomm'].decode('utf-8', 'replace'),
418 syscall['ppid'], syscall['mnt_ns'], call))
419 else:
420 print('{:16} {:<7} {:<7} {:<11} {}'.format(
421 syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'],
422 syscall['pid'], syscall['mnt_ns'], call))
xingfeng25107b2c1142022-03-24 10:51:26 +0800423 sys.stdout.flush()
Omar Sandovale822a812016-10-16 12:31:32 -0700424 except KeyError:
425 # This might happen if we lost an event.
426 pass
427
428
429def main():
430 parser = argparse.ArgumentParser(
431 description='trace mount() and umount() syscalls'
432 )
Nathan Scottcf0792f2018-02-02 16:56:50 +1100433 parser.add_argument("--ebpf", action="store_true",
434 help=argparse.SUPPRESS)
Wen Yang24645012021-04-21 16:21:56 +0800435 parser.add_argument("-P", "--parent_process", action="store_true",
436 help="also snoop the parent process")
Francis Laniel4eb9cee2021-08-22 20:23:23 +0200437 parser.add_argument("--cgroupmap",
438 help="trace cgroups in this BPF map only")
439 parser.add_argument("--mntnsmap",
440 help="trace mount namespaces in this BPF map only")
Omar Sandovale822a812016-10-16 12:31:32 -0700441 args = parser.parse_args()
442
443 mounts = {}
444 umounts = {}
Francis Laniel4eb9cee2021-08-22 20:23:23 +0200445 global bpf_text
446 bpf_text = filter_by_containers(args) + bpf_text
Nathan Scottcf0792f2018-02-02 16:56:50 +1100447 if args.ebpf:
448 print(bpf_text)
449 exit()
Omar Sandovale822a812016-10-16 12:31:32 -0700450 b = bcc.BPF(text=bpf_text)
Yonghong Song64335692018-04-25 00:40:13 -0700451 mount_fnname = b.get_syscall_fnname("mount")
yonghong-song2da34262018-06-13 06:12:22 -0700452 b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount")
Yonghong Song64335692018-04-25 00:40:13 -0700453 b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount")
454 umount_fnname = b.get_syscall_fnname("umount")
yonghong-song2da34262018-06-13 06:12:22 -0700455 b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount")
Yonghong Song64335692018-04-25 00:40:13 -0700456 b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount")
Omar Sandovale822a812016-10-16 12:31:32 -0700457 b['events'].open_perf_buffer(
Wen Yang24645012021-04-21 16:21:56 +0800458 functools.partial(print_event, mounts, umounts, args.parent_process))
459
460 if args.parent_process:
461 print('{:16} {:<7} {:<7} {:16} {:<7} {:<11} {}'.format(
462 'COMM', 'PID', 'TID', 'PCOMM', 'PPID', 'MNT_NS', 'CALL'))
463 else:
464 print('{:16} {:<7} {:<7} {:<11} {}'.format(
465 'COMM', 'PID', 'TID', 'MNT_NS', 'CALL'))
466
Omar Sandovale822a812016-10-16 12:31:32 -0700467 while True:
Jerome Marchand51671272018-12-19 01:57:24 +0100468 try:
469 b.perf_buffer_poll()
470 except KeyboardInterrupt:
471 exit()
472
Omar Sandovale822a812016-10-16 12:31:32 -0700473
474
475if __name__ == '__main__':
476 main()