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