diff options
author | Eric Biggers <ebiggers@google.com> | 2018-02-06 18:41:53 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-06 21:32:47 -0500 |
commit | 85c2dd5473b2718b4b63e74bfeb1ca876868e11f (patch) | |
tree | ed089d058c979a7ff1b2e70b0087edb7f11bc75b | |
parent | 319e0a21bb7823abbb4818fe2724e572bbac77a2 (diff) |
pipe: actually allow root to exceed the pipe buffer limits
pipe-user-pages-hard and pipe-user-pages-soft are only supposed to apply
to unprivileged users, as documented in both Documentation/sysctl/fs.txt
and the pipe(7) man page.
However, the capabilities are actually only checked when increasing a
pipe's size using F_SETPIPE_SZ, not when creating a new pipe. Therefore,
if pipe-user-pages-hard has been set, the root user can run into it and be
unable to create pipes. Similarly, if pipe-user-pages-soft has been set,
the root user can run into it and have their pipes limited to 1 page each.
Fix this by allowing the privileged override in both cases.
Link: http://lkml.kernel.org/r/20180111052902.14409-4-ebiggers3@gmail.com
Fixes: 759c01142a5d ("pipe: limit the per-user amount of pages allocated in pipes")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Acked-by: Kees Cook <keescook@chromium.org>
Acked-by: Joe Lawrence <joe.lawrence@redhat.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: "Luis R . Rodriguez" <mcgrof@kernel.org>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: Willy Tarreau <w@1wt.eu>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/pipe.c | 11 |
1 files changed, 8 insertions, 3 deletions
@@ -613,6 +613,11 @@ static bool too_many_pipe_buffers_hard(unsigned long user_bufs) | |||
613 | return pipe_user_pages_hard && user_bufs >= pipe_user_pages_hard; | 613 | return pipe_user_pages_hard && user_bufs >= pipe_user_pages_hard; |
614 | } | 614 | } |
615 | 615 | ||
616 | static bool is_unprivileged_user(void) | ||
617 | { | ||
618 | return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN); | ||
619 | } | ||
620 | |||
616 | struct pipe_inode_info *alloc_pipe_info(void) | 621 | struct pipe_inode_info *alloc_pipe_info(void) |
617 | { | 622 | { |
618 | struct pipe_inode_info *pipe; | 623 | struct pipe_inode_info *pipe; |
@@ -629,12 +634,12 @@ struct pipe_inode_info *alloc_pipe_info(void) | |||
629 | 634 | ||
630 | user_bufs = account_pipe_buffers(user, 0, pipe_bufs); | 635 | user_bufs = account_pipe_buffers(user, 0, pipe_bufs); |
631 | 636 | ||
632 | if (too_many_pipe_buffers_soft(user_bufs)) { | 637 | if (too_many_pipe_buffers_soft(user_bufs) && is_unprivileged_user()) { |
633 | user_bufs = account_pipe_buffers(user, pipe_bufs, 1); | 638 | user_bufs = account_pipe_buffers(user, pipe_bufs, 1); |
634 | pipe_bufs = 1; | 639 | pipe_bufs = 1; |
635 | } | 640 | } |
636 | 641 | ||
637 | if (too_many_pipe_buffers_hard(user_bufs)) | 642 | if (too_many_pipe_buffers_hard(user_bufs) && is_unprivileged_user()) |
638 | goto out_revert_acct; | 643 | goto out_revert_acct; |
639 | 644 | ||
640 | pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer), | 645 | pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer), |
@@ -1065,7 +1070,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) | |||
1065 | if (nr_pages > pipe->buffers && | 1070 | if (nr_pages > pipe->buffers && |
1066 | (too_many_pipe_buffers_hard(user_bufs) || | 1071 | (too_many_pipe_buffers_hard(user_bufs) || |
1067 | too_many_pipe_buffers_soft(user_bufs)) && | 1072 | too_many_pipe_buffers_soft(user_bufs)) && |
1068 | !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { | 1073 | is_unprivileged_user()) { |
1069 | ret = -EPERM; | 1074 | ret = -EPERM; |
1070 | goto out_revert_acct; | 1075 | goto out_revert_acct; |
1071 | } | 1076 | } |