Skip to content

Commit 7104f30

Browse files
rostedtIngo Molnar
authored andcommitted
ftrace: type cast filter+verifier
The mmiotrace map had a bug that would typecast the entry from the trace to the wrong type. That is a known danger of C typecasts, there's absolutely zero checking done on them. Help that problem a bit by using a GCC extension to implement a type filter that restricts the types that a trace record can be cast into, and by adding a dynamic check (in debug mode) to verify the type of the entry. This patch adds a macro to assign all entries of ftrace using the type of the variable and checking the entry id. The typecasts are now done in the macro for only those types that it knows about, which should be all the types that are allowed to be read from the tracer. Signed-off-by: Steven Rostedt <[email protected]> Signed-off-by: Ingo Molnar <[email protected]>
1 parent 797d371 commit 7104f30

File tree

3 files changed

+112
-29
lines changed

3 files changed

+112
-29
lines changed

kernel/trace/trace.c

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,7 +1350,9 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
13501350
}
13511351
switch (entry->type) {
13521352
case TRACE_FN: {
1353-
struct ftrace_entry *field = (struct ftrace_entry *)entry;
1353+
struct ftrace_entry *field;
1354+
1355+
trace_assign_type(field, entry);
13541356

13551357
seq_print_ip_sym(s, field->ip, sym_flags);
13561358
trace_seq_puts(s, " (");
@@ -1363,8 +1365,9 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
13631365
}
13641366
case TRACE_CTX:
13651367
case TRACE_WAKE: {
1366-
struct ctx_switch_entry *field =
1367-
(struct ctx_switch_entry *)entry;
1368+
struct ctx_switch_entry *field;
1369+
1370+
trace_assign_type(field, entry);
13681371

13691372
T = field->next_state < sizeof(state_to_char) ?
13701373
state_to_char[field->next_state] : 'X';
@@ -1384,7 +1387,9 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
13841387
break;
13851388
}
13861389
case TRACE_SPECIAL: {
1387-
struct special_entry *field = (struct special_entry *)entry;
1390+
struct special_entry *field;
1391+
1392+
trace_assign_type(field, entry);
13881393

13891394
trace_seq_printf(s, "# %ld %ld %ld\n",
13901395
field->arg1,
@@ -1393,7 +1398,9 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
13931398
break;
13941399
}
13951400
case TRACE_STACK: {
1396-
struct stack_entry *field = (struct stack_entry *)entry;
1401+
struct stack_entry *field;
1402+
1403+
trace_assign_type(field, entry);
13971404

13981405
for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
13991406
if (i)
@@ -1404,7 +1411,9 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
14041411
break;
14051412
}
14061413
case TRACE_PRINT: {
1407-
struct print_entry *field = (struct print_entry *)entry;
1414+
struct print_entry *field;
1415+
1416+
trace_assign_type(field, entry);
14081417

14091418
seq_print_ip_sym(s, field->ip, sym_flags);
14101419
trace_seq_printf(s, ": %s", field->buf);
@@ -1454,7 +1463,9 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
14541463

14551464
switch (entry->type) {
14561465
case TRACE_FN: {
1457-
struct ftrace_entry *field = (struct ftrace_entry *)entry;
1466+
struct ftrace_entry *field;
1467+
1468+
trace_assign_type(field, entry);
14581469

14591470
ret = seq_print_ip_sym(s, field->ip, sym_flags);
14601471
if (!ret)
@@ -1480,8 +1491,9 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
14801491
}
14811492
case TRACE_CTX:
14821493
case TRACE_WAKE: {
1483-
struct ctx_switch_entry *field =
1484-
(struct ctx_switch_entry *)entry;
1494+
struct ctx_switch_entry *field;
1495+
1496+
trace_assign_type(field, entry);
14851497

14861498
S = field->prev_state < sizeof(state_to_char) ?
14871499
state_to_char[field->prev_state] : 'X';
@@ -1501,7 +1513,9 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
15011513
break;
15021514
}
15031515
case TRACE_SPECIAL: {
1504-
struct special_entry *field = (struct special_entry *)entry;
1516+
struct special_entry *field;
1517+
1518+
trace_assign_type(field, entry);
15051519

15061520
ret = trace_seq_printf(s, "# %ld %ld %ld\n",
15071521
field->arg1,
@@ -1512,7 +1526,9 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
15121526
break;
15131527
}
15141528
case TRACE_STACK: {
1515-
struct stack_entry *field = (struct stack_entry *)entry;
1529+
struct stack_entry *field;
1530+
1531+
trace_assign_type(field, entry);
15161532

15171533
for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
15181534
if (i) {
@@ -1531,7 +1547,9 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
15311547
break;
15321548
}
15331549
case TRACE_PRINT: {
1534-
struct print_entry *field = (struct print_entry *)entry;
1550+
struct print_entry *field;
1551+
1552+
trace_assign_type(field, entry);
15351553

15361554
seq_print_ip_sym(s, field->ip, sym_flags);
15371555
trace_seq_printf(s, ": %s", field->buf);
@@ -1562,7 +1580,9 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
15621580

15631581
switch (entry->type) {
15641582
case TRACE_FN: {
1565-
struct ftrace_entry *field = (struct ftrace_entry *)entry;
1583+
struct ftrace_entry *field;
1584+
1585+
trace_assign_type(field, entry);
15661586

15671587
ret = trace_seq_printf(s, "%x %x\n",
15681588
field->ip,
@@ -1573,8 +1593,9 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
15731593
}
15741594
case TRACE_CTX:
15751595
case TRACE_WAKE: {
1576-
struct ctx_switch_entry *field =
1577-
(struct ctx_switch_entry *)entry;
1596+
struct ctx_switch_entry *field;
1597+
1598+
trace_assign_type(field, entry);
15781599

15791600
S = field->prev_state < sizeof(state_to_char) ?
15801601
state_to_char[field->prev_state] : 'X';
@@ -1596,7 +1617,9 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
15961617
}
15971618
case TRACE_SPECIAL:
15981619
case TRACE_STACK: {
1599-
struct special_entry *field = (struct special_entry *)entry;
1620+
struct special_entry *field;
1621+
1622+
trace_assign_type(field, entry);
16001623

16011624
ret = trace_seq_printf(s, "# %ld %ld %ld\n",
16021625
field->arg1,
@@ -1607,7 +1630,9 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
16071630
break;
16081631
}
16091632
case TRACE_PRINT: {
1610-
struct print_entry *field = (struct print_entry *)entry;
1633+
struct print_entry *field;
1634+
1635+
trace_assign_type(field, entry);
16111636

16121637
trace_seq_printf(s, "# %lx %s", field->ip, field->buf);
16131638
if (entry->flags & TRACE_FLAG_CONT)
@@ -1648,16 +1673,19 @@ static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
16481673

16491674
switch (entry->type) {
16501675
case TRACE_FN: {
1651-
struct ftrace_entry *field = (struct ftrace_entry *)entry;
1676+
struct ftrace_entry *field;
1677+
1678+
trace_assign_type(field, entry);
16521679

16531680
SEQ_PUT_HEX_FIELD_RET(s, field->ip);
16541681
SEQ_PUT_HEX_FIELD_RET(s, field->parent_ip);
16551682
break;
16561683
}
16571684
case TRACE_CTX:
16581685
case TRACE_WAKE: {
1659-
struct ctx_switch_entry *field =
1660-
(struct ctx_switch_entry *)entry;
1686+
struct ctx_switch_entry *field;
1687+
1688+
trace_assign_type(field, entry);
16611689

16621690
S = field->prev_state < sizeof(state_to_char) ?
16631691
state_to_char[field->prev_state] : 'X';
@@ -1676,7 +1704,9 @@ static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
16761704
}
16771705
case TRACE_SPECIAL:
16781706
case TRACE_STACK: {
1679-
struct special_entry *field = (struct special_entry *)entry;
1707+
struct special_entry *field;
1708+
1709+
trace_assign_type(field, entry);
16801710

16811711
SEQ_PUT_HEX_FIELD_RET(s, field->arg1);
16821712
SEQ_PUT_HEX_FIELD_RET(s, field->arg2);
@@ -1705,15 +1735,18 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
17051735

17061736
switch (entry->type) {
17071737
case TRACE_FN: {
1708-
struct ftrace_entry *field = (struct ftrace_entry *)entry;
1738+
struct ftrace_entry *field;
1739+
1740+
trace_assign_type(field, entry);
17091741

17101742
SEQ_PUT_FIELD_RET(s, field->ip);
17111743
SEQ_PUT_FIELD_RET(s, field->parent_ip);
17121744
break;
17131745
}
17141746
case TRACE_CTX: {
1715-
struct ctx_switch_entry *field =
1716-
(struct ctx_switch_entry *)entry;
1747+
struct ctx_switch_entry *field;
1748+
1749+
trace_assign_type(field, entry);
17171750

17181751
SEQ_PUT_FIELD_RET(s, field->prev_pid);
17191752
SEQ_PUT_FIELD_RET(s, field->prev_prio);
@@ -1725,7 +1758,9 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
17251758
}
17261759
case TRACE_SPECIAL:
17271760
case TRACE_STACK: {
1728-
struct special_entry *field = (struct special_entry *)entry;
1761+
struct special_entry *field;
1762+
1763+
trace_assign_type(field, entry);
17291764

17301765
SEQ_PUT_FIELD_RET(s, field->arg1);
17311766
SEQ_PUT_FIELD_RET(s, field->arg2);

kernel/trace/trace.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,48 @@ struct trace_array {
177177
struct trace_array_cpu *data[NR_CPUS];
178178
};
179179

180+
#define FTRACE_CMP_TYPE(var, type) \
181+
__builtin_types_compatible_p(typeof(var), type *)
182+
183+
#undef IF_ASSIGN
184+
#define IF_ASSIGN(var, entry, etype, id) \
185+
if (FTRACE_CMP_TYPE(var, etype)) { \
186+
var = (typeof(var))(entry); \
187+
WARN_ON(id && (entry)->type != id); \
188+
break; \
189+
}
190+
191+
/* Will cause compile errors if type is not found. */
192+
extern void __ftrace_bad_type(void);
193+
194+
/*
195+
* The trace_assign_type is a verifier that the entry type is
196+
* the same as the type being assigned. To add new types simply
197+
* add a line with the following format:
198+
*
199+
* IF_ASSIGN(var, ent, type, id);
200+
*
201+
* Where "type" is the trace type that includes the trace_entry
202+
* as the "ent" item. And "id" is the trace identifier that is
203+
* used in the trace_type enum.
204+
*
205+
* If the type can have more than one id, then use zero.
206+
*/
207+
#define trace_assign_type(var, ent) \
208+
do { \
209+
IF_ASSIGN(var, ent, struct ftrace_entry, TRACE_FN); \
210+
IF_ASSIGN(var, ent, struct ctx_switch_entry, 0); \
211+
IF_ASSIGN(var, ent, struct trace_field_cont, TRACE_CONT); \
212+
IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \
213+
IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \
214+
IF_ASSIGN(var, ent, struct special_entry, 0); \
215+
IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \
216+
TRACE_MMIO_RW); \
217+
IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \
218+
TRACE_MMIO_MAP); \
219+
IF_ASSIGN(var, ent, struct trace_boot, TRACE_BOOT); \
220+
__ftrace_bad_type(); \
221+
} while (0)
180222

181223
/* Return values for print_line callback */
182224
enum print_line_t {

kernel/trace/trace_mmiotrace.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,17 @@ static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp,
178178
static enum print_line_t mmio_print_rw(struct trace_iterator *iter)
179179
{
180180
struct trace_entry *entry = iter->ent;
181-
struct trace_mmiotrace_rw *field =
182-
(struct trace_mmiotrace_rw *)entry;
183-
struct mmiotrace_rw *rw = &field->rw;
181+
struct trace_mmiotrace_rw *field;
182+
struct mmiotrace_rw *rw;
184183
struct trace_seq *s = &iter->seq;
185184
unsigned long long t = ns2usecs(iter->ts);
186185
unsigned long usec_rem = do_div(t, 1000000ULL);
187186
unsigned secs = (unsigned long)t;
188187
int ret = 1;
189188

189+
trace_assign_type(field, entry);
190+
rw = &field->rw;
191+
190192
switch (rw->opcode) {
191193
case MMIO_READ:
192194
ret = trace_seq_printf(s,
@@ -222,13 +224,17 @@ static enum print_line_t mmio_print_rw(struct trace_iterator *iter)
222224
static enum print_line_t mmio_print_map(struct trace_iterator *iter)
223225
{
224226
struct trace_entry *entry = iter->ent;
225-
struct mmiotrace_map *m = (struct mmiotrace_map *)entry;
227+
struct trace_mmiotrace_map *field;
228+
struct mmiotrace_map *m;
226229
struct trace_seq *s = &iter->seq;
227230
unsigned long long t = ns2usecs(iter->ts);
228231
unsigned long usec_rem = do_div(t, 1000000ULL);
229232
unsigned secs = (unsigned long)t;
230233
int ret;
231234

235+
trace_assign_type(field, entry);
236+
m = &field->map;
237+
232238
switch (m->opcode) {
233239
case MMIO_PROBE:
234240
ret = trace_seq_printf(s,

0 commit comments

Comments
 (0)