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

tracing/user_events: Ensure user provided strings are safely formatted

User processes can provide bad strings that may cause issues or leak
kernel details back out. Don't trust the content of these strings
when formatting strings for matching.

This also moves to a consistent dynamic length string creation model.

Link: https://lkml.kernel.org/r/20220728233309.1896-4-beaub@linux.microsoft.com
Link: https://lore.kernel.org/all/2059213643.196683.1648499088753.JavaMail.zimbra@efficios.com/

Reported-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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)
e6f89a14 95f18760

+61 -34
+61 -34
kernel/trace/trace_events_user.c
··· 45 45 #define MAX_EVENT_DESC 512 46 46 #define EVENT_NAME(user_event) ((user_event)->tracepoint.name) 47 47 #define MAX_FIELD_ARRAY_SIZE 1024 48 - #define MAX_FIELD_ARG_NAME 256 49 48 50 49 static char *register_page_data; 51 50 ··· 482 483 } 483 484 484 485 #define LEN_OR_ZERO (len ? len - pos : 0) 486 + static int user_dyn_field_set_string(int argc, const char **argv, int *iout, 487 + char *buf, int len, bool *colon) 488 + { 489 + int pos = 0, i = *iout; 490 + 491 + *colon = false; 492 + 493 + for (; i < argc; ++i) { 494 + if (i != *iout) 495 + pos += snprintf(buf + pos, LEN_OR_ZERO, " "); 496 + 497 + pos += snprintf(buf + pos, LEN_OR_ZERO, "%s", argv[i]); 498 + 499 + if (strchr(argv[i], ';')) { 500 + ++i; 501 + *colon = true; 502 + break; 503 + } 504 + } 505 + 506 + /* Actual set, advance i */ 507 + if (len != 0) 508 + *iout = i; 509 + 510 + return pos + 1; 511 + } 512 + 513 + static int user_field_set_string(struct ftrace_event_field *field, 514 + char *buf, int len, bool colon) 515 + { 516 + int pos = 0; 517 + 518 + pos += snprintf(buf + pos, LEN_OR_ZERO, "%s", field->type); 519 + pos += snprintf(buf + pos, LEN_OR_ZERO, " "); 520 + pos += snprintf(buf + pos, LEN_OR_ZERO, "%s", field->name); 521 + 522 + if (colon) 523 + pos += snprintf(buf + pos, LEN_OR_ZERO, ";"); 524 + 525 + return pos + 1; 526 + } 527 + 485 528 static int user_event_set_print_fmt(struct user_event *user, char *buf, int len) 486 529 { 487 530 struct ftrace_event_field *field, *next; ··· 967 926 static bool user_field_match(struct ftrace_event_field *field, int argc, 968 927 const char **argv, int *iout) 969 928 { 970 - char *field_name, *arg_name; 971 - int len, pos, i = *iout; 929 + char *field_name = NULL, *dyn_field_name = NULL; 972 930 bool colon = false, match = false; 931 + int dyn_len, len; 973 932 974 - if (i >= argc) 933 + if (*iout >= argc) 975 934 return false; 976 935 977 - len = MAX_FIELD_ARG_NAME; 978 - field_name = kmalloc(len, GFP_KERNEL); 979 - arg_name = kmalloc(len, GFP_KERNEL); 936 + dyn_len = user_dyn_field_set_string(argc, argv, iout, dyn_field_name, 937 + 0, &colon); 980 938 981 - if (!arg_name || !field_name) 939 + len = user_field_set_string(field, field_name, 0, colon); 940 + 941 + if (dyn_len != len) 942 + return false; 943 + 944 + dyn_field_name = kmalloc(dyn_len, GFP_KERNEL); 945 + field_name = kmalloc(len, GFP_KERNEL); 946 + 947 + if (!dyn_field_name || !field_name) 982 948 goto out; 983 949 984 - pos = 0; 950 + user_dyn_field_set_string(argc, argv, iout, dyn_field_name, 951 + dyn_len, &colon); 985 952 986 - for (; i < argc; ++i) { 987 - if (i != *iout) 988 - pos += snprintf(arg_name + pos, len - pos, " "); 953 + user_field_set_string(field, field_name, len, colon); 989 954 990 - pos += snprintf(arg_name + pos, len - pos, argv[i]); 991 - 992 - if (strchr(argv[i], ';')) { 993 - ++i; 994 - colon = true; 995 - break; 996 - } 997 - } 998 - 999 - pos = 0; 1000 - 1001 - pos += snprintf(field_name + pos, len - pos, field->type); 1002 - pos += snprintf(field_name + pos, len - pos, " "); 1003 - pos += snprintf(field_name + pos, len - pos, field->name); 1004 - 1005 - if (colon) 1006 - pos += snprintf(field_name + pos, len - pos, ";"); 1007 - 1008 - *iout = i; 1009 - 1010 - match = strcmp(arg_name, field_name) == 0; 955 + match = strcmp(dyn_field_name, field_name) == 0; 1011 956 out: 1012 - kfree(arg_name); 957 + kfree(dyn_field_name); 1013 958 kfree(field_name); 1014 959 1015 960 return match;