forked from mirrors/linux
		
	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 <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									b44f687386
								
							
						
					
					
						commit
						7e71609f64
					
				
					 1 changed files with 60 additions and 44 deletions
				
			
		
							
								
								
									
										104
									
								
								fs/select.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								fs/select.c
									
									
									
									
									
								
							| 
						 | 
					@ -766,22 +766,38 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 | 
				
			||||||
 * which has a pointer to the sigset_t itself followed by a size_t containing
 | 
					 * which has a pointer to the sigset_t itself followed by a size_t containing
 | 
				
			||||||
 * the sigset size.
 | 
					 * the sigset size.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					struct sigset_argpack {
 | 
				
			||||||
 | 
						sigset_t __user *p;
 | 
				
			||||||
 | 
						size_t size;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int get_sigset_argpack(struct sigset_argpack *to,
 | 
				
			||||||
 | 
									     struct sigset_argpack __user *from)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// the path is hot enough for overhead of copy_from_user() to matter
 | 
				
			||||||
 | 
						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();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					Efault:
 | 
				
			||||||
 | 
						user_access_end();
 | 
				
			||||||
 | 
						return -EFAULT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp,
 | 
					SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp,
 | 
				
			||||||
		fd_set __user *, exp, struct __kernel_timespec __user *, tsp,
 | 
							fd_set __user *, exp, struct __kernel_timespec __user *, tsp,
 | 
				
			||||||
		void __user *, sig)
 | 
							void __user *, sig)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t sigsetsize = 0;
 | 
						struct sigset_argpack x = {NULL, 0};
 | 
				
			||||||
	sigset_t __user *up = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sig) {
 | 
						if (get_sigset_argpack(&x, sig))
 | 
				
			||||||
		if (!access_ok(sig, sizeof(void *)+sizeof(size_t))
 | 
							return -EFAULT;
 | 
				
			||||||
		    || __get_user(up, (sigset_t __user * __user *)sig)
 | 
					 | 
				
			||||||
		    || __get_user(sigsetsize,
 | 
					 | 
				
			||||||
				(size_t __user *)(sig+sizeof(void *))))
 | 
					 | 
				
			||||||
			return -EFAULT;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return do_pselect(n, inp, outp, exp, tsp, up, sigsetsize, PT_TIMESPEC);
 | 
						return do_pselect(n, inp, outp, exp, tsp, x.p, x.size, PT_TIMESPEC);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(CONFIG_COMPAT_32BIT_TIME) && !defined(CONFIG_64BIT)
 | 
					#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 *,
 | 
				
			||||||
		fd_set __user *, exp, struct old_timespec32 __user *, tsp,
 | 
							fd_set __user *, exp, struct old_timespec32 __user *, tsp,
 | 
				
			||||||
		void __user *, sig)
 | 
							void __user *, sig)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t sigsetsize = 0;
 | 
						struct sigset_argpack x = {NULL, 0};
 | 
				
			||||||
	sigset_t __user *up = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sig) {
 | 
						if (get_sigset_argpack(&x, sig))
 | 
				
			||||||
		if (!access_ok(sig, sizeof(void *)+sizeof(size_t))
 | 
							return -EFAULT;
 | 
				
			||||||
		    || __get_user(up, (sigset_t __user * __user *)sig)
 | 
					 | 
				
			||||||
		    || __get_user(sigsetsize,
 | 
					 | 
				
			||||||
				(size_t __user *)(sig+sizeof(void *))))
 | 
					 | 
				
			||||||
			return -EFAULT;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return do_pselect(n, inp, outp, exp, tsp, up, sigsetsize, PT_OLD_TIMESPEC);
 | 
						return do_pselect(n, inp, outp, exp, tsp, x.p, x.size, PT_OLD_TIMESPEC);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -1325,24 +1335,37 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 | 
				
			||||||
	return poll_select_finish(&end_time, tsp, type, ret);
 | 
						return poll_select_finish(&end_time, tsp, type, ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct compat_sigset_argpack {
 | 
				
			||||||
 | 
						compat_uptr_t p;
 | 
				
			||||||
 | 
						compat_size_t size;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					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();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					Efault:
 | 
				
			||||||
 | 
						user_access_end();
 | 
				
			||||||
 | 
						return -EFAULT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp,
 | 
					COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp,
 | 
				
			||||||
	compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
 | 
						compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
 | 
				
			||||||
	struct __kernel_timespec __user *, tsp, void __user *, sig)
 | 
						struct __kernel_timespec __user *, tsp, void __user *, sig)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	compat_size_t sigsetsize = 0;
 | 
						struct compat_sigset_argpack x = {0, 0};
 | 
				
			||||||
	compat_uptr_t up = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sig) {
 | 
						if (get_compat_sigset_argpack(&x, sig))
 | 
				
			||||||
		if (!access_ok(sig,
 | 
							return -EFAULT;
 | 
				
			||||||
				sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
 | 
					 | 
				
			||||||
				__get_user(up, (compat_uptr_t __user *)sig) ||
 | 
					 | 
				
			||||||
				__get_user(sigsetsize,
 | 
					 | 
				
			||||||
				(compat_size_t __user *)(sig+sizeof(up))))
 | 
					 | 
				
			||||||
			return -EFAULT;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
 | 
						return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(x.p),
 | 
				
			||||||
				 sigsetsize, PT_TIMESPEC);
 | 
									 x.size, PT_TIMESPEC);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(CONFIG_COMPAT_32BIT_TIME)
 | 
					#if defined(CONFIG_COMPAT_32BIT_TIME)
 | 
				
			||||||
| 
						 | 
					@ -1351,20 +1374,13 @@ COMPAT_SYSCALL_DEFINE6(pselect6_time32, int, n, compat_ulong_t __user *, inp,
 | 
				
			||||||
	compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
 | 
						compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
 | 
				
			||||||
	struct old_timespec32 __user *, tsp, void __user *, sig)
 | 
						struct old_timespec32 __user *, tsp, void __user *, sig)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	compat_size_t sigsetsize = 0;
 | 
						struct compat_sigset_argpack x = {0, 0};
 | 
				
			||||||
	compat_uptr_t up = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sig) {
 | 
						if (get_compat_sigset_argpack(&x, sig))
 | 
				
			||||||
		if (!access_ok(sig,
 | 
							return -EFAULT;
 | 
				
			||||||
				sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
 | 
					 | 
				
			||||||
		    	__get_user(up, (compat_uptr_t __user *)sig) ||
 | 
					 | 
				
			||||||
		    	__get_user(sigsetsize,
 | 
					 | 
				
			||||||
				(compat_size_t __user *)(sig+sizeof(up))))
 | 
					 | 
				
			||||||
			return -EFAULT;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
 | 
						return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(x.p),
 | 
				
			||||||
				 sigsetsize, PT_OLD_TIMESPEC);
 | 
									 x.size, PT_OLD_TIMESPEC);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue