1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
|
#include "inc.h"
#include <sys/ioctl.h>
static char ioctlbuf[IOCPARM_MASK];
static const struct {
const char *(*name)(unsigned long);
int (*arg)(struct trace_proc *, unsigned long, void *, int);
int is_svrctl;
} ioctl_table[] = {
{ block_ioctl_name, block_ioctl_arg, FALSE },
{ char_ioctl_name, char_ioctl_arg, FALSE },
{ net_ioctl_name, net_ioctl_arg, FALSE },
{ svrctl_name, svrctl_arg, TRUE },
};
/*
* Print an IOCTL request code, and save certain values in the corresponding
* process structure in order to be able to print the IOCTL argument.
*/
void
put_ioctl_req(struct trace_proc * proc, const char * name, unsigned long req,
int is_svrctl)
{
const char *text;
size_t size;
unsigned int i, group, cmd;
int r, w, big;
proc->ioctl_index = -1;
if (valuesonly > 1) {
put_value(proc, name, "0x%lx", req);
return;
}
/*
* Lookups are bruteforce across the IOCTL submodules; they're all
* checked. We could use the group letter but that would create more
* issues than it solves. Our hope is that at least the compiler is
* smart about looking up particular codes in each switch statement,
* although in the worst case, it's a full O(n) lookup.
*/
for (i = 0; i < COUNT(ioctl_table); i++) {
/* IOCTLs and SVRCTLs are considered different name spaces. */
if (ioctl_table[i].is_svrctl != is_svrctl)
continue;
if ((text = ioctl_table[i].name(req)) != NULL) {
proc->ioctl_index = i;
if (valuesonly)
break;
put_field(proc, name, text);
return;
}
}
r = _MINIX_IOCTL_IOR(req);
w = _MINIX_IOCTL_IOW(req);
big = _MINIX_IOCTL_BIG(req);
size = (size_t)(big ? _MINIX_IOCTL_SIZE_BIG(req) : IOCPARM_LEN(req));
group = big ? 0 : IOCGROUP(req);
cmd = req & 0xff; /* shockingly there is no macro for this.. */
/*
* Not sure why an entire bit is wasted on IOC_VOID (legacy reasons?),
* but since the redundancy is there, we might as well check whether
* this is a valid IOCTL request. Also, we expect the group to be a
* printable character. If either check fails, print just a number.
*/
if (((req & IOC_VOID) && (r || w || big || size > 0)) ||
(!(req & IOC_VOID) && ((!r && !w) || size == 0)) ||
(!big && (group < 32 || group > 127))) {
put_value(proc, name, "0x%lx", req);
return;
}
if (big) {
/* For big IOCTLs, "R" becomes before "W" (old MINIX style). */
put_value(proc, name, "_IO%s%s_BIG(%u,%zu)",
r ? "R" : "", w ? "W" : "", cmd, size);
} else if (IOCGROUP(req) >= 32 && IOCGROUP(req) < 127) {
/* For normal IOCTLs, "W" comes before "R" (NetBSD style). */
put_value(proc, name, "_IO%s%s('%c',%u,%zu)",
w ? "W" : "", r ? "R" : "", group, cmd, size);
}
}
/*
* Print the supplied (out) part of an IOCTL argument, as applicable. For
* efficiency reasons, this function assumes that put_ioctl_req() has been
* called for the corresponding IOCTL already, so that the necessary fields in
* the given proc structure are set as expected.
*/
int
put_ioctl_arg_out(struct trace_proc * proc, const char * name,
unsigned long req, vir_bytes addr, int is_svrctl)
{
size_t size;
int dir, all;
dir = (_MINIX_IOCTL_IOW(req) ? IF_OUT : 0) |
(_MINIX_IOCTL_IOR(req) ? IF_IN : 0);
if (dir == 0) {
proc->ioctl_index = -1; /* no argument to print at all */
return CT_DONE;
}
/* No support for printing big-IOCTL contents just yet. */
if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
proc->ioctl_index == -1) {
put_ptr(proc, name, addr);
return CT_DONE;
}
assert(proc->ioctl_index >= 0);
assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table));
assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl);
proc->ioctl_flags =
ioctl_table[proc->ioctl_index].arg(proc, req, NULL, dir);
if (proc->ioctl_flags == 0) { /* no argument printing for this IOCTL */
put_ptr(proc, name, addr);
proc->ioctl_index = -1; /* forget about the IOCTL handler */
return CT_DONE;
}
/*
* If this triggers, the IOCTL handler returns a direction that is not
* part of the actual IOCTL, and the handler should be fixed.
*/
if (proc->ioctl_flags & ~dir) {
output_flush(); /* show the IOCTL name for debugging */
assert(0);
}
if (!(proc->ioctl_flags & IF_OUT))
return CT_NOTDONE;
size = IOCPARM_LEN(req);
if (size > sizeof(ioctlbuf) ||
mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) {
put_ptr(proc, name, addr);
/* There's no harm in trying the _in side later anyhow.. */
return CT_DONE;
}
put_open(proc, name, 0, "{", ", ");
all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_OUT);
if (!all)
put_field(proc, NULL, "..");
put_close(proc, "}");
return CT_DONE;
}
/*
* Print the returned (in) part of an IOCTL argument, as applicable. This
* function assumes that it is preceded by a call to put_ioctl_arg_out for this
* process.
*/
void
put_ioctl_arg_in(struct trace_proc * proc, const char * name, int failed,
unsigned long req, vir_bytes addr, int is_svrctl)
{
size_t size;
int all;
if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
proc->ioctl_index == -1) {
put_result(proc);
return;
}
assert(proc->ioctl_index >= 0);
assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table));
assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl);
assert(proc->ioctl_flags != 0);
if (proc->ioctl_flags & IF_OUT)
put_result(proc);
if (!(proc->ioctl_flags & IF_IN))
return;
size = IOCPARM_LEN(req);
if (failed || size > sizeof(ioctlbuf) ||
mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) {
if (!(proc->ioctl_flags & IF_OUT)) {
put_ptr(proc, name, addr);
put_equals(proc);
put_result(proc);
} else if (!failed)
put_field(proc, NULL, "{..}");
return;
}
put_open(proc, name, 0, "{", ", ");
all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_IN);
if (!all)
put_field(proc, NULL, "..");
put_close(proc, "}");
if (!(proc->ioctl_flags & IF_OUT)) {
put_equals(proc);
put_result(proc);
}
}
|