Merge tag 'i2c-for-6.4-rc1-part2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / kernel / trace / trace_events_inject.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * trace_events_inject - trace event injection
4  *
5  * Copyright (C) 2019 Cong Wang <cwang@twitter.com>
6  */
7
8 #include <linux/module.h>
9 #include <linux/ctype.h>
10 #include <linux/mutex.h>
11 #include <linux/slab.h>
12 #include <linux/rculist.h>
13
14 #include "trace.h"
15
16 static int
17 trace_inject_entry(struct trace_event_file *file, void *rec, int len)
18 {
19         struct trace_event_buffer fbuffer;
20         int written = 0;
21         void *entry;
22
23         rcu_read_lock_sched();
24         entry = trace_event_buffer_reserve(&fbuffer, file, len);
25         if (entry) {
26                 memcpy(entry, rec, len);
27                 written = len;
28                 trace_event_buffer_commit(&fbuffer);
29         }
30         rcu_read_unlock_sched();
31
32         return written;
33 }
34
35 static int
36 parse_field(char *str, struct trace_event_call *call,
37             struct ftrace_event_field **pf, u64 *pv)
38 {
39         struct ftrace_event_field *field;
40         char *field_name;
41         int s, i = 0;
42         int len;
43         u64 val;
44
45         if (!str[i])
46                 return 0;
47         /* First find the field to associate to */
48         while (isspace(str[i]))
49                 i++;
50         s = i;
51         while (isalnum(str[i]) || str[i] == '_')
52                 i++;
53         len = i - s;
54         if (!len)
55                 return -EINVAL;
56
57         field_name = kmemdup_nul(str + s, len, GFP_KERNEL);
58         if (!field_name)
59                 return -ENOMEM;
60         field = trace_find_event_field(call, field_name);
61         kfree(field_name);
62         if (!field)
63                 return -ENOENT;
64
65         *pf = field;
66         while (isspace(str[i]))
67                 i++;
68         if (str[i] != '=')
69                 return -EINVAL;
70         i++;
71         while (isspace(str[i]))
72                 i++;
73         s = i;
74         if (isdigit(str[i]) || str[i] == '-') {
75                 char *num, c;
76                 int ret;
77
78                 /* Make sure the field is not a string */
79                 if (is_string_field(field))
80                         return -EINVAL;
81
82                 if (str[i] == '-')
83                         i++;
84
85                 /* We allow 0xDEADBEEF */
86                 while (isalnum(str[i]))
87                         i++;
88                 num = str + s;
89                 c = str[i];
90                 if (c != '\0' && !isspace(c))
91                         return -EINVAL;
92                 str[i] = '\0';
93                 /* Make sure it is a value */
94                 if (field->is_signed)
95                         ret = kstrtoll(num, 0, &val);
96                 else
97                         ret = kstrtoull(num, 0, &val);
98                 str[i] = c;
99                 if (ret)
100                         return ret;
101
102                 *pv = val;
103                 return i;
104         } else if (str[i] == '\'' || str[i] == '"') {
105                 char q = str[i];
106
107                 /* Make sure the field is OK for strings */
108                 if (!is_string_field(field))
109                         return -EINVAL;
110
111                 for (i++; str[i]; i++) {
112                         if (str[i] == '\\' && str[i + 1]) {
113                                 i++;
114                                 continue;
115                         }
116                         if (str[i] == q)
117                                 break;
118                 }
119                 if (!str[i])
120                         return -EINVAL;
121
122                 /* Skip quotes */
123                 s++;
124                 len = i - s;
125                 if (len >= MAX_FILTER_STR_VAL)
126                         return -EINVAL;
127
128                 *pv = (unsigned long)(str + s);
129                 str[i] = 0;
130                 /* go past the last quote */
131                 i++;
132                 return i;
133         }
134
135         return -EINVAL;
136 }
137
138 static int trace_get_entry_size(struct trace_event_call *call)
139 {
140         struct ftrace_event_field *field;
141         struct list_head *head;
142         int size = 0;
143
144         head = trace_get_fields(call);
145         list_for_each_entry(field, head, link) {
146                 if (field->size + field->offset > size)
147                         size = field->size + field->offset;
148         }
149
150         return size;
151 }
152
153 static void *trace_alloc_entry(struct trace_event_call *call, int *size)
154 {
155         int entry_size = trace_get_entry_size(call);
156         struct ftrace_event_field *field;
157         struct list_head *head;
158         void *entry = NULL;
159
160         /* We need an extra '\0' at the end. */
161         entry = kzalloc(entry_size + 1, GFP_KERNEL);
162         if (!entry)
163                 return NULL;
164
165         head = trace_get_fields(call);
166         list_for_each_entry(field, head, link) {
167                 if (!is_string_field(field))
168                         continue;
169                 if (field->filter_type == FILTER_STATIC_STRING)
170                         continue;
171                 if (field->filter_type == FILTER_DYN_STRING ||
172                     field->filter_type == FILTER_RDYN_STRING) {
173                         u32 *str_item;
174                         int str_loc = entry_size & 0xffff;
175
176                         if (field->filter_type == FILTER_RDYN_STRING)
177                                 str_loc -= field->offset + field->size;
178
179                         str_item = (u32 *)(entry + field->offset);
180                         *str_item = str_loc; /* string length is 0. */
181                 } else {
182                         char **paddr;
183
184                         paddr = (char **)(entry + field->offset);
185                         *paddr = "";
186                 }
187         }
188
189         *size = entry_size + 1;
190         return entry;
191 }
192
193 #define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
194
195 /* Caller is responsible to free the *pentry. */
196 static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
197 {
198         struct ftrace_event_field *field;
199         void *entry = NULL;
200         int entry_size;
201         u64 val = 0;
202         int len;
203
204         entry = trace_alloc_entry(call, &entry_size);
205         *pentry = entry;
206         if (!entry)
207                 return -ENOMEM;
208
209         tracing_generic_entry_update(entry, call->event.type,
210                                      tracing_gen_ctx());
211
212         while ((len = parse_field(str, call, &field, &val)) > 0) {
213                 if (is_function_field(field))
214                         return -EINVAL;
215
216                 if (is_string_field(field)) {
217                         char *addr = (char *)(unsigned long) val;
218
219                         if (field->filter_type == FILTER_STATIC_STRING) {
220                                 strlcpy(entry + field->offset, addr, field->size);
221                         } else if (field->filter_type == FILTER_DYN_STRING ||
222                                    field->filter_type == FILTER_RDYN_STRING) {
223                                 int str_len = strlen(addr) + 1;
224                                 int str_loc = entry_size & 0xffff;
225                                 u32 *str_item;
226
227                                 entry_size += str_len;
228                                 *pentry = krealloc(entry, entry_size, GFP_KERNEL);
229                                 if (!*pentry) {
230                                         kfree(entry);
231                                         return -ENOMEM;
232                                 }
233                                 entry = *pentry;
234
235                                 strlcpy(entry + (entry_size - str_len), addr, str_len);
236                                 str_item = (u32 *)(entry + field->offset);
237                                 if (field->filter_type == FILTER_RDYN_STRING)
238                                         str_loc -= field->offset + field->size;
239                                 *str_item = (str_len << 16) | str_loc;
240                         } else {
241                                 char **paddr;
242
243                                 paddr = (char **)(entry + field->offset);
244                                 *paddr = INJECT_STRING;
245                         }
246                 } else {
247                         switch (field->size) {
248                         case 1: {
249                                 u8 tmp = (u8) val;
250
251                                 memcpy(entry + field->offset, &tmp, 1);
252                                 break;
253                         }
254                         case 2: {
255                                 u16 tmp = (u16) val;
256
257                                 memcpy(entry + field->offset, &tmp, 2);
258                                 break;
259                         }
260                         case 4: {
261                                 u32 tmp = (u32) val;
262
263                                 memcpy(entry + field->offset, &tmp, 4);
264                                 break;
265                         }
266                         case 8:
267                                 memcpy(entry + field->offset, &val, 8);
268                                 break;
269                         default:
270                                 return -EINVAL;
271                         }
272                 }
273
274                 str += len;
275         }
276
277         if (len < 0)
278                 return len;
279
280         return entry_size;
281 }
282
283 static ssize_t
284 event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
285                    loff_t *ppos)
286 {
287         struct trace_event_call *call;
288         struct trace_event_file *file;
289         int err = -ENODEV, size;
290         void *entry = NULL;
291         char *buf;
292
293         if (cnt >= PAGE_SIZE)
294                 return -EINVAL;
295
296         buf = memdup_user_nul(ubuf, cnt);
297         if (IS_ERR(buf))
298                 return PTR_ERR(buf);
299         strim(buf);
300
301         mutex_lock(&event_mutex);
302         file = event_file_data(filp);
303         if (file) {
304                 call = file->event_call;
305                 size = parse_entry(buf, call, &entry);
306                 if (size < 0)
307                         err = size;
308                 else
309                         err = trace_inject_entry(file, entry, size);
310         }
311         mutex_unlock(&event_mutex);
312
313         kfree(entry);
314         kfree(buf);
315
316         if (err < 0)
317                 return err;
318
319         *ppos += err;
320         return cnt;
321 }
322
323 static ssize_t
324 event_inject_read(struct file *file, char __user *buf, size_t size,
325                   loff_t *ppos)
326 {
327         return -EPERM;
328 }
329
330 const struct file_operations event_inject_fops = {
331         .open = tracing_open_generic,
332         .read = event_inject_read,
333         .write = event_inject_write,
334 };