Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b249b1b authored by Al Viro's avatar Al Viro
Browse files

pipe(2) - race-free error recovery



don't mess with sys_close() if copy_to_user() fails; just postpone
fd_install() until we know it hasn't.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent a79f41ed
Loading
Loading
Loading
Loading
+22 −9
Original line number Diff line number Diff line
@@ -1064,9 +1064,8 @@ int create_pipe_files(struct file **res, int flags)
	return err;
}

int do_pipe_flags(int *fd, int flags)
static int __do_pipe_flags(int *fd, struct file **files, int flags)
{
	struct file *files[2];
	int error;
	int fdw, fdr;

@@ -1088,11 +1087,8 @@ int do_pipe_flags(int *fd, int flags)
	fdw = error;

	audit_fd_pair(fdr, fdw);
	fd_install(fdr, files[0]);
	fd_install(fdw, files[1]);
	fd[0] = fdr;
	fd[1] = fdw;

	return 0;

 err_fdr:
@@ -1103,21 +1099,38 @@ int do_pipe_flags(int *fd, int flags)
	return error;
}

int do_pipe_flags(int *fd, int flags)
{
	struct file *files[2];
	int error = __do_pipe_flags(fd, files, flags);
	if (!error) {
		fd_install(fd[0], files[0]);
		fd_install(fd[1], files[1]);
	}
	return error;
}

/*
 * sys_pipe() is the normal C calling standard for creating
 * a pipe. It's not the way Unix traditionally does this, though.
 */
SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags)
{
	struct file *files[2];
	int fd[2];
	int error;

	error = do_pipe_flags(fd, flags);
	error = __do_pipe_flags(fd, files, flags);
	if (!error) {
		if (copy_to_user(fildes, fd, sizeof(fd))) {
			sys_close(fd[0]);
			sys_close(fd[1]);
		if (unlikely(copy_to_user(fildes, fd, sizeof(fd)))) {
			fput(files[0]);
			fput(files[1]);
			put_unused_fd(fd[0]);
			put_unused_fd(fd[1]);
			error = -EFAULT;
		} else {
			fd_install(fd[0], files[0]);
			fd_install(fd[1], files[1]);
		}
	}
	return error;