[PATCH] knfsd: Fix two problems that can cause rmmod nfsd to die
[pandora-kernel.git] / fs / compat.c
index a2ba78b..b1f6478 100644 (file)
@@ -114,6 +114,7 @@ asmlinkage long compat_sys_newlstat(char __user * filename,
        return error;
 }
 
+#ifndef __ARCH_WANT_STAT64
 asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename,
                struct compat_stat __user *statbuf, int flag)
 {
@@ -134,6 +135,7 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename,
 out:
        return error;
 }
+#endif
 
 asmlinkage long compat_sys_newfstat(unsigned int fd,
                struct compat_stat __user * statbuf)
@@ -1215,6 +1217,10 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
        if (ret < 0)
                goto out;
 
+       ret = security_file_permission(file, type == READ ? MAY_READ:MAY_WRITE);
+       if (ret)
+               goto out;
+
        fnv = NULL;
        if (type == READ) {
                fn = file->f_op->read;
@@ -1311,6 +1317,26 @@ out:
        return ret;
 }
 
+asmlinkage long
+compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32,
+                   unsigned int nr_segs, unsigned int flags)
+{
+       unsigned i;
+       struct iovec *iov;
+       if (nr_segs > UIO_MAXIOV)
+               return -EINVAL;
+       iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec));
+       for (i = 0; i < nr_segs; i++) {
+               struct compat_iovec v;
+               if (get_user(v.iov_base, &iov32[i].iov_base) ||
+                   get_user(v.iov_len, &iov32[i].iov_len) ||
+                   put_user(compat_ptr(v.iov_base), &iov[i].iov_base) ||
+                   put_user(v.iov_len, &iov[i].iov_len))
+                       return -EFAULT;
+       }
+       return sys_vmsplice(fd, iov, nr_segs, flags);
+}
+
 /*
  * Exactly like fs/open.c:sys_open(), except that it doesn't set the
  * O_LARGEFILE flag.
@@ -1474,10 +1500,9 @@ int compat_do_execve(char * filename,
        int i;
 
        retval = -ENOMEM;
-       bprm = kmalloc(sizeof(*bprm), GFP_KERNEL);
+       bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
        if (!bprm)
                goto out_ret;
-       memset(bprm, 0, sizeof(*bprm));
 
        file = open_exec(filename);
        retval = PTR_ERR(file);
@@ -1638,15 +1663,6 @@ void compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
  * This is a virtual copy of sys_select from fs/select.c and probably
  * should be compared to it from time to time
  */
-static void *select_bits_alloc(int size)
-{
-       return kmalloc(6 * size, GFP_KERNEL);
-}
-
-static void select_bits_free(void *bits, int size)
-{
-       kfree(bits);
-}
 
 /*
  * We can actually return ERESTARTSYS instead of EINTR, but I'd
@@ -1685,7 +1701,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
         */
        ret = -ENOMEM;
        size = FDS_BYTES(n);
-       bits = select_bits_alloc(size);
+       bits = kmalloc(6 * size, GFP_KERNEL);
        if (!bits)
                goto out_nofds;
        fds.in      = (unsigned long *)  bits;
@@ -1719,7 +1735,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
        compat_set_fd_set(n, exp, fds.res_ex);
 
 out:
-       select_bits_free(bits, size);
+       kfree(bits);
 out_nofds:
        return ret;
 }
@@ -1757,7 +1773,7 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
                        goto sticky;
                rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));
                rtv.tv_sec = timeout;
-               if (compat_timeval_compare(&rtv, &tv) < 0)
+               if (compat_timeval_compare(&rtv, &tv) >= 0)
                        rtv = tv;
                if (copy_to_user(tvp, &rtv, sizeof(rtv))) {
 sticky:
@@ -1834,7 +1850,7 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
                        rts.tv_sec++;
                        rts.tv_nsec -= NSEC_PER_SEC;
                }
-               if (compat_timespec_compare(&rts, &ts) < 0)
+               if (compat_timespec_compare(&rts, &ts) >= 0)
                        rts = ts;
                copy_to_user(tsp, &rts, sizeof(rts));
        }
@@ -1897,7 +1913,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
        }
 
        if (sigmask) {
-               if (sigsetsize |= sizeof(compat_sigset_t))
+               if (sigsetsize != sizeof(compat_sigset_t))
                        return -EINVAL;
                if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
                        return -EFAULT;
@@ -1934,7 +1950,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
                rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
                                        1000;
                rts.tv_sec = timeout;
-               if (compat_timespec_compare(&rts, &ts) < 0)
+               if (compat_timespec_compare(&rts, &ts) >= 0)
                        rts = ts;
                if (copy_to_user(tsp, &rts, sizeof(rts))) {
 sticky:
@@ -2014,109 +2030,115 @@ union compat_nfsctl_res {
        struct knfsd_fh         cr32_getfs;
 };
 
-static int compat_nfs_svc_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg)
+static int compat_nfs_svc_trans(struct nfsctl_arg *karg,
+                               struct compat_nfsctl_arg __user *arg)
 {
-       int err;
-
-       err = access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc));
-       err |= get_user(karg->ca_version, &arg->ca32_version);
-       err |= __get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port);
-       err |= __get_user(karg->ca_svc.svc_nthreads, &arg->ca32_svc.svc32_nthreads);
-       return (err) ? -EFAULT : 0;
+       if (!access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)) ||
+               get_user(karg->ca_version, &arg->ca32_version) ||
+               __get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port) ||
+               __get_user(karg->ca_svc.svc_nthreads,
+                               &arg->ca32_svc.svc32_nthreads))
+               return -EFAULT;
+       return 0;
 }
 
-static int compat_nfs_clnt_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg)
-{
-       int err;
-
-       err = access_ok(VERIFY_READ, &arg->ca32_client, sizeof(arg->ca32_client));
-       err |= get_user(karg->ca_version, &arg->ca32_version);
-       err |= __copy_from_user(&karg->ca_client.cl_ident[0],
-                         &arg->ca32_client.cl32_ident[0],
-                         NFSCLNT_IDMAX);
-       err |= __get_user(karg->ca_client.cl_naddr, &arg->ca32_client.cl32_naddr);
-       err |= __copy_from_user(&karg->ca_client.cl_addrlist[0],
-                         &arg->ca32_client.cl32_addrlist[0],
-                         (sizeof(struct in_addr) * NFSCLNT_ADDRMAX));
-       err |= __get_user(karg->ca_client.cl_fhkeytype,
-                     &arg->ca32_client.cl32_fhkeytype);
-       err |= __get_user(karg->ca_client.cl_fhkeylen,
-                     &arg->ca32_client.cl32_fhkeylen);
-       err |= __copy_from_user(&karg->ca_client.cl_fhkey[0],
-                         &arg->ca32_client.cl32_fhkey[0],
-                         NFSCLNT_KEYMAX);
+static int compat_nfs_clnt_trans(struct nfsctl_arg *karg,
+                               struct compat_nfsctl_arg __user *arg)
+{
+       if (!access_ok(VERIFY_READ, &arg->ca32_client,
+                       sizeof(arg->ca32_client)) ||
+               get_user(karg->ca_version, &arg->ca32_version) ||
+               __copy_from_user(&karg->ca_client.cl_ident[0],
+                               &arg->ca32_client.cl32_ident[0],
+                               NFSCLNT_IDMAX) ||
+               __get_user(karg->ca_client.cl_naddr,
+                               &arg->ca32_client.cl32_naddr) ||
+               __copy_from_user(&karg->ca_client.cl_addrlist[0],
+                               &arg->ca32_client.cl32_addrlist[0],
+                               (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)) ||
+               __get_user(karg->ca_client.cl_fhkeytype,
+                               &arg->ca32_client.cl32_fhkeytype) ||
+               __get_user(karg->ca_client.cl_fhkeylen,
+                               &arg->ca32_client.cl32_fhkeylen) ||
+               __copy_from_user(&karg->ca_client.cl_fhkey[0],
+                               &arg->ca32_client.cl32_fhkey[0],
+                               NFSCLNT_KEYMAX))
+               return -EFAULT;
 
-       return (err) ? -EFAULT : 0;
+       return 0;
 }
 
-static int compat_nfs_exp_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg)
-{
-       int err;
-
-       err = access_ok(VERIFY_READ, &arg->ca32_export, sizeof(arg->ca32_export));
-       err |= get_user(karg->ca_version, &arg->ca32_version);
-       err |= __copy_from_user(&karg->ca_export.ex_client[0],
-                         &arg->ca32_export.ex32_client[0],
-                         NFSCLNT_IDMAX);
-       err |= __copy_from_user(&karg->ca_export.ex_path[0],
-                         &arg->ca32_export.ex32_path[0],
-                         NFS_MAXPATHLEN);
-       err |= __get_user(karg->ca_export.ex_dev,
-                     &arg->ca32_export.ex32_dev);
-       err |= __get_user(karg->ca_export.ex_ino,
-                     &arg->ca32_export.ex32_ino);
-       err |= __get_user(karg->ca_export.ex_flags,
-                     &arg->ca32_export.ex32_flags);
-       err |= __get_user(karg->ca_export.ex_anon_uid,
-                     &arg->ca32_export.ex32_anon_uid);
-       err |= __get_user(karg->ca_export.ex_anon_gid,
-                     &arg->ca32_export.ex32_anon_gid);
+static int compat_nfs_exp_trans(struct nfsctl_arg *karg,
+                               struct compat_nfsctl_arg __user *arg)
+{
+       if (!access_ok(VERIFY_READ, &arg->ca32_export,
+                               sizeof(arg->ca32_export)) ||
+               get_user(karg->ca_version, &arg->ca32_version) ||
+               __copy_from_user(&karg->ca_export.ex_client[0],
+                               &arg->ca32_export.ex32_client[0],
+                               NFSCLNT_IDMAX) ||
+               __copy_from_user(&karg->ca_export.ex_path[0],
+                               &arg->ca32_export.ex32_path[0],
+                               NFS_MAXPATHLEN) ||
+               __get_user(karg->ca_export.ex_dev,
+                               &arg->ca32_export.ex32_dev) ||
+               __get_user(karg->ca_export.ex_ino,
+                               &arg->ca32_export.ex32_ino) ||
+               __get_user(karg->ca_export.ex_flags,
+                               &arg->ca32_export.ex32_flags) ||
+               __get_user(karg->ca_export.ex_anon_uid,
+                               &arg->ca32_export.ex32_anon_uid) ||
+               __get_user(karg->ca_export.ex_anon_gid,
+                               &arg->ca32_export.ex32_anon_gid))
+               return -EFAULT;
        SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid);
        SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid);
 
-       return (err) ? -EFAULT : 0;
+       return 0;
 }
 
-static int compat_nfs_getfd_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg)
-{
-       int err;
-
-       err = access_ok(VERIFY_READ, &arg->ca32_getfd, sizeof(arg->ca32_getfd));
-       err |= get_user(karg->ca_version, &arg->ca32_version);
-       err |= __copy_from_user(&karg->ca_getfd.gd_addr,
-                         &arg->ca32_getfd.gd32_addr,
-                         (sizeof(struct sockaddr)));
-       err |= __copy_from_user(&karg->ca_getfd.gd_path,
-                         &arg->ca32_getfd.gd32_path,
-                         (NFS_MAXPATHLEN+1));
-       err |= __get_user(karg->ca_getfd.gd_version,
-                     &arg->ca32_getfd.gd32_version);
+static int compat_nfs_getfd_trans(struct nfsctl_arg *karg,
+                               struct compat_nfsctl_arg __user *arg)
+{
+       if (!access_ok(VERIFY_READ, &arg->ca32_getfd,
+                       sizeof(arg->ca32_getfd)) ||
+               get_user(karg->ca_version, &arg->ca32_version) ||
+               __copy_from_user(&karg->ca_getfd.gd_addr,
+                               &arg->ca32_getfd.gd32_addr,
+                               (sizeof(struct sockaddr))) ||
+               __copy_from_user(&karg->ca_getfd.gd_path,
+                               &arg->ca32_getfd.gd32_path,
+                               (NFS_MAXPATHLEN+1)) ||
+               __get_user(karg->ca_getfd.gd_version,
+                               &arg->ca32_getfd.gd32_version))
+               return -EFAULT;
 
-       return (err) ? -EFAULT : 0;
+       return 0;
 }
 
-static int compat_nfs_getfs_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg)
+static int compat_nfs_getfs_trans(struct nfsctl_arg *karg,
+                               struct compat_nfsctl_arg __user *arg)
 {
-       int err;
-
-       err = access_ok(VERIFY_READ, &arg->ca32_getfs, sizeof(arg->ca32_getfs));
-       err |= get_user(karg->ca_version, &arg->ca32_version);
-       err |= __copy_from_user(&karg->ca_getfs.gd_addr,
-                         &arg->ca32_getfs.gd32_addr,
-                         (sizeof(struct sockaddr)));
-       err |= __copy_from_user(&karg->ca_getfs.gd_path,
-                         &arg->ca32_getfs.gd32_path,
-                         (NFS_MAXPATHLEN+1));
-       err |= __get_user(karg->ca_getfs.gd_maxlen,
-                     &arg->ca32_getfs.gd32_maxlen);
+       if (!access_ok(VERIFY_READ,&arg->ca32_getfs,sizeof(arg->ca32_getfs)) ||
+               get_user(karg->ca_version, &arg->ca32_version) ||
+               __copy_from_user(&karg->ca_getfs.gd_addr,
+                               &arg->ca32_getfs.gd32_addr,
+                               (sizeof(struct sockaddr))) ||
+               __copy_from_user(&karg->ca_getfs.gd_path,
+                               &arg->ca32_getfs.gd32_path,
+                               (NFS_MAXPATHLEN+1)) ||
+               __get_user(karg->ca_getfs.gd_maxlen,
+                               &arg->ca32_getfs.gd32_maxlen))
+               return -EFAULT;
 
-       return (err) ? -EFAULT : 0;
+       return 0;
 }
 
 /* This really doesn't need translations, we are only passing
  * back a union which contains opaque nfs file handle data.
  */
-static int compat_nfs_getfh_res_trans(union nfsctl_res *kres, union compat_nfsctl_res __user *res)
+static int compat_nfs_getfh_res_trans(union nfsctl_res *kres,
+                               union compat_nfsctl_res __user *res)
 {
        int err;
 
@@ -2125,8 +2147,9 @@ static int compat_nfs_getfh_res_trans(union nfsctl_res *kres, union compat_nfsct
        return (err) ? -EFAULT : 0;
 }
 
-asmlinkage long compat_sys_nfsservctl(int cmd, struct compat_nfsctl_arg __user *arg,
-                                       union compat_nfsctl_res __user *res)
+asmlinkage long compat_sys_nfsservctl(int cmd,
+                               struct compat_nfsctl_arg __user *arg,
+                               union compat_nfsctl_res __user *res)
 {
        struct nfsctl_arg *karg;
        union nfsctl_res *kres;
@@ -2168,9 +2191,12 @@ asmlinkage long compat_sys_nfsservctl(int cmd, struct compat_nfsctl_arg __user *
 
        default:
                err = -EINVAL;
-               goto done;
+               break;
        }
 
+       if (err)
+               goto done;
+
        oldfs = get_fs();
        set_fs(KERNEL_DS);
        /* The __user pointer casts are valid because of the set_fs() */