When creating a trace_probe we would set nr_args prior to truncating the arguments to MAX_TRACE_ARGS. However, we would only initialize arguments up to the limit. This caused invalid memory access when attempting to set up probes with more than 128 fetchargs. BUG: kernel NULL pointer dereference, address: 0000000000000020 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 [#1] PREEMPT SMP PTI CPU: 0 UID: 0 PID: 1769 Comm: cat Not tainted 6.11.0-rc7+ #8 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-1.fc39 04/01/2014 RIP: 0010:__set_print_fmt+0x134/0x330 Resolve the issue by applying the MAX_TRACE_ARGS limit earlier. Return an error when there are too many arguments instead of silently truncating. Link: https://lore.kernel.org/all/20240930202656.292869-1-mikel@mikelr.com/
Unified · Split
| @@ -912,6 +912,11 @@ static int __trace_eprobe_create(int argc, const char *argv[]) · 911 unmodified lines | |||
| 912 | } | 912 | } |
| 913 | } | 913 | } |
| 914 | 914 | ||
| 915 | if (argc - 2 > MAX_TRACE_ARGS) { | ||
| 916 | ret = -E2BIG; | ||
| 917 | goto error; | ||
| 918 | } | ||
| 919 | |||
| 915 | mutex_lock(&event_mutex); | 920 | mutex_lock(&event_mutex); |
| 916 | event_call = find_and_get_event(sys_name, sys_event); | 921 | event_call = find_and_get_event(sys_name, sys_event); |
| 917 | ep = alloc_event_probe(group, event, event_call, argc - 2); | 922 | ep = alloc_event_probe(group, event, event_call, argc - 2); |
| @@ -937,7 +942,7 @@ static int __trace_eprobe_create(int argc, const char *argv[]) · 19 unmodified lines | |||
| 937 | 942 | ||
| 938 | argc -= 2; argv += 2; | 943 | argc -= 2; argv += 2; |
| 939 | /* parse arguments */ | 944 | /* parse arguments */ |
| 940 | for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { | 945 | for (i = 0; i < argc; i++) { |
| 941 | trace_probe_log_set_index(i + 2); | 946 | trace_probe_log_set_index(i + 2); |
| 942 | ret = trace_eprobe_tp_update_arg(ep, argv, i); | 947 | ret = trace_eprobe_tp_update_arg(ep, argv, i); |
| 943 | if (ret) | 948 | if (ret) |
| @@ -1187,6 +1187,10 @@ static int __trace_fprobe_create(int argc, const char *argv[]) · 1186 unmodified lines | |||
| 1187 | argc = new_argc; | 1187 | argc = new_argc; |
| 1188 | argv = new_argv; | 1188 | argv = new_argv; |
| 1189 | } | 1189 | } |
| 1190 | if (argc > MAX_TRACE_ARGS) { | ||
| 1191 | ret = -E2BIG; | ||
| 1192 | goto out; | ||
| 1193 | } | ||
| 1190 | 1194 | ||
| 1191 | ret = traceprobe_expand_dentry_args(argc, argv, &dbuf); | 1195 | ret = traceprobe_expand_dentry_args(argc, argv, &dbuf); |
| 1192 | if (ret) | 1196 | if (ret) |
| @@ -1203,7 +1207,7 @@ static int __trace_fprobe_create(int argc, const char *argv[]) · 10 unmodified lines | |||
| 1203 | } | 1207 | } |
| 1204 | 1208 | ||
| 1205 | /* parse arguments */ | 1209 | /* parse arguments */ |
| 1206 | for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { | 1210 | for (i = 0; i < argc; i++) { |
| 1207 | trace_probe_log_set_index(i + 2); | 1211 | trace_probe_log_set_index(i + 2); |
| 1208 | ctx.offset = 0; | 1212 | ctx.offset = 0; |
| 1209 | ret = traceprobe_parse_probe_arg(&tf->tp, i, argv[i], &ctx); | 1213 | ret = traceprobe_parse_probe_arg(&tf->tp, i, argv[i], &ctx); |
| @@ -1013,6 +1013,10 @@ static int __trace_kprobe_create(int argc, const char *argv[]) · 1012 unmodified lines | |||
| 1013 | argc = new_argc; | 1013 | argc = new_argc; |
| 1014 | argv = new_argv; | 1014 | argv = new_argv; |
| 1015 | } | 1015 | } |
| 1016 | if (argc > MAX_TRACE_ARGS) { | ||
| 1017 | ret = -E2BIG; | ||
| 1018 | goto out; | ||
| 1019 | } | ||
| 1016 | 1020 | ||
| 1017 | ret = traceprobe_expand_dentry_args(argc, argv, &dbuf); | 1021 | ret = traceprobe_expand_dentry_args(argc, argv, &dbuf); |
| 1018 | if (ret) | 1022 | if (ret) |
| @@ -1029,7 +1033,7 @@ static int __trace_kprobe_create(int argc, const char *argv[]) · 10 unmodified lines | |||
| 1029 | } | 1033 | } |
| 1030 | 1034 | ||
| 1031 | /* parse arguments */ | 1035 | /* parse arguments */ |
| 1032 | for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { | 1036 | for (i = 0; i < argc; i++) { |
| 1033 | trace_probe_log_set_index(i + 2); | 1037 | trace_probe_log_set_index(i + 2); |
| 1034 | ctx.offset = 0; | 1038 | ctx.offset = 0; |
| 1035 | ret = traceprobe_parse_probe_arg(&tk->tp, i, argv[i], &ctx); | 1039 | ret = traceprobe_parse_probe_arg(&tk->tp, i, argv[i], &ctx); |
| @@ -565,6 +565,8 @@ static int __trace_uprobe_create(int argc, const char **argv) · 564 unmodified lines | |||
| 565 | 565 | ||
| 566 | if (argc < 2) | 566 | if (argc < 2) |
| 567 | return -ECANCELED; | 567 | return -ECANCELED; |
| 568 | if (argc - 2 > MAX_TRACE_ARGS) | ||
| 569 | return -E2BIG; | ||
| 568 | 570 | ||
| 569 | if (argv[0][1] == ':') | 571 | if (argv[0][1] == ':') |
| 570 | event = &argv[0][2]; | 572 | event = &argv[0][2]; |
| @@ -690,7 +692,7 @@ static int __trace_uprobe_create(int argc, const char **argv) · 119 unmodified lines | |||
| 690 | tu->filename = filename; | 692 | tu->filename = filename; |
| 691 | 693 | ||
| 692 | /* parse arguments */ | 694 | /* parse arguments */ |
| 693 | for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { | 695 | for (i = 0; i < argc; i++) { |
| 694 | struct traceprobe_parse_context ctx = { | 696 | struct traceprobe_parse_context ctx = { |
| 695 | .flags = (is_return ? TPARG_FL_RETURN : 0) | TPARG_FL_USER, | 697 | .flags = (is_return ? TPARG_FL_RETURN : 0) | TPARG_FL_USER, |
| 696 | }; | 698 | }; |