Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * trace_events_inject - trace event injection
0004  *
0005  * Copyright (C) 2019 Cong Wang <cwang@twitter.com>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/ctype.h>
0010 #include <linux/mutex.h>
0011 #include <linux/slab.h>
0012 #include <linux/rculist.h>
0013 
0014 #include "trace.h"
0015 
0016 static int
0017 trace_inject_entry(struct trace_event_file *file, void *rec, int len)
0018 {
0019     struct trace_event_buffer fbuffer;
0020     int written = 0;
0021     void *entry;
0022 
0023     rcu_read_lock_sched();
0024     entry = trace_event_buffer_reserve(&fbuffer, file, len);
0025     if (entry) {
0026         memcpy(entry, rec, len);
0027         written = len;
0028         trace_event_buffer_commit(&fbuffer);
0029     }
0030     rcu_read_unlock_sched();
0031 
0032     return written;
0033 }
0034 
0035 static int
0036 parse_field(char *str, struct trace_event_call *call,
0037         struct ftrace_event_field **pf, u64 *pv)
0038 {
0039     struct ftrace_event_field *field;
0040     char *field_name;
0041     int s, i = 0;
0042     int len;
0043     u64 val;
0044 
0045     if (!str[i])
0046         return 0;
0047     /* First find the field to associate to */
0048     while (isspace(str[i]))
0049         i++;
0050     s = i;
0051     while (isalnum(str[i]) || str[i] == '_')
0052         i++;
0053     len = i - s;
0054     if (!len)
0055         return -EINVAL;
0056 
0057     field_name = kmemdup_nul(str + s, len, GFP_KERNEL);
0058     if (!field_name)
0059         return -ENOMEM;
0060     field = trace_find_event_field(call, field_name);
0061     kfree(field_name);
0062     if (!field)
0063         return -ENOENT;
0064 
0065     *pf = field;
0066     while (isspace(str[i]))
0067         i++;
0068     if (str[i] != '=')
0069         return -EINVAL;
0070     i++;
0071     while (isspace(str[i]))
0072         i++;
0073     s = i;
0074     if (isdigit(str[i]) || str[i] == '-') {
0075         char *num, c;
0076         int ret;
0077 
0078         /* Make sure the field is not a string */
0079         if (is_string_field(field))
0080             return -EINVAL;
0081 
0082         if (str[i] == '-')
0083             i++;
0084 
0085         /* We allow 0xDEADBEEF */
0086         while (isalnum(str[i]))
0087             i++;
0088         num = str + s;
0089         c = str[i];
0090         if (c != '\0' && !isspace(c))
0091             return -EINVAL;
0092         str[i] = '\0';
0093         /* Make sure it is a value */
0094         if (field->is_signed)
0095             ret = kstrtoll(num, 0, &val);
0096         else
0097             ret = kstrtoull(num, 0, &val);
0098         str[i] = c;
0099         if (ret)
0100             return ret;
0101 
0102         *pv = val;
0103         return i;
0104     } else if (str[i] == '\'' || str[i] == '"') {
0105         char q = str[i];
0106 
0107         /* Make sure the field is OK for strings */
0108         if (!is_string_field(field))
0109             return -EINVAL;
0110 
0111         for (i++; str[i]; i++) {
0112             if (str[i] == '\\' && str[i + 1]) {
0113                 i++;
0114                 continue;
0115             }
0116             if (str[i] == q)
0117                 break;
0118         }
0119         if (!str[i])
0120             return -EINVAL;
0121 
0122         /* Skip quotes */
0123         s++;
0124         len = i - s;
0125         if (len >= MAX_FILTER_STR_VAL)
0126             return -EINVAL;
0127 
0128         *pv = (unsigned long)(str + s);
0129         str[i] = 0;
0130         /* go past the last quote */
0131         i++;
0132         return i;
0133     }
0134 
0135     return -EINVAL;
0136 }
0137 
0138 static int trace_get_entry_size(struct trace_event_call *call)
0139 {
0140     struct ftrace_event_field *field;
0141     struct list_head *head;
0142     int size = 0;
0143 
0144     head = trace_get_fields(call);
0145     list_for_each_entry(field, head, link) {
0146         if (field->size + field->offset > size)
0147             size = field->size + field->offset;
0148     }
0149 
0150     return size;
0151 }
0152 
0153 static void *trace_alloc_entry(struct trace_event_call *call, int *size)
0154 {
0155     int entry_size = trace_get_entry_size(call);
0156     struct ftrace_event_field *field;
0157     struct list_head *head;
0158     void *entry = NULL;
0159 
0160     /* We need an extra '\0' at the end. */
0161     entry = kzalloc(entry_size + 1, GFP_KERNEL);
0162     if (!entry)
0163         return NULL;
0164 
0165     head = trace_get_fields(call);
0166     list_for_each_entry(field, head, link) {
0167         if (!is_string_field(field))
0168             continue;
0169         if (field->filter_type == FILTER_STATIC_STRING)
0170             continue;
0171         if (field->filter_type == FILTER_DYN_STRING ||
0172             field->filter_type == FILTER_RDYN_STRING) {
0173             u32 *str_item;
0174             int str_loc = entry_size & 0xffff;
0175 
0176             if (field->filter_type == FILTER_RDYN_STRING)
0177                 str_loc -= field->offset + field->size;
0178 
0179             str_item = (u32 *)(entry + field->offset);
0180             *str_item = str_loc; /* string length is 0. */
0181         } else {
0182             char **paddr;
0183 
0184             paddr = (char **)(entry + field->offset);
0185             *paddr = "";
0186         }
0187     }
0188 
0189     *size = entry_size + 1;
0190     return entry;
0191 }
0192 
0193 #define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
0194 
0195 /* Caller is responsible to free the *pentry. */
0196 static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
0197 {
0198     struct ftrace_event_field *field;
0199     void *entry = NULL;
0200     int entry_size;
0201     u64 val = 0;
0202     int len;
0203 
0204     entry = trace_alloc_entry(call, &entry_size);
0205     *pentry = entry;
0206     if (!entry)
0207         return -ENOMEM;
0208 
0209     tracing_generic_entry_update(entry, call->event.type,
0210                      tracing_gen_ctx());
0211 
0212     while ((len = parse_field(str, call, &field, &val)) > 0) {
0213         if (is_function_field(field))
0214             return -EINVAL;
0215 
0216         if (is_string_field(field)) {
0217             char *addr = (char *)(unsigned long) val;
0218 
0219             if (field->filter_type == FILTER_STATIC_STRING) {
0220                 strlcpy(entry + field->offset, addr, field->size);
0221             } else if (field->filter_type == FILTER_DYN_STRING ||
0222                    field->filter_type == FILTER_RDYN_STRING) {
0223                 int str_len = strlen(addr) + 1;
0224                 int str_loc = entry_size & 0xffff;
0225                 u32 *str_item;
0226 
0227                 entry_size += str_len;
0228                 *pentry = krealloc(entry, entry_size, GFP_KERNEL);
0229                 if (!*pentry) {
0230                     kfree(entry);
0231                     return -ENOMEM;
0232                 }
0233                 entry = *pentry;
0234 
0235                 strlcpy(entry + (entry_size - str_len), addr, str_len);
0236                 str_item = (u32 *)(entry + field->offset);
0237                 if (field->filter_type == FILTER_RDYN_STRING)
0238                     str_loc -= field->offset + field->size;
0239                 *str_item = (str_len << 16) | str_loc;
0240             } else {
0241                 char **paddr;
0242 
0243                 paddr = (char **)(entry + field->offset);
0244                 *paddr = INJECT_STRING;
0245             }
0246         } else {
0247             switch (field->size) {
0248             case 1: {
0249                 u8 tmp = (u8) val;
0250 
0251                 memcpy(entry + field->offset, &tmp, 1);
0252                 break;
0253             }
0254             case 2: {
0255                 u16 tmp = (u16) val;
0256 
0257                 memcpy(entry + field->offset, &tmp, 2);
0258                 break;
0259             }
0260             case 4: {
0261                 u32 tmp = (u32) val;
0262 
0263                 memcpy(entry + field->offset, &tmp, 4);
0264                 break;
0265             }
0266             case 8:
0267                 memcpy(entry + field->offset, &val, 8);
0268                 break;
0269             default:
0270                 return -EINVAL;
0271             }
0272         }
0273 
0274         str += len;
0275     }
0276 
0277     if (len < 0)
0278         return len;
0279 
0280     return entry_size;
0281 }
0282 
0283 static ssize_t
0284 event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
0285            loff_t *ppos)
0286 {
0287     struct trace_event_call *call;
0288     struct trace_event_file *file;
0289     int err = -ENODEV, size;
0290     void *entry = NULL;
0291     char *buf;
0292 
0293     if (cnt >= PAGE_SIZE)
0294         return -EINVAL;
0295 
0296     buf = memdup_user_nul(ubuf, cnt);
0297     if (IS_ERR(buf))
0298         return PTR_ERR(buf);
0299     strim(buf);
0300 
0301     mutex_lock(&event_mutex);
0302     file = event_file_data(filp);
0303     if (file) {
0304         call = file->event_call;
0305         size = parse_entry(buf, call, &entry);
0306         if (size < 0)
0307             err = size;
0308         else
0309             err = trace_inject_entry(file, entry, size);
0310     }
0311     mutex_unlock(&event_mutex);
0312 
0313     kfree(entry);
0314     kfree(buf);
0315 
0316     if (err < 0)
0317         return err;
0318 
0319     *ppos += err;
0320     return cnt;
0321 }
0322 
0323 static ssize_t
0324 event_inject_read(struct file *file, char __user *buf, size_t size,
0325           loff_t *ppos)
0326 {
0327     return -EPERM;
0328 }
0329 
0330 const struct file_operations event_inject_fops = {
0331     .open = tracing_open_generic,
0332     .read = event_inject_read,
0333     .write = event_inject_write,
0334 };