1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Augment syscalls with the contents of the pointer arguments.
4  *
5  * Test it with:
6  *
7  * perf trace -e tools/perf/examples/bpf/augmented_syscalls.c cat /etc/passwd > /dev/null
8  *
9  * It'll catch some openat syscalls related to the dynamic linked and
10  * the last one should be the one for '/etc/passwd'.
11  *
12  * This matches what is marshalled into the raw_syscall:sys_enter payload
13  * expected by the 'perf trace' beautifiers, and can be used by them, that will
14  * check if perf_sample->raw_data is more than what is expected for each
15  * syscalls:sys_{enter,exit}_SYSCALL tracepoint, uing the extra data as the
16  * contents of pointer arguments.
17  */
18 
19 #include <stdio.h>
20 #include <linux/socket.h>
21 
22 /* bpf-output associated map */
23 bpf_map(__augmented_syscalls__, PERF_EVENT_ARRAY, int, u32, __NR_CPUS__);
24 
25 struct syscall_exit_args {
26 	unsigned long long common_tp_fields;
27 	long		   syscall_nr;
28 	long		   ret;
29 };
30 
31 struct augmented_filename {
32 	unsigned int	size;
33 	int		reserved;
34 	char		value[256];
35 };
36 
37 #define augmented_filename_syscall(syscall)							\
38 struct augmented_enter_##syscall##_args {			 				\
39 	struct syscall_enter_##syscall##_args	args;				 		\
40 	struct augmented_filename		filename;				 	\
41 };												\
42 int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args)				\
43 {												\
44 	struct augmented_enter_##syscall##_args augmented_args = { .filename.reserved = 0, }; 	\
45 	unsigned int len = sizeof(augmented_args);						\
46 	probe_read(&augmented_args.args, sizeof(augmented_args.args), args);			\
47 	augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, 		\
48 						      sizeof(augmented_args.filename.value), 	\
49 						      args->filename_ptr); 			\
50 	if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) {		\
51 		len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size;	\
52 		len &= sizeof(augmented_args.filename.value) - 1;				\
53 	}											\
54 	/* If perf_event_output fails, return non-zero so that it gets recorded unaugmented */	\
55 	return perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, 		\
56 				 &augmented_args, len);						\
57 }												\
58 int syscall_exit(syscall)(struct syscall_exit_args *args)					\
59 {												\
60        return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */	\
61 }
62 
63 struct syscall_enter_openat_args {
64 	unsigned long long common_tp_fields;
65 	long		   syscall_nr;
66 	long		   dfd;
67 	char		   *filename_ptr;
68 	long		   flags;
69 	long		   mode;
70 };
71 
72 augmented_filename_syscall(openat);
73 
74 struct syscall_enter_open_args {
75 	unsigned long long common_tp_fields;
76 	long		   syscall_nr;
77 	char		   *filename_ptr;
78 	long		   flags;
79 	long		   mode;
80 };
81 
82 augmented_filename_syscall(open);
83 
84 struct syscall_enter_inotify_add_watch_args {
85 	unsigned long long common_tp_fields;
86 	long		   syscall_nr;
87 	long		   fd;
88 	char		   *filename_ptr;
89 	long		   mask;
90 };
91 
92 augmented_filename_syscall(inotify_add_watch);
93 
94 struct statbuf;
95 
96 struct syscall_enter_newstat_args {
97 	unsigned long long common_tp_fields;
98 	long		   syscall_nr;
99 	char		   *filename_ptr;
100 	struct stat	   *statbuf;
101 };
102 
103 augmented_filename_syscall(newstat);
104 
105 #ifndef _K_SS_MAXSIZE
106 #define _K_SS_MAXSIZE 128
107 #endif
108 
109 #define augmented_sockaddr_syscall(syscall)						\
110 struct augmented_enter_##syscall##_args {			 				\
111 	struct syscall_enter_##syscall##_args	args;				 		\
112 	struct sockaddr_storage			addr;						\
113 };												\
114 int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args)				\
115 {												\
116 	struct augmented_enter_##syscall##_args augmented_args;				 	\
117 	unsigned long addrlen = sizeof(augmented_args.addr);					\
118 	probe_read(&augmented_args.args, sizeof(augmented_args.args), args);			\
119 /* FIXME_CLANG_OPTIMIZATION_THAT_ACCESSES_USER_CONTROLLED_ADDRLEN_DESPITE_THIS_CHECK */		\
120 /*	if (addrlen > augmented_args.args.addrlen)				     */		\
121 /*		addrlen = augmented_args.args.addrlen;				     */		\
122 /*										     */		\
123 	probe_read(&augmented_args.addr, addrlen, args->addr_ptr); 				\
124 	/* If perf_event_output fails, return non-zero so that it gets recorded unaugmented */	\
125 	return perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, 		\
126 				 &augmented_args, 						\
127 				sizeof(augmented_args) - sizeof(augmented_args.addr) + addrlen);\
128 }												\
129 int syscall_exit(syscall)(struct syscall_exit_args *args)					\
130 {												\
131        return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */	\
132 }
133 
134 struct sockaddr;
135 
136 struct syscall_enter_bind_args {
137 	unsigned long long common_tp_fields;
138 	long		   syscall_nr;
139 	long		   fd;
140 	struct sockaddr	   *addr_ptr;
141 	unsigned long	   addrlen;
142 };
143 
144 augmented_sockaddr_syscall(bind);
145 
146 struct syscall_enter_connect_args {
147 	unsigned long long common_tp_fields;
148 	long		   syscall_nr;
149 	long		   fd;
150 	struct sockaddr	   *addr_ptr;
151 	unsigned long	   addrlen;
152 };
153 
154 augmented_sockaddr_syscall(connect);
155 
156 struct syscall_enter_sendto_args {
157 	unsigned long long common_tp_fields;
158 	long		   syscall_nr;
159 	long		   fd;
160 	void		   *buff;
161 	long		   len;
162 	unsigned long	   flags;
163 	struct sockaddr	   *addr_ptr;
164 	long		   addr_len;
165 };
166 
167 augmented_sockaddr_syscall(sendto);
168 
169 license(GPL);
170