Loading...
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 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | // SPDX-License-Identifier: GPL-2.0 /* * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) * * These routines maintain argument size conversion between 32bit and 64bit * ioctls. */ #include <linux/types.h> #include <linux/compat.h> #include <linux/kernel.h> #include <linux/capability.h> #include <linux/compiler.h> #include <linux/sched.h> #include <linux/smp.h> #include <linux/ioctl.h> #include <linux/if.h> #include <linux/raid/md_u.h> #include <linux/falloc.h> #include <linux/file.h> #include <linux/ppp-ioctl.h> #include <linux/if_pppox.h> #include <linux/tty.h> #include <linux/vt_kern.h> #include <linux/blkdev.h> #include <linux/serial.h> #include <linux/ctype.h> #include <linux/syscalls.h> #include <linux/gfp.h> #include <linux/cec.h> #include "internal.h" #ifdef CONFIG_BLOCK #include <linux/cdrom.h> #include <linux/fd.h> #include <scsi/scsi.h> #include <scsi/scsi_ioctl.h> #include <scsi/sg.h> #endif #include <linux/uaccess.h> #include <linux/watchdog.h> #include <linux/hiddev.h> #include <linux/sort.h> /* * simple reversible transform to make our table more evenly * distributed after sorting. */ #define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff) #define COMPATIBLE_IOCTL(cmd) XFORM((u32)cmd), static unsigned int ioctl_pointer[] = { #ifdef CONFIG_BLOCK /* Big S */ COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN) COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK) COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK) COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY) COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER) COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND) COMPATIBLE_IOCTL(SCSI_IOCTL_PROBE_HOST) COMPATIBLE_IOCTL(SCSI_IOCTL_GET_PCI) #endif #ifdef CONFIG_BLOCK /* SG stuff */ COMPATIBLE_IOCTL(SG_IO) COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE) COMPATIBLE_IOCTL(SG_SET_TIMEOUT) COMPATIBLE_IOCTL(SG_GET_TIMEOUT) COMPATIBLE_IOCTL(SG_EMULATED_HOST) COMPATIBLE_IOCTL(SG_GET_TRANSFORM) COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE) COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE) COMPATIBLE_IOCTL(SG_GET_SCSI_ID) COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA) COMPATIBLE_IOCTL(SG_GET_LOW_DMA) COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID) COMPATIBLE_IOCTL(SG_GET_PACK_ID) COMPATIBLE_IOCTL(SG_GET_NUM_WAITING) COMPATIBLE_IOCTL(SG_SET_DEBUG) COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE) COMPATIBLE_IOCTL(SG_GET_COMMAND_Q) COMPATIBLE_IOCTL(SG_SET_COMMAND_Q) COMPATIBLE_IOCTL(SG_GET_VERSION_NUM) COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN) COMPATIBLE_IOCTL(SG_SCSI_RESET) COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE) COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN) COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN) #endif }; /* * Convert common ioctl arguments based on their command number * * Please do not add any code in here. Instead, implement * a compat_ioctl operation in the place that handleѕ the * ioctl for the native case. */ static long do_ioctl_trans(unsigned int cmd, unsigned long arg, struct file *file) { return -ENOIOCTLCMD; } static int compat_ioctl_check_table(unsigned int xcmd) { #ifdef CONFIG_BLOCK int i; const int max = ARRAY_SIZE(ioctl_pointer) - 1; BUILD_BUG_ON(max >= (1 << 16)); /* guess initial offset into table, assuming a normalized distribution */ i = ((xcmd >> 16) * max) >> 16; /* do linear search up first, until greater or equal */ while (ioctl_pointer[i] < xcmd && i < max) i++; /* then do linear search down */ while (ioctl_pointer[i] > xcmd && i > 0) i--; return ioctl_pointer[i] == xcmd; #else return 0; #endif } COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, compat_ulong_t, arg32) { unsigned long arg = arg32; struct fd f = fdget(fd); int error = -EBADF; if (!f.file) goto out; /* RED-PEN how should LSM module know it's handling 32bit? */ error = security_file_ioctl(f.file, cmd, arg); if (error) goto out_fput; switch (cmd) { /* these are never seen by ->ioctl(), no argument or int argument */ case FIOCLEX: case FIONCLEX: case FIFREEZE: case FITHAW: case FICLONE: goto do_ioctl; /* these are never seen by ->ioctl(), pointer argument */ case FIONBIO: case FIOASYNC: case FIOQSIZE: case FS_IOC_FIEMAP: case FIGETBSZ: case FICLONERANGE: case FIDEDUPERANGE: goto found_handler; /* * The next group is the stuff handled inside file_ioctl(). * For regular files these never reach ->ioctl(); for * devices, sockets, etc. they do and one (FIONREAD) is * even accepted in some cases. In all those cases * argument has the same type, so we can handle these * here, shunting them towards do_vfs_ioctl(). * ->compat_ioctl() will never see any of those. */ /* pointer argument, never actually handled by ->ioctl() */ case FIBMAP: goto found_handler; /* handled by some ->ioctl(); always a pointer to int */ case FIONREAD: goto found_handler; /* these get messy on amd64 due to alignment differences */ #if defined(CONFIG_X86_64) case FS_IOC_RESVSP_32: case FS_IOC_RESVSP64_32: error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg)); goto out_fput; case FS_IOC_UNRESVSP_32: case FS_IOC_UNRESVSP64_32: error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE, compat_ptr(arg)); goto out_fput; case FS_IOC_ZERO_RANGE_32: error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE, compat_ptr(arg)); goto out_fput; #else case FS_IOC_RESVSP: case FS_IOC_RESVSP64: case FS_IOC_UNRESVSP: case FS_IOC_UNRESVSP64: case FS_IOC_ZERO_RANGE: goto found_handler; #endif default: if (f.file->f_op->compat_ioctl) { error = f.file->f_op->compat_ioctl(f.file, cmd, arg); if (error != -ENOIOCTLCMD) goto out_fput; } if (!f.file->f_op->unlocked_ioctl) goto do_ioctl; break; } if (compat_ioctl_check_table(XFORM(cmd))) goto found_handler; error = do_ioctl_trans(cmd, arg, f.file); if (error == -ENOIOCTLCMD) error = -ENOTTY; goto out_fput; found_handler: arg = (unsigned long)compat_ptr(arg); do_ioctl: error = do_vfs_ioctl(f.file, fd, cmd, arg); out_fput: fdput(f); out: return error; } static int __init init_sys32_ioctl_cmp(const void *p, const void *q) { unsigned int a, b; a = *(unsigned int *)p; b = *(unsigned int *)q; if (a > b) return 1; if (a < b) return -1; return 0; } static int __init init_sys32_ioctl(void) { sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer), init_sys32_ioctl_cmp, NULL); return 0; } __initcall(init_sys32_ioctl); |