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: &fbuffer, trace_file: file, len); |
25 | if (entry) { |
26 | memcpy(entry, rec, len); |
27 | written = len; |
28 | trace_event_buffer_commit(fbuffer: &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(s: str + s, len, GFP_KERNEL); |
58 | if (!field_name) |
59 | return -ENOMEM; |
60 | field = trace_find_event_field(call, name: field_name); |
61 | kfree(objp: 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(c: 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(s: num, base: 0, res: &val); |
96 | else |
97 | ret = kstrtoull(s: num, base: 0, res: &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(event_call: 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(size: entry_size + 1, GFP_KERNEL); |
162 | if (!entry) |
163 | return NULL; |
164 | |
165 | head = trace_get_fields(event_call: 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, size: &entry_size); |
205 | *pentry = entry; |
206 | if (!entry) |
207 | return -ENOMEM; |
208 | |
209 | tracing_generic_entry_update(entry, type: call->event.type, |
210 | trace_ctx: tracing_gen_ctx()); |
211 | |
212 | while ((len = parse_field(str, call, pf: &field, pv: &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 | strscpy(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(objp: entry, new_size: entry_size, GFP_KERNEL); |
229 | if (!*pentry) { |
230 | kfree(objp: entry); |
231 | return -ENOMEM; |
232 | } |
233 | entry = *pentry; |
234 | |
235 | strscpy(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(ptr: buf)) |
298 | return PTR_ERR(ptr: 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(str: buf, call, pentry: &entry); |
306 | if (size < 0) |
307 | err = size; |
308 | else |
309 | err = trace_inject_entry(file, rec: entry, len: size); |
310 | } |
311 | mutex_unlock(lock: &event_mutex); |
312 | |
313 | kfree(objp: entry); |
314 | kfree(objp: 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_file_tr, |
332 | .read = event_inject_read, |
333 | .write = event_inject_write, |
334 | .release = tracing_release_file_tr, |
335 | }; |
336 | |