Skip to content

Commit a662962

Browse files
committed
tracing: Fix test_event_printk() to process entire print argument
The test_event_printk() analyzes print formats of trace events looking for cases where it may dereference a pointer that is not in the ring buffer which can possibly be a bug when the trace event is read from the ring buffer and the content of that pointer no longer exists. The function needs to accurately go from one print format argument to the next. It handles quotes and parenthesis that may be included in an argument. When it finds the start of the next argument, it uses a simple "c = strstr(fmt + i, ',')" to find the end of that argument! In order to include "%s" dereferencing, it needs to process the entire content of the print format argument and not just the content of the first ',' it finds. As there may be content like: ({ const char *saved_ptr = trace_seq_buffer_ptr(p); static const char *access_str[] = { "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" }; union kvm_mmu_page_role role; role.word = REC->role; trace_seq_printf(p, "sp gen %u gfn %llx l%u %u-byte q%u%s %s%s" " %snxe %sad root %u %s%c", REC->mmu_valid_gen, REC->gfn, role.level, role.has_4_byte_gpte ? 4 : 8, role.quadrant, role.direct ? " direct" : "", access_str[role.access], role.invalid ? " invalid" : "", role.efer_nx ? "" : "!", role.ad_disabled ? "!" : "", REC->root_count, REC->unsync ? "unsync" : "sync", 0); saved_ptr; }) Which is an example of a full argument of an existing event. As the code already handles finding the next print format argument, process the argument at the end of it and not the start of it. This way it has both the start of the argument as well as the end of it. Add a helper function "process_pointer()" that will do the processing during the loop as well as at the end. It also makes the code cleaner and easier to read. Cc: [email protected] Cc: Masami Hiramatsu <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Al Viro <[email protected]> Cc: Linus Torvalds <[email protected]> Link: https://lore.kernel.org/[email protected] Fixes: 5013f45 ("tracing: Add check of trace event print fmts for dereferencing pointers") Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 78d4f34 commit a662962

File tree

1 file changed

+53
-29
lines changed

1 file changed

+53
-29
lines changed

kernel/trace/trace_events.c

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,7 @@ static bool test_field(const char *fmt, struct trace_event_call *call)
265265
len = p - fmt;
266266

267267
for (; field->type; field++) {
268-
if (strncmp(field->name, fmt, len) ||
269-
field->name[len])
268+
if (strncmp(field->name, fmt, len) || field->name[len])
270269
continue;
271270
array_descriptor = strchr(field->type, '[');
272271
/* This is an array and is OK to dereference. */
@@ -275,6 +274,32 @@ static bool test_field(const char *fmt, struct trace_event_call *call)
275274
return false;
276275
}
277276

277+
/* Return true if the argument pointer is safe */
278+
static bool process_pointer(const char *fmt, int len, struct trace_event_call *call)
279+
{
280+
const char *r, *e, *a;
281+
282+
e = fmt + len;
283+
284+
/* Find the REC-> in the argument */
285+
r = strstr(fmt, "REC->");
286+
if (r && r < e) {
287+
/*
288+
* Addresses of events on the buffer, or an array on the buffer is
289+
* OK to dereference. There's ways to fool this, but
290+
* this is to catch common mistakes, not malicious code.
291+
*/
292+
a = strchr(fmt, '&');
293+
if ((a && (a < r)) || test_field(r, call))
294+
return true;
295+
} else if ((r = strstr(fmt, "__get_dynamic_array(")) && r < e) {
296+
return true;
297+
} else if ((r = strstr(fmt, "__get_sockaddr(")) && r < e) {
298+
return true;
299+
}
300+
return false;
301+
}
302+
278303
/*
279304
* Examine the print fmt of the event looking for unsafe dereference
280305
* pointers using %p* that could be recorded in the trace event and
@@ -285,12 +310,12 @@ static void test_event_printk(struct trace_event_call *call)
285310
{
286311
u64 dereference_flags = 0;
287312
bool first = true;
288-
const char *fmt, *c, *r, *a;
313+
const char *fmt;
289314
int parens = 0;
290315
char in_quote = 0;
291316
int start_arg = 0;
292317
int arg = 0;
293-
int i;
318+
int i, e;
294319

295320
fmt = call->print_fmt;
296321

@@ -403,42 +428,41 @@ static void test_event_printk(struct trace_event_call *call)
403428
case ',':
404429
if (in_quote || parens)
405430
continue;
431+
e = i;
406432
i++;
407433
while (isspace(fmt[i]))
408434
i++;
409-
start_arg = i;
410-
if (!(dereference_flags & (1ULL << arg)))
411-
goto next_arg;
412435

413-
/* Find the REC-> in the argument */
414-
c = strchr(fmt + i, ',');
415-
r = strstr(fmt + i, "REC->");
416-
if (r && (!c || r < c)) {
417-
/*
418-
* Addresses of events on the buffer,
419-
* or an array on the buffer is
420-
* OK to dereference.
421-
* There's ways to fool this, but
422-
* this is to catch common mistakes,
423-
* not malicious code.
424-
*/
425-
a = strchr(fmt + i, '&');
426-
if ((a && (a < r)) || test_field(r, call))
436+
/*
437+
* If start_arg is zero, then this is the start of the
438+
* first argument. The processing of the argument happens
439+
* when the end of the argument is found, as it needs to
440+
* handle paranthesis and such.
441+
*/
442+
if (!start_arg) {
443+
start_arg = i;
444+
/* Balance out the i++ in the for loop */
445+
i--;
446+
continue;
447+
}
448+
449+
if (dereference_flags & (1ULL << arg)) {
450+
if (process_pointer(fmt + start_arg, e - start_arg, call))
427451
dereference_flags &= ~(1ULL << arg);
428-
} else if ((r = strstr(fmt + i, "__get_dynamic_array(")) &&
429-
(!c || r < c)) {
430-
dereference_flags &= ~(1ULL << arg);
431-
} else if ((r = strstr(fmt + i, "__get_sockaddr(")) &&
432-
(!c || r < c)) {
433-
dereference_flags &= ~(1ULL << arg);
434452
}
435453

436-
next_arg:
437-
i--;
454+
start_arg = i;
438455
arg++;
456+
/* Balance out the i++ in the for loop */
457+
i--;
439458
}
440459
}
441460

461+
if (dereference_flags & (1ULL << arg)) {
462+
if (process_pointer(fmt + start_arg, i - start_arg, call))
463+
dereference_flags &= ~(1ULL << arg);
464+
}
465+
442466
/*
443467
* If you triggered the below warning, the trace event reported
444468
* uses an unsafe dereference pointer %p*. As the data stored

0 commit comments

Comments
 (0)