Skip to content

Commit 759c011

Browse files
wtarreauAl Viro
authored and
Al Viro
committed
pipe: limit the per-user amount of pages allocated in pipes
On no-so-small systems, it is possible for a single process to cause an OOM condition by filling large pipes with data that are never read. A typical process filling 4000 pipes with 1 MB of data will use 4 GB of memory. On small systems it may be tricky to set the pipe max size to prevent this from happening. This patch makes it possible to enforce a per-user soft limit above which new pipes will be limited to a single page, effectively limiting them to 4 kB each, as well as a hard limit above which no new pipes may be created for this user. This has the effect of protecting the system against memory abuse without hurting other users, and still allowing pipes to work correctly though with less data at once. The limit are controlled by two new sysctls : pipe-user-pages-soft, and pipe-user-pages-hard. Both may be disabled by setting them to zero. The default soft limit allows the default number of FDs per process (1024) to create pipes of the default size (64kB), thus reaching a limit of 64MB before starting to create only smaller pipes. With 256 processes limited to 1024 FDs each, this results in 1024*64kB + (256*1024 - 1024) * 4kB = 1084 MB of memory allocated for a user. The hard limit is disabled by default to avoid breaking existing applications that make intensive use of pipes (eg: for splicing). Reported-by: socketpair@gmail.com Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Mitigates: CVE-2013-4312 (Linux 2.0+) Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Willy Tarreau <w@1wt.eu> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 558041d commit 759c011

File tree

5 files changed

+87
-2
lines changed

5 files changed

+87
-2
lines changed

Diff for: Documentation/sysctl/fs.txt

+23
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs:
3232
- nr_open
3333
- overflowuid
3434
- overflowgid
35+
- pipe-user-pages-hard
36+
- pipe-user-pages-soft
3537
- protected_hardlinks
3638
- protected_symlinks
3739
- suid_dumpable
@@ -159,6 +161,27 @@ The default is 65534.
159161

160162
==============================================================
161163

164+
pipe-user-pages-hard:
165+
166+
Maximum total number of pages a non-privileged user may allocate for pipes.
167+
Once this limit is reached, no new pipes may be allocated until usage goes
168+
below the limit again. When set to 0, no limit is applied, which is the default
169+
setting.
170+
171+
==============================================================
172+
173+
pipe-user-pages-soft:
174+
175+
Maximum total number of pages a non-privileged user may allocate for pipes
176+
before the pipe size gets limited to a single page. Once this limit is reached,
177+
new pipes will be limited to a single page in size for this user in order to
178+
limit total memory usage, and trying to increase them using fcntl() will be
179+
denied until usage goes below the limit again. The default value allows to
180+
allocate up to 1024 pipes at their default size. When set to 0, no limit is
181+
applied.
182+
183+
==============================================================
184+
162185
protected_hardlinks:
163186

164187
A long-standing class of security issues is the hardlink-based

Diff for: fs/pipe.c

+45-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ unsigned int pipe_max_size = 1048576;
3838
*/
3939
unsigned int pipe_min_size = PAGE_SIZE;
4040

41+
/* Maximum allocatable pages per user. Hard limit is unset by default, soft
42+
* matches default values.
43+
*/
44+
unsigned long pipe_user_pages_hard;
45+
unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;
46+
4147
/*
4248
* We use a start+len construction, which provides full use of the
4349
* allocated memory.
@@ -583,20 +589,49 @@ pipe_fasync(int fd, struct file *filp, int on)
583589
return retval;
584590
}
585591

592+
static void account_pipe_buffers(struct pipe_inode_info *pipe,
593+
unsigned long old, unsigned long new)
594+
{
595+
atomic_long_add(new - old, &pipe->user->pipe_bufs);
596+
}
597+
598+
static bool too_many_pipe_buffers_soft(struct user_struct *user)
599+
{
600+
return pipe_user_pages_soft &&
601+
atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft;
602+
}
603+
604+
static bool too_many_pipe_buffers_hard(struct user_struct *user)
605+
{
606+
return pipe_user_pages_hard &&
607+
atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard;
608+
}
609+
586610
struct pipe_inode_info *alloc_pipe_info(void)
587611
{
588612
struct pipe_inode_info *pipe;
589613

590614
pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
591615
if (pipe) {
592-
pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL);
616+
unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
617+
struct user_struct *user = get_current_user();
618+
619+
if (!too_many_pipe_buffers_hard(user)) {
620+
if (too_many_pipe_buffers_soft(user))
621+
pipe_bufs = 1;
622+
pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL);
623+
}
624+
593625
if (pipe->bufs) {
594626
init_waitqueue_head(&pipe->wait);
595627
pipe->r_counter = pipe->w_counter = 1;
596-
pipe->buffers = PIPE_DEF_BUFFERS;
628+
pipe->buffers = pipe_bufs;
629+
pipe->user = user;
630+
account_pipe_buffers(pipe, 0, pipe_bufs);
597631
mutex_init(&pipe->mutex);
598632
return pipe;
599633
}
634+
free_uid(user);
600635
kfree(pipe);
601636
}
602637

@@ -607,6 +642,8 @@ void free_pipe_info(struct pipe_inode_info *pipe)
607642
{
608643
int i;
609644

645+
account_pipe_buffers(pipe, pipe->buffers, 0);
646+
free_uid(pipe->user);
610647
for (i = 0; i < pipe->buffers; i++) {
611648
struct pipe_buffer *buf = pipe->bufs + i;
612649
if (buf->ops)
@@ -998,6 +1035,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
9981035
memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
9991036
}
10001037

1038+
account_pipe_buffers(pipe, pipe->buffers, nr_pages);
10011039
pipe->curbuf = 0;
10021040
kfree(pipe->bufs);
10031041
pipe->bufs = bufs;
@@ -1069,6 +1107,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
10691107
if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
10701108
ret = -EPERM;
10711109
goto out;
1110+
} else if ((too_many_pipe_buffers_hard(pipe->user) ||
1111+
too_many_pipe_buffers_soft(pipe->user)) &&
1112+
!capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
1113+
ret = -EPERM;
1114+
goto out;
10721115
}
10731116
ret = pipe_set_size(pipe, nr_pages);
10741117
break;

Diff for: include/linux/pipe_fs_i.h

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct pipe_buffer {
4242
* @fasync_readers: reader side fasync
4343
* @fasync_writers: writer side fasync
4444
* @bufs: the circular array of pipe buffers
45+
* @user: the user who created this pipe
4546
**/
4647
struct pipe_inode_info {
4748
struct mutex mutex;
@@ -57,6 +58,7 @@ struct pipe_inode_info {
5758
struct fasync_struct *fasync_readers;
5859
struct fasync_struct *fasync_writers;
5960
struct pipe_buffer *bufs;
61+
struct user_struct *user;
6062
};
6163

6264
/*
@@ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info *);
123125
void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *);
124126

125127
extern unsigned int pipe_max_size, pipe_min_size;
128+
extern unsigned long pipe_user_pages_hard;
129+
extern unsigned long pipe_user_pages_soft;
126130
int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *);
127131

128132

Diff for: include/linux/sched.h

+1
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ struct user_struct {
835835
#endif
836836
unsigned long locked_shm; /* How many pages of mlocked shm ? */
837837
unsigned long unix_inflight; /* How many files in flight in unix sockets */
838+
atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */
838839

839840
#ifdef CONFIG_KEYS
840841
struct key *uid_keyring; /* UID specific keyring */

Diff for: kernel/sysctl.c

+14
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,20 @@ static struct ctl_table fs_table[] = {
17571757
.proc_handler = &pipe_proc_fn,
17581758
.extra1 = &pipe_min_size,
17591759
},
1760+
{
1761+
.procname = "pipe-user-pages-hard",
1762+
.data = &pipe_user_pages_hard,
1763+
.maxlen = sizeof(pipe_user_pages_hard),
1764+
.mode = 0644,
1765+
.proc_handler = proc_doulongvec_minmax,
1766+
},
1767+
{
1768+
.procname = "pipe-user-pages-soft",
1769+
.data = &pipe_user_pages_soft,
1770+
.maxlen = sizeof(pipe_user_pages_soft),
1771+
.mode = 0644,
1772+
.proc_handler = proc_doulongvec_minmax,
1773+
},
17601774
{ }
17611775
};
17621776

0 commit comments

Comments
 (0)