ioctls, verify_area, ENOTTY and fixes

Heiko Eissfeldt (heiko@colossus.escape.de)
Thu, 18 Apr 1996 17:39:40 +0200 (MEST)


Hi all,

I did some source browsing, and checked some ioctl functions
(so far fs/* and net/*).

I would like to bring to your attention that POSIX.1 expects
certain errno values for certain situations. In the kernel source

ENODEV is often used instead of ENXIO
EINVAL is often used instead of ENOTTY

Here are quotes from POSIX.1 regarding the semantics:

ENXIO (page 26):
No such device or address
Input or output on a special file referred to a device that did not
exist, or made a request beyond the limits of the device. This
error may also occur when, for example, a tape drive is not online
or a disk pack is not loaded on a drive.

ENODEV (page 25):
No such device
An attempt was made to apply an inappropriate function to a device;
for example, trying to read a write-only device such as a printer.

ENOTTY (page 26):
Inappropriate I/O control operation
A control function was attempted for a file or a special file for
which the operation was inappropriate.

EINVAL (page 25):
Invalid argument
Some invalid argument was supplied. [For example, specifying
an undefined signal to a signal() or kill() function].

In my point of view ENODEV has an unfortunate short description and
is meant as 'this device exists, but is not such a type of device as
would be necessary'.

EINVAL seems to include all cases of ENOTTY, but at least certain
POSIX.1 test code wants to see ENOTTY for wrong cmd arguments to
fcntl(). ioctl() will not be tested by POSIX.1.

I hope you agree...

TODO:
correct fcntl() error return codes,
checking the driver directory for ioctl functions
exchange get/put_user_long, etc. with get/put_user
introduce a #define VERIFY_READWRITE which is the same value as
VERIFY_WRITE, but could be used to show read/write usage in the
source. Two verify_area calls (one with VERIFY_READ and the other
with VERIFY_WRITE) are not necessary.

I have some patches based on reading the source. I could compile
them, but did not use most of the functions.

Here are my findings so far (against 1.3.90):
--- fs/ext2/ioctl.c.or Wed Jan 17 06:31:58 1996
+++ fs/ext2/ioctl.c Thu Apr 18 14:09:57 1996
@@ -31,6 +31,8 @@
put_fs_long (inode->u.ext2_i.i_flags, (long *) arg);
return 0;
case EXT2_IOC_SETFLAGS:
+ if ((err = verify_area (VERIFY_READ, (long *) arg, sizeof(long))))
+ return err;
flags = get_fs_long ((long *) arg);
/*
* The IMMUTABLE flag can only be changed by the super user
@@ -68,11 +70,13 @@
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
+ if ((err = verify_area (VERIFY_READ, (long *) arg, sizeof(long))))
+ return err;
inode->u.ext2_i.i_version = get_fs_long ((long *) arg);
inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
return 0;
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
--- fs/umsdos/ioctl.c.or Tue Apr 2 22:34:33 1996
+++ fs/umsdos/ioctl.c Thu Apr 18 14:35:43 1996
@@ -74,7 +74,7 @@
}else if (current->euid == 0
|| cmd == UMSDOS_GETVERSION){
struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
- ret = -EINVAL;
+ ret = -ENOTTY;
/* #Specification: ioctl / prototypes
The official prototype for the umsdos ioctl on directory
is:
--- fs/smbfs/ioctl.c.or Fri Nov 10 06:54:00 1995
+++ fs/smbfs/ioctl.c Thu Apr 18 14:30:47 1996
@@ -28,7 +28,7 @@
put_fs_word(SMB_SERVER(inode)->m.mounted_uid, (uid_t*) arg);
return 0;
default:
- return -EINVAL;
+ return -ENOTTY;
}
}

--- fs/ncpfs/ioctl.c.or Tue Apr 2 22:34:33 1996
+++ fs/ncpfs/ioctl.c Thu Apr 18 14:29:20 1996
@@ -153,8 +153,8 @@
return 0;

default:
- return -EINVAL;
+ return -ENOTTY;
}

- return -EINVAL;
+ return -ENOTTY;
}
--- fs/fat/dir.c.or Tue Apr 2 22:33:23 1996
+++ fs/fat/dir.c Thu Apr 18 14:15:38 1996
@@ -380,6 +380,7 @@
int fat_dir_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg)
{
+ int err;
/*
* We want to provide an interface for Samba to be able
* to get the short filename for a given long filename.
@@ -389,18 +390,24 @@
switch (cmd) {
case VFAT_IOCTL_READDIR_BOTH: {
struct dirent *d1 = (struct dirent *)arg;
+ err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ if (err)
+ return err;
put_user(0, &d1->d_reclen);
return fat_readdirx(inode,filp,(void *)arg,
vfat_ioctl_fill, NULL, 0, 1, 1);
}
case VFAT_IOCTL_READDIR_SHORT: {
struct dirent *d1 = (struct dirent *)arg;
+ err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ if (err)
+ return err;
put_user(0, &d1->d_reclen);
return fat_readdirx(inode,filp,(void *)arg,
vfat_ioctl_fill, NULL, 1, 0, 1);
}
default:
- return -EINVAL;
+ return -ENOTTY;
}

return 0;
--- fs/super.c.or Tue Apr 16 00:57:18 1996
+++ fs/super.c Thu Apr 18 12:23:42 1996
@@ -15,6 +15,7 @@
*
* Added kerneld support: Jacques Gelinas and Bjorn Ekwall
* Added change_root: Werner Almesberger & Hans Lermen, Feb '96
+ * Added verify_area: Heiko Eissfeldt, Apr '96
*/

#include <stdarg.h>
@@ -785,7 +786,7 @@

static int copy_mount_options (const void * data, unsigned long *where)
{
- int i;
+ int i, err;
unsigned long page;
struct vm_area_struct * vma;

@@ -799,6 +800,11 @@
i = vma->vm_end - (unsigned long) data;
if (PAGE_SIZE <= (unsigned long) i)
i = PAGE_SIZE-1;
+
+ err = verify_area(VERIFY_READ, data, i);
+ if (err)
+ return err;
+
if (!(page = __get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
--- fs/ioctl.c.or Tue Apr 2 22:33:25 1996
+++ fs/ioctl.c Thu Apr 18 13:56:03 1996
@@ -51,7 +51,7 @@
}
if (filp->f_op && filp->f_op->ioctl)
return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
- return -EINVAL;
+ return -ENOTTY;
}

@@ -103,6 +103,6 @@
if (filp->f_op && filp->f_op->ioctl)
return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);

- return -EINVAL;
+ return -ENOTTY;
}
}
--- fs/pipe.c.or Tue Apr 2 22:56:17 1996
+++ fs/pipe.c Thu Apr 18 14:04:03 1996
@@ -148,7 +148,7 @@
put_user(PIPE_SIZE(*pino),(int *) arg);
return error;
default:
- return -EINVAL;
+ return -ENOTTY;
}
}

