blob: c188045c5f976cc35d20e6c72d108f934f0c27d6 [file] [log] [blame]
Cong Wang6c3edaf2019-11-29 20:52:18 -08001// 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
16static int
17trace_inject_entry(struct trace_event_file *file, void *rec, int len)
18{
19 struct trace_event_buffer fbuffer;
Cong Wang6c3edaf2019-11-29 20:52:18 -080020 int written = 0;
21 void *entry;
22
23 rcu_read_lock_sched();
Cong Wang6c3edaf2019-11-29 20:52:18 -080024 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
35static int
36parse_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
138static 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
153static 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. */
192static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
193{
194 struct ftrace_event_field *field;
Cong Wang6c3edaf2019-11-29 20:52:18 -0800195 void *entry = NULL;
196 int entry_size;
Steven Rostedt (VMware)02f4e012020-01-02 19:04:57 -0500197 u64 val = 0;
Cong Wang6c3edaf2019-11-29 20:52:18 -0800198 int len;
199
200 entry = trace_alloc_entry(call, &entry_size);
201 *pentry = entry;
202 if (!entry)
203 return -ENOMEM;
204
Sebastian Andrzej Siewior36590c502021-01-25 20:45:08 +0100205 tracing_generic_entry_update(entry, call->event.type,
206 tracing_gen_ctx());
Cong Wang6c3edaf2019-11-29 20:52:18 -0800207
208 while ((len = parse_field(str, call, &field, &val)) > 0) {
209 if (is_function_field(field))
210 return -EINVAL;
211
212 if (is_string_field(field)) {
213 char *addr = (char *)(unsigned long) val;
214
215 if (field->filter_type == FILTER_STATIC_STRING) {
216 strlcpy(entry + field->offset, addr, field->size);
217 } else if (field->filter_type == FILTER_DYN_STRING) {
218 int str_len = strlen(addr) + 1;
219 int str_loc = entry_size & 0xffff;
220 u32 *str_item;
221
222 entry_size += str_len;
223 *pentry = krealloc(entry, entry_size, GFP_KERNEL);
224 if (!*pentry) {
225 kfree(entry);
226 return -ENOMEM;
227 }
228 entry = *pentry;
229
230 strlcpy(entry + (entry_size - str_len), addr, str_len);
231 str_item = (u32 *)(entry + field->offset);
232 *str_item = (str_len << 16) | str_loc;
233 } else {
234 char **paddr;
235
236 paddr = (char **)(entry + field->offset);
237 *paddr = INJECT_STRING;
238 }
239 } else {
240 switch (field->size) {
241 case 1: {
242 u8 tmp = (u8) val;
243
244 memcpy(entry + field->offset, &tmp, 1);
245 break;
246 }
247 case 2: {
248 u16 tmp = (u16) val;
249
250 memcpy(entry + field->offset, &tmp, 2);
251 break;
252 }
253 case 4: {
254 u32 tmp = (u32) val;
255
256 memcpy(entry + field->offset, &tmp, 4);
257 break;
258 }
259 case 8:
260 memcpy(entry + field->offset, &val, 8);
261 break;
262 default:
263 return -EINVAL;
264 }
265 }
266
267 str += len;
268 }
269
270 if (len < 0)
271 return len;
272
273 return entry_size;
274}
275
276static ssize_t
277event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
278 loff_t *ppos)
279{
280 struct trace_event_call *call;
281 struct trace_event_file *file;
282 int err = -ENODEV, size;
283 void *entry = NULL;
284 char *buf;
285
286 if (cnt >= PAGE_SIZE)
287 return -EINVAL;
288
289 buf = memdup_user_nul(ubuf, cnt);
290 if (IS_ERR(buf))
291 return PTR_ERR(buf);
292 strim(buf);
293
294 mutex_lock(&event_mutex);
295 file = event_file_data(filp);
296 if (file) {
297 call = file->event_call;
298 size = parse_entry(buf, call, &entry);
299 if (size < 0)
300 err = size;
301 else
302 err = trace_inject_entry(file, entry, size);
303 }
304 mutex_unlock(&event_mutex);
305
306 kfree(entry);
307 kfree(buf);
308
309 if (err < 0)
310 return err;
311
312 *ppos += err;
313 return cnt;
314}
315
316static ssize_t
317event_inject_read(struct file *file, char __user *buf, size_t size,
318 loff_t *ppos)
319{
320 return -EPERM;
321}
322
323const struct file_operations event_inject_fops = {
324 .open = tracing_open_generic,
325 .read = event_inject_read,
326 .write = event_inject_write,
327};