blob: 6a0eea1eb027672b5bf45938904b4ffdd8cc3449 [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
16import ctypes
17import errno
18import functools
19import sys
20
21
22bpf_text = r"""
23#include <uapi/linux/ptrace.h>
24#include <linux/sched.h>
25
26#include <linux/nsproxy.h>
27#include <linux/ns_common.h>
28
29/*
30 * XXX: struct mnt_namespace is defined in fs/mount.h, which is private to the
31 * VFS and not installed in any kernel-devel packages. So, let's duplicate the
32 * important part of the definition. There are actually more members in the
33 * real struct, but we don't need them, and they're more likely to change.
34 */
35struct mnt_namespace {
sum12332e2ea2021-07-20 16:47:48 +020036 // This field was removed in https://github.com/torvalds/linux/commit/1a7b8969e664d6af328f00fe6eb7aabd61a71d13
37 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
Omar Sandovale822a812016-10-16 12:31:32 -070038 atomic_t count;
sum12332e2ea2021-07-20 16:47:48 +020039 #endif
Omar Sandovale822a812016-10-16 12:31:32 -070040 struct ns_common ns;
41};
42
43/*
44 * XXX: this could really use first-class string support in BPF. target is a
45 * NUL-terminated path up to PATH_MAX in length. source and type are
46 * NUL-terminated strings up to PAGE_SIZE in length. data is a weird case: it's
47 * almost always a NUL-terminated string, but for some filesystems (e.g., older
48 * NFS variants), it's a binary structure with plenty of NUL bytes, so the
49 * kernel always copies up to PAGE_SIZE bytes, stopping when it hits a fault.
50 *
51 * The best we can do with the existing BPF helpers is to copy as much of each
52 * argument as we can. Our stack space is limited, and we need to leave some
53 * headroom for the rest of the function, so this should be a decent value.
54 */
55#define MAX_STR_LEN 412
56
57enum event_type {
58 EVENT_MOUNT,
59 EVENT_MOUNT_SOURCE,
60 EVENT_MOUNT_TARGET,
61 EVENT_MOUNT_TYPE,
62 EVENT_MOUNT_DATA,
63 EVENT_MOUNT_RET,
64 EVENT_UMOUNT,
65 EVENT_UMOUNT_TARGET,
66 EVENT_UMOUNT_RET,
67};
68
69struct data_t {
70 enum event_type type;
71 pid_t pid, tgid;
72 union {
73 /* EVENT_MOUNT, EVENT_UMOUNT */
74 struct {
75 /* current->nsproxy->mnt_ns->ns.inum */
76 unsigned int mnt_ns;
77 char comm[TASK_COMM_LEN];
Wen Yang24645012021-04-21 16:21:56 +080078 char pcomm[TASK_COMM_LEN];
79 pid_t ppid;
Omar Sandovale822a812016-10-16 12:31:32 -070080 unsigned long flags;
81 } enter;
82 /*
83 * EVENT_MOUNT_SOURCE, EVENT_MOUNT_TARGET, EVENT_MOUNT_TYPE,
84 * EVENT_MOUNT_DATA, EVENT_UMOUNT_TARGET
85 */
86 char str[MAX_STR_LEN];
87 /* EVENT_MOUNT_RET, EVENT_UMOUNT_RET */
88 int retval;
89 };
90};
91
92BPF_PERF_OUTPUT(events);
93
yonghong-song2da34262018-06-13 06:12:22 -070094int syscall__mount(struct pt_regs *ctx, char __user *source,
Omar Sandovale822a812016-10-16 12:31:32 -070095 char __user *target, char __user *type,
Yonghong Song6ee245b2019-11-16 22:47:07 -080096 unsigned long flags, char __user *data)
Omar Sandovale822a812016-10-16 12:31:32 -070097{
Omar Sandovale822a812016-10-16 12:31:32 -070098 struct data_t event = {};
99 struct task_struct *task;
100 struct nsproxy *nsproxy;
101 struct mnt_namespace *mnt_ns;
102
103 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
104 event.tgid = bpf_get_current_pid_tgid() >> 32;
105
106 event.type = EVENT_MOUNT;
107 bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
108 event.enter.flags = flags;
109 task = (struct task_struct *)bpf_get_current_task();
Wen Yang24645012021-04-21 16:21:56 +0800110 event.enter.ppid = task->real_parent->tgid;
111 bpf_probe_read_kernel_str(&event.enter.pcomm, TASK_COMM_LEN, task->real_parent->comm);
Paul Chaignon719e1002017-08-06 14:33:20 +0200112 nsproxy = task->nsproxy;
113 mnt_ns = nsproxy->mnt_ns;
114 event.enter.mnt_ns = mnt_ns->ns.inum;
Omar Sandovale822a812016-10-16 12:31:32 -0700115 events.perf_submit(ctx, &event, sizeof(event));
116
117 event.type = EVENT_MOUNT_SOURCE;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900118 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500119 bpf_probe_read_user(event.str, sizeof(event.str), source);
Omar Sandovale822a812016-10-16 12:31:32 -0700120 events.perf_submit(ctx, &event, sizeof(event));
121
122 event.type = EVENT_MOUNT_TARGET;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900123 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500124 bpf_probe_read_user(event.str, sizeof(event.str), target);
Omar Sandovale822a812016-10-16 12:31:32 -0700125 events.perf_submit(ctx, &event, sizeof(event));
126
127 event.type = EVENT_MOUNT_TYPE;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900128 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500129 bpf_probe_read_user(event.str, sizeof(event.str), type);
Omar Sandovale822a812016-10-16 12:31:32 -0700130 events.perf_submit(ctx, &event, sizeof(event));
131
132 event.type = EVENT_MOUNT_DATA;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900133 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500134 bpf_probe_read_user(event.str, sizeof(event.str), data);
Omar Sandovale822a812016-10-16 12:31:32 -0700135 events.perf_submit(ctx, &event, sizeof(event));
136
137 return 0;
138}
139
Yonghong Song64335692018-04-25 00:40:13 -0700140int do_ret_sys_mount(struct pt_regs *ctx)
Omar Sandovale822a812016-10-16 12:31:32 -0700141{
142 struct data_t event = {};
143
144 event.type = EVENT_MOUNT_RET;
145 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
146 event.tgid = bpf_get_current_pid_tgid() >> 32;
147 event.retval = PT_REGS_RC(ctx);
148 events.perf_submit(ctx, &event, sizeof(event));
149
150 return 0;
151}
152
yonghong-song2da34262018-06-13 06:12:22 -0700153int syscall__umount(struct pt_regs *ctx, char __user *target, int flags)
Omar Sandovale822a812016-10-16 12:31:32 -0700154{
155 struct data_t event = {};
156 struct task_struct *task;
157 struct nsproxy *nsproxy;
158 struct mnt_namespace *mnt_ns;
159
160 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
161 event.tgid = bpf_get_current_pid_tgid() >> 32;
162
163 event.type = EVENT_UMOUNT;
164 bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
165 event.enter.flags = flags;
166 task = (struct task_struct *)bpf_get_current_task();
Wen Yang24645012021-04-21 16:21:56 +0800167 event.enter.ppid = task->real_parent->tgid;
168 bpf_probe_read_kernel_str(&event.enter.pcomm, TASK_COMM_LEN, task->real_parent->comm);
Paul Chaignon719e1002017-08-06 14:33:20 +0200169 nsproxy = task->nsproxy;
170 mnt_ns = nsproxy->mnt_ns;
171 event.enter.mnt_ns = mnt_ns->ns.inum;
Omar Sandovale822a812016-10-16 12:31:32 -0700172 events.perf_submit(ctx, &event, sizeof(event));
173
174 event.type = EVENT_UMOUNT_TARGET;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900175 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500176 bpf_probe_read_user(event.str, sizeof(event.str), target);
Omar Sandovale822a812016-10-16 12:31:32 -0700177 events.perf_submit(ctx, &event, sizeof(event));
178
179 return 0;
180}
181
Yonghong Song64335692018-04-25 00:40:13 -0700182int do_ret_sys_umount(struct pt_regs *ctx)
Omar Sandovale822a812016-10-16 12:31:32 -0700183{
184 struct data_t event = {};
185
186 event.type = EVENT_UMOUNT_RET;
187 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
188 event.tgid = bpf_get_current_pid_tgid() >> 32;
189 event.retval = PT_REGS_RC(ctx);
190 events.perf_submit(ctx, &event, sizeof(event));
191
192 return 0;
193}
194"""
195
196# sys/mount.h
197MS_MGC_VAL = 0xc0ed0000
198MS_MGC_MSK = 0xffff0000
199MOUNT_FLAGS = [
200 ('MS_RDONLY', 1),
201 ('MS_NOSUID', 2),
202 ('MS_NODEV', 4),
203 ('MS_NOEXEC', 8),
204 ('MS_SYNCHRONOUS', 16),
205 ('MS_REMOUNT', 32),
206 ('MS_MANDLOCK', 64),
207 ('MS_DIRSYNC', 128),
208 ('MS_NOATIME', 1024),
209 ('MS_NODIRATIME', 2048),
210 ('MS_BIND', 4096),
211 ('MS_MOVE', 8192),
212 ('MS_REC', 16384),
213 ('MS_SILENT', 32768),
214 ('MS_POSIXACL', 1 << 16),
215 ('MS_UNBINDABLE', 1 << 17),
216 ('MS_PRIVATE', 1 << 18),
217 ('MS_SLAVE', 1 << 19),
218 ('MS_SHARED', 1 << 20),
219 ('MS_RELATIME', 1 << 21),
220 ('MS_KERNMOUNT', 1 << 22),
221 ('MS_I_VERSION', 1 << 23),
222 ('MS_STRICTATIME', 1 << 24),
223 ('MS_LAZYTIME', 1 << 25),
224 ('MS_ACTIVE', 1 << 30),
225 ('MS_NOUSER', 1 << 31),
226]
227UMOUNT_FLAGS = [
228 ('MNT_FORCE', 1),
229 ('MNT_DETACH', 2),
230 ('MNT_EXPIRE', 4),
231 ('UMOUNT_NOFOLLOW', 8),
232]
233
234
235TASK_COMM_LEN = 16 # linux/sched.h
236MAX_STR_LEN = 412
237
238
239class EventType(object):
240 EVENT_MOUNT = 0
241 EVENT_MOUNT_SOURCE = 1
242 EVENT_MOUNT_TARGET = 2
243 EVENT_MOUNT_TYPE = 3
244 EVENT_MOUNT_DATA = 4
245 EVENT_MOUNT_RET = 5
246 EVENT_UMOUNT = 6
247 EVENT_UMOUNT_TARGET = 7
248 EVENT_UMOUNT_RET = 8
249
250
251class EnterData(ctypes.Structure):
252 _fields_ = [
253 ('mnt_ns', ctypes.c_uint),
254 ('comm', ctypes.c_char * TASK_COMM_LEN),
Wen Yang24645012021-04-21 16:21:56 +0800255 ('pcomm', ctypes.c_char * TASK_COMM_LEN),
256 ('ppid', ctypes.c_uint),
Omar Sandovale822a812016-10-16 12:31:32 -0700257 ('flags', ctypes.c_ulong),
258 ]
259
260
261class DataUnion(ctypes.Union):
262 _fields_ = [
263 ('enter', EnterData),
264 ('str', ctypes.c_char * MAX_STR_LEN),
265 ('retval', ctypes.c_int),
266 ]
267
268
269class Event(ctypes.Structure):
270 _fields_ = [
271 ('type', ctypes.c_uint),
272 ('pid', ctypes.c_uint),
273 ('tgid', ctypes.c_uint),
274 ('union', DataUnion),
275 ]
276
277
278def _decode_flags(flags, flag_list):
279 str_flags = []
280 for flag, bit in flag_list:
281 if flags & bit:
282 str_flags.append(flag)
283 flags &= ~bit
284 if flags or not str_flags:
285 str_flags.append('0x{:x}'.format(flags))
286 return str_flags
287
288
289def decode_flags(flags, flag_list):
290 return '|'.join(_decode_flags(flags, flag_list))
291
292
293def decode_mount_flags(flags):
294 str_flags = []
295 if flags & MS_MGC_MSK == MS_MGC_VAL:
296 flags &= ~MS_MGC_MSK
297 str_flags.append('MS_MGC_VAL')
298 str_flags.extend(_decode_flags(flags, MOUNT_FLAGS))
299 return '|'.join(str_flags)
300
301
302def decode_umount_flags(flags):
303 return decode_flags(flags, UMOUNT_FLAGS)
304
305
306def decode_errno(retval):
307 try:
308 return '-' + errno.errorcode[-retval]
309 except KeyError:
310 return str(retval)
311
312
313_escape_chars = {
314 ord('\a'): '\\a',
315 ord('\b'): '\\b',
316 ord('\t'): '\\t',
317 ord('\n'): '\\n',
318 ord('\v'): '\\v',
319 ord('\f'): '\\f',
320 ord('\r'): '\\r',
321 ord('"'): '\\"',
322 ord('\\'): '\\\\',
323}
324
325
326def escape_character(c):
327 try:
328 return _escape_chars[c]
329 except KeyError:
330 if 0x20 <= c <= 0x7e:
331 return chr(c)
332 else:
333 return '\\x{:02x}'.format(c)
334
335
336if sys.version_info.major < 3:
337 def decode_mount_string(s):
338 return '"{}"'.format(''.join(escape_character(ord(c)) for c in s))
339else:
340 def decode_mount_string(s):
341 return '"{}"'.format(''.join(escape_character(c) for c in s))
342
343
Wen Yang24645012021-04-21 16:21:56 +0800344def print_event(mounts, umounts, parent, cpu, data, size):
Omar Sandovale822a812016-10-16 12:31:32 -0700345 event = ctypes.cast(data, ctypes.POINTER(Event)).contents
346
347 try:
348 if event.type == EventType.EVENT_MOUNT:
349 mounts[event.pid] = {
350 'pid': event.pid,
351 'tgid': event.tgid,
352 'mnt_ns': event.union.enter.mnt_ns,
353 'comm': event.union.enter.comm,
354 'flags': event.union.enter.flags,
Wen Yang24645012021-04-21 16:21:56 +0800355 'ppid': event.union.enter.ppid,
356 'pcomm': event.union.enter.pcomm,
Omar Sandovale822a812016-10-16 12:31:32 -0700357 }
358 elif event.type == EventType.EVENT_MOUNT_SOURCE:
359 mounts[event.pid]['source'] = event.union.str
360 elif event.type == EventType.EVENT_MOUNT_TARGET:
361 mounts[event.pid]['target'] = event.union.str
362 elif event.type == EventType.EVENT_MOUNT_TYPE:
363 mounts[event.pid]['type'] = event.union.str
364 elif event.type == EventType.EVENT_MOUNT_DATA:
365 # XXX: data is not always a NUL-terminated string
366 mounts[event.pid]['data'] = event.union.str
367 elif event.type == EventType.EVENT_UMOUNT:
368 umounts[event.pid] = {
369 'pid': event.pid,
370 'tgid': event.tgid,
371 'mnt_ns': event.union.enter.mnt_ns,
372 'comm': event.union.enter.comm,
373 'flags': event.union.enter.flags,
Wen Yang24645012021-04-21 16:21:56 +0800374 'ppid': event.union.enter.ppid,
375 'pcomm': event.union.enter.pcomm,
Omar Sandovale822a812016-10-16 12:31:32 -0700376 }
377 elif event.type == EventType.EVENT_UMOUNT_TARGET:
378 umounts[event.pid]['target'] = event.union.str
379 elif (event.type == EventType.EVENT_MOUNT_RET or
380 event.type == EventType.EVENT_UMOUNT_RET):
381 if event.type == EventType.EVENT_MOUNT_RET:
382 syscall = mounts.pop(event.pid)
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300383 call = ('mount({source}, {target}, {type}, {flags}, {data}) ' +
384 '= {retval}').format(
Omar Sandovale822a812016-10-16 12:31:32 -0700385 source=decode_mount_string(syscall['source']),
386 target=decode_mount_string(syscall['target']),
387 type=decode_mount_string(syscall['type']),
388 flags=decode_mount_flags(syscall['flags']),
389 data=decode_mount_string(syscall['data']),
390 retval=decode_errno(event.union.retval))
391 else:
392 syscall = umounts.pop(event.pid)
393 call = 'umount({target}, {flags}) = {retval}'.format(
394 target=decode_mount_string(syscall['target']),
395 flags=decode_umount_flags(syscall['flags']),
396 retval=decode_errno(event.union.retval))
Wen Yang24645012021-04-21 16:21:56 +0800397 if parent:
398 print('{:16} {:<7} {:<7} {:16} {:<7} {:<11} {}'.format(
399 syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'],
400 syscall['pid'], syscall['pcomm'].decode('utf-8', 'replace'),
401 syscall['ppid'], syscall['mnt_ns'], call))
402 else:
403 print('{:16} {:<7} {:<7} {:<11} {}'.format(
404 syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'],
405 syscall['pid'], syscall['mnt_ns'], call))
Omar Sandovale822a812016-10-16 12:31:32 -0700406 except KeyError:
407 # This might happen if we lost an event.
408 pass
409
410
411def main():
412 parser = argparse.ArgumentParser(
413 description='trace mount() and umount() syscalls'
414 )
Nathan Scottcf0792f2018-02-02 16:56:50 +1100415 parser.add_argument("--ebpf", action="store_true",
416 help=argparse.SUPPRESS)
Wen Yang24645012021-04-21 16:21:56 +0800417 parser.add_argument("-P", "--parent_process", action="store_true",
418 help="also snoop the parent process")
Omar Sandovale822a812016-10-16 12:31:32 -0700419 args = parser.parse_args()
420
421 mounts = {}
422 umounts = {}
Nathan Scottcf0792f2018-02-02 16:56:50 +1100423 if args.ebpf:
424 print(bpf_text)
425 exit()
Omar Sandovale822a812016-10-16 12:31:32 -0700426 b = bcc.BPF(text=bpf_text)
Yonghong Song64335692018-04-25 00:40:13 -0700427 mount_fnname = b.get_syscall_fnname("mount")
yonghong-song2da34262018-06-13 06:12:22 -0700428 b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount")
Yonghong Song64335692018-04-25 00:40:13 -0700429 b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount")
430 umount_fnname = b.get_syscall_fnname("umount")
yonghong-song2da34262018-06-13 06:12:22 -0700431 b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount")
Yonghong Song64335692018-04-25 00:40:13 -0700432 b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount")
Omar Sandovale822a812016-10-16 12:31:32 -0700433 b['events'].open_perf_buffer(
Wen Yang24645012021-04-21 16:21:56 +0800434 functools.partial(print_event, mounts, umounts, args.parent_process))
435
436 if args.parent_process:
437 print('{:16} {:<7} {:<7} {:16} {:<7} {:<11} {}'.format(
438 'COMM', 'PID', 'TID', 'PCOMM', 'PPID', 'MNT_NS', 'CALL'))
439 else:
440 print('{:16} {:<7} {:<7} {:<11} {}'.format(
441 'COMM', 'PID', 'TID', 'MNT_NS', 'CALL'))
442
Omar Sandovale822a812016-10-16 12:31:32 -0700443 while True:
Jerome Marchand51671272018-12-19 01:57:24 +0100444 try:
445 b.perf_buffer_poll()
446 except KeyboardInterrupt:
447 exit()
448
Omar Sandovale822a812016-10-16 12:31:32 -0700449
450
451if __name__ == '__main__':
452 main()