Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
4 *
5 * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
6 * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
7 * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
8 * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz)
9 *
10 * These routines maintain argument size conversion between 32bit and 64bit
11 * ioctls.
12 */
13
14#include <linux/types.h>
15#include <linux/compat.h>
16#include <linux/kernel.h>
17#include <linux/capability.h>
18#include <linux/compiler.h>
19#include <linux/sched.h>
20#include <linux/smp.h>
21#include <linux/ioctl.h>
22#include <linux/if.h>
23#include <linux/raid/md_u.h>
24#include <linux/falloc.h>
25#include <linux/file.h>
26#include <linux/ppp-ioctl.h>
27#include <linux/if_pppox.h>
28#include <linux/tty.h>
29#include <linux/vt_kern.h>
30#include <linux/blkdev.h>
31#include <linux/serial.h>
32#include <linux/ctype.h>
33#include <linux/syscalls.h>
34#include <linux/gfp.h>
35#include <linux/cec.h>
36
37#include "internal.h"
38
39#ifdef CONFIG_BLOCK
40#include <linux/cdrom.h>
41#include <linux/fd.h>
42#include <scsi/scsi.h>
43#include <scsi/scsi_ioctl.h>
44#include <scsi/sg.h>
45#endif
46
47#include <linux/uaccess.h>
48#include <linux/watchdog.h>
49
50#include <linux/hiddev.h>
51
52
53#include <linux/sort.h>
54
55/*
56 * simple reversible transform to make our table more evenly
57 * distributed after sorting.
58 */
59#define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff)
60
61#define COMPATIBLE_IOCTL(cmd) XFORM((u32)cmd),
62static unsigned int ioctl_pointer[] = {
63#ifdef CONFIG_BLOCK
64/* Big S */
65COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN)
66COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK)
67COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK)
68COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY)
69COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER)
70COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND)
71COMPATIBLE_IOCTL(SCSI_IOCTL_PROBE_HOST)
72COMPATIBLE_IOCTL(SCSI_IOCTL_GET_PCI)
73#endif
74#ifdef CONFIG_BLOCK
75/* SG stuff */
76COMPATIBLE_IOCTL(SG_IO)
77COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE)
78COMPATIBLE_IOCTL(SG_SET_TIMEOUT)
79COMPATIBLE_IOCTL(SG_GET_TIMEOUT)
80COMPATIBLE_IOCTL(SG_EMULATED_HOST)
81COMPATIBLE_IOCTL(SG_GET_TRANSFORM)
82COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE)
83COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE)
84COMPATIBLE_IOCTL(SG_GET_SCSI_ID)
85COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA)
86COMPATIBLE_IOCTL(SG_GET_LOW_DMA)
87COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID)
88COMPATIBLE_IOCTL(SG_GET_PACK_ID)
89COMPATIBLE_IOCTL(SG_GET_NUM_WAITING)
90COMPATIBLE_IOCTL(SG_SET_DEBUG)
91COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE)
92COMPATIBLE_IOCTL(SG_GET_COMMAND_Q)
93COMPATIBLE_IOCTL(SG_SET_COMMAND_Q)
94COMPATIBLE_IOCTL(SG_GET_VERSION_NUM)
95COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN)
96COMPATIBLE_IOCTL(SG_SCSI_RESET)
97COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE)
98COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN)
99COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN)
100#endif
101};
102
103/*
104 * Convert common ioctl arguments based on their command number
105 *
106 * Please do not add any code in here. Instead, implement
107 * a compat_ioctl operation in the place that handleѕ the
108 * ioctl for the native case.
109 */
110static long do_ioctl_trans(unsigned int cmd,
111 unsigned long arg, struct file *file)
112{
113 return -ENOIOCTLCMD;
114}
115
116static int compat_ioctl_check_table(unsigned int xcmd)
117{
118#ifdef CONFIG_BLOCK
119 int i;
120 const int max = ARRAY_SIZE(ioctl_pointer) - 1;
121
122 BUILD_BUG_ON(max >= (1 << 16));
123
124 /* guess initial offset into table, assuming a
125 normalized distribution */
126 i = ((xcmd >> 16) * max) >> 16;
127
128 /* do linear search up first, until greater or equal */
129 while (ioctl_pointer[i] < xcmd && i < max)
130 i++;
131
132 /* then do linear search down */
133 while (ioctl_pointer[i] > xcmd && i > 0)
134 i--;
135
136 return ioctl_pointer[i] == xcmd;
137#else
138 return 0;
139#endif
140}
141
142COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
143 compat_ulong_t, arg32)
144{
145 unsigned long arg = arg32;
146 struct fd f = fdget(fd);
147 int error = -EBADF;
148 if (!f.file)
149 goto out;
150
151 /* RED-PEN how should LSM module know it's handling 32bit? */
152 error = security_file_ioctl(f.file, cmd, arg);
153 if (error)
154 goto out_fput;
155
156 switch (cmd) {
157 /* these are never seen by ->ioctl(), no argument or int argument */
158 case FIOCLEX:
159 case FIONCLEX:
160 case FIFREEZE:
161 case FITHAW:
162 case FICLONE:
163 goto do_ioctl;
164 /* these are never seen by ->ioctl(), pointer argument */
165 case FIONBIO:
166 case FIOASYNC:
167 case FIOQSIZE:
168 case FS_IOC_FIEMAP:
169 case FIGETBSZ:
170 case FICLONERANGE:
171 case FIDEDUPERANGE:
172 goto found_handler;
173 /*
174 * The next group is the stuff handled inside file_ioctl().
175 * For regular files these never reach ->ioctl(); for
176 * devices, sockets, etc. they do and one (FIONREAD) is
177 * even accepted in some cases. In all those cases
178 * argument has the same type, so we can handle these
179 * here, shunting them towards do_vfs_ioctl().
180 * ->compat_ioctl() will never see any of those.
181 */
182 /* pointer argument, never actually handled by ->ioctl() */
183 case FIBMAP:
184 goto found_handler;
185 /* handled by some ->ioctl(); always a pointer to int */
186 case FIONREAD:
187 goto found_handler;
188 /* these get messy on amd64 due to alignment differences */
189#if defined(CONFIG_X86_64)
190 case FS_IOC_RESVSP_32:
191 case FS_IOC_RESVSP64_32:
192 error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg));
193 goto out_fput;
194 case FS_IOC_UNRESVSP_32:
195 case FS_IOC_UNRESVSP64_32:
196 error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE,
197 compat_ptr(arg));
198 goto out_fput;
199 case FS_IOC_ZERO_RANGE_32:
200 error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE,
201 compat_ptr(arg));
202 goto out_fput;
203#else
204 case FS_IOC_RESVSP:
205 case FS_IOC_RESVSP64:
206 case FS_IOC_UNRESVSP:
207 case FS_IOC_UNRESVSP64:
208 case FS_IOC_ZERO_RANGE:
209 goto found_handler;
210#endif
211
212 default:
213 if (f.file->f_op->compat_ioctl) {
214 error = f.file->f_op->compat_ioctl(f.file, cmd, arg);
215 if (error != -ENOIOCTLCMD)
216 goto out_fput;
217 }
218
219 if (!f.file->f_op->unlocked_ioctl)
220 goto do_ioctl;
221 break;
222 }
223
224 if (compat_ioctl_check_table(XFORM(cmd)))
225 goto found_handler;
226
227 error = do_ioctl_trans(cmd, arg, f.file);
228 if (error == -ENOIOCTLCMD)
229 error = -ENOTTY;
230
231 goto out_fput;
232
233 found_handler:
234 arg = (unsigned long)compat_ptr(arg);
235 do_ioctl:
236 error = do_vfs_ioctl(f.file, fd, cmd, arg);
237 out_fput:
238 fdput(f);
239 out:
240 return error;
241}
242
243static int __init init_sys32_ioctl_cmp(const void *p, const void *q)
244{
245 unsigned int a, b;
246 a = *(unsigned int *)p;
247 b = *(unsigned int *)q;
248 if (a > b)
249 return 1;
250 if (a < b)
251 return -1;
252 return 0;
253}
254
255static int __init init_sys32_ioctl(void)
256{
257 sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer),
258 init_sys32_ioctl_cmp, NULL);
259 return 0;
260}
261__initcall(init_sys32_ioctl);