Merge tag 'ktest-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[linux-2.6-microblaze.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                         u32 *str_item;
173                         int str_loc = entry_size & 0xffff;
174
175                         str_item = (u32 *)(entry + field->offset);
176                         *str_item = str_loc; /* string length is 0. */
177                 } else {
178                         char **paddr;
179
180                         paddr = (char **)(entry + field->offset);
181                         *paddr = "";
182                 }
183         }
184
185         *size = entry_size + 1;
186         return entry;
187 }
188
189 #define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
190
191 /* Caller is responsible to free the *pentry. */
192 static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
193 {
194         struct ftrace_event_field *field;
195         unsigned long irq_flags;
196         void *entry = NULL;
197         int entry_size;
198         u64 val = 0;
199         int len;
200
201         entry = trace_alloc_entry(call, &entry_size);
202         *pentry = entry;
203         if (!entry)
204                 return -ENOMEM;
205
206         local_save_flags(irq_flags);
207         tracing_generic_entry_update(entry, call->event.type, irq_flags,
208                                      preempt_count());
209
210         while ((len = parse_field(str, call, &field, &val)) > 0) {
211                 if (is_function_field(field))
212                         return -EINVAL;
213
214                 if (is_string_field(field)) {
215                         char *addr = (char *)(unsigned long) val;
216
217                         if (field->filter_type == FILTER_STATIC_STRING) {
218                                 strlcpy(entry + field->offset, addr, field->size);
219                         } else if (field->filter_type == FILTER_DYN_STRING) {
220                                 int str_len = strlen(addr) + 1;
221                                 int str_loc = entry_size & 0xffff;
222                                 u32 *str_item;
223
224                                 entry_size += str_len;
225                                 *pentry = krealloc(entry, entry_size, GFP_KERNEL);
226                                 if (!*pentry) {
227                                         kfree(entry);
228                                         return -ENOMEM;
229                                 }
230                                 entry = *pentry;
231
232                                 strlcpy(entry + (entry_size - str_len), addr, str_len);
233                                 str_item = (u32 *)(entry + field->offset);
234                                 *str_item = (str_len << 16) | str_loc;
235                         } else {
236                                 char **paddr;
237
238                                 paddr = (char **)(entry + field->offset);
239                                 *paddr = INJECT_STRING;
240                         }
241                 } else {
242                         switch (field->size) {
243                         case 1: {
244                                 u8 tmp = (u8) val;
245
246                                 memcpy(entry + field->offset, &tmp, 1);
247                                 break;
248                         }
249                         case 2: {
250                                 u16 tmp = (u16) val;
251
252                                 memcpy(entry + field->offset, &tmp, 2);
253                                 break;
254                         }
255                         case 4: {
256                                 u32 tmp = (u32) val;
257
258                                 memcpy(entry + field->offset, &tmp, 4);
259                                 break;
260                         }
261                         case 8:
262                                 memcpy(entry + field->offset, &val, 8);
263                                 break;
264                         default:
265                                 return -EINVAL;
266                         }
267                 }
268
269                 str += len;
270         }
271
272         if (len < 0)
273                 return len;
274
275         return entry_size;
276 }
277
278 static ssize_t
279 event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
280                    loff_t *ppos)
281 {
282         struct trace_event_call *call;
283         struct trace_event_file *file;
284         int err = -ENODEV, size;
285         void *entry = NULL;
286         char *buf;
287
288         if (cnt >= PAGE_SIZE)
289                 return -EINVAL;
290
291         buf = memdup_user_nul(ubuf, cnt);
292         if (IS_ERR(buf))
293                 return PTR_ERR(buf);
294         strim(buf);
295
296         mutex_lock(&event_mutex);
297         file = event_file_data(filp);
298         if (file) {
299                 call = file->event_call;
300                 size = parse_entry(buf, call, &entry);
301                 if (size < 0)
302                         err = size;
303                 else
304                         err = trace_inject_entry(file, entry, size);
305         }
306         mutex_unlock(&event_mutex);
307
308         kfree(entry);
309         kfree(buf);
310
311         if (err < 0)
312                 return err;
313
314         *ppos += err;
315         return cnt;
316 }
317
318 static ssize_t
319 event_inject_read(struct file *file, char __user *buf, size_t size,
320                   loff_t *ppos)
321 {
322         return -EPERM;
323 }
324
325 const struct file_operations event_inject_fops = {
326         .open = tracing_open_generic,
327         .read = event_inject_read,
328         .write = event_inject_write,
329 };