• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     comedi/comedi_compat32.c
3     32-bit ioctl compatibility for 64-bit comedi kernel module.
4 
5     Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
6     Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
7 
8     COMEDI - Linux Control and Measurement Device Interface
9     Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
10 
11     This program is free software; you can redistribute it and/or modify
12     it under the terms of the GNU General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU General Public License for more details.
20 */
21 
22 #include <linux/uaccess.h>
23 #include <linux/compat.h>
24 #include <linux/fs.h>
25 #include "comedi.h"
26 #include "comedi_compat32.h"
27 
28 #define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
29 #define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
30 /* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
31  * It's too late to change it now, but it only affects the command number. */
32 #define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
33 /* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
34  * It's too late to change it now, but it only affects the command number. */
35 #define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
36 #define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
37 #define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
38 
39 struct comedi32_chaninfo_struct {
40 	unsigned int subdev;
41 	compat_uptr_t maxdata_list;	/* 32-bit 'unsigned int *' */
42 	compat_uptr_t flaglist;	/* 32-bit 'unsigned int *' */
43 	compat_uptr_t rangelist;	/* 32-bit 'unsigned int *' */
44 	unsigned int unused[4];
45 };
46 
47 struct comedi32_rangeinfo_struct {
48 	unsigned int range_type;
49 	compat_uptr_t range_ptr;	/* 32-bit 'void *' */
50 };
51 
52 struct comedi32_cmd_struct {
53 	unsigned int subdev;
54 	unsigned int flags;
55 	unsigned int start_src;
56 	unsigned int start_arg;
57 	unsigned int scan_begin_src;
58 	unsigned int scan_begin_arg;
59 	unsigned int convert_src;
60 	unsigned int convert_arg;
61 	unsigned int scan_end_src;
62 	unsigned int scan_end_arg;
63 	unsigned int stop_src;
64 	unsigned int stop_arg;
65 	compat_uptr_t chanlist;	/* 32-bit 'unsigned int *' */
66 	unsigned int chanlist_len;
67 	compat_uptr_t data;	/* 32-bit 'short *' */
68 	unsigned int data_len;
69 };
70 
71 struct comedi32_insn_struct {
72 	unsigned int insn;
73 	unsigned int n;
74 	compat_uptr_t data;	/* 32-bit 'unsigned int *' */
75 	unsigned int subdev;
76 	unsigned int chanspec;
77 	unsigned int unused[3];
78 };
79 
80 struct comedi32_insnlist_struct {
81 	unsigned int n_insns;
82 	compat_uptr_t insns;	/* 32-bit 'struct comedi_insn *' */
83 };
84 
85 /* Handle translated ioctl. */
translated_ioctl(struct file * file,unsigned int cmd,unsigned long arg)86 static int translated_ioctl(struct file *file, unsigned int cmd,
87 			    unsigned long arg)
88 {
89 	if (file->f_op->unlocked_ioctl)
90 		return file->f_op->unlocked_ioctl(file, cmd, arg);
91 
92 	return -ENOTTY;
93 }
94 
95 /* Handle 32-bit COMEDI_CHANINFO ioctl. */
compat_chaninfo(struct file * file,unsigned long arg)96 static int compat_chaninfo(struct file *file, unsigned long arg)
97 {
98 	struct comedi_chaninfo __user *chaninfo;
99 	struct comedi32_chaninfo_struct __user *chaninfo32;
100 	int err;
101 	union {
102 		unsigned int uint;
103 		compat_uptr_t uptr;
104 	} temp;
105 
106 	chaninfo32 = compat_ptr(arg);
107 	chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
108 
109 	/* Copy chaninfo structure.  Ignore unused members. */
110 	if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32)) ||
111 	    !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo)))
112 		return -EFAULT;
113 
114 	err = 0;
115 	err |= __get_user(temp.uint, &chaninfo32->subdev);
116 	err |= __put_user(temp.uint, &chaninfo->subdev);
117 	err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
118 	err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
119 	err |= __get_user(temp.uptr, &chaninfo32->flaglist);
120 	err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
121 	err |= __get_user(temp.uptr, &chaninfo32->rangelist);
122 	err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
123 	if (err)
124 		return -EFAULT;
125 
126 	return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
127 }
128 
129 /* Handle 32-bit COMEDI_RANGEINFO ioctl. */
compat_rangeinfo(struct file * file,unsigned long arg)130 static int compat_rangeinfo(struct file *file, unsigned long arg)
131 {
132 	struct comedi_rangeinfo __user *rangeinfo;
133 	struct comedi32_rangeinfo_struct __user *rangeinfo32;
134 	int err;
135 	union {
136 		unsigned int uint;
137 		compat_uptr_t uptr;
138 	} temp;
139 
140 	rangeinfo32 = compat_ptr(arg);
141 	rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
142 
143 	/* Copy rangeinfo structure. */
144 	if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32)) ||
145 	    !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo)))
146 		return -EFAULT;
147 
148 	err = 0;
149 	err |= __get_user(temp.uint, &rangeinfo32->range_type);
150 	err |= __put_user(temp.uint, &rangeinfo->range_type);
151 	err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
152 	err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
153 	if (err)
154 		return -EFAULT;
155 
156 	return translated_ioctl(file, COMEDI_RANGEINFO,
157 				(unsigned long)rangeinfo);
158 }
159 
160 /* Copy 32-bit cmd structure to native cmd structure. */
get_compat_cmd(struct comedi_cmd __user * cmd,struct comedi32_cmd_struct __user * cmd32)161 static int get_compat_cmd(struct comedi_cmd __user *cmd,
162 			  struct comedi32_cmd_struct __user *cmd32)
163 {
164 	int err;
165 	union {
166 		unsigned int uint;
167 		compat_uptr_t uptr;
168 	} temp;
169 
170 	/* Copy cmd structure. */
171 	if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32)) ||
172 	    !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd)))
173 		return -EFAULT;
174 
175 	err = 0;
176 	err |= __get_user(temp.uint, &cmd32->subdev);
177 	err |= __put_user(temp.uint, &cmd->subdev);
178 	err |= __get_user(temp.uint, &cmd32->flags);
179 	err |= __put_user(temp.uint, &cmd->flags);
180 	err |= __get_user(temp.uint, &cmd32->start_src);
181 	err |= __put_user(temp.uint, &cmd->start_src);
182 	err |= __get_user(temp.uint, &cmd32->start_arg);
183 	err |= __put_user(temp.uint, &cmd->start_arg);
184 	err |= __get_user(temp.uint, &cmd32->scan_begin_src);
185 	err |= __put_user(temp.uint, &cmd->scan_begin_src);
186 	err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
187 	err |= __put_user(temp.uint, &cmd->scan_begin_arg);
188 	err |= __get_user(temp.uint, &cmd32->convert_src);
189 	err |= __put_user(temp.uint, &cmd->convert_src);
190 	err |= __get_user(temp.uint, &cmd32->convert_arg);
191 	err |= __put_user(temp.uint, &cmd->convert_arg);
192 	err |= __get_user(temp.uint, &cmd32->scan_end_src);
193 	err |= __put_user(temp.uint, &cmd->scan_end_src);
194 	err |= __get_user(temp.uint, &cmd32->scan_end_arg);
195 	err |= __put_user(temp.uint, &cmd->scan_end_arg);
196 	err |= __get_user(temp.uint, &cmd32->stop_src);
197 	err |= __put_user(temp.uint, &cmd->stop_src);
198 	err |= __get_user(temp.uint, &cmd32->stop_arg);
199 	err |= __put_user(temp.uint, &cmd->stop_arg);
200 	err |= __get_user(temp.uptr, &cmd32->chanlist);
201 	err |= __put_user(compat_ptr(temp.uptr), &cmd->chanlist);
202 	err |= __get_user(temp.uint, &cmd32->chanlist_len);
203 	err |= __put_user(temp.uint, &cmd->chanlist_len);
204 	err |= __get_user(temp.uptr, &cmd32->data);
205 	err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
206 	err |= __get_user(temp.uint, &cmd32->data_len);
207 	err |= __put_user(temp.uint, &cmd->data_len);
208 	return err ? -EFAULT : 0;
209 }
210 
211 /* Copy native cmd structure to 32-bit cmd structure. */
put_compat_cmd(struct comedi32_cmd_struct __user * cmd32,struct comedi_cmd __user * cmd)212 static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
213 			  struct comedi_cmd __user *cmd)
214 {
215 	int err;
216 	unsigned int temp;
217 
218 	/* Copy back most of cmd structure. */
219 	/* Assume the pointer values are already valid. */
220 	/* (Could use ptr_to_compat() to set them, but that wasn't implemented
221 	 * until kernel version 2.6.11.) */
222 	if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) ||
223 	    !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32)))
224 		return -EFAULT;
225 
226 	err = 0;
227 	err |= __get_user(temp, &cmd->subdev);
228 	err |= __put_user(temp, &cmd32->subdev);
229 	err |= __get_user(temp, &cmd->flags);
230 	err |= __put_user(temp, &cmd32->flags);
231 	err |= __get_user(temp, &cmd->start_src);
232 	err |= __put_user(temp, &cmd32->start_src);
233 	err |= __get_user(temp, &cmd->start_arg);
234 	err |= __put_user(temp, &cmd32->start_arg);
235 	err |= __get_user(temp, &cmd->scan_begin_src);
236 	err |= __put_user(temp, &cmd32->scan_begin_src);
237 	err |= __get_user(temp, &cmd->scan_begin_arg);
238 	err |= __put_user(temp, &cmd32->scan_begin_arg);
239 	err |= __get_user(temp, &cmd->convert_src);
240 	err |= __put_user(temp, &cmd32->convert_src);
241 	err |= __get_user(temp, &cmd->convert_arg);
242 	err |= __put_user(temp, &cmd32->convert_arg);
243 	err |= __get_user(temp, &cmd->scan_end_src);
244 	err |= __put_user(temp, &cmd32->scan_end_src);
245 	err |= __get_user(temp, &cmd->scan_end_arg);
246 	err |= __put_user(temp, &cmd32->scan_end_arg);
247 	err |= __get_user(temp, &cmd->stop_src);
248 	err |= __put_user(temp, &cmd32->stop_src);
249 	err |= __get_user(temp, &cmd->stop_arg);
250 	err |= __put_user(temp, &cmd32->stop_arg);
251 	/* Assume chanlist pointer is unchanged. */
252 	err |= __get_user(temp, &cmd->chanlist_len);
253 	err |= __put_user(temp, &cmd32->chanlist_len);
254 	/* Assume data pointer is unchanged. */
255 	err |= __get_user(temp, &cmd->data_len);
256 	err |= __put_user(temp, &cmd32->data_len);
257 	return err ? -EFAULT : 0;
258 }
259 
260 /* Handle 32-bit COMEDI_CMD ioctl. */
compat_cmd(struct file * file,unsigned long arg)261 static int compat_cmd(struct file *file, unsigned long arg)
262 {
263 	struct comedi_cmd __user *cmd;
264 	struct comedi32_cmd_struct __user *cmd32;
265 	int rc, err;
266 
267 	cmd32 = compat_ptr(arg);
268 	cmd = compat_alloc_user_space(sizeof(*cmd));
269 
270 	rc = get_compat_cmd(cmd, cmd32);
271 	if (rc)
272 		return rc;
273 
274 	rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
275 	if (rc == -EAGAIN) {
276 		/* Special case: copy cmd back to user. */
277 		err = put_compat_cmd(cmd32, cmd);
278 		if (err)
279 			rc = err;
280 	}
281 
282 	return rc;
283 }
284 
285 /* Handle 32-bit COMEDI_CMDTEST ioctl. */
compat_cmdtest(struct file * file,unsigned long arg)286 static int compat_cmdtest(struct file *file, unsigned long arg)
287 {
288 	struct comedi_cmd __user *cmd;
289 	struct comedi32_cmd_struct __user *cmd32;
290 	int rc, err;
291 
292 	cmd32 = compat_ptr(arg);
293 	cmd = compat_alloc_user_space(sizeof(*cmd));
294 
295 	rc = get_compat_cmd(cmd, cmd32);
296 	if (rc)
297 		return rc;
298 
299 	rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
300 	if (rc < 0)
301 		return rc;
302 
303 	err = put_compat_cmd(cmd32, cmd);
304 	if (err)
305 		rc = err;
306 
307 	return rc;
308 }
309 
310 /* Copy 32-bit insn structure to native insn structure. */
get_compat_insn(struct comedi_insn __user * insn,struct comedi32_insn_struct __user * insn32)311 static int get_compat_insn(struct comedi_insn __user *insn,
312 			   struct comedi32_insn_struct __user *insn32)
313 {
314 	int err;
315 	union {
316 		unsigned int uint;
317 		compat_uptr_t uptr;
318 	} temp;
319 
320 	/* Copy insn structure.  Ignore the unused members. */
321 	err = 0;
322 	if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32)) ||
323 	    !access_ok(VERIFY_WRITE, insn, sizeof(*insn)))
324 		return -EFAULT;
325 
326 	err |= __get_user(temp.uint, &insn32->insn);
327 	err |= __put_user(temp.uint, &insn->insn);
328 	err |= __get_user(temp.uint, &insn32->n);
329 	err |= __put_user(temp.uint, &insn->n);
330 	err |= __get_user(temp.uptr, &insn32->data);
331 	err |= __put_user(compat_ptr(temp.uptr), &insn->data);
332 	err |= __get_user(temp.uint, &insn32->subdev);
333 	err |= __put_user(temp.uint, &insn->subdev);
334 	err |= __get_user(temp.uint, &insn32->chanspec);
335 	err |= __put_user(temp.uint, &insn->chanspec);
336 	return err ? -EFAULT : 0;
337 }
338 
339 /* Handle 32-bit COMEDI_INSNLIST ioctl. */
compat_insnlist(struct file * file,unsigned long arg)340 static int compat_insnlist(struct file *file, unsigned long arg)
341 {
342 	struct combined_insnlist {
343 		struct comedi_insnlist insnlist;
344 		struct comedi_insn insn[1];
345 	} __user *s;
346 	struct comedi32_insnlist_struct __user *insnlist32;
347 	struct comedi32_insn_struct __user *insn32;
348 	compat_uptr_t uptr;
349 	unsigned int n_insns, n;
350 	int err, rc;
351 
352 	insnlist32 = compat_ptr(arg);
353 
354 	/* Get 32-bit insnlist structure.  */
355 	if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32)))
356 		return -EFAULT;
357 
358 	err = 0;
359 	err |= __get_user(n_insns, &insnlist32->n_insns);
360 	err |= __get_user(uptr, &insnlist32->insns);
361 	insn32 = compat_ptr(uptr);
362 	if (err)
363 		return -EFAULT;
364 
365 	/* Allocate user memory to copy insnlist and insns into. */
366 	s = compat_alloc_user_space(offsetof(struct combined_insnlist,
367 					     insn[n_insns]));
368 
369 	/* Set native insnlist structure. */
370 	if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist)))
371 		return -EFAULT;
372 
373 	err |= __put_user(n_insns, &s->insnlist.n_insns);
374 	err |= __put_user(&s->insn[0], &s->insnlist.insns);
375 	if (err)
376 		return -EFAULT;
377 
378 	/* Copy insn structures. */
379 	for (n = 0; n < n_insns; n++) {
380 		rc = get_compat_insn(&s->insn[n], &insn32[n]);
381 		if (rc)
382 			return rc;
383 	}
384 
385 	return translated_ioctl(file, COMEDI_INSNLIST,
386 				(unsigned long)&s->insnlist);
387 }
388 
389 /* Handle 32-bit COMEDI_INSN ioctl. */
compat_insn(struct file * file,unsigned long arg)390 static int compat_insn(struct file *file, unsigned long arg)
391 {
392 	struct comedi_insn __user *insn;
393 	struct comedi32_insn_struct __user *insn32;
394 	int rc;
395 
396 	insn32 = compat_ptr(arg);
397 	insn = compat_alloc_user_space(sizeof(*insn));
398 
399 	rc = get_compat_insn(insn, insn32);
400 	if (rc)
401 		return rc;
402 
403 	return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
404 }
405 
406 /* Process untranslated ioctl. */
407 /* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
raw_ioctl(struct file * file,unsigned int cmd,unsigned long arg)408 static inline int raw_ioctl(struct file *file, unsigned int cmd,
409 			    unsigned long arg)
410 {
411 	int rc;
412 
413 	switch (cmd) {
414 	case COMEDI_DEVCONFIG:
415 	case COMEDI_DEVINFO:
416 	case COMEDI_SUBDINFO:
417 	case COMEDI_BUFCONFIG:
418 	case COMEDI_BUFINFO:
419 		/* Just need to translate the pointer argument. */
420 		arg = (unsigned long)compat_ptr(arg);
421 		rc = translated_ioctl(file, cmd, arg);
422 		break;
423 	case COMEDI_LOCK:
424 	case COMEDI_UNLOCK:
425 	case COMEDI_CANCEL:
426 	case COMEDI_POLL:
427 		/* No translation needed. */
428 		rc = translated_ioctl(file, cmd, arg);
429 		break;
430 	case COMEDI32_CHANINFO:
431 		rc = compat_chaninfo(file, arg);
432 		break;
433 	case COMEDI32_RANGEINFO:
434 		rc = compat_rangeinfo(file, arg);
435 		break;
436 	case COMEDI32_CMD:
437 		rc = compat_cmd(file, arg);
438 		break;
439 	case COMEDI32_CMDTEST:
440 		rc = compat_cmdtest(file, arg);
441 		break;
442 	case COMEDI32_INSNLIST:
443 		rc = compat_insnlist(file, arg);
444 		break;
445 	case COMEDI32_INSN:
446 		rc = compat_insn(file, arg);
447 		break;
448 	default:
449 		rc = -ENOIOCTLCMD;
450 		break;
451 	}
452 	return rc;
453 }
454 
455 /* compat_ioctl file operation. */
456 /* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
comedi_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)457 long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
458 {
459 	return raw_ioctl(file, cmd, arg);
460 }
461