diff options
Diffstat (limited to 'fs/select.c')
| -rw-r--r-- | fs/select.c | 50 |
1 files changed, 27 insertions, 23 deletions
diff --git a/fs/select.c b/fs/select.c index e0244dbe4429..95d76531015a 100644 --- a/fs/select.c +++ b/fs/select.c @@ -150,7 +150,7 @@ void poll_freewait(struct poll_wqueues *pwq) } while (entry > p->entries); old = p; p = p->next; - free_page((unsigned long) old); + kfree(old); } } EXPORT_SYMBOL(poll_freewait); @@ -165,7 +165,7 @@ static struct poll_table_entry *poll_get_entry(struct poll_wqueues *p) if (!table || POLL_TABLE_FULL(table)) { struct poll_table_page *new_table; - new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL); + new_table = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!new_table) { p->error = -ENOMEM; return NULL; @@ -708,6 +708,17 @@ static int kern_select(int n, fd_set __user *inp, fd_set __user *outp, if (copy_from_user(&tv, tvp, sizeof(tv))) return -EFAULT; + /* + * Reject negative components before normalisation. The seconds + * sum below is performed in signed long and a crafted negative + * timeval can wrap to a positive value that passes + * timespec64_valid() and turns into an effectively-infinite + * deadline via timespec64_add_safe()'s saturation, instead of + * the -EINVAL POSIX requires for negative timeouts. + */ + if (tv.tv_sec < 0 || tv.tv_usec < 0) + return -EINVAL; + to = &end_time; if (poll_select_set_timeout(to, tv.tv_sec + (tv.tv_usec / USEC_PER_SEC), @@ -1004,17 +1015,17 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, fdcount = do_poll(head, &table, end_time); poll_freewait(&table); - if (!user_write_access_begin(ufds, nfds * sizeof(*ufds))) - goto out_fds; + scoped_user_write_access_size(ufds, nfds * sizeof(*ufds), out_fds) { + struct pollfd __user *_ufds = ufds; - for (walk = head; walk; walk = walk->next) { - struct pollfd *fds = walk->entries; - unsigned int j; + for (walk = head; walk; walk = walk->next) { + struct pollfd *fds = walk->entries; + unsigned int j; - for (j = walk->len; j; fds++, ufds++, j--) - unsafe_put_user(fds->revents, &ufds->revents, Efault); - } - user_write_access_end(); + for (j = walk->len; j; fds++, _ufds++, j--) + unsafe_put_user(fds->revents, &_ufds->revents, out_fds); + } + } err = fdcount; out_fds: @@ -1026,11 +1037,6 @@ out_fds: } return err; - -Efault: - user_write_access_end(); - err = -EFAULT; - goto out_fds; } static long do_restart_poll(struct restart_block *restart_block) @@ -1338,15 +1344,13 @@ static inline int get_compat_sigset_argpack(struct compat_sigset_argpack *to, struct compat_sigset_argpack __user *from) { if (from) { - if (!user_read_access_begin(from, sizeof(*from))) - return -EFAULT; - unsafe_get_user(to->p, &from->p, Efault); - unsafe_get_user(to->size, &from->size, Efault); - user_read_access_end(); + scoped_user_read_access(from, efault) { + unsafe_get_user(to->p, &from->p, efault); + unsafe_get_user(to->size, &from->size, efault); + } } return 0; -Efault: - user_read_access_end(); +efault: return -EFAULT; } |