--- net/socket.c.or Sat Apr 13 14:19:02 1996
+++ net/socket.c Thu Apr 18 14:52:14 1996
@@ -403,7 +403,9 @@
{
struct socket *sock;
sock = socki_lookup(inode);
- return(sock->ops->ioctl(sock, cmd, arg));
+ if (sock && sock->ops && sock->ops->ioctl)
+ return(sock->ops->ioctl(sock, cmd, arg));
+ return -ENOTTY;
}

--- net/netlink.c.or Wed Feb 7 07:55:56 1996
+++ net/netlink.c Thu Apr 18 14:55:46 1996
@@ -53,7 +53,7 @@
}

/*
- * Exported do nothing receiver for one way
+ * Exported do nothing receive for one way
* interfaces.
*/

@@ -150,7 +150,7 @@
return -ENODEV;
switch ( cmd ) {
default:
- retval = -EINVAL;
+ retval = -ENOTTY;
}
return retval;
}
--- net/appletalk/ddp.c.or Sat Apr 13 14:18:56 1996
+++ net/appletalk/ddp.c Thu Apr 18 15:11:50 1996
@@ -758,7 +758,7 @@
if(!suser())
return -EPERM;
if(sa->sat_family!=AF_APPLETALK)
- return -EINVAL;
+ return -ENOTTY;
if(dev->type!=ARPHRD_ETHER&&dev->type!=ARPHRD_LOOPBACK
&&dev->type!=ARPHRD_LOCALTLK && dev->type!=ARPHRD_PPP)
return -EPROTONOSUPPORT;
@@ -854,6 +854,8 @@
((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_net=atif->address.s_net;
((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_node=ATADDR_BCAST;
break;
+ default:
+ return -ENOTTY;
}
memcpy_tofs(arg,&atreq,sizeof(atreq));
return 0;
@@ -877,12 +879,12 @@
{
case SIOCDELRT:
if(rt.rt_dst.sa_family!=AF_APPLETALK)
- return -EINVAL;
+ return -ENOTTY;
return atrtr_delete(&((struct sockaddr_at *)&rt.rt_dst)->sat_addr);
case SIOCADDRT:
return atrtr_create(&rt, NULL);
default:
- return -EINVAL;
+ return -ENOTTY;
}
}

--- net/ax25/af_ax25.c.or Sat Apr 13 14:18:58 1996
+++ net/ax25/af_ax25.c Thu Apr 18 15:22:42 1996
@@ -526,7 +526,7 @@
}
}

- return -EINVAL; /*NOTREACHED */
+ return -ENOTTY; /*NOTREACHED */
}

/*
@@ -644,7 +644,7 @@
ax25->maxqueue = ax25_ctl.arg;
break;
default:
- return -EINVAL;
+ return -ENOTTY;
}

return 0;
@@ -2247,7 +2247,7 @@
case SIOCSIFNETMASK:
case SIOCGIFMETRIC:
case SIOCSIFMETRIC:
- return -EINVAL;
+ return -ENOTTY;

default:
return(dev_ioctl(cmd, (void *)arg));
--- net/bridge/br.c.or Sat Apr 13 14:18:58 1996
+++ net/bridge/br.c Thu Apr 18 15:28:36 1996
@@ -1539,7 +1539,7 @@
}
return(0);
default:
- return -EINVAL;
+ return -ENOTTY;
}
/*NOTREACHED*/
return 0;
--- net/core/dev.c.or Sat Apr 13 14:17:57 1996
+++ net/core/dev.c Thu Apr 18 15:32:57 1996
@@ -1182,11 +1182,11 @@

case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
do not support it */
- ret = -EINVAL;
+ ret = -ENOTTY;
break;

case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */
- ret = -EINVAL;
+ ret = -ENOTTY;
break;

case SIOCGIFHWADDR:
@@ -1245,7 +1245,7 @@
break;
}

- ret = -EINVAL;
+ ret = -ENOTTY;
}
return(ret);
/*
@@ -1309,7 +1309,7 @@
return dev_ifsioc(arg, cmd);

case SIOCSIFLINK:
- return -EINVAL;
+ return -ENOTTY;

/*
* Unknown or private ioctl.
@@ -1320,7 +1320,7 @@
(cmd <= (SIOCDEVPRIVATE + 15))) {
return dev_ifsioc(arg, cmd);
}
- return -EINVAL;
+ return -ENOTTY;
}
}

--- net/ipv4/af_inet.c.or Sat Apr 13 14:18:58 1996
+++ net/ipv4/af_inet.c Thu Apr 18 15:36:47 1996
@@ -1295,7 +1295,7 @@
return(dev_ioctl(cmd,(void *) arg));

if (sk->prot->ioctl==NULL)
- return(-EINVAL);
+ return(-ENOTTY);
return(sk->prot->ioctl(sk, cmd, arg));
}
/*NOTREACHED*/
--- net/ipv4/arp.c.or Sat Apr 13 14:18:59 1996
+++ net/ipv4/arp.c Thu Apr 18 16:13:38 1996
@@ -2114,7 +2114,7 @@
memset(&r.arp_dev, 0, sizeof(r.arp_dev));
break;
default:
- return -EINVAL;
+ return -ENOTTY;
}

