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