blob: 250927f5e7db027c955d00e07abbef28c15e575a [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];
78 unsigned long flags;
79 } enter;
80 /*
81 * EVENT_MOUNT_SOURCE, EVENT_MOUNT_TARGET, EVENT_MOUNT_TYPE,
82 * EVENT_MOUNT_DATA, EVENT_UMOUNT_TARGET
83 */
84 char str[MAX_STR_LEN];
85 /* EVENT_MOUNT_RET, EVENT_UMOUNT_RET */
86 int retval;
87 };
88};
89
90BPF_PERF_OUTPUT(events);
91
yonghong-song2da34262018-06-13 06:12:22 -070092int syscall__mount(struct pt_regs *ctx, char __user *source,
Omar Sandovale822a812016-10-16 12:31:32 -070093 char __user *target, char __user *type,
Yonghong Song6ee245b2019-11-16 22:47:07 -080094 unsigned long flags, char __user *data)
Omar Sandovale822a812016-10-16 12:31:32 -070095{
Omar Sandovale822a812016-10-16 12:31:32 -070096 struct data_t event = {};
97 struct task_struct *task;
98 struct nsproxy *nsproxy;
99 struct mnt_namespace *mnt_ns;
100
101 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
102 event.tgid = bpf_get_current_pid_tgid() >> 32;
103
104 event.type = EVENT_MOUNT;
105 bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
106 event.enter.flags = flags;
107 task = (struct task_struct *)bpf_get_current_task();
Paul Chaignon719e1002017-08-06 14:33:20 +0200108 nsproxy = task->nsproxy;
109 mnt_ns = nsproxy->mnt_ns;
110 event.enter.mnt_ns = mnt_ns->ns.inum;
Omar Sandovale822a812016-10-16 12:31:32 -0700111 events.perf_submit(ctx, &event, sizeof(event));
112
113 event.type = EVENT_MOUNT_SOURCE;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900114 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500115 bpf_probe_read_user(event.str, sizeof(event.str), source);
Omar Sandovale822a812016-10-16 12:31:32 -0700116 events.perf_submit(ctx, &event, sizeof(event));
117
118 event.type = EVENT_MOUNT_TARGET;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900119 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500120 bpf_probe_read_user(event.str, sizeof(event.str), target);
Omar Sandovale822a812016-10-16 12:31:32 -0700121 events.perf_submit(ctx, &event, sizeof(event));
122
123 event.type = EVENT_MOUNT_TYPE;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900124 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500125 bpf_probe_read_user(event.str, sizeof(event.str), type);
Omar Sandovale822a812016-10-16 12:31:32 -0700126 events.perf_submit(ctx, &event, sizeof(event));
127
128 event.type = EVENT_MOUNT_DATA;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900129 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500130 bpf_probe_read_user(event.str, sizeof(event.str), data);
Omar Sandovale822a812016-10-16 12:31:32 -0700131 events.perf_submit(ctx, &event, sizeof(event));
132
133 return 0;
134}
135
Yonghong Song64335692018-04-25 00:40:13 -0700136int do_ret_sys_mount(struct pt_regs *ctx)
Omar Sandovale822a812016-10-16 12:31:32 -0700137{
138 struct data_t event = {};
139
140 event.type = EVENT_MOUNT_RET;
141 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
142 event.tgid = bpf_get_current_pid_tgid() >> 32;
143 event.retval = PT_REGS_RC(ctx);
144 events.perf_submit(ctx, &event, sizeof(event));
145
146 return 0;
147}
148
yonghong-song2da34262018-06-13 06:12:22 -0700149int syscall__umount(struct pt_regs *ctx, char __user *target, int flags)
Omar Sandovale822a812016-10-16 12:31:32 -0700150{
151 struct data_t event = {};
152 struct task_struct *task;
153 struct nsproxy *nsproxy;
154 struct mnt_namespace *mnt_ns;
155
156 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
157 event.tgid = bpf_get_current_pid_tgid() >> 32;
158
159 event.type = EVENT_UMOUNT;
160 bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
161 event.enter.flags = flags;
162 task = (struct task_struct *)bpf_get_current_task();
Paul Chaignon719e1002017-08-06 14:33:20 +0200163 nsproxy = task->nsproxy;
164 mnt_ns = nsproxy->mnt_ns;
165 event.enter.mnt_ns = mnt_ns->ns.inum;
Omar Sandovale822a812016-10-16 12:31:32 -0700166 events.perf_submit(ctx, &event, sizeof(event));
167
168 event.type = EVENT_UMOUNT_TARGET;
Prashant Bhole419a7db2019-01-11 06:03:21 +0900169 __builtin_memset(event.str, 0, sizeof(event.str));
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500170 bpf_probe_read_user(event.str, sizeof(event.str), target);
Omar Sandovale822a812016-10-16 12:31:32 -0700171 events.perf_submit(ctx, &event, sizeof(event));
172
173 return 0;
174}
175
Yonghong Song64335692018-04-25 00:40:13 -0700176int do_ret_sys_umount(struct pt_regs *ctx)
Omar Sandovale822a812016-10-16 12:31:32 -0700177{
178 struct data_t event = {};
179
180 event.type = EVENT_UMOUNT_RET;
181 event.pid = bpf_get_current_pid_tgid() & 0xffffffff;
182 event.tgid = bpf_get_current_pid_tgid() >> 32;
183 event.retval = PT_REGS_RC(ctx);
184 events.perf_submit(ctx, &event, sizeof(event));
185
186 return 0;
187}
188"""
189
190# sys/mount.h
191MS_MGC_VAL = 0xc0ed0000
192MS_MGC_MSK = 0xffff0000
193MOUNT_FLAGS = [
194 ('MS_RDONLY', 1),
195 ('MS_NOSUID', 2),
196 ('MS_NODEV', 4),
197 ('MS_NOEXEC', 8),
198 ('MS_SYNCHRONOUS', 16),
199 ('MS_REMOUNT', 32),
200 ('MS_MANDLOCK', 64),
201 ('MS_DIRSYNC', 128),
202 ('MS_NOATIME', 1024),
203 ('MS_NODIRATIME', 2048),
204 ('MS_BIND', 4096),
205 ('MS_MOVE', 8192),
206 ('MS_REC', 16384),
207 ('MS_SILENT', 32768),
208 ('MS_POSIXACL', 1 << 16),
209 ('MS_UNBINDABLE', 1 << 17),
210 ('MS_PRIVATE', 1 << 18),
211 ('MS_SLAVE', 1 << 19),
212 ('MS_SHARED', 1 << 20),
213 ('MS_RELATIME', 1 << 21),
214 ('MS_KERNMOUNT', 1 << 22),
215 ('MS_I_VERSION', 1 << 23),
216 ('MS_STRICTATIME', 1 << 24),
217 ('MS_LAZYTIME', 1 << 25),
218 ('MS_ACTIVE', 1 << 30),
219 ('MS_NOUSER', 1 << 31),
220]
221UMOUNT_FLAGS = [
222 ('MNT_FORCE', 1),
223 ('MNT_DETACH', 2),
224 ('MNT_EXPIRE', 4),
225 ('UMOUNT_NOFOLLOW', 8),
226]
227
228
229TASK_COMM_LEN = 16 # linux/sched.h
230MAX_STR_LEN = 412
231
232
233class EventType(object):
234 EVENT_MOUNT = 0
235 EVENT_MOUNT_SOURCE = 1
236 EVENT_MOUNT_TARGET = 2
237 EVENT_MOUNT_TYPE = 3
238 EVENT_MOUNT_DATA = 4
239 EVENT_MOUNT_RET = 5
240 EVENT_UMOUNT = 6
241 EVENT_UMOUNT_TARGET = 7
242 EVENT_UMOUNT_RET = 8
243
244
245class EnterData(ctypes.Structure):
246 _fields_ = [
247 ('mnt_ns', ctypes.c_uint),
248 ('comm', ctypes.c_char * TASK_COMM_LEN),
249 ('flags', ctypes.c_ulong),
250 ]
251
252
253class DataUnion(ctypes.Union):
254 _fields_ = [
255 ('enter', EnterData),
256 ('str', ctypes.c_char * MAX_STR_LEN),
257 ('retval', ctypes.c_int),
258 ]
259
260
261class Event(ctypes.Structure):
262 _fields_ = [
263 ('type', ctypes.c_uint),
264 ('pid', ctypes.c_uint),
265 ('tgid', ctypes.c_uint),
266 ('union', DataUnion),
267 ]
268
269
270def _decode_flags(flags, flag_list):
271 str_flags = []
272 for flag, bit in flag_list:
273 if flags & bit:
274 str_flags.append(flag)
275 flags &= ~bit
276 if flags or not str_flags:
277 str_flags.append('0x{:x}'.format(flags))
278 return str_flags
279
280
281def decode_flags(flags, flag_list):
282 return '|'.join(_decode_flags(flags, flag_list))
283
284
285def decode_mount_flags(flags):
286 str_flags = []
287 if flags & MS_MGC_MSK == MS_MGC_VAL:
288 flags &= ~MS_MGC_MSK
289 str_flags.append('MS_MGC_VAL')
290 str_flags.extend(_decode_flags(flags, MOUNT_FLAGS))
291 return '|'.join(str_flags)
292
293
294def decode_umount_flags(flags):
295 return decode_flags(flags, UMOUNT_FLAGS)
296
297
298def decode_errno(retval):
299 try:
300 return '-' + errno.errorcode[-retval]
301 except KeyError:
302 return str(retval)
303
304
305_escape_chars = {
306 ord('\a'): '\\a',
307 ord('\b'): '\\b',
308 ord('\t'): '\\t',
309 ord('\n'): '\\n',
310 ord('\v'): '\\v',
311 ord('\f'): '\\f',
312 ord('\r'): '\\r',
313 ord('"'): '\\"',
314 ord('\\'): '\\\\',
315}
316
317
318def escape_character(c):
319 try:
320 return _escape_chars[c]
321 except KeyError:
322 if 0x20 <= c <= 0x7e:
323 return chr(c)
324 else:
325 return '\\x{:02x}'.format(c)
326
327
328if sys.version_info.major < 3:
329 def decode_mount_string(s):
330 return '"{}"'.format(''.join(escape_character(ord(c)) for c in s))
331else:
332 def decode_mount_string(s):
333 return '"{}"'.format(''.join(escape_character(c) for c in s))
334
335
336def print_event(mounts, umounts, cpu, data, size):
337 event = ctypes.cast(data, ctypes.POINTER(Event)).contents
338
339 try:
340 if event.type == EventType.EVENT_MOUNT:
341 mounts[event.pid] = {
342 'pid': event.pid,
343 'tgid': event.tgid,
344 'mnt_ns': event.union.enter.mnt_ns,
345 'comm': event.union.enter.comm,
346 'flags': event.union.enter.flags,
347 }
348 elif event.type == EventType.EVENT_MOUNT_SOURCE:
349 mounts[event.pid]['source'] = event.union.str
350 elif event.type == EventType.EVENT_MOUNT_TARGET:
351 mounts[event.pid]['target'] = event.union.str
352 elif event.type == EventType.EVENT_MOUNT_TYPE:
353 mounts[event.pid]['type'] = event.union.str
354 elif event.type == EventType.EVENT_MOUNT_DATA:
355 # XXX: data is not always a NUL-terminated string
356 mounts[event.pid]['data'] = event.union.str
357 elif event.type == EventType.EVENT_UMOUNT:
358 umounts[event.pid] = {
359 'pid': event.pid,
360 'tgid': event.tgid,
361 'mnt_ns': event.union.enter.mnt_ns,
362 'comm': event.union.enter.comm,
363 'flags': event.union.enter.flags,
364 }
365 elif event.type == EventType.EVENT_UMOUNT_TARGET:
366 umounts[event.pid]['target'] = event.union.str
367 elif (event.type == EventType.EVENT_MOUNT_RET or
368 event.type == EventType.EVENT_UMOUNT_RET):
369 if event.type == EventType.EVENT_MOUNT_RET:
370 syscall = mounts.pop(event.pid)
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300371 call = ('mount({source}, {target}, {type}, {flags}, {data}) ' +
372 '= {retval}').format(
Omar Sandovale822a812016-10-16 12:31:32 -0700373 source=decode_mount_string(syscall['source']),
374 target=decode_mount_string(syscall['target']),
375 type=decode_mount_string(syscall['type']),
376 flags=decode_mount_flags(syscall['flags']),
377 data=decode_mount_string(syscall['data']),
378 retval=decode_errno(event.union.retval))
379 else:
380 syscall = umounts.pop(event.pid)
381 call = 'umount({target}, {flags}) = {retval}'.format(
382 target=decode_mount_string(syscall['target']),
383 flags=decode_umount_flags(syscall['flags']),
384 retval=decode_errno(event.union.retval))
385 print('{:16} {:<7} {:<7} {:<11} {}'.format(
jeromemarchandb96ebcd2018-10-10 01:58:15 +0200386 syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'],
387 syscall['pid'], syscall['mnt_ns'], call))
Omar Sandovale822a812016-10-16 12:31:32 -0700388 except KeyError:
389 # This might happen if we lost an event.
390 pass
391
392
393def main():
394 parser = argparse.ArgumentParser(
395 description='trace mount() and umount() syscalls'
396 )
Nathan Scottcf0792f2018-02-02 16:56:50 +1100397 parser.add_argument("--ebpf", action="store_true",
398 help=argparse.SUPPRESS)
Omar Sandovale822a812016-10-16 12:31:32 -0700399 args = parser.parse_args()
400
401 mounts = {}
402 umounts = {}
Nathan Scottcf0792f2018-02-02 16:56:50 +1100403 if args.ebpf:
404 print(bpf_text)
405 exit()
Omar Sandovale822a812016-10-16 12:31:32 -0700406 b = bcc.BPF(text=bpf_text)
Yonghong Song64335692018-04-25 00:40:13 -0700407 mount_fnname = b.get_syscall_fnname("mount")
yonghong-song2da34262018-06-13 06:12:22 -0700408 b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount")
Yonghong Song64335692018-04-25 00:40:13 -0700409 b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount")
410 umount_fnname = b.get_syscall_fnname("umount")
yonghong-song2da34262018-06-13 06:12:22 -0700411 b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount")
Yonghong Song64335692018-04-25 00:40:13 -0700412 b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount")
Omar Sandovale822a812016-10-16 12:31:32 -0700413 b['events'].open_perf_buffer(
414 functools.partial(print_event, mounts, umounts))
415 print('{:16} {:<7} {:<7} {:<11} {}'.format(
416 'COMM', 'PID', 'TID', 'MNT_NS', 'CALL'))
417 while True:
Jerome Marchand51671272018-12-19 01:57:24 +0100418 try:
419 b.perf_buffer_poll()
420 except KeyboardInterrupt:
421 exit()
422
Omar Sandovale822a812016-10-16 12:31:32 -0700423
424
425if __name__ == '__main__':
426 main()