if (r.arp_pa.sa_family != AF_INET)
--- net/ipv4/ip_input.c.or Sat Apr 13 14:18:59 1996
+++ net/ipv4/ip_input.c Thu Apr 18 16:14:36 1996
@@ -185,7 +185,7 @@
switch(cmd)
{
default:
- return(-EINVAL);
+ return(-ENOTTY);
}
}

--- net/ipv4/ipmr.c.or Sat Apr 13 14:18:59 1996
+++ net/ipv4/ipmr.c Thu Apr 18 16:16:02 1996
@@ -628,7 +628,7 @@
memcpy_tofs((void *)arg,&sr,sizeof(sr));
return 0;
default:
- return -EINVAL;
+ return -ENOTTY;
}
}

--- net/ipv4/rarp.c.or Tue Apr 2 22:34:50 1996
+++ net/ipv4/rarp.c Thu Apr 18 16:17:45 1996
@@ -474,7 +474,7 @@
return err;
return rarp_req_set((struct arpreq *)arg);
default:
- return -EINVAL;
+ return -ENOTTY;
}

/*NOTREACHED*/
--- net/ipv4/route.c.or Sat Apr 13 14:19:00 1996
+++ net/ipv4/route.c Thu Apr 18 16:19:20 1996
@@ -1774,7 +1774,7 @@
return (cmd == SIOCDELRT) ? ip_rt_kill(&rt) : ip_rt_new(&rt);
}

- return -EINVAL;
+ return -ENOTTY;
}

void ip_rt_advice(struct rtable **rp, int advice)
--- net/ipv4/tcp.c.or Sat Apr 13 14:19:00 1996
+++ net/ipv4/tcp.c Thu Apr 18 16:21:05 1996
@@ -802,7 +802,7 @@
return(0);
}
default:
- return(-EINVAL);
+ return(-ENOTTY);
}
}

--- net/ipv4/udp.c.or Sat Apr 13 14:19:00 1996
+++ net/ipv4/udp.c Thu Apr 18 16:22:19 1996
@@ -485,7 +485,7 @@
}

default:
- return(-EINVAL);
+ return(-ENOTTY);
}
return(0);
}

--- net/ipx/af_ipx.c.or Sat Apr 13 14:19:00 1996
+++ net/ipx/af_ipx.c Thu Apr 18 16:28:29 1996
@@ -1076,7 +1076,7 @@
return err;
return ipxcfg_set_auto_select(get_fs_byte(arg));
default:
- return -EINVAL;
+ return -ENOTTY;
}
}

@@ -1387,7 +1387,7 @@
return ipxrtr_create(&f);
}
default:
- return -EINVAL;
+ return -ENOTTY;
}
}

@@ -2232,7 +2232,7 @@
case SIOCSIFBRDADDR:
case SIOCGIFNETMASK:
case SIOCSIFNETMASK:
- return -EINVAL;
+ return -ENOTTY;
default:
return(dev_ioctl(cmd,(void *) arg));
}
--- net/netrom/af_netrom.c.or Sat Apr 13 14:19:01 1996
+++ net/netrom/af_netrom.c Thu Apr 18 16:30:44 1996
@@ -1187,7 +1187,7 @@
case SIOCSIFNETMASK:
case SIOCGIFMETRIC:
case SIOCSIFMETRIC:
- return -EINVAL;
+ return -ENOTTY;

case SIOCADDRT:
case SIOCDELRT:
--- net/unix/af_unix.c.or Sat Apr 13 14:19:02 1996
+++ net/unix/af_unix.c Thu Apr 18 16:34:12 1996
@@ -1222,12 +1222,14 @@
if((skb=skb_peek(&sk->receive_queue))!=NULL)
amount=skb->len;
err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
+ if(err)
+ return err;
put_fs_long(amount,(unsigned long *)arg);
return 0;
}

default:
- return -EINVAL;
+ return -ENOTTY;
}
/*NOTREACHED*/
return(0);