xref: /linux/kernel/trace/trace_events_inject.c (revision e999db587312e5b798421d803495f41d1283d7d7)
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 	void *entry = NULL;
196 	int entry_size;
197 	u64 val = 0;
198 	int len;
199 
200 	entry = trace_alloc_entry(call, &entry_size);
201 	*pentry = entry;
202 	if (!entry)
203 		return -ENOMEM;
204 
205 	tracing_generic_entry_update(entry, call->event.type,
206 				     tracing_gen_ctx());
207 
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 
276 static ssize_t
277 event_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 
316 static ssize_t
317 event_inject_read(struct file *file, char __user *buf, size_t size,
318 		  loff_t *ppos)
319 {
320 	return -EPERM;
321 }
322 
323 const struct file_operations event_inject_fops = {
324 	.open = tracing_open_generic,
325 	.read = event_inject_read,
326 	.write = event_inject_write,
327 };
328