xref: /linux/kernel/trace/trace_events_inject.c (revision 9466b6ae6befa87b27cc13ee7263e01b9c48efc0)
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
trace_inject_entry(struct trace_event_file * file,void * rec,int len)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
parse_field(char * str,struct trace_event_call * call,struct ftrace_event_field ** pf,u64 * pv)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 
trace_get_entry_size(struct trace_event_call * call)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 
trace_alloc_entry(struct trace_event_call * call,int * size)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. */
parse_entry(char * str,struct trace_event_call * call,void ** 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 				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(entry, entry_size, GFP_KERNEL);
229 				if (!*pentry) {
230 					kfree(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
event_inject_write(struct file * filp,const char __user * ubuf,size_t cnt,loff_t * ppos)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_file(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
event_inject_read(struct file * file,char __user * buf,size_t size,loff_t * ppos)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