Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/viro/vfs

Pull more vfs updates from Al Viro:
 "Embarrassing braino fix + pipe page accounting + fixing an eyesore in
  find_filesystem() (checking that s1 is equal to prefix of s2 of given
  length can be done in many ways, but "compare strlen(s1) with length
  and then do strncmp()" is not a good one...)"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  [regression] fix braino in fs/dlm/user.c
  pipe: limit the per-user amount of pages allocated in pipes
  find_filesystem(): simplify comparison
  • Loading branch information
torvalds committed Jan 22, 2016
2 parents 6fb11e6 + 117aa41 commit eadee0c
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 6 deletions.
23 changes: 23 additions & 0 deletions Documentation/sysctl/fs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs:
- nr_open
- overflowuid
- overflowgid
- pipe-user-pages-hard
- pipe-user-pages-soft
- protected_hardlinks
- protected_symlinks
- suid_dumpable
Expand Down Expand Up @@ -159,6 +161,27 @@ The default is 65534.

==============================================================

pipe-user-pages-hard:

Maximum total number of pages a non-privileged user may allocate for pipes.
Once this limit is reached, no new pipes may be allocated until usage goes
below the limit again. When set to 0, no limit is applied, which is the default
setting.

==============================================================

pipe-user-pages-soft:

Maximum total number of pages a non-privileged user may allocate for pipes
before the pipe size gets limited to a single page. Once this limit is reached,
new pipes will be limited to a single page in size for this user in order to
limit total memory usage, and trying to increase them using fcntl() will be
denied until usage goes below the limit again. The default value allows to
allocate up to 1024 pipes at their default size. When set to 0, no limit is
applied.

==============================================================

protected_hardlinks:

A long-standing class of security issues is the hardlink-based
Expand Down
2 changes: 1 addition & 1 deletion fs/dlm/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,
return -EINVAL;

kbuf = memdup_user_nul(buf, count);
if (!IS_ERR(kbuf))
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);

if (check_version(kbuf)) {
Expand Down
6 changes: 3 additions & 3 deletions fs/filesystems.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ void put_filesystem(struct file_system_type *fs)
static struct file_system_type **find_filesystem(const char *name, unsigned len)
{
struct file_system_type **p;
for (p=&file_systems; *p; p=&(*p)->next)
if (strlen((*p)->name) == len &&
strncmp((*p)->name, name, len) == 0)
for (p = &file_systems; *p; p = &(*p)->next)
if (strncmp((*p)->name, name, len) == 0 &&
!(*p)->name[len])
break;
return p;
}
Expand Down
47 changes: 45 additions & 2 deletions fs/pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ unsigned int pipe_max_size = 1048576;
*/
unsigned int pipe_min_size = PAGE_SIZE;

/* Maximum allocatable pages per user. Hard limit is unset by default, soft
* matches default values.
*/
unsigned long pipe_user_pages_hard;
unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;

/*
* We use a start+len construction, which provides full use of the
* allocated memory.
Expand Down Expand Up @@ -583,20 +589,49 @@ pipe_fasync(int fd, struct file *filp, int on)
return retval;
}

static void account_pipe_buffers(struct pipe_inode_info *pipe,
unsigned long old, unsigned long new)
{
atomic_long_add(new - old, &pipe->user->pipe_bufs);
}

static bool too_many_pipe_buffers_soft(struct user_struct *user)
{
return pipe_user_pages_soft &&
atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft;
}

static bool too_many_pipe_buffers_hard(struct user_struct *user)
{
return pipe_user_pages_hard &&
atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard;
}

struct pipe_inode_info *alloc_pipe_info(void)
{
struct pipe_inode_info *pipe;

pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
if (pipe) {
pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL);
unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
struct user_struct *user = get_current_user();

if (!too_many_pipe_buffers_hard(user)) {
if (too_many_pipe_buffers_soft(user))
pipe_bufs = 1;
pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL);
}

if (pipe->bufs) {
init_waitqueue_head(&pipe->wait);
pipe->r_counter = pipe->w_counter = 1;
pipe->buffers = PIPE_DEF_BUFFERS;
pipe->buffers = pipe_bufs;
pipe->user = user;
account_pipe_buffers(pipe, 0, pipe_bufs);
mutex_init(&pipe->mutex);
return pipe;
}
free_uid(user);
kfree(pipe);
}

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

account_pipe_buffers(pipe, pipe->buffers, 0);
free_uid(pipe->user);
for (i = 0; i < pipe->buffers; i++) {
struct pipe_buffer *buf = pipe->bufs + i;
if (buf->ops)
Expand Down Expand Up @@ -998,6 +1035,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
}

account_pipe_buffers(pipe, pipe->buffers, nr_pages);
pipe->curbuf = 0;
kfree(pipe->bufs);
pipe->bufs = bufs;
Expand Down Expand Up @@ -1069,6 +1107,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
ret = -EPERM;
goto out;
} else if ((too_many_pipe_buffers_hard(pipe->user) ||
too_many_pipe_buffers_soft(pipe->user)) &&
!capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
ret = -EPERM;
goto out;
}
ret = pipe_set_size(pipe, nr_pages);
break;
Expand Down
4 changes: 4 additions & 0 deletions include/linux/pipe_fs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct pipe_buffer {
* @fasync_readers: reader side fasync
* @fasync_writers: writer side fasync
* @bufs: the circular array of pipe buffers
* @user: the user who created this pipe
**/
struct pipe_inode_info {
struct mutex mutex;
Expand All @@ -57,6 +58,7 @@ struct pipe_inode_info {
struct fasync_struct *fasync_readers;
struct fasync_struct *fasync_writers;
struct pipe_buffer *bufs;
struct user_struct *user;
};

/*
Expand Down Expand Up @@ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info *);
void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *);

extern unsigned int pipe_max_size, pipe_min_size;
extern unsigned long pipe_user_pages_hard;
extern unsigned long pipe_user_pages_soft;
int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *);


Expand Down
1 change: 1 addition & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ struct user_struct {
#endif
unsigned long locked_shm; /* How many pages of mlocked shm ? */
unsigned long unix_inflight; /* How many files in flight in unix sockets */
atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */

#ifdef CONFIG_KEYS
struct key *uid_keyring; /* UID specific keyring */
Expand Down
14 changes: 14 additions & 0 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,20 @@ static struct ctl_table fs_table[] = {
.proc_handler = &pipe_proc_fn,
.extra1 = &pipe_min_size,
},
{
.procname = "pipe-user-pages-hard",
.data = &pipe_user_pages_hard,
.maxlen = sizeof(pipe_user_pages_hard),
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
{
.procname = "pipe-user-pages-soft",
.data = &pipe_user_pages_soft,
.maxlen = sizeof(pipe_user_pages_soft),
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
{ }
};

Expand Down

0 comments on commit eadee0c

Please sign in to comment.