linux/kernel/trace/trace_events_inject.c
<<
>>
Prefs
   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                        u32 *str_item;
 173                        int str_loc = entry_size & 0xffff;
 174
 175                        str_item = (u32 *)(entry + field->offset);
 176                        *str_item = str_loc; /* string length is 0. */
 177                } else {
 178                        char **paddr;
 179
 180                        paddr = (char **)(entry + field->offset);
 181                        *paddr = "";
 182                }
 183        }
 184
 185        *size = entry_size + 1;
 186        return entry;
 187}
 188
 189#define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
 190
 191/* Caller is responsible to free the *pentry. */
 192static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
 193{
 194        struct ftrace_event_field *field;
 195        void *entry = NULL;
 196        int entry_size;
 197        u64 val = 0;
 198        int len;
 199
 200        entry = trace_alloc_entry(call, &entry_size);
 201        *pentry = entry;
 202        if (!entry)
 203                return -ENOMEM;
 204
 205        tracing_generic_entry_update(entry, call->event.type,
 206                                     tracing_gen_ctx());
 207
 208        while ((len = parse_field(str, call, &field, &val)) > 0) {
 209                if (is_function_field(field))
 210                        return -EINVAL;
 211
 212                if (is_string_field(field)) {
 213                        char *addr = (char *)(unsigned long) val;
 214
 215                        if (field->filter_type == FILTER_STATIC_STRING) {
 216                                strlcpy(entry + field->offset, addr, field->size);
 217                        } else if (field->filter_type == FILTER_DYN_STRING) {
 218                                int str_len = strlen(addr) + 1;
 219                                int str_loc = entry_size & 0xffff;
 220                                u32 *str_item;
 221
 222                                entry_size += str_len;
 223                                *pentry = krealloc(entry, entry_size, GFP_KERNEL);
 224                                if (!*pentry) {
 225                                        kfree(entry);
 226                                        return -ENOMEM;
 227                                }
 228                                entry = *pentry;
 229
 230                                strlcpy(entry + (entry_size - str_len), addr, str_len);
 231                                str_item = (u32 *)(entry + field->offset);
 232                                *str_item = (str_len << 16) | str_loc;
 233                        } else {
 234                                char **paddr;
 235
 236                                paddr = (char **)(entry + field->offset);
 237                                *paddr = INJECT_STRING;
 238                        }
 239                } else {
 240                        switch (field->size) {
 241                        case 1: {
 242                                u8 tmp = (u8) val;
 243
 244                                memcpy(entry + field->offset, &tmp, 1);
 245                                break;
 246                        }
 247                        case 2: {
 248                                u16 tmp = (u16) val;
 249
 250                                memcpy(entry + field->offset, &tmp, 2);
 251                                break;
 252                        }
 253                        case 4: {
 254                                u32 tmp = (u32) val;
 255
 256                                memcpy(entry + field->offset, &tmp, 4);
 257                                break;
 258                        }
 259                        case 8:
 260                                memcpy(entry + field->offset, &val, 8);
 261                                break;
 262                        default:
 263                                return -EINVAL;
 264                        }
 265                }
 266
 267                str += len;
 268        }
 269
 270        if (len < 0)
 271                return len;
 272
 273        return entry_size;
 274}
 275
 276static ssize_t
 277event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
 278                   loff_t *ppos)
 279{
 280        struct trace_event_call *call;
 281        struct trace_event_file *file;
 282        int err = -ENODEV, size;
 283        void *entry = NULL;
 284        char *buf;
 285
 286        if (cnt >= PAGE_SIZE)
 287                return -EINVAL;
 288
 289        buf = memdup_user_nul(ubuf, cnt);
 290        if (IS_ERR(buf))
 291                return PTR_ERR(buf);
 292        strim(buf);
 293
 294        mutex_lock(&event_mutex);
 295        file = event_file_data(filp);
 296        if (file) {
 297                call = file->event_call;
 298                size = parse_entry(buf, call, &entry);
 299                if (size < 0)
 300                        err = size;
 301                else
 302                        err = trace_inject_entry(file, entry, size);
 303        }
 304        mutex_unlock(&event_mutex);
 305
 306        kfree(entry);
 307        kfree(buf);
 308
 309        if (err < 0)
 310                return err;
 311
 312        *ppos += err;
 313        return cnt;
 314}
 315
 316static ssize_t
 317event_inject_read(struct file *file, char __user *buf, size_t size,
 318                  loff_t *ppos)
 319{
 320        return -EPERM;
 321}
 322
 323const struct file_operations event_inject_fops = {
 324        .open = tracing_open_generic,
 325        .read = event_inject_read,
 326        .write = event_inject_write,
 327};
 328