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 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 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 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