// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2021, Microsoft Corporation. * * Authors: * Beau Belgrave <beaub@linux.microsoft.com> */ #include <linux/bitmap.h>#include <linux/cdev.h>#include <linux/hashtable.h>#include <linux/list.h>#include <linux/io.h>#include <linux/uio.h>#include <linux/ioctl.h>#include <linux/jhash.h>#include <linux/trace_events.h>#include <linux/tracefs.h>#include <linux/types.h>#include <linux/uaccess.h>/* Reminder to move to uapi when everything works */#ifdef CONFIG_COMPILE_TEST#include <linux/user_events.h>#else#include <uapi/linux/user_events.h>#endif#include "trace.h"#include "trace_dynevent.h" #define USER_EVENTS_PREFIX_LEN (sizeof(USER_EVENTS_PREFIX)-1) #define FIELD_DEPTH_TYPE 0#define FIELD_DEPTH_NAME 1#define FIELD_DEPTH_SIZE 2 /* * Limits how many trace_event calls user processes can create: * Must be a power of two of PAGE_SIZE. */#define MAX_PAGE_ORDER 0#define MAX_PAGES (1 << MAX_PAGE_ORDER)#define MAX_EVENTS (MAX_PAGES * PAGE_SIZE) /* Limit how long of an event name plus args within the subsystem. */#define MAX_EVENT_DESC 512#define EVENT_NAME(user_event) ((user_event)->tracepoint.name)#define MAX_FIELD_ARRAY_SIZE 1024#define MAX_FIELD_ARG_NAME 256 static char *register_page_data; static DEFINE_MUTEX(reg_mutex);static DEFINE_HASHTABLE(register_table, 4);static DECLARE_BITMAP(page_bitmap, MAX_EVENTS); /* * Stores per-event properties, as users register events * within a file a user_event might be created if it does not * already exist. These are globally used and their lifetime * is tied to the refcnt member. These cannot go away until the * refcnt reaches zero. */struct user_event { struct tracepoint tracepoint; struct trace_event_call call; struct trace_event_class class; struct dyn_event devent; struct hlist_node node; struct list_head fields; struct list_head validators; atomic_t refcnt; int index; int flags; int min_size;}; /* * Stores per-file events references, as users register events * within a file this structure is modified and freed via RCU. * The lifetime of this struct is tied to the lifetime of the file. * These are not shared and only accessible by the file that created it. */struct user_event_refs { struct rcu_head rcu; int count; struct user_event *events[];}; #define VALIDATOR_ENSURE_NULL (1 << 0)#define VALIDATOR_REL (1 << 1) struct user_event_validator { struct list_head link; int offset; int flags;}; typedef void (*user_event_func_t) (struct user_event *user, struct iov_iter *i, void *tpdata, bool *faulted); static int user_event_parse(char *name, char *args, char *flags, struct user_event **newuser); static u32 user_event_key(char *name){ return jhash(name, strlen(name), 0);} static __always_inline __must_checksize_t copy_nofault(void *addr, size_t bytes, struct iov_iter *i){ size_t ret; pagefault_disable(); ret = copy_from_iter_nocache(addr, bytes, i); pagefault_enable(); return ret;} static struct list_head *user_event_get_fields(struct trace_event_call *call){ struct user_event *user = (struct user_event *)call->data; return &user->fields;} /* * Parses a register command for user_events * Format: event_name[:FLAG1[,FLAG2...]] [field1[;field2...]] * * Example event named 'test' with a 20 char 'msg' field with an unsigned int * 'id' field after: * test char[20] msg;unsigned int id * * NOTE: Offsets are from the user data perspective, they are not from the * trace_entry/buffer perspective. We automatically add the common properties * sizes to the offset for the user. * * Upon success user_event has its ref count increased by 1. */static int user_event_parse_cmd(char *raw_command, struct user_event **newuser){ char *name = raw_command; char *args = strpbrk(name, " "); char *flags; if (args) *args++ = '\0'; flags = strpbrk(name, ":"); if (flags) *flags++ = '\0'; return user_event_parse(name, args, flags, newuser);} static int user_field_array_size(const char *type){ const char *start = strchr(type, '['); char val[8]; char *bracket; int size = 0; if (start == NULL) return -EINVAL; if (strscpy(val, start + 1, sizeof(val)) <= 0) return -EINVAL; bracket = strchr(val, ']'); if (!bracket) return -EINVAL; *bracket = '\0'; if (kstrtouint(val, 0, &size)) return -EINVAL; if (size > MAX_FIELD_ARRAY_SIZE) return -EINVAL; return size;} static int user_field_size(const char *type){ /* long is not allowed from a user, since it's ambigious in size */ if (strcmp(type, "s64") == 0) return sizeof(s64); if (strcmp(type, "u64") == 0) return sizeof(u64); if (strcmp(type, "s32") == 0) return sizeof(s32); if (strcmp(type, "u32") == 0) return sizeof(u32); if (strcmp(type, "int") == 0) return sizeof(int); if (strcmp(type, "unsigned int") == 0) return sizeof(unsigned int); if (strcmp(type, "s16") == 0) return sizeof(s16); if (strcmp(type, "u16") == 0) return sizeof(u16); if (strcmp(type, "short") == 0) return sizeof(short); if (strcmp(type, "unsigned short") == 0) return sizeof(unsigned short); if (strcmp(type, "s8") == 0) return sizeof(s8); if (strcmp(type, "u8") == 0) return sizeof(u8); if (strcmp(type, "char") == 0) return sizeof(char); if (strcmp(type, "unsigned char") == 0) return sizeof(unsigned char); if (str_has_prefix(type, "char[")) return user_field_array_size(type); if (str_has_prefix(type, "unsigned char[")) return user_field_array_size(type); if (str_has_prefix(type, "__data_loc ")) return sizeof(u32); if (str_has_prefix(type, "__rel_loc ")) return sizeof(u32); /* Uknown basic type, error */ return -EINVAL;} static void user_event_destroy_validators(struct user_event *user){ struct user_event_validator *validator, *next; struct list_head *head = &user->validators; list_for_each_entry_safe(validator, next, head, link) { list_del(&validator->link); kfree(validator); }} static void user_event_destroy_fields(struct user_event *user){ struct ftrace_event_field *field, *next; struct list_head *head = &user->fields; list_for_each_entry_safe(field, next, head, link) { list_del(&field->link); kfree(field); }} static int user_event_add_field(struct user_event *user, const char *type, const char *name, int offset, int size, int is_signed, int filter_type){ struct user_event_validator *validator; struct ftrace_event_field *field; int validator_flags = 0; field = kmalloc(sizeof(*field), GFP_KERNEL); if (!field) return -ENOMEM; if (str_has_prefix(type, "__data_loc ")) goto add_validator; if (str_has_prefix(type, "__rel_loc ")) { validator_flags |= VALIDATOR_REL; goto add_validator; } goto add_field; add_validator: if (strstr(type, "char") != 0) validator_flags |= VALIDATOR_ENSURE_NULL; validator = kmalloc(sizeof(*validator), GFP_KERNEL); if (!validator) { kfree(field); return -ENOMEM; } validator->flags = validator_flags; validator->offset = offset; /* Want sequential access when validating */ list_add_tail(&validator->link, &user->validators); add_field: field->type = type; field->name = name; field->offset = offset; field->size = size; field->is_signed = is_signed; field->filter_type = filter_type; list_add(&field->link, &user->fields); /* * Min size from user writes that are required, this does not include * the size of trace_entry (common fields). */ user->min_size = (offset + size) - sizeof(struct trace_entry); return 0;} /* * Parses the values of a field within the description * Format: type name [size] */static int user_event_parse_field(char *field, struct user_event *user, u32 *offset){ char *part, *type, *name; u32 depth = 0, saved_offset = *offset; int len, size = -EINVAL; bool is_struct = false; field = skip_spaces(field); if (*field == '\0') return 0; /* Handle types that have a space within */ len = str_has_prefix(field, "unsigned "); if (len) goto skip_next; len = str_has_prefix(field, "struct "); if (len) { is_struct = true; goto skip_next; } len = str_has_prefix(field, "__data_loc unsigned "); if (len) goto skip_next; len = str_has_prefix(field, "__data_loc "); if (len) goto skip_next; len = str_has_prefix(field, "__rel_loc unsigned "); if (len) goto skip_next; len = str_has_prefix(field, "__rel_loc "); if (len) goto skip_next; goto parse;skip_next: type = field; field = strpbrk(field + len, " "); if (field == NULL) return -EINVAL; *field++ = '\0'; depth++;parse: name = NULL; while ((part = strsep(&field, " ")) != NULL) { switch (depth++) { case FIELD_DEPTH_TYPE: type = part; break; case FIELD_DEPTH_NAME: name = part; break; case FIELD_DEPTH_SIZE: if (!is_struct) return -EINVAL; if (kstrtou32(part, 10, &size)) return -EINVAL; break; default: return -EINVAL; } } if (depth < FIELD_DEPTH_SIZE || !name) return -EINVAL; if (depth == FIELD_DEPTH_SIZE) size = user_field_size(type); if (size == 0) return -EINVAL; if (size < 0) return size; *offset = saved_offset + size; return user_event_add_field(user, type, name, saved_offset, size, type[0] != 'u', FILTER_OTHER);} static int user_event_parse_fields(struct user_event *user, char *args){ char *field; u32 offset = sizeof(struct trace_entry); int ret = -EINVAL; if (args == NULL) return 0; while ((field = strsep(&args, ";")) != NULL) { ret = user_event_parse_field(field, user, &offset); if (ret) break; } return ret;} static struct trace_event_fields user_event_fields_array[1]; static const char *user_field_format(const char *type){ if (strcmp(type, "s64") == 0) return "%lld"; if (strcmp(type, "u64") == 0) return "%llu"; if (strcmp(type, "s32") == 0) return "%d"; if (strcmp(type, "u32") == 0) return "%u"; if (strcmp(type, "int") == 0) return "%d"; if (strcmp(type, "unsigned int") == 0) return "%u"; if (strcmp(type, "s16") == 0) return "%d"; if (strcmp(type, "u16") == 0) return "%u"; if (strcmp(type, "short") == 0) return "%d"; if (strcmp(type, "unsigned short") == 0) return "%u"; if (strcmp(type, "s8") == 0) return "%d"; if (strcmp(type, "u8") == 0) return "%u"; if (strcmp(type, "char") == 0) return "%d"; if (strcmp(type, "unsigned char") == 0) return "%u"; if (strstr(type, "char[") != 0) return "%s"; /* Unknown, likely struct, allowed treat as 64-bit */ return "%llu";} static bool user_field_is_dyn_string(const char *type, const char **str_func){ if (str_has_prefix(type, "__data_loc ")) { *str_func = "__get_str"; goto check; } if (str_has_prefix(type, "__rel_loc ")) { *str_func = "__get_rel_str"; goto check; } return false;check: return strstr(type, "char") != 0;} #define LEN_OR_ZERO (len ? len - pos : 0)static int user_event_set_print_fmt(struct user_event *user, char *buf, int len){ struct ftrace_event_field *field, *next; struct list_head *head = &user->fields; int pos = 0, depth = 0; const char *str_func; pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); list_for_each_entry_safe_reverse(field, next, head, link) { if (depth != 0) pos += snprintf(buf + pos, LEN_OR_ZERO, " "); pos += snprintf(buf + pos, LEN_OR_ZERO, "%s=%s", field->name, user_field_format(field->type));Lines 1–500 of 1628. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021, Microsoft Corporation. * * Authors: * Beau Belgrave <beaub@linux.microsoft.com> */ #include <linux/bitmap.h> #include <linux/cdev.h> #include <linux/hashtable.h> #include <linux/list.h> #include <linux/io.h> #include <linux/uio.h> #include <linux/ioctl.h> #include <linux/jhash.h> #include <linux/trace_events.h> #include <linux/tracefs.h> #include <linux/types.h> #include <linux/uaccess.h> /* Reminder to move to uapi when everything works */ #ifdef CONFIG_COMPILE_TEST #include <linux/user_events.h> #else #include <uapi/linux/user_events.h> #endif #include "trace.h" #include "trace_dynevent.h" #define USER_EVENTS_PREFIX_LEN (sizeof(USER_EVENTS_PREFIX)-1) #define FIELD_DEPTH_TYPE 0 #define FIELD_DEPTH_NAME 1 #define FIELD_DEPTH_SIZE 2 /* * Limits how many trace_event calls user processes can create: * Must be a power of two of PAGE_SIZE. */ #define MAX_PAGE_ORDER 0 #define MAX_PAGES (1 << MAX_PAGE_ORDER) #define MAX_EVENTS (MAX_PAGES * PAGE_SIZE) /* Limit how long of an event name plus args within the subsystem. */ #define MAX_EVENT_DESC 512 #define EVENT_NAME(user_event) ((user_event)->tracepoint.name) #define MAX_FIELD_ARRAY_SIZE 1024 #define MAX_FIELD_ARG_NAME 256 static char *register_page_data; static DEFINE_MUTEX(reg_mutex); static DEFINE_HASHTABLE(register_table, 4); static DECLARE_BITMAP(page_bitmap, MAX_EVENTS); /* * Stores per-event properties, as users register events * within a file a user_event might be created if it does not * already exist. These are globally used and their lifetime * is tied to the refcnt member. These cannot go away until the * refcnt reaches zero. */ struct user_event { struct tracepoint tracepoint; struct trace_event_call call; struct trace_event_class class; struct dyn_event devent; struct hlist_node node; struct list_head fields; struct list_head validators; atomic_t refcnt; int index; int flags; int min_size; }; /* * Stores per-file events references, as users register events * within a file this structure is modified and freed via RCU. * The lifetime of this struct is tied to the lifetime of the file. * These are not shared and only accessible by the file that created it. */ struct user_event_refs { struct rcu_head rcu; int count; struct user_event *events[]; }; #define VALIDATOR_ENSURE_NULL (1 << 0) #define VALIDATOR_REL (1 << 1) struct user_event_validator { struct list_head link; int offset; int flags; }; typedef void (*user_event_func_t) (struct user_event *user, struct iov_iter *i, void *tpdata, bool *faulted); static int user_event_parse(char *name, char *args, char *flags, struct user_event **newuser); static u32 user_event_key(char *name) { return jhash(name, strlen(name), 0); } static __always_inline __must_check size_t copy_nofault(void *addr, size_t bytes, struct iov_iter *i) { size_t ret; pagefault_disable(); ret = copy_from_iter_nocache(addr, bytes, i); pagefault_enable(); return ret; } static struct list_head *user_event_get_fields(struct trace_event_call *call) { struct user_event *user = (struct user_event *)call->data; return &user->fields; } /* * Parses a register command for user_events * Format: event_name[:FLAG1[,FLAG2...]] [field1[;field2...]] * * Example event named 'test' with a 20 char 'msg' field with an unsigned int * 'id' field after: * test char[20] msg;unsigned int id * * NOTE: Offsets are from the user data perspective, they are not from the * trace_entry/buffer perspective. We automatically add the common properties * sizes to the offset for the user. * * Upon success user_event has its ref count increased by 1. */ static int user_event_parse_cmd(char *raw_command, struct user_event **newuser) { char *name = raw_command; char *args = strpbrk(name, " "); char *flags; if (args) *args++ = '\0'; flags = strpbrk(name, ":"); if (flags) *flags++ = '\0'; return user_event_parse(name, args, flags, newuser); } static int user_field_array_size(const char *type) { const char *start = strchr(type, '['); char val[8]; char *bracket; int size = 0; if (start == NULL) return -EINVAL; if (strscpy(val, start + 1, sizeof(val)) <= 0) return -EINVAL; bracket = strchr(val, ']'); if (!bracket) return -EINVAL; *bracket = '\0'; if (kstrtouint(val, 0, &size)) return -EINVAL; if (size > MAX_FIELD_ARRAY_SIZE) return -EINVAL; return size; } static int user_field_size(const char *type) { /* long is not allowed from a user, since it's ambigious in size */ if (strcmp(type, "s64") == 0) return sizeof(s64); if (strcmp(type, "u64") == 0) return sizeof(u64); if (strcmp(type, "s32") == 0) return sizeof(s32); if (strcmp(type, "u32") == 0) return sizeof(u32); if (strcmp(type, "int") == 0) return sizeof(int); if (strcmp(type, "unsigned int") == 0) return sizeof(unsigned int); if (strcmp(type, "s16") == 0) return sizeof(s16); if (strcmp(type, "u16") == 0) return sizeof(u16); if (strcmp(type, "short") == 0) return sizeof(short); if (strcmp(type, "unsigned short") == 0) return sizeof(unsigned short); if (strcmp(type, "s8") == 0) return sizeof(s8); if (strcmp(type, "u8") == 0) return sizeof(u8); if (strcmp(type, "char") == 0) return sizeof(char); if (strcmp(type, "unsigned char") == 0) return sizeof(unsigned char); if (str_has_prefix(type, "char[")) return user_field_array_size(type); if (str_has_prefix(type, "unsigned char[")) return user_field_array_size(type); if (str_has_prefix(type, "__data_loc ")) return sizeof(u32); if (str_has_prefix(type, "__rel_loc ")) return sizeof(u32); /* Uknown basic type, error */ return -EINVAL; } static void user_event_destroy_validators(struct user_event *user) { struct user_event_validator *validator, *next; struct list_head *head = &user->validators; list_for_each_entry_safe(validator, next, head, link) { list_del(&validator->link); kfree(validator); } } static void user_event_destroy_fields(struct user_event *user) { struct ftrace_event_field *field, *next; struct list_head *head = &user->fields; list_for_each_entry_safe(field, next, head, link) { list_del(&field->link); kfree(field); } } static int user_event_add_field(struct user_event *user, const char *type, const char *name, int offset, int size, int is_signed, int filter_type) { struct user_event_validator *validator; struct ftrace_event_field *field; int validator_flags = 0; field = kmalloc(sizeof(*field), GFP_KERNEL); if (!field) return -ENOMEM; if (str_has_prefix(type, "__data_loc ")) goto add_validator; if (str_has_prefix(type, "__rel_loc ")) { validator_flags |= VALIDATOR_REL; goto add_validator; } goto add_field; add_validator: if (strstr(type, "char") != 0) validator_flags |= VALIDATOR_ENSURE_NULL; validator = kmalloc(sizeof(*validator), GFP_KERNEL); if (!validator) { kfree(field); return -ENOMEM; } validator->flags = validator_flags; validator->offset = offset; /* Want sequential access when validating */ list_add_tail(&validator->link, &user->validators); add_field: field->type = type; field->name = name; field->offset = offset; field->size = size; field->is_signed = is_signed; field->filter_type = filter_type; list_add(&field->link, &user->fields); /* * Min size from user writes that are required, this does not include * the size of trace_entry (common fields). */ user->min_size = (offset + size) - sizeof(struct trace_entry); return 0; } /* * Parses the values of a field within the description * Format: type name [size] */ static int user_event_parse_field(char *field, struct user_event *user, u32 *offset) { char *part, *type, *name; u32 depth = 0, saved_offset = *offset; int len, size = -EINVAL; bool is_struct = false; field = skip_spaces(field); if (*field == '\0') return 0; /* Handle types that have a space within */ len = str_has_prefix(field, "unsigned "); if (len) goto skip_next; len = str_has_prefix(field, "struct "); if (len) { is_struct = true; goto skip_next; } len = str_has_prefix(field, "__data_loc unsigned "); if (len) goto skip_next; len = str_has_prefix(field, "__data_loc "); if (len) goto skip_next; len = str_has_prefix(field, "__rel_loc unsigned "); if (len) goto skip_next; len = str_has_prefix(field, "__rel_loc "); if (len) goto skip_next; goto parse; skip_next: type = field; field = strpbrk(field + len, " "); if (field == NULL) return -EINVAL; *field++ = '\0'; depth++; parse: name = NULL; while ((part = strsep(&field, " ")) != NULL) { switch (depth++) { case FIELD_DEPTH_TYPE: type = part; break; case FIELD_DEPTH_NAME: name = part; break; case FIELD_DEPTH_SIZE: if (!is_struct) return -EINVAL; if (kstrtou32(part, 10, &size)) return -EINVAL; break; default: return -EINVAL; } } if (depth < FIELD_DEPTH_SIZE || !name) return -EINVAL; if (depth == FIELD_DEPTH_SIZE) size = user_field_size(type); if (size == 0) return -EINVAL; if (size < 0) return size; *offset = saved_offset + size; return user_event_add_field(user, type, name, saved_offset, size, type[0] != 'u', FILTER_OTHER); } static int user_event_parse_fields(struct user_event *user, char *args) { char *field; u32 offset = sizeof(struct trace_entry); int ret = -EINVAL; if (args == NULL) return 0; while ((field = strsep(&args, ";")) != NULL) { ret = user_event_parse_field(field, user, &offset); if (ret) break; } return ret; } static struct trace_event_fields user_event_fields_array[1]; static const char *user_field_format(const char *type) { if (strcmp(type, "s64") == 0) return "%lld"; if (strcmp(type, "u64") == 0) return "%llu"; if (strcmp(type, "s32") == 0) return "%d"; if (strcmp(type, "u32") == 0) return "%u"; if (strcmp(type, "int") == 0) return "%d"; if (strcmp(type, "unsigned int") == 0) return "%u"; if (strcmp(type, "s16") == 0) return "%d"; if (strcmp(type, "u16") == 0) return "%u"; if (strcmp(type, "short") == 0) return "%d"; if (strcmp(type, "unsigned short") == 0) return "%u"; if (strcmp(type, "s8") == 0) return "%d"; if (strcmp(type, "u8") == 0) return "%u"; if (strcmp(type, "char") == 0) return "%d"; if (strcmp(type, "unsigned char") == 0) return "%u"; if (strstr(type, "char[") != 0) return "%s"; /* Unknown, likely struct, allowed treat as 64-bit */ return "%llu"; } static bool user_field_is_dyn_string(const char *type, const char **str_func) { if (str_has_prefix(type, "__data_loc ")) { *str_func = "__get_str"; goto check; } if (str_has_prefix(type, "__rel_loc ")) { *str_func = "__get_rel_str"; goto check; } return false; check: return strstr(type, "char") != 0; } #define LEN_OR_ZERO (len ? len - pos : 0) static int user_event_set_print_fmt(struct user_event *user, char *buf, int len) { struct ftrace_event_field *field, *next; struct list_head *head = &user->fields; int pos = 0, depth = 0; const char *str_func; pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); list_for_each_entry_safe_reverse(field, next, head, link) { if (depth != 0) pos += snprintf(buf + pos, LEN_OR_ZERO, " "); pos += snprintf(buf + pos, LEN_OR_ZERO, "%s=%s", field->name, user_field_format(field->type));