Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

user_events: Validate user payloads for size and null termination

Add validation to ensure data is at or greater than the min size for the
fields of the event. If a dynamic array is used and is a type of char,
ensure null termination of the array exists.

Link: https://lkml.kernel.org/r/20220118204326.2169-7-beaub@linux.microsoft.com

Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Beau Belgrave <beaub@linux.microsoft.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

Beau Belgrave and committed by
Steven Rostedt (Google)
2467cda1 0279400a

+133 -14
+133 -14
kernel/trace/trace_events_user.c
··· 64 64 struct dyn_event devent; 65 65 struct hlist_node node; 66 66 struct list_head fields; 67 + struct list_head validators; 67 68 atomic_t refcnt; 68 69 int index; 69 70 int flags; 71 + int min_size; 70 72 }; 71 73 72 74 /* ··· 83 81 struct user_event *events[]; 84 82 }; 85 83 84 + #define VALIDATOR_ENSURE_NULL (1 << 0) 85 + #define VALIDATOR_REL (1 << 1) 86 + 87 + struct user_event_validator { 88 + struct list_head link; 89 + int offset; 90 + int flags; 91 + }; 92 + 86 93 typedef void (*user_event_func_t) (struct user_event *user, struct iov_iter *i, 87 - void *tpdata); 94 + void *tpdata, bool *faulted); 88 95 89 96 static int user_event_parse(char *name, char *args, char *flags, 90 97 struct user_event **newuser); ··· 226 215 return -EINVAL; 227 216 } 228 217 218 + static void user_event_destroy_validators(struct user_event *user) 219 + { 220 + struct user_event_validator *validator, *next; 221 + struct list_head *head = &user->validators; 222 + 223 + list_for_each_entry_safe(validator, next, head, link) { 224 + list_del(&validator->link); 225 + kfree(validator); 226 + } 227 + } 228 + 229 229 static void user_event_destroy_fields(struct user_event *user) 230 230 { 231 231 struct ftrace_event_field *field, *next; ··· 252 230 const char *name, int offset, int size, 253 231 int is_signed, int filter_type) 254 232 { 233 + struct user_event_validator *validator; 255 234 struct ftrace_event_field *field; 235 + int validator_flags = 0; 256 236 257 237 field = kmalloc(sizeof(*field), GFP_KERNEL); 258 238 259 239 if (!field) 260 240 return -ENOMEM; 261 241 242 + if (str_has_prefix(type, "__data_loc ")) 243 + goto add_validator; 244 + 245 + if (str_has_prefix(type, "__rel_loc ")) { 246 + validator_flags |= VALIDATOR_REL; 247 + goto add_validator; 248 + } 249 + 250 + goto add_field; 251 + 252 + add_validator: 253 + if (strstr(type, "char") != 0) 254 + validator_flags |= VALIDATOR_ENSURE_NULL; 255 + 256 + validator = kmalloc(sizeof(*validator), GFP_KERNEL); 257 + 258 + if (!validator) { 259 + kfree(field); 260 + return -ENOMEM; 261 + } 262 + 263 + validator->flags = validator_flags; 264 + validator->offset = offset; 265 + 266 + /* Want sequential access when validating */ 267 + list_add_tail(&validator->link, &user->validators); 268 + 269 + add_field: 262 270 field->type = type; 263 271 field->name = name; 264 272 field->offset = offset; ··· 297 245 field->filter_type = filter_type; 298 246 299 247 list_add(&field->link, &user->fields); 248 + 249 + /* 250 + * Min size from user writes that are required, this does not include 251 + * the size of trace_entry (common fields). 252 + */ 253 + user->min_size = (offset + size) - sizeof(struct trace_entry); 300 254 301 255 return 0; 302 256 } ··· 575 517 clear_bit(user->index, page_bitmap); 576 518 hash_del(&user->node); 577 519 520 + user_event_destroy_validators(user); 578 521 kfree(user->call.print_fmt); 579 522 kfree(EVENT_NAME(user)); 580 523 kfree(user); ··· 597 538 return NULL; 598 539 } 599 540 541 + static int user_event_validate(struct user_event *user, void *data, int len) 542 + { 543 + struct list_head *head = &user->validators; 544 + struct user_event_validator *validator; 545 + void *pos, *end = data + len; 546 + u32 loc, offset, size; 547 + 548 + list_for_each_entry(validator, head, link) { 549 + pos = data + validator->offset; 550 + 551 + /* Already done min_size check, no bounds check here */ 552 + loc = *(u32 *)pos; 553 + offset = loc & 0xffff; 554 + size = loc >> 16; 555 + 556 + if (likely(validator->flags & VALIDATOR_REL)) 557 + pos += offset + sizeof(loc); 558 + else 559 + pos = data + offset; 560 + 561 + pos += size; 562 + 563 + if (unlikely(pos > end)) 564 + return -EFAULT; 565 + 566 + if (likely(validator->flags & VALIDATOR_ENSURE_NULL)) 567 + if (unlikely(*(char *)(pos - 1) != '\0')) 568 + return -EFAULT; 569 + } 570 + 571 + return 0; 572 + } 573 + 600 574 /* 601 575 * Writes the user supplied payload out to a trace file. 602 576 */ 603 577 static void user_event_ftrace(struct user_event *user, struct iov_iter *i, 604 - void *tpdata) 578 + void *tpdata, bool *faulted) 605 579 { 606 580 struct trace_event_file *file; 607 581 struct trace_entry *entry; 608 582 struct trace_event_buffer event_buffer; 583 + size_t size = sizeof(*entry) + i->count; 609 584 610 585 file = (struct trace_event_file *)tpdata; 611 586 ··· 649 556 return; 650 557 651 558 /* Allocates and fills trace_entry, + 1 of this is data payload */ 652 - entry = trace_event_buffer_reserve(&event_buffer, file, 653 - sizeof(*entry) + i->count); 559 + entry = trace_event_buffer_reserve(&event_buffer, file, size); 654 560 655 561 if (unlikely(!entry)) 656 562 return; 657 563 658 564 if (unlikely(!copy_nofault(entry + 1, i->count, i))) 659 - __trace_event_discard_commit(event_buffer.buffer, 660 - event_buffer.event); 661 - else 662 - trace_event_buffer_commit(&event_buffer); 565 + goto discard; 566 + 567 + if (!list_empty(&user->validators) && 568 + unlikely(user_event_validate(user, entry, size))) 569 + goto discard; 570 + 571 + trace_event_buffer_commit(&event_buffer); 572 + 573 + return; 574 + discard: 575 + *faulted = true; 576 + __trace_event_discard_commit(event_buffer.buffer, 577 + event_buffer.event); 663 578 } 664 579 665 580 #ifdef CONFIG_PERF_EVENTS ··· 722 621 * Writes the user supplied payload out to perf ring buffer or eBPF program. 723 622 */ 724 623 static void user_event_perf(struct user_event *user, struct iov_iter *i, 725 - void *tpdata) 624 + void *tpdata, bool *faulted) 726 625 { 727 626 struct hlist_head *perf_head; 728 627 ··· 745 644 746 645 perf_fetch_caller_regs(regs); 747 646 748 - if (unlikely(!copy_nofault(perf_entry + 1, i->count, i))) { 749 - perf_swevent_put_recursion_context(context); 750 - return; 751 - } 647 + if (unlikely(!copy_nofault(perf_entry + 1, i->count, i))) 648 + goto discard; 649 + 650 + if (!list_empty(&user->validators) && 651 + unlikely(user_event_validate(user, perf_entry, size))) 652 + goto discard; 752 653 753 654 perf_trace_buf_submit(perf_entry, size, context, 754 655 user->call.event.type, 1, regs, 755 656 perf_head, NULL); 657 + 658 + return; 659 + discard: 660 + *faulted = true; 661 + perf_swevent_put_recursion_context(context); 756 662 } 757 663 } 758 664 #endif ··· 1079 971 1080 972 INIT_LIST_HEAD(&user->class.fields); 1081 973 INIT_LIST_HEAD(&user->fields); 974 + INIT_LIST_HEAD(&user->validators); 1082 975 1083 976 user->tracepoint.name = name; 1084 977 ··· 1128 1019 return 0; 1129 1020 put_user: 1130 1021 user_event_destroy_fields(user); 1022 + user_event_destroy_validators(user); 1131 1023 kfree(user); 1132 1024 return ret; 1133 1025 } ··· 1186 1076 if (unlikely(user == NULL)) 1187 1077 return -ENOENT; 1188 1078 1079 + if (unlikely(i->count < user->min_size)) 1080 + return -EINVAL; 1081 + 1189 1082 tp = &user->tracepoint; 1190 1083 1191 1084 /* ··· 1200 1087 user_event_func_t probe_func; 1201 1088 struct iov_iter copy; 1202 1089 void *tpdata; 1090 + bool faulted; 1203 1091 1204 1092 if (unlikely(fault_in_iov_iter_readable(i, i->count))) 1205 1093 return -EFAULT; 1094 + 1095 + faulted = false; 1206 1096 1207 1097 rcu_read_lock_sched(); 1208 1098 ··· 1216 1100 copy = *i; 1217 1101 probe_func = probe_func_ptr->func; 1218 1102 tpdata = probe_func_ptr->data; 1219 - probe_func(user, &copy, tpdata); 1103 + probe_func(user, &copy, tpdata, &faulted); 1220 1104 } while ((++probe_func_ptr)->func); 1221 1105 } 1222 1106 1223 1107 rcu_read_unlock_sched(); 1108 + 1109 + if (unlikely(faulted)) 1110 + return -EFAULT; 1224 1111 } 1225 1112 1226 1113 return ret;