trace_events_inject.c (6787B)
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 16static int 17trace_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 35static int 36parse_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 138static 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 153static 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. */ 196static 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 strlcpy(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 strlcpy(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 283static ssize_t 284event_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_data(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 323static ssize_t 324event_inject_read(struct file *file, char __user *buf, size_t size, 325 loff_t *ppos) 326{ 327 return -EPERM; 328} 329 330const struct file_operations event_inject_fops = { 331 .open = tracing_open_generic, 332 .read = event_inject_read, 333 .write = event_inject_write, 334};