/kernel/trace/trace_events_inject.c

https://github.com/tekkamanninja/linux · C · 327 lines · 304 code · 13 blank · 10 comment · 30 complexity · 5ff632c612a878d6681b366c5ed279f8 MD5 · raw file

  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. #include <linux/module.h>
  8. #include <linux/ctype.h>
  9. #include <linux/mutex.h>
  10. #include <linux/slab.h>
  11. #include <linux/rculist.h>
  12. #include "trace.h"
  13. static int
  14. trace_inject_entry(struct trace_event_file *file, void *rec, int len)
  15. {
  16. struct trace_event_buffer fbuffer;
  17. int written = 0;
  18. void *entry;
  19. rcu_read_lock_sched();
  20. entry = trace_event_buffer_reserve(&fbuffer, file, len);
  21. if (entry) {
  22. memcpy(entry, rec, len);
  23. written = len;
  24. trace_event_buffer_commit(&fbuffer);
  25. }
  26. rcu_read_unlock_sched();
  27. return written;
  28. }
  29. static int
  30. parse_field(char *str, struct trace_event_call *call,
  31. struct ftrace_event_field **pf, u64 *pv)
  32. {
  33. struct ftrace_event_field *field;
  34. char *field_name;
  35. int s, i = 0;
  36. int len;
  37. u64 val;
  38. if (!str[i])
  39. return 0;
  40. /* First find the field to associate to */
  41. while (isspace(str[i]))
  42. i++;
  43. s = i;
  44. while (isalnum(str[i]) || str[i] == '_')
  45. i++;
  46. len = i - s;
  47. if (!len)
  48. return -EINVAL;
  49. field_name = kmemdup_nul(str + s, len, GFP_KERNEL);
  50. if (!field_name)
  51. return -ENOMEM;
  52. field = trace_find_event_field(call, field_name);
  53. kfree(field_name);
  54. if (!field)
  55. return -ENOENT;
  56. *pf = field;
  57. while (isspace(str[i]))
  58. i++;
  59. if (str[i] != '=')
  60. return -EINVAL;
  61. i++;
  62. while (isspace(str[i]))
  63. i++;
  64. s = i;
  65. if (isdigit(str[i]) || str[i] == '-') {
  66. char *num, c;
  67. int ret;
  68. /* Make sure the field is not a string */
  69. if (is_string_field(field))
  70. return -EINVAL;
  71. if (str[i] == '-')
  72. i++;
  73. /* We allow 0xDEADBEEF */
  74. while (isalnum(str[i]))
  75. i++;
  76. num = str + s;
  77. c = str[i];
  78. if (c != '\0' && !isspace(c))
  79. return -EINVAL;
  80. str[i] = '\0';
  81. /* Make sure it is a value */
  82. if (field->is_signed)
  83. ret = kstrtoll(num, 0, &val);
  84. else
  85. ret = kstrtoull(num, 0, &val);
  86. str[i] = c;
  87. if (ret)
  88. return ret;
  89. *pv = val;
  90. return i;
  91. } else if (str[i] == '\'' || str[i] == '"') {
  92. char q = str[i];
  93. /* Make sure the field is OK for strings */
  94. if (!is_string_field(field))
  95. return -EINVAL;
  96. for (i++; str[i]; i++) {
  97. if (str[i] == '\\' && str[i + 1]) {
  98. i++;
  99. continue;
  100. }
  101. if (str[i] == q)
  102. break;
  103. }
  104. if (!str[i])
  105. return -EINVAL;
  106. /* Skip quotes */
  107. s++;
  108. len = i - s;
  109. if (len >= MAX_FILTER_STR_VAL)
  110. return -EINVAL;
  111. *pv = (unsigned long)(str + s);
  112. str[i] = 0;
  113. /* go past the last quote */
  114. i++;
  115. return i;
  116. }
  117. return -EINVAL;
  118. }
  119. static int trace_get_entry_size(struct trace_event_call *call)
  120. {
  121. struct ftrace_event_field *field;
  122. struct list_head *head;
  123. int size = 0;
  124. head = trace_get_fields(call);
  125. list_for_each_entry(field, head, link) {
  126. if (field->size + field->offset > size)
  127. size = field->size + field->offset;
  128. }
  129. return size;
  130. }
  131. static void *trace_alloc_entry(struct trace_event_call *call, int *size)
  132. {
  133. int entry_size = trace_get_entry_size(call);
  134. struct ftrace_event_field *field;
  135. struct list_head *head;
  136. void *entry = NULL;
  137. /* We need an extra '\0' at the end. */
  138. entry = kzalloc(entry_size + 1, GFP_KERNEL);
  139. if (!entry)
  140. return NULL;
  141. head = trace_get_fields(call);
  142. list_for_each_entry(field, head, link) {
  143. if (!is_string_field(field))
  144. continue;
  145. if (field->filter_type == FILTER_STATIC_STRING)
  146. continue;
  147. if (field->filter_type == FILTER_DYN_STRING) {
  148. u32 *str_item;
  149. int str_loc = entry_size & 0xffff;
  150. str_item = (u32 *)(entry + field->offset);
  151. *str_item = str_loc; /* string length is 0. */
  152. } else {
  153. char **paddr;
  154. paddr = (char **)(entry + field->offset);
  155. *paddr = "";
  156. }
  157. }
  158. *size = entry_size + 1;
  159. return entry;
  160. }
  161. #define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
  162. /* Caller is responsible to free the *pentry. */
  163. static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
  164. {
  165. struct ftrace_event_field *field;
  166. void *entry = NULL;
  167. int entry_size;
  168. u64 val = 0;
  169. int len;
  170. entry = trace_alloc_entry(call, &entry_size);
  171. *pentry = entry;
  172. if (!entry)
  173. return -ENOMEM;
  174. tracing_generic_entry_update(entry, call->event.type,
  175. tracing_gen_ctx());
  176. while ((len = parse_field(str, call, &field, &val)) > 0) {
  177. if (is_function_field(field))
  178. return -EINVAL;
  179. if (is_string_field(field)) {
  180. char *addr = (char *)(unsigned long) val;
  181. if (field->filter_type == FILTER_STATIC_STRING) {
  182. strlcpy(entry + field->offset, addr, field->size);
  183. } else if (field->filter_type == FILTER_DYN_STRING) {
  184. int str_len = strlen(addr) + 1;
  185. int str_loc = entry_size & 0xffff;
  186. u32 *str_item;
  187. entry_size += str_len;
  188. *pentry = krealloc(entry, entry_size, GFP_KERNEL);
  189. if (!*pentry) {
  190. kfree(entry);
  191. return -ENOMEM;
  192. }
  193. entry = *pentry;
  194. strlcpy(entry + (entry_size - str_len), addr, str_len);
  195. str_item = (u32 *)(entry + field->offset);
  196. *str_item = (str_len << 16) | str_loc;
  197. } else {
  198. char **paddr;
  199. paddr = (char **)(entry + field->offset);
  200. *paddr = INJECT_STRING;
  201. }
  202. } else {
  203. switch (field->size) {
  204. case 1: {
  205. u8 tmp = (u8) val;
  206. memcpy(entry + field->offset, &tmp, 1);
  207. break;
  208. }
  209. case 2: {
  210. u16 tmp = (u16) val;
  211. memcpy(entry + field->offset, &tmp, 2);
  212. break;
  213. }
  214. case 4: {
  215. u32 tmp = (u32) val;
  216. memcpy(entry + field->offset, &tmp, 4);
  217. break;
  218. }
  219. case 8:
  220. memcpy(entry + field->offset, &val, 8);
  221. break;
  222. default:
  223. return -EINVAL;
  224. }
  225. }
  226. str += len;
  227. }
  228. if (len < 0)
  229. return len;
  230. return entry_size;
  231. }
  232. static ssize_t
  233. event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
  234. loff_t *ppos)
  235. {
  236. struct trace_event_call *call;
  237. struct trace_event_file *file;
  238. int err = -ENODEV, size;
  239. void *entry = NULL;
  240. char *buf;
  241. if (cnt >= PAGE_SIZE)
  242. return -EINVAL;
  243. buf = memdup_user_nul(ubuf, cnt);
  244. if (IS_ERR(buf))
  245. return PTR_ERR(buf);
  246. strim(buf);
  247. mutex_lock(&event_mutex);
  248. file = event_file_data(filp);
  249. if (file) {
  250. call = file->event_call;
  251. size = parse_entry(buf, call, &entry);
  252. if (size < 0)
  253. err = size;
  254. else
  255. err = trace_inject_entry(file, entry, size);
  256. }
  257. mutex_unlock(&event_mutex);
  258. kfree(entry);
  259. kfree(buf);
  260. if (err < 0)
  261. return err;
  262. *ppos += err;
  263. return cnt;
  264. }
  265. static ssize_t
  266. event_inject_read(struct file *file, char __user *buf, size_t size,
  267. loff_t *ppos)
  268. {
  269. return -EPERM;
  270. }
  271. const struct file_operations event_inject_fops = {
  272. .open = tracing_open_generic,
  273. .read = event_inject_read,
  274. .write = event_inject_write,
  275. };