18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * trace_events_inject - trace event injection 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Cong Wang <cwang@twitter.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/mutex.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/rculist.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "trace.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int 178c2ecf20Sopenharmony_citrace_inject_entry(struct trace_event_file *file, void *rec, int len) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct trace_event_buffer fbuffer; 208c2ecf20Sopenharmony_ci int written = 0; 218c2ecf20Sopenharmony_ci void *entry; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci rcu_read_lock_sched(); 248c2ecf20Sopenharmony_ci entry = trace_event_buffer_reserve(&fbuffer, file, len); 258c2ecf20Sopenharmony_ci if (entry) { 268c2ecf20Sopenharmony_ci memcpy(entry, rec, len); 278c2ecf20Sopenharmony_ci written = len; 288c2ecf20Sopenharmony_ci trace_event_buffer_commit(&fbuffer); 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci rcu_read_unlock_sched(); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return written; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int 368c2ecf20Sopenharmony_ciparse_field(char *str, struct trace_event_call *call, 378c2ecf20Sopenharmony_ci struct ftrace_event_field **pf, u64 *pv) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct ftrace_event_field *field; 408c2ecf20Sopenharmony_ci char *field_name; 418c2ecf20Sopenharmony_ci int s, i = 0; 428c2ecf20Sopenharmony_ci int len; 438c2ecf20Sopenharmony_ci u64 val; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (!str[i]) 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci /* First find the field to associate to */ 488c2ecf20Sopenharmony_ci while (isspace(str[i])) 498c2ecf20Sopenharmony_ci i++; 508c2ecf20Sopenharmony_ci s = i; 518c2ecf20Sopenharmony_ci while (isalnum(str[i]) || str[i] == '_') 528c2ecf20Sopenharmony_ci i++; 538c2ecf20Sopenharmony_ci len = i - s; 548c2ecf20Sopenharmony_ci if (!len) 558c2ecf20Sopenharmony_ci return -EINVAL; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci field_name = kmemdup_nul(str + s, len, GFP_KERNEL); 588c2ecf20Sopenharmony_ci if (!field_name) 598c2ecf20Sopenharmony_ci return -ENOMEM; 608c2ecf20Sopenharmony_ci field = trace_find_event_field(call, field_name); 618c2ecf20Sopenharmony_ci kfree(field_name); 628c2ecf20Sopenharmony_ci if (!field) 638c2ecf20Sopenharmony_ci return -ENOENT; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci *pf = field; 668c2ecf20Sopenharmony_ci while (isspace(str[i])) 678c2ecf20Sopenharmony_ci i++; 688c2ecf20Sopenharmony_ci if (str[i] != '=') 698c2ecf20Sopenharmony_ci return -EINVAL; 708c2ecf20Sopenharmony_ci i++; 718c2ecf20Sopenharmony_ci while (isspace(str[i])) 728c2ecf20Sopenharmony_ci i++; 738c2ecf20Sopenharmony_ci s = i; 748c2ecf20Sopenharmony_ci if (isdigit(str[i]) || str[i] == '-') { 758c2ecf20Sopenharmony_ci char *num, c; 768c2ecf20Sopenharmony_ci int ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Make sure the field is not a string */ 798c2ecf20Sopenharmony_ci if (is_string_field(field)) 808c2ecf20Sopenharmony_ci return -EINVAL; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (str[i] == '-') 838c2ecf20Sopenharmony_ci i++; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* We allow 0xDEADBEEF */ 868c2ecf20Sopenharmony_ci while (isalnum(str[i])) 878c2ecf20Sopenharmony_ci i++; 888c2ecf20Sopenharmony_ci num = str + s; 898c2ecf20Sopenharmony_ci c = str[i]; 908c2ecf20Sopenharmony_ci if (c != '\0' && !isspace(c)) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci str[i] = '\0'; 938c2ecf20Sopenharmony_ci /* Make sure it is a value */ 948c2ecf20Sopenharmony_ci if (field->is_signed) 958c2ecf20Sopenharmony_ci ret = kstrtoll(num, 0, &val); 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci ret = kstrtoull(num, 0, &val); 988c2ecf20Sopenharmony_ci str[i] = c; 998c2ecf20Sopenharmony_ci if (ret) 1008c2ecf20Sopenharmony_ci return ret; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci *pv = val; 1038c2ecf20Sopenharmony_ci return i; 1048c2ecf20Sopenharmony_ci } else if (str[i] == '\'' || str[i] == '"') { 1058c2ecf20Sopenharmony_ci char q = str[i]; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Make sure the field is OK for strings */ 1088c2ecf20Sopenharmony_ci if (!is_string_field(field)) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci for (i++; str[i]; i++) { 1128c2ecf20Sopenharmony_ci if (str[i] == '\\' && str[i + 1]) { 1138c2ecf20Sopenharmony_ci i++; 1148c2ecf20Sopenharmony_ci continue; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci if (str[i] == q) 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci if (!str[i]) 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Skip quotes */ 1238c2ecf20Sopenharmony_ci s++; 1248c2ecf20Sopenharmony_ci len = i - s; 1258c2ecf20Sopenharmony_ci if (len >= MAX_FILTER_STR_VAL) 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci *pv = (unsigned long)(str + s); 1298c2ecf20Sopenharmony_ci str[i] = 0; 1308c2ecf20Sopenharmony_ci /* go past the last quote */ 1318c2ecf20Sopenharmony_ci i++; 1328c2ecf20Sopenharmony_ci return i; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int trace_get_entry_size(struct trace_event_call *call) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct ftrace_event_field *field; 1418c2ecf20Sopenharmony_ci struct list_head *head; 1428c2ecf20Sopenharmony_ci int size = 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci head = trace_get_fields(call); 1458c2ecf20Sopenharmony_ci list_for_each_entry(field, head, link) { 1468c2ecf20Sopenharmony_ci if (field->size + field->offset > size) 1478c2ecf20Sopenharmony_ci size = field->size + field->offset; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return size; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void *trace_alloc_entry(struct trace_event_call *call, int *size) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int entry_size = trace_get_entry_size(call); 1568c2ecf20Sopenharmony_ci struct ftrace_event_field *field; 1578c2ecf20Sopenharmony_ci struct list_head *head; 1588c2ecf20Sopenharmony_ci void *entry = NULL; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* We need an extra '\0' at the end. */ 1618c2ecf20Sopenharmony_ci entry = kzalloc(entry_size + 1, GFP_KERNEL); 1628c2ecf20Sopenharmony_ci if (!entry) 1638c2ecf20Sopenharmony_ci return NULL; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci head = trace_get_fields(call); 1668c2ecf20Sopenharmony_ci list_for_each_entry(field, head, link) { 1678c2ecf20Sopenharmony_ci if (!is_string_field(field)) 1688c2ecf20Sopenharmony_ci continue; 1698c2ecf20Sopenharmony_ci if (field->filter_type == FILTER_STATIC_STRING) 1708c2ecf20Sopenharmony_ci continue; 1718c2ecf20Sopenharmony_ci if (field->filter_type == FILTER_DYN_STRING) { 1728c2ecf20Sopenharmony_ci u32 *str_item; 1738c2ecf20Sopenharmony_ci int str_loc = entry_size & 0xffff; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci str_item = (u32 *)(entry + field->offset); 1768c2ecf20Sopenharmony_ci *str_item = str_loc; /* string length is 0. */ 1778c2ecf20Sopenharmony_ci } else { 1788c2ecf20Sopenharmony_ci char **paddr; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci paddr = (char **)(entry + field->offset); 1818c2ecf20Sopenharmony_ci *paddr = ""; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci *size = entry_size + 1; 1868c2ecf20Sopenharmony_ci return entry; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED" 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* Caller is responsible to free the *pentry. */ 1928c2ecf20Sopenharmony_cistatic int parse_entry(char *str, struct trace_event_call *call, void **pentry) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct ftrace_event_field *field; 1958c2ecf20Sopenharmony_ci unsigned long irq_flags; 1968c2ecf20Sopenharmony_ci void *entry = NULL; 1978c2ecf20Sopenharmony_ci int entry_size; 1988c2ecf20Sopenharmony_ci u64 val = 0; 1998c2ecf20Sopenharmony_ci int len; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci entry = trace_alloc_entry(call, &entry_size); 2028c2ecf20Sopenharmony_ci *pentry = entry; 2038c2ecf20Sopenharmony_ci if (!entry) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci local_save_flags(irq_flags); 2078c2ecf20Sopenharmony_ci tracing_generic_entry_update(entry, call->event.type, irq_flags, 2088c2ecf20Sopenharmony_ci preempt_count()); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci while ((len = parse_field(str, call, &field, &val)) > 0) { 2118c2ecf20Sopenharmony_ci if (is_function_field(field)) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (is_string_field(field)) { 2158c2ecf20Sopenharmony_ci char *addr = (char *)(unsigned long) val; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (field->filter_type == FILTER_STATIC_STRING) { 2188c2ecf20Sopenharmony_ci strlcpy(entry + field->offset, addr, field->size); 2198c2ecf20Sopenharmony_ci } else if (field->filter_type == FILTER_DYN_STRING) { 2208c2ecf20Sopenharmony_ci int str_len = strlen(addr) + 1; 2218c2ecf20Sopenharmony_ci int str_loc = entry_size & 0xffff; 2228c2ecf20Sopenharmony_ci u32 *str_item; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci entry_size += str_len; 2258c2ecf20Sopenharmony_ci *pentry = krealloc(entry, entry_size, GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!*pentry) { 2278c2ecf20Sopenharmony_ci kfree(entry); 2288c2ecf20Sopenharmony_ci return -ENOMEM; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci entry = *pentry; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci strlcpy(entry + (entry_size - str_len), addr, str_len); 2338c2ecf20Sopenharmony_ci str_item = (u32 *)(entry + field->offset); 2348c2ecf20Sopenharmony_ci *str_item = (str_len << 16) | str_loc; 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci char **paddr; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci paddr = (char **)(entry + field->offset); 2398c2ecf20Sopenharmony_ci *paddr = INJECT_STRING; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci switch (field->size) { 2438c2ecf20Sopenharmony_ci case 1: { 2448c2ecf20Sopenharmony_ci u8 tmp = (u8) val; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci memcpy(entry + field->offset, &tmp, 1); 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci case 2: { 2508c2ecf20Sopenharmony_ci u16 tmp = (u16) val; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci memcpy(entry + field->offset, &tmp, 2); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci case 4: { 2568c2ecf20Sopenharmony_ci u32 tmp = (u32) val; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci memcpy(entry + field->offset, &tmp, 4); 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci case 8: 2628c2ecf20Sopenharmony_ci memcpy(entry + field->offset, &val, 8); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci str += len; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (len < 0) 2738c2ecf20Sopenharmony_ci return len; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return entry_size; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic ssize_t 2798c2ecf20Sopenharmony_cievent_inject_write(struct file *filp, const char __user *ubuf, size_t cnt, 2808c2ecf20Sopenharmony_ci loff_t *ppos) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct trace_event_call *call; 2838c2ecf20Sopenharmony_ci struct trace_event_file *file; 2848c2ecf20Sopenharmony_ci int err = -ENODEV, size; 2858c2ecf20Sopenharmony_ci void *entry = NULL; 2868c2ecf20Sopenharmony_ci char *buf; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (cnt >= PAGE_SIZE) 2898c2ecf20Sopenharmony_ci return -EINVAL; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci buf = memdup_user_nul(ubuf, cnt); 2928c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 2938c2ecf20Sopenharmony_ci return PTR_ERR(buf); 2948c2ecf20Sopenharmony_ci strim(buf); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 2978c2ecf20Sopenharmony_ci file = event_file_data(filp); 2988c2ecf20Sopenharmony_ci if (file) { 2998c2ecf20Sopenharmony_ci call = file->event_call; 3008c2ecf20Sopenharmony_ci size = parse_entry(buf, call, &entry); 3018c2ecf20Sopenharmony_ci if (size < 0) 3028c2ecf20Sopenharmony_ci err = size; 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci err = trace_inject_entry(file, entry, size); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci kfree(entry); 3098c2ecf20Sopenharmony_ci kfree(buf); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (err < 0) 3128c2ecf20Sopenharmony_ci return err; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci *ppos += err; 3158c2ecf20Sopenharmony_ci return cnt; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic ssize_t 3198c2ecf20Sopenharmony_cievent_inject_read(struct file *file, char __user *buf, size_t size, 3208c2ecf20Sopenharmony_ci loff_t *ppos) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci return -EPERM; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciconst struct file_operations event_inject_fops = { 3268c2ecf20Sopenharmony_ci .open = tracing_open_file_tr, 3278c2ecf20Sopenharmony_ci .read = event_inject_read, 3288c2ecf20Sopenharmony_ci .write = event_inject_write, 3298c2ecf20Sopenharmony_ci .release = tracing_release_file_tr, 3308c2ecf20Sopenharmony_ci}; 331