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 struct ring_buffer *buffer; 21 int written = 0; 22 void *entry; 23 24 rcu_read_lock_sched(); 25 buffer = file->tr->trace_buffer.buffer; 26 entry = trace_event_buffer_reserve(&fbuffer, file, len); 27 if (entry) { 28 memcpy(entry, rec, len); 29 written = len; 30 trace_event_buffer_commit(&fbuffer); 31 } 32 rcu_read_unlock_sched(); 33 34 return written; 35 } 36 37 static int 38 parse_field(char *str, struct trace_event_call *call, 39 struct ftrace_event_field **pf, u64 *pv) 40 { 41 struct ftrace_event_field *field; 42 char *field_name; 43 int s, i = 0; 44 int len; 45 u64 val; 46 47 if (!str[i]) 48 return 0; 49 /* First find the field to associate to */ 50 while (isspace(str[i])) 51 i++; 52 s = i; 53 while (isalnum(str[i]) || str[i] == '_') 54 i++; 55 len = i - s; 56 if (!len) 57 return -EINVAL; 58 59 field_name = kmemdup_nul(str + s, len, GFP_KERNEL); 60 if (!field_name) 61 return -ENOMEM; 62 field = trace_find_event_field(call, field_name); 63 kfree(field_name); 64 if (!field) 65 return -ENOENT; 66 67 *pf = field; 68 while (isspace(str[i])) 69 i++; 70 if (str[i] != '=') 71 return -EINVAL; 72 i++; 73 while (isspace(str[i])) 74 i++; 75 s = i; 76 if (isdigit(str[i]) || str[i] == '-') { 77 char *num, c; 78 int ret; 79 80 /* Make sure the field is not a string */ 81 if (is_string_field(field)) 82 return -EINVAL; 83 84 if (str[i] == '-') 85 i++; 86 87 /* We allow 0xDEADBEEF */ 88 while (isalnum(str[i])) 89 i++; 90 num = str + s; 91 c = str[i]; 92 if (c != '\0' && !isspace(c)) 93 return -EINVAL; 94 str[i] = '\0'; 95 /* Make sure it is a value */ 96 if (field->is_signed) 97 ret = kstrtoll(num, 0, &val); 98 else 99 ret = kstrtoull(num, 0, &val); 100 str[i] = c; 101 if (ret) 102 return ret; 103 104 *pv = val; 105 return i; 106 } else if (str[i] == '\'' || str[i] == '"') { 107 char q = str[i]; 108 109 /* Make sure the field is OK for strings */ 110 if (!is_string_field(field)) 111 return -EINVAL; 112 113 for (i++; str[i]; i++) { 114 if (str[i] == '\\' && str[i + 1]) { 115 i++; 116 continue; 117 } 118 if (str[i] == q) 119 break; 120 } 121 if (!str[i]) 122 return -EINVAL; 123 124 /* Skip quotes */ 125 s++; 126 len = i - s; 127 if (len >= MAX_FILTER_STR_VAL) 128 return -EINVAL; 129 130 *pv = (unsigned long)(str + s); 131 str[i] = 0; 132 /* go past the last quote */ 133 i++; 134 return i; 135 } 136 137 return -EINVAL; 138 } 139 140 static int trace_get_entry_size(struct trace_event_call *call) 141 { 142 struct ftrace_event_field *field; 143 struct list_head *head; 144 int size = 0; 145 146 head = trace_get_fields(call); 147 list_for_each_entry(field, head, link) { 148 if (field->size + field->offset > size) 149 size = field->size + field->offset; 150 } 151 152 return size; 153 } 154 155 static void *trace_alloc_entry(struct trace_event_call *call, int *size) 156 { 157 int entry_size = trace_get_entry_size(call); 158 struct ftrace_event_field *field; 159 struct list_head *head; 160 void *entry = NULL; 161 162 /* We need an extra '\0' at the end. */ 163 entry = kzalloc(entry_size + 1, GFP_KERNEL); 164 if (!entry) 165 return NULL; 166 167 head = trace_get_fields(call); 168 list_for_each_entry(field, head, link) { 169 if (!is_string_field(field)) 170 continue; 171 if (field->filter_type == FILTER_STATIC_STRING) 172 continue; 173 if (field->filter_type == FILTER_DYN_STRING) { 174 u32 *str_item; 175 int str_loc = entry_size & 0xffff; 176 177 str_item = (u32 *)(entry + field->offset); 178 *str_item = str_loc; /* string length is 0. */ 179 } else { 180 char **paddr; 181 182 paddr = (char **)(entry + field->offset); 183 *paddr = ""; 184 } 185 } 186 187 *size = entry_size + 1; 188 return entry; 189 } 190 191 #define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED" 192 193 /* Caller is responsible to free the *pentry. */ 194 static int parse_entry(char *str, struct trace_event_call *call, void **pentry) 195 { 196 struct ftrace_event_field *field; 197 unsigned long irq_flags; 198 void *entry = NULL; 199 int entry_size; 200 u64 val; 201 int len; 202 203 entry = trace_alloc_entry(call, &entry_size); 204 *pentry = entry; 205 if (!entry) 206 return -ENOMEM; 207 208 local_save_flags(irq_flags); 209 tracing_generic_entry_update(entry, call->event.type, irq_flags, 210 preempt_count()); 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 strlcpy(entry + field->offset, addr, field->size); 221 } else if (field->filter_type == FILTER_DYN_STRING) { 222 int str_len = strlen(addr) + 1; 223 int str_loc = entry_size & 0xffff; 224 u32 *str_item; 225 226 entry_size += str_len; 227 *pentry = krealloc(entry, entry_size, GFP_KERNEL); 228 if (!*pentry) { 229 kfree(entry); 230 return -ENOMEM; 231 } 232 entry = *pentry; 233 234 strlcpy(entry + (entry_size - str_len), addr, str_len); 235 str_item = (u32 *)(entry + field->offset); 236 *str_item = (str_len << 16) | str_loc; 237 } else { 238 char **paddr; 239 240 paddr = (char **)(entry + field->offset); 241 *paddr = INJECT_STRING; 242 } 243 } else { 244 switch (field->size) { 245 case 1: { 246 u8 tmp = (u8) val; 247 248 memcpy(entry + field->offset, &tmp, 1); 249 break; 250 } 251 case 2: { 252 u16 tmp = (u16) val; 253 254 memcpy(entry + field->offset, &tmp, 2); 255 break; 256 } 257 case 4: { 258 u32 tmp = (u32) val; 259 260 memcpy(entry + field->offset, &tmp, 4); 261 break; 262 } 263 case 8: 264 memcpy(entry + field->offset, &val, 8); 265 break; 266 default: 267 return -EINVAL; 268 } 269 } 270 271 str += len; 272 } 273 274 if (len < 0) 275 return len; 276 277 return entry_size; 278 } 279 280 static ssize_t 281 event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt, 282 loff_t *ppos) 283 { 284 struct trace_event_call *call; 285 struct trace_event_file *file; 286 int err = -ENODEV, size; 287 void *entry = NULL; 288 char *buf; 289 290 if (cnt >= PAGE_SIZE) 291 return -EINVAL; 292 293 buf = memdup_user_nul(ubuf, cnt); 294 if (IS_ERR(buf)) 295 return PTR_ERR(buf); 296 strim(buf); 297 298 mutex_lock(&event_mutex); 299 file = event_file_data(filp); 300 if (file) { 301 call = file->event_call; 302 size = parse_entry(buf, call, &entry); 303 if (size < 0) 304 err = size; 305 else 306 err = trace_inject_entry(file, entry, size); 307 } 308 mutex_unlock(&event_mutex); 309 310 kfree(entry); 311 kfree(buf); 312 313 if (err < 0) 314 return err; 315 316 *ppos += err; 317 return cnt; 318 } 319 320 static ssize_t 321 event_inject_read(struct file *file, char __user *buf, size_t size, 322 loff_t *ppos) 323 { 324 return -EPERM; 325 } 326 327 const struct file_operations event_inject_fops = { 328 .open = tracing_open_generic, 329 .read = event_inject_read, 330 .write = event_inject_write, 331 }; 332