[CVE-2009-0029] Make sys_pselect7 static
[linux-2.6.git] / fs / compat.c
index 332a869..65a070e 100644 (file)
@@ -137,6 +137,45 @@ asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval _
        return compat_sys_futimesat(AT_FDCWD, filename, t);
 }
 
+static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
+{
+       compat_ino_t ino = stat->ino;
+       typeof(ubuf->st_uid) uid = 0;
+       typeof(ubuf->st_gid) gid = 0;
+       int err;
+
+       SET_UID(uid, stat->uid);
+       SET_GID(gid, stat->gid);
+
+       if ((u64) stat->size > MAX_NON_LFS ||
+           !old_valid_dev(stat->dev) ||
+           !old_valid_dev(stat->rdev))
+               return -EOVERFLOW;
+       if (sizeof(ino) < sizeof(stat->ino) && ino != stat->ino)
+               return -EOVERFLOW;
+
+       if (clear_user(ubuf, sizeof(*ubuf)))
+               return -EFAULT;
+
+       err  = __put_user(old_encode_dev(stat->dev), &ubuf->st_dev);
+       err |= __put_user(ino, &ubuf->st_ino);
+       err |= __put_user(stat->mode, &ubuf->st_mode);
+       err |= __put_user(stat->nlink, &ubuf->st_nlink);
+       err |= __put_user(uid, &ubuf->st_uid);
+       err |= __put_user(gid, &ubuf->st_gid);
+       err |= __put_user(old_encode_dev(stat->rdev), &ubuf->st_rdev);
+       err |= __put_user(stat->size, &ubuf->st_size);
+       err |= __put_user(stat->atime.tv_sec, &ubuf->st_atime);
+       err |= __put_user(stat->atime.tv_nsec, &ubuf->st_atime_nsec);
+       err |= __put_user(stat->mtime.tv_sec, &ubuf->st_mtime);
+       err |= __put_user(stat->mtime.tv_nsec, &ubuf->st_mtime_nsec);
+       err |= __put_user(stat->ctime.tv_sec, &ubuf->st_ctime);
+       err |= __put_user(stat->ctime.tv_nsec, &ubuf->st_ctime_nsec);
+       err |= __put_user(stat->blksize, &ubuf->st_blksize);
+       err |= __put_user(stat->blocks, &ubuf->st_blocks);
+       return err;
+}
+
 asmlinkage long compat_sys_newstat(char __user * filename,
                struct compat_stat __user *statbuf)
 {
@@ -197,8 +236,8 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
 {
        
        if (sizeof ubuf->f_blocks == 4) {
-               if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) &
-                   0xffffffff00000000ULL)
+               if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail |
+                    kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
                        return -EOVERFLOW;
                /* f_files and f_ffree may be -1; it's okay
                 * to stuff that into 32 bits */
@@ -234,18 +273,18 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
  * The following statfs calls are copies of code from fs/open.c and
  * should be checked against those from time to time
  */
-asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf)
+asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct kstatfs tmp;
-               error = vfs_statfs(nd.path.dentry, &tmp);
+               error = vfs_statfs(path.dentry, &tmp);
                if (!error)
                        error = put_compat_statfs(buf, &tmp);
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
@@ -271,8 +310,8 @@ out:
 static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf)
 {
        if (sizeof ubuf->f_blocks == 4) {
-               if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) &
-                   0xffffffff00000000ULL)
+               if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail |
+                    kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
                        return -EOVERFLOW;
                /* f_files and f_ffree may be -1; it's okay
                 * to stuff that into 32 bits */
@@ -299,21 +338,21 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat
        return 0;
 }
 
-asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf)
+asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
        if (sz != sizeof(*buf))
                return -EINVAL;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct kstatfs tmp;
