1 // SPDX-License-Identifier: GPL-2.0
2
3 #include "trace.h"
4
5 /**
6 * trace_find_filtered_pid - check if a pid exists in a filtered_pid list
7 * @filtered_pids: The list of pids to check
8 * @search_pid: The PID to find in @filtered_pids
9 *
10 * Returns true if @search_pid is found in @filtered_pids, and false otherwise.
11 */
12 bool
trace_find_filtered_pid(struct trace_pid_list * filtered_pids,pid_t search_pid)13 trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid)
14 {
15 return trace_pid_list_is_set(filtered_pids, search_pid);
16 }
17
18 /**
19 * trace_ignore_this_task - should a task be ignored for tracing
20 * @filtered_pids: The list of pids to check
21 * @filtered_no_pids: The list of pids not to be traced
22 * @task: The task that should be ignored if not filtered
23 *
24 * Checks if @task should be traced or not from @filtered_pids.
25 * Returns true if @task should *NOT* be traced.
26 * Returns false if @task should be traced.
27 */
28 bool
trace_ignore_this_task(struct trace_pid_list * filtered_pids,struct trace_pid_list * filtered_no_pids,struct task_struct * task)29 trace_ignore_this_task(struct trace_pid_list *filtered_pids,
30 struct trace_pid_list *filtered_no_pids,
31 struct task_struct *task)
32 {
33 /*
34 * If filtered_no_pids is not empty, and the task's pid is listed
35 * in filtered_no_pids, then return true.
36 * Otherwise, if filtered_pids is empty, that means we can
37 * trace all tasks. If it has content, then only trace pids
38 * within filtered_pids.
39 */
40
41 return (filtered_pids &&
42 !trace_find_filtered_pid(filtered_pids, task->pid)) ||
43 (filtered_no_pids &&
44 trace_find_filtered_pid(filtered_no_pids, task->pid));
45 }
46
47 /**
48 * trace_filter_add_remove_task - Add or remove a task from a pid_list
49 * @pid_list: The list to modify
50 * @self: The current task for fork or NULL for exit
51 * @task: The task to add or remove
52 *
53 * If adding a task, if @self is defined, the task is only added if @self
54 * is also included in @pid_list. This happens on fork and tasks should
55 * only be added when the parent is listed. If @self is NULL, then the
56 * @task pid will be removed from the list, which would happen on exit
57 * of a task.
58 */
trace_filter_add_remove_task(struct trace_pid_list * pid_list,struct task_struct * self,struct task_struct * task)59 void trace_filter_add_remove_task(struct trace_pid_list *pid_list,
60 struct task_struct *self,
61 struct task_struct *task)
62 {
63 if (!pid_list)
64 return;
65
66 /* For forks, we only add if the forking task is listed */
67 if (self) {
68 if (!trace_find_filtered_pid(pid_list, self->pid))
69 return;
70 }
71
72 /* "self" is set for forks, and NULL for exits */
73 if (self)
74 trace_pid_list_set(pid_list, task->pid);
75 else
76 trace_pid_list_clear(pid_list, task->pid);
77 }
78
79 /**
80 * trace_pid_next - Used for seq_file to get to the next pid of a pid_list
81 * @pid_list: The pid list to show
82 * @v: The last pid that was shown (+1 the actual pid to let zero be displayed)
83 * @pos: The position of the file
84 *
85 * This is used by the seq_file "next" operation to iterate the pids
86 * listed in a trace_pid_list structure.
87 *
88 * Returns the pid+1 as we want to display pid of zero, but NULL would
89 * stop the iteration.
90 */
trace_pid_next(struct trace_pid_list * pid_list,void * v,loff_t * pos)91 void *trace_pid_next(struct trace_pid_list *pid_list, void *v, loff_t *pos)
92 {
93 long pid = (unsigned long)v;
94 unsigned int next;
95
96 (*pos)++;
97
98 /* pid already is +1 of the actual previous bit */
99 if (trace_pid_list_next(pid_list, pid, &next) < 0)
100 return NULL;
101
102 pid = next;
103
104 /* Return pid + 1 to allow zero to be represented */
105 return (void *)(pid + 1);
106 }
107
108 /**
109 * trace_pid_start - Used for seq_file to start reading pid lists
110 * @pid_list: The pid list to show
111 * @pos: The position of the file
112 *
113 * This is used by seq_file "start" operation to start the iteration
114 * of listing pids.
115 *
116 * Returns the pid+1 as we want to display pid of zero, but NULL would
117 * stop the iteration.
118 */
trace_pid_start(struct trace_pid_list * pid_list,loff_t * pos)119 void *trace_pid_start(struct trace_pid_list *pid_list, loff_t *pos)
120 {
121 unsigned long pid;
122 unsigned int first;
123 loff_t l = 0;
124
125 if (trace_pid_list_first(pid_list, &first) < 0)
126 return NULL;
127
128 pid = first;
129
130 /* Return pid + 1 so that zero can be the exit value */
131 for (pid++; pid && l < *pos;
132 pid = (unsigned long)trace_pid_next(pid_list, (void *)pid, &l))
133 ;
134 return (void *)pid;
135 }
136
137 /**
138 * trace_pid_show - show the current pid in seq_file processing
139 * @m: The seq_file structure to write into
140 * @v: A void pointer of the pid (+1) value to display
141 *
142 * Can be directly used by seq_file operations to display the current
143 * pid value.
144 */
trace_pid_show(struct seq_file * m,void * v)145 int trace_pid_show(struct seq_file *m, void *v)
146 {
147 unsigned long pid = (unsigned long)v - 1;
148
149 seq_printf(m, "%lu\n", pid);
150 return 0;
151 }
152
153 /* 128 should be much more than enough */
154 #define PID_BUF_SIZE 127
155
trace_pid_write(struct trace_pid_list * filtered_pids,struct trace_pid_list ** new_pid_list,const char __user * ubuf,size_t cnt)156 int trace_pid_write(struct trace_pid_list *filtered_pids,
157 struct trace_pid_list **new_pid_list,
158 const char __user *ubuf, size_t cnt)
159 {
160 struct trace_pid_list *pid_list;
161 struct trace_parser parser;
162 unsigned long val;
163 int nr_pids = 0;
164 ssize_t read = 0;
165 ssize_t ret;
166 loff_t pos;
167 pid_t pid;
168
169 if (trace_parser_get_init(&parser, PID_BUF_SIZE + 1))
170 return -ENOMEM;
171
172 /*
173 * Always recreate a new array. The write is an all or nothing
174 * operation. Always create a new array when adding new pids by
175 * the user. If the operation fails, then the current list is
176 * not modified.
177 */
178 pid_list = trace_pid_list_alloc();
179 if (!pid_list) {
180 trace_parser_put(&parser);
181 return -ENOMEM;
182 }
183
184 if (filtered_pids) {
185 /* copy the current bits to the new max */
186 ret = trace_pid_list_first(filtered_pids, &pid);
187 while (!ret) {
188 ret = trace_pid_list_set(pid_list, pid);
189 if (ret < 0)
190 goto out;
191
192 ret = trace_pid_list_next(filtered_pids, pid + 1, &pid);
193 nr_pids++;
194 }
195 }
196
197 ret = 0;
198 while (cnt > 0) {
199
200 pos = 0;
201
202 ret = trace_get_user(&parser, ubuf, cnt, &pos);
203 if (ret < 0)
204 break;
205
206 read += ret;
207 ubuf += ret;
208 cnt -= ret;
209
210 if (!trace_parser_loaded(&parser))
211 break;
212
213 ret = -EINVAL;
214 if (kstrtoul(parser.buffer, 0, &val))
215 break;
216
217 pid = (pid_t)val;
218
219 if (trace_pid_list_set(pid_list, pid) < 0) {
220 ret = -1;
221 break;
222 }
223 nr_pids++;
224
225 trace_parser_clear(&parser);
226 ret = 0;
227 }
228 out:
229 trace_parser_put(&parser);
230
231 if (ret < 0) {
232 trace_pid_list_free(pid_list);
233 return ret;
234 }
235
236 if (!nr_pids) {
237 /* Cleared the list of pids */
238 trace_pid_list_free(pid_list);
239 pid_list = NULL;
240 }
241
242 *new_pid_list = pid_list;
243
244 return read;
245 }
246
247