~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/kernel/trace/trace_events_inject.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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                     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, &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
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
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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php