Skip to content

Commit 7e71609

Browse files
author
Al Viro
committed
pselect6() and friends: take handling the combined 6th/7th args into helper
... and use unsafe_get_user(), while we are at it. Signed-off-by: Al Viro <[email protected]>
1 parent b44f687 commit 7e71609

File tree

1 file changed

+64
-48
lines changed

1 file changed

+64
-48
lines changed

fs/select.c

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -766,22 +766,38 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
766766
* which has a pointer to the sigset_t itself followed by a size_t containing
767767
* the sigset size.
768768
*/
769+
struct sigset_argpack {
770+
sigset_t __user *p;
771+
size_t size;
772+
};
773+
774+
static inline int get_sigset_argpack(struct sigset_argpack *to,
775+
struct sigset_argpack __user *from)
776+
{
777+
// the path is hot enough for overhead of copy_from_user() to matter
778+
if (from) {
779+
if (!user_read_access_begin(from, sizeof(*from)))
780+
return -EFAULT;
781+
unsafe_get_user(to->p, &from->p, Efault);
782+
unsafe_get_user(to->size, &from->size, Efault);
783+
user_read_access_end();
784+
}
785+
return 0;
786+
Efault:
787+
user_access_end();
788+
return -EFAULT;
789+
}
790+
769791
SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp,
770792
fd_set __user *, exp, struct __kernel_timespec __user *, tsp,
771793
void __user *, sig)
772794
{
773-
size_t sigsetsize = 0;
774-
sigset_t __user *up = NULL;
775-
776-
if (sig) {
777-
if (!access_ok(sig, sizeof(void *)+sizeof(size_t))
778-
|| __get_user(up, (sigset_t __user * __user *)sig)
779-
|| __get_user(sigsetsize,
780-
(size_t __user *)(sig+sizeof(void *))))
781-
return -EFAULT;
782-
}
795+
struct sigset_argpack x = {NULL, 0};
796+
797+
if (get_sigset_argpack(&x, sig))
798+
return -EFAULT;
783799

784-
return do_pselect(n, inp, outp, exp, tsp, up, sigsetsize, PT_TIMESPEC);
800+
return do_pselect(n, inp, outp, exp, tsp, x.p, x.size, PT_TIMESPEC);
785801
}
786802

787803
#if defined(CONFIG_COMPAT_32BIT_TIME) && !defined(CONFIG_64BIT)
@@ -790,18 +806,12 @@ SYSCALL_DEFINE6(pselect6_time32, int, n, fd_set __user *, inp, fd_set __user *,
790806
fd_set __user *, exp, struct old_timespec32 __user *, tsp,
791807
void __user *, sig)
792808
{
793-
size_t sigsetsize = 0;
794-
sigset_t __user *up = NULL;
795-
796-
if (sig) {
797-
if (!access_ok(sig, sizeof(void *)+sizeof(size_t))
798-
|| __get_user(up, (sigset_t __user * __user *)sig)
799-
|| __get_user(sigsetsize,
800-
(size_t __user *)(sig+sizeof(void *))))
801-
return -EFAULT;
802-
}
809+
struct sigset_argpack x = {NULL, 0};
810+
811+
if (get_sigset_argpack(&x, sig))
812+
return -EFAULT;
803813

804-
return do_pselect(n, inp, outp, exp, tsp, up, sigsetsize, PT_OLD_TIMESPEC);
814+
return do_pselect(n, inp, outp, exp, tsp, x.p, x.size, PT_OLD_TIMESPEC);
805815
}
806816

807817
#endif
@@ -1325,24 +1335,37 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
13251335
return poll_select_finish(&end_time, tsp, type, ret);
13261336
}
13271337

1338+
struct compat_sigset_argpack {
1339+
compat_uptr_t p;
1340+
compat_size_t size;
1341+
};
1342+
static inline int get_compat_sigset_argpack(struct compat_sigset_argpack *to,
1343+
struct compat_sigset_argpack __user *from)
1344+
{
1345+
if (from) {
1346+
if (!user_read_access_begin(from, sizeof(*from)))
1347+
return -EFAULT;
1348+
unsafe_get_user(to->p, &from->p, Efault);
1349+
unsafe_get_user(to->size, &from->size, Efault);
1350+
user_read_access_end();
1351+
}
1352+
return 0;
1353+
Efault:
1354+
user_access_end();
1355+
return -EFAULT;
1356+
}
1357+
13281358
COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp,
13291359
compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
13301360
struct __kernel_timespec __user *, tsp, void __user *, sig)
13311361
{
1332-
compat_size_t sigsetsize = 0;
1333-
compat_uptr_t up = 0;
1334-
1335-
if (sig) {
1336-
if (!access_ok(sig,
1337-
sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
1338-
__get_user(up, (compat_uptr_t __user *)sig) ||
1339-
__get_user(sigsetsize,
1340-
(compat_size_t __user *)(sig+sizeof(up))))
1341-
return -EFAULT;
1342-
}
1362+
struct compat_sigset_argpack x = {0, 0};
1363+
1364+
if (get_compat_sigset_argpack(&x, sig))
1365+
return -EFAULT;
13431366

1344-
return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
1345-
sigsetsize, PT_TIMESPEC);
1367+
return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(x.p),
1368+
x.size, PT_TIMESPEC);
13461369
}
13471370

13481371
#if defined(CONFIG_COMPAT_32BIT_TIME)
@@ -1351,20 +1374,13 @@ COMPAT_SYSCALL_DEFINE6(pselect6_time32, int, n, compat_ulong_t __user *, inp,
13511374
compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
13521375
struct old_timespec32 __user *, tsp, void __user *, sig)
13531376
{
1354-
compat_size_t sigsetsize = 0;
1355-
compat_uptr_t up = 0;
1356-
1357-
if (sig) {
1358-
if (!access_ok(sig,
1359-
sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
1360-
__get_user(up, (compat_uptr_t __user *)sig) ||
1361-
__get_user(sigsetsize,
1362-
(compat_size_t __user *)(sig+sizeof(up))))
1363-
return -EFAULT;
1364-
}
1377+
struct compat_sigset_argpack x = {0, 0};
1378+
1379+
if (get_compat_sigset_argpack(&x, sig))
1380+
return -EFAULT;
13651381

1366-
return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
1367-
sigsetsize, PT_OLD_TIMESPEC);
1382+
return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(x.p),
1383+
x.size, PT_OLD_TIMESPEC);
13681384
}
13691385

13701386
#endif

0 commit comments

Comments
 (0)