-               error = vfs_statfs(nd.path.dentry, &tmp);
+               error = vfs_statfs(path.dentry, &tmp);
                if (!error)
                        error = put_compat_statfs64(buf, &tmp);
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
@@ -792,8 +831,10 @@ static int compat_fillonedir(void *__buf, const char *name, int namlen,
        if (buf->result)
                return -EINVAL;
        d_ino = ino;
-       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
+       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
+               buf->result = -EOVERFLOW;
                return -EOVERFLOW;
+       }
        buf->result++;
        dirent = buf->dirent;
        if (!access_ok(VERIFY_WRITE, dirent,
@@ -828,7 +869,7 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd,
        buf.dirent = dirent;
 
        error = vfs_readdir(file, compat_fillonedir, &buf);
-       if (error >= 0)
+       if (buf.result)
                error = buf.result;
 
        fput(file);
@@ -862,8 +903,10 @@ static int compat_filldir(void *__buf, const char *name, int namlen,
        if (reclen > buf->count)
                return -EINVAL;
        d_ino = ino;
-       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
+       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
+               buf->error = -EOVERFLOW;
                return -EOVERFLOW;
+       }
        dirent = buf->previous;
        if (dirent) {
                if (__put_user(offset, &dirent->d_off))
@@ -913,9 +956,8 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
        buf.error = 0;
 
        error = vfs_readdir(file, compat_filldir, &buf);
-       if (error < 0)
-               goto out_putf;
-       error = buf.error;
+       if (error >= 0)
+               error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
                if (put_user(file->f_pos, &lastdirent->d_off))
@@ -923,8 +965,6 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
                else
                        error = count - buf.count;
        }
-
-out_putf:
        fput(file);
 out:
        return error;
@@ -1004,19 +1044,16 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
        buf.error = 0;
 
        error = vfs_readdir(file, compat_filldir64, &buf);
-       if (error < 0)
-               goto out_putf;
-       error = buf.error;
+       if (error >= 0)
+               error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
                typeof(lastdirent->d_off) d_off = file->f_pos;
-               error = -EFAULT;
                if (__put_user_unaligned(d_off, &lastdirent->d_off))
-                       goto out_putf;
-               error = count - buf.count;
+                       error = -EFAULT;
+               else
+                       error = count - buf.count;
        }
-
-out_putf:
        fput(file);
 out:
        return error;
@@ -1150,6 +1187,9 @@ compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec, unsign
        ret = compat_do_readv_writev(READ, file, vec, vlen, &file->f_pos);
 
 out:
+       if (ret > 0)
+               add_rchar(current, ret);
+       inc_syscr(current);
        fput(file);
        return ret;
 }
@@ -1173,6 +1213,9 @@ compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec, unsig
        ret = compat_do_readv_writev(WRITE, file, vec, vlen, &file->f_pos);
 
 out:
+       if (ret > 0)
+               add_wchar(current, ret);
+       inc_syscw(current);
        fput(file);
        return ret;
 }
@@ -1235,7 +1278,7 @@ static int compat_count(compat_uptr_t __user *argv, int max)
                        if (!p)
                                break;
                        argv++;
-                       if(++i > max)
+                       if (i++ >= max)
                                return -E2BIG;
                }
        }
@@ -1356,10 +1399,20 @@ int compat_do_execve(char * filename,
        if (!bprm)
                goto out_ret;
 
+       retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+       if (retval < 0)
+               goto out_free;
+
+       retval = -ENOMEM;
+       bprm->cred = prepare_exec_creds();
+       if (!bprm->cred)
+               goto out_unlock;
+       check_unsafe_exec(bprm);
+
        file = open_exec(filename);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
-               goto out_kfree;
+               goto out_unlock;
 
        sched_exec();
 
@@ -1373,14 +1426,10 @@ int compat_do_execve(char * filename,
 
        bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
        if ((retval = bprm->argc) < 0)
-               goto out_mm;
+               goto out;
 
        bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
        if ((retval = bprm->envc) < 0)
-               goto out_mm;
-
-       retval = security_bprm_alloc(bprm);
-       if (retval)
                goto out;
 
        retval = prepare_binprm(bprm);
@@ -1401,19 +1450,16 @@ int compat_do_execve(char * filename,
                goto out;
 
        retval = search_binary_handler(bprm, regs);
-       if (retval >= 0) {
-               /* execve success */
-               security_bprm_free(bprm);
-               acct_update_integrals(current);
-               kfree(bprm);
-               return retval;
-       }
+       if (retval < 0)
+               goto out;
 
-out:
-       if (bprm->security)
-               security_bprm_free(bprm);
+       /* execve succeeded */
+       mutex_unlock(&current->cred_exec_mutex);
+       acct_update_integrals(current);
+       free_bprm(bprm);
+       return retval;
 
-out_mm:
+out:
        if (bprm->mm)
                mmput(bprm->mm);
 
@@ -1423,8 +1469,11 @@ out_file:
                fput(bprm->file);
        }
 
-out_kfree:
-       kfree(bprm);
+out_unlock:
+       mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
+       free_bprm(bprm);
 
 out_ret:
        return retval;
@@ -1432,6 +1481,57 @@ out_ret:
 
 #define __COMPAT_NFDBITS       (8 * sizeof(compat_ulong_t))
 
+static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
+                                     int timeval, int ret)
+{
+       struct timespec ts;
+
+       if (!p)
+               return ret;
+
+       if (current->personality & STICKY_TIMEOUTS)
+               goto sticky;
+
+       /* No update for zero timeout */
+       if (!end_time->tv_sec && !end_time->tv_nsec)
+               return ret;
+
+       ktime_get_ts(&ts);
+       ts = timespec_sub(*end_time, ts);
+       if (ts.tv_sec < 0)
+               ts.tv_sec = ts.tv_nsec = 0;
+
+       if (timeval) {
+               struct compat_timeval rtv;
+
+               rtv.tv_sec = ts.tv_sec;
+               rtv.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+               if (!copy_to_user(p, &rtv, sizeof(rtv)))
+                       return ret;
+       } else {
+               struct compat_timespec rts;
+
+               rts.tv_sec = ts.tv_sec;
+               rts.tv_nsec = ts.tv_nsec;
+
+               if (!copy_to_user(p, &rts, sizeof(rts)))
+                       return ret;
+       }
+       /*
+        * If an application puts its timeval in read-only memory, we
+        * don't want the Linux-specific update to the timeval to
+        * cause a fault after the select has completed
+        * successfully. However, because we're not updating the
+        * timeval, we can't restart the system call.
+        */
+
+sticky:
+       if (ret == -ERESTARTNOHAND)
+               ret = -EINTR;
+       return ret;
+}
+
 /*
  * Ooo, nasty.  We need here to frob 32-bit unsigned longs to
  * 64-bit unsigned longs.
@@ -1513,7 +1613,8 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
        ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
 
 int compat_core_sys_select(int n, compat_ulong_t __user *inp,
-       compat_ulong_t __user *outp, compat_ulong_t __user *exp, s64 *timeout)
+       compat_ulong_t __user *outp, compat_ulong_t __user *exp,
+       struct timespec *end_time)
 {
        fd_set_bits fds;
        void *bits;
@@ -1560,7 +1661,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
        zero_fd_set(n, fds.res_out);
        zero_fd_set(n, fds.res_ex);
 
-       ret = do_select(n, &fds, timeout);
+       ret = do_select(n, &fds, end_time);
 
        if (ret < 0)
                goto out;
@@ -1586,7 +1687,7 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
        compat_ulong_t __user *outp, compat_ulong_t __user *exp,
        struct compat_timeval __user *tvp)
 {
-       s64 timeout = -1;
+       struct timespec end_time, *to = NULL;
        struct compat_timeval tv;
        int ret;
 
@@ -1594,64 +1695,37 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
                if (copy_from_user(&tv, tvp, sizeof(tv)))
                        return -EFAULT;
 
-               if (tv.tv_sec < 0 || tv.tv_usec < 0)
+               to = &end_time;
+               if (poll_select_set_timeout(to,
+                               tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
+                               (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
                        return -EINVAL;
-
-               /* Cast to u64 to make GCC stop complaining */
-               if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS)
-                       timeout = -1;   /* infinite */
-               else {
-                       timeout = DIV_ROUND_UP(tv.tv_usec, 1000000/HZ);
-                       timeout += tv.tv_sec * HZ;
-               }
        }
 
-       ret = compat_core_sys_select(n, inp, outp, exp, &timeout);
-
-       if (tvp) {
-               struct compat_timeval rtv;
-
-               if (current->personality & STICKY_TIMEOUTS)
-                       goto sticky;
-               rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));
-               rtv.tv_sec = timeout;
-               if (compat_timeval_compare(&rtv, &tv) >= 0)
-                       rtv = tv;
-               if (copy_to_user(tvp, &rtv, sizeof(rtv))) {
-sticky:
-                       /*
-                        * If an application puts its timeval in read-only
-                        * memory, we don't want the Linux-specific update to
-                        * the timeval to cause a fault after the select has
-                        * completed successfully. However, because we're not
-                        * updating the timeval, we can't restart the system
-                        * call.
-                        */
-                       if (ret == -ERESTARTNOHAND)
-                               ret = -EINTR;
-               }
-       }
+       ret = compat_core_sys_select(n, inp, outp, exp, to);
+       ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
 
        return ret;
 }
 
 #ifdef HAVE_SET_RESTORE_SIGMASK
-asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
+static long do_compat_pselect(int n, compat_ulong_t __user *inp,
        compat_ulong_t __user *outp, compat_ulong_t __user *exp,
        struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
        compat_size_t sigsetsize)
 {
        compat_sigset_t ss32;
        sigset_t ksigmask, sigsaved;
-       s64 timeout = MAX_SCHEDULE_TIMEOUT;
        struct compat_timespec ts;
+       struct timespec end_time, *to = NULL;
        int ret;
 
        if (tsp) {
                if (copy_from_user(&ts, tsp, sizeof(ts)))
                        return -EFAULT;
 
-               if (ts.tv_sec < 0 || ts.tv_nsec < 0)
+               to = &end_time;
+               if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
                        return -EINVAL;
        }
 
@@ -1666,51 +1740,8 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
                sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
        }
 
-       do {
-               if (tsp) {
-                       if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) {
-                               timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
-                               timeout += ts.tv_sec * (unsigned long)HZ;
-                               ts.tv_sec = 0;
-                               ts.tv_nsec = 0;
-                       } else {
-                               ts.tv_sec -= MAX_SELECT_SECONDS;
-                               timeout = MAX_SELECT_SECONDS * HZ;
-                       }
-               }
-
-               ret = compat_core_sys_select(n, inp, outp, exp, &timeout);
-
-       } while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec));
-
-       if (tsp) {
-               struct compat_timespec rts;
-
-               if (current->personality & STICKY_TIMEOUTS)
-                       goto sticky;
-
-               rts.tv_sec = timeout / HZ;
-               rts.tv_nsec = (timeout % HZ) * (NSEC_PER_SEC/HZ);
-               if (rts.tv_nsec >= NSEC_PER_SEC) {
-                       rts.tv_sec++;
-                       rts.tv_nsec -= NSEC_PER_SEC;
-               }
-               if (compat_timespec_compare(&rts, &ts) >= 0)
-                       rts = ts;
-               if (copy_to_user(tsp, &rts, sizeof(rts))) {
-sticky:
-                       /*
-                        * If an application puts its timeval in read-only
-                        * memory, we don't want the Linux-specific update to
-                        * the timeval to cause a fault after the select has
-                        * completed successfully. However, because we're not
-                        * updating the timeval, we can't restart the system
-                        * call.
-                        */
-                       if (ret == -ERESTARTNOHAND)
-                               ret = -EINTR;
-               }
-       }
+       ret = compat_core_sys_select(n, inp, outp, exp, to);
+       ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
 
        if (ret == -ERESTARTNOHAND) {
                /*
@@ -1744,8 +1775,8 @@ asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp,
                                (compat_size_t __user *)(sig+sizeof(up))))
                        return -EFAULT;
        }
-       return compat_sys_pselect7(n, inp, outp, exp, tsp, compat_ptr(up),
-                                       sigsetsize);
+       return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
+                                sigsetsize);
 }
 
 asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
@@ -1755,18 +1786,16 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
        compat_sigset_t ss32;
        sigset_t ksigmask, sigsaved;
        struct compat_timespec ts;
-       s64 timeout = -1;
+       struct timespec end_time, *to = NULL;
        int ret;
 
        if (tsp) {
                if (copy_from_user(&ts, tsp, sizeof(ts)))
                        return -EFAULT;
 
-               /* We assume that ts.tv_sec is always lower than
-                  the number of seconds that can be expressed in
-                  an s64. Otherwise the compiler bitches at us */
-               timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
-               timeout += ts.tv_sec * HZ;
+               to = &end_time;
+               if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
+                       return -EINVAL;
        }
 
        if (sigmask) {
@@ -1780,7 +1809,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
                sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
        }
 
-       ret = do_sys_poll(ufds, nfds, &timeout);
+       ret = do_sys_poll(ufds, nfds, to);
 
        /* We can restart this syscall, usually */
        if (ret == -EINTR) {
@@ -1798,31 +1827,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
        } else if (sigmask)
                sigprocmask(SIG_SETMASK, &sigsaved, NULL);
 
-       if (tsp && timeout >= 0) {
-               struct compat_timespec rts;
-
-               if (current->personality & STICKY_TIMEOUTS)
-                       goto sticky;
-               /* Yes, we know it's actually an s64, but it's also positive. */
-               rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
-                                       1000;
-               rts.tv_sec = timeout;
-               if (compat_timespec_compare(&rts, &ts) >= 0)
-                       rts = ts;
-               if (copy_to_user(tsp, &rts, sizeof(rts))) {
-sticky:
-                       /*
-                        * If an application puts its timeval in read-only
-                        * memory, we don't want the Linux-specific update to
-                        * the timeval to cause a fault after the select has
-                        * completed successfully. However, because we're not
-                        * updating the timeval, we can't restart the system
-                        * call.
-                        */
-                       if (ret == -ERESTARTNOHAND && timeout >= 0)
-                               ret = -EINTR;
-               }
-       }
+       ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
 
        return ret;
 }
@@ -2131,9 +2136,9 @@ asmlinkage long compat_sys_epoll_pwait(int epfd,
 
 #ifdef CONFIG_SIGNALFD
 
-asmlinkage long compat_sys_signalfd(int ufd,
-                                   const compat_sigset_t __user *sigmask,
-                                   compat_size_t sigsetsize)
+asmlinkage long compat_sys_signalfd4(int ufd,
+                                    const compat_sigset_t __user *sigmask,
+                                    compat_size_t sigsetsize, int flags)
 {
        compat_sigset_t ss32;
        sigset_t tmp;
@@ -2148,9 +2153,15 @@ asmlinkage long compat_sys_signalfd(int ufd,
        if (copy_to_user(ksigmask, &tmp, sizeof(sigset_t)))
                return -EFAULT;
 
-       return sys_signalfd(ufd, ksigmask, sizeof(sigset_t));
+       return sys_signalfd4(ufd, ksigmask, sizeof(sigset_t), flags);
 }
 
+asmlinkage long compat_sys_signalfd(int ufd,
+                                   const compat_sigset_t __user *sigmask,
+                                   compat_size_t sigsetsize)
+{
+       return compat_sys_signalfd4(ufd, sigmask, sigsetsize, 0);
+}
 #endif /* CONFIG_SIGNALFD */
 
 #ifdef CONFIG_TIMERFD