Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/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
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
47
fs/pipe.c
47
fs/pipe.c
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user