Skip to content

Commit 5013f45

Browse files
committed
tracing: Add check of trace event print fmts for dereferencing pointers
Trace events record data into the ring buffer at the time of the event. The trace event has a printf logic to display the recorded data at a much later time when the user reads the trace file. This makes using dereferencing pointers unsafe if the dereferenced pointer points to the original source. The safe way to handle this is to create an array within the trace event and copy the source into the array. Then the dereference pointer may point to that array. As this is a easy mistake to make, a check is added to examine all trace event print fmts to make sure that they are safe to use. This only checks the various %p* dereferenced pointers like %pB, %pR, etc. It does not handle dereferencing of strings, as there are some use cases that are OK to dereference the source. That will be dealt with differently. Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent e0196ae commit 5013f45

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

kernel/trace/trace_events.c

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,214 @@ int trace_event_get_offsets(struct trace_event_call *call)
217217
return tail->offset + tail->size;
218218
}
219219

220+
/*
221+
* Check if the referenced field is an array and return true,
222+
* as arrays are OK to dereference.
223+
*/
224+
static bool test_field(const char *fmt, struct trace_event_call *call)
225+
{
226+
struct trace_event_fields *field = call->class->fields_array;
227+
const char *array_descriptor;
228+
const char *p = fmt;
229+
int len;
230+
231+
if (!(len = str_has_prefix(fmt, "REC->")))
232+
return false;
233+
fmt += len;
234+
for (p = fmt; *p; p++) {
235+
if (!isalnum(*p) && *p != '_')
236+
break;
237+
}
238+
len = p - fmt;
239+
240+
for (; field->type; field++) {
241+
if (strncmp(field->name, fmt, len) ||
242+
field->name[len])
243+
continue;
244+
array_descriptor = strchr(field->type, '[');
245+
/* This is an array and is OK to dereference. */
246+
return array_descriptor != NULL;
247+
}
248+
return false;
249+
}
250+
251+
/*
252+
* Examine the print fmt of the event looking for unsafe dereference
253+
* pointers using %p* that could be recorded in the trace event and
254+
* much later referenced after the pointer was freed. Dereferencing
255+
* pointers are OK, if it is dereferenced into the event itself.
256+
*/
257+
static void test_event_printk(struct trace_event_call *call)
258+
{
259+
u64 dereference_flags = 0;
260+
bool first = true;
261+
const char *fmt, *c, *r, *a;
262+
int parens = 0;
263+
char in_quote = 0;
264+
int start_arg = 0;
265+
int arg = 0;
266+
int i;
267+
268+
fmt = call->print_fmt;
269+
270+
if (!fmt)
271+
return;
272+
273+
for (i = 0; fmt[i]; i++) {
274+
switch (fmt[i]) {
275+
case '\\':
276+
i++;
277+
if (!fmt[i])
278+
return;
279+
continue;
280+
case '"':
281+
case '\'':
282+
/*
283+
* The print fmt starts with a string that
284+
* is processed first to find %p* usage,
285+
* then after the first string, the print fmt
286+
* contains arguments that are used to check
287+
* if the dereferenced %p* usage is safe.
288+
*/
289+
if (first) {
290+
if (fmt[i] == '\'')
291+
continue;
292+
if (in_quote) {
293+
arg = 0;
294+
first = false;
295+
/*
296+
* If there was no %p* uses
297+
* the fmt is OK.
298+
*/
299+
if (!dereference_flags)
300+
return;
301+
}
302+
}
303+
if (in_quote) {
304+
if (in_quote == fmt[i])
305+
in_quote = 0;
306+
} else {
307+
in_quote = fmt[i];
308+
}
309+
continue;
310+
case '%':
311+
if (!first || !in_quote)
312+
continue;
313+
i++;
314+
if (!fmt[i])
315+
return;
316+
switch (fmt[i]) {
317+
case '%':
318+
continue;
319+
case 'p':
320+
/* Find dereferencing fields */
321+
switch (fmt[i + 1]) {
322+
case 'B': case 'R': case 'r':
323+
case 'b': case 'M': case 'm':
324+
case 'I': case 'i': case 'E':
325+
case 'U': case 'V': case 'N':
326+
case 'a': case 'd': case 'D':
327+
case 'g': case 't': case 'C':
328+
case 'O': case 'f':
329+
if (WARN_ONCE(arg == 63,
330+
"Too many args for event: %s",
331+
trace_event_name(call)))
332+
return;
333+
dereference_flags |= 1ULL << arg;
334+
}
335+
break;
336+
default:
337+
{
338+
bool star = false;
339+
int j;
340+
341+
/* Increment arg if %*s exists. */
342+
for (j = 0; fmt[i + j]; j++) {
343+
if (isdigit(fmt[i + j]) ||
344+
fmt[i + j] == '.')
345+
continue;
346+
if (fmt[i + j] == '*') {
347+
star = true;
348+
continue;
349+
}
350+
if ((fmt[i + j] == 's') && star)
351+
arg++;
352+
break;
353+
}
354+
break;
355+
} /* default */
356+
357+
} /* switch */
358+
arg++;
359+
continue;
360+
case '(':
361+
if (in_quote)
362+
continue;
363+
parens++;
364+
continue;
365+
case ')':
366+
if (in_quote)
367+
continue;
368+
parens--;
369+
if (WARN_ONCE(parens < 0,
370+
"Paren mismatch for event: %s\narg='%s'\n%*s",
371+
trace_event_name(call),
372+
fmt + start_arg,
373+
(i - start_arg) + 5, "^"))
374+
return;
375+
continue;
376+
case ',':
377+
if (in_quote || parens)
378+
continue;
379+
i++;
380+
while (isspace(fmt[i]))
381+
i++;
382+
start_arg = i;
383+
if (!(dereference_flags & (1ULL << arg)))
384+
goto next_arg;
385+
386+
/* Find the REC-> in the argument */
387+
c = strchr(fmt + i, ',');
388+
r = strstr(fmt + i, "REC->");
389+
if (r && (!c || r < c)) {
390+
/*
391+
* Addresses of events on the buffer,
392+
* or an array on the buffer is
393+
* OK to dereference.
394+
* There's ways to fool this, but
395+
* this is to catch common mistakes,
396+
* not malicious code.
397+
*/
398+
a = strchr(fmt + i, '&');
399+
if ((a && (a < r)) || test_field(r, call))
400+
dereference_flags &= ~(1ULL << arg);
401+
}
402+
next_arg:
403+
i--;
404+
arg++;
405+
}
406+
}
407+
408+
/*
409+
* If you triggered the below warning, the trace event reported
410+
* uses an unsafe dereference pointer %p*. As the data stored
411+
* at the trace event time may no longer exist when the trace
412+
* event is printed, dereferencing to the original source is
413+
* unsafe. The source of the dereference must be copied into the
414+
* event itself, and the dereference must access the copy instead.
415+
*/
416+
if (WARN_ON_ONCE(dereference_flags)) {
417+
arg = 1;
418+
while (!(dereference_flags & 1)) {
419+
dereference_flags >>= 1;
420+
arg++;
421+
}
422+
pr_warn("event %s has unsafe dereference of argument %d\n",
423+
trace_event_name(call), arg);
424+
pr_warn("print_fmt: %s\n", fmt);
425+
}
426+
}
427+
220428
int trace_event_raw_init(struct trace_event_call *call)
221429
{
222430
int id;
@@ -225,6 +433,8 @@ int trace_event_raw_init(struct trace_event_call *call)
225433
if (!id)
226434
return -ENODEV;
227435

436+
test_event_printk(call);
437+
228438
return 0;
229439
}
230440
EXPORT_SYMBOL_GPL(trace_event_raw_init);

0 commit comments

Comments
 (0)