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/ebiederm/user-namespace

Pull namespace updates from Eric Biederman:
 "This is a bunch of small changes built against 3.16-rc6.  The most
  significant change for users is the first patch which makes setns
  drmatically faster by removing unneded rcu handling.

  The next chunk of changes are so that "mount -o remount,.." will not
  allow the user namespace root to drop flags on a mount set by the
  system wide root.  Aks this forces read-only mounts to stay read-only,
  no-dev mounts to stay no-dev, no-suid mounts to stay no-suid, no-exec
  mounts to stay no exec and it prevents unprivileged users from messing
  with a mounts atime settings.  I have included my test case as the
  last patch in this series so people performing backports can verify
  this change works correctly.

  The next change fixes a bug in NFS that was discovered while auditing
  nsproxy users for the first optimization.  Today you can oops the
  kernel by reading /proc/fs/nfsfs/{servers,volumes} if you are clever
  with pid namespaces.  I rebased and fixed the build of the
  !CONFIG_NFS_FS case yesterday when a build bot caught my typo.  Given
  that no one to my knowledge bases anything on my tree fixing the typo
  in place seems more responsible that requiring a typo-fix to be
  backported as well.

  The last change is a small semantic cleanup introducing
  /proc/thread-self and pointing /proc/mounts and /proc/net at it.  This
  prevents several kinds of problemantic corner cases.  It is a
  user-visible change so it has a minute chance of causing regressions
  so the change to /proc/mounts and /proc/net are individual one line
  commits that can be trivially reverted.  Unfortunately I lost and
  could not find the email of the original reporter so he is not
  credited.  From at least one perspective this change to /proc/net is a
  refgression fix to allow pthread /proc/net uses that were broken by
  the introduction of the network namespace"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  proc: Point /proc/mounts at /proc/thread-self/mounts instead of /proc/self/mounts
  proc: Point /proc/net at /proc/thread-self/net instead of /proc/self/net
  proc: Implement /proc/thread-self to point at the directory of the current thread
  proc: Have net show up under /proc/<tgid>/task/<tid>
  NFS: Fix /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes
  mnt: Add tests for unprivileged remount cases that have found to be faulty
  mnt: Change the default remount atime from relatime to the existing value
  mnt: Correct permission checks in do_remount
  mnt: Move the test for MNT_LOCK_READONLY from change_mount_flags into do_remount
  mnt: Only change user settable mount flags in remount
  namespaces: Use task_lock and not rcu to protect nsproxy
  • Loading branch information
torvalds committed Aug 10, 2014
2 parents 96784de + 344470c commit 77e40aa
Show file tree
Hide file tree
Showing 23 changed files with 537 additions and 97 deletions.
65 changes: 55 additions & 10 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -890,8 +890,21 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,

mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED);
/* Don't allow unprivileged users to change mount flags */
if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
if (flag & CL_UNPRIVILEGED) {
mnt->mnt.mnt_flags |= MNT_LOCK_ATIME;

if (mnt->mnt.mnt_flags & MNT_READONLY)
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;

if (mnt->mnt.mnt_flags & MNT_NODEV)
mnt->mnt.mnt_flags |= MNT_LOCK_NODEV;

if (mnt->mnt.mnt_flags & MNT_NOSUID)
mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID;

if (mnt->mnt.mnt_flags & MNT_NOEXEC)
mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC;
}

/* Don't allow unprivileged users to reveal what is under a mount */
if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire))
Expand Down Expand Up @@ -1896,9 +1909,6 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
if (readonly_request == __mnt_is_readonly(mnt))
return 0;

if (mnt->mnt_flags & MNT_LOCK_READONLY)
return -EPERM;

if (readonly_request)
error = mnt_make_readonly(real_mount(mnt));
else
Expand All @@ -1924,6 +1934,33 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
if (path->dentry != path->mnt->mnt_root)
return -EINVAL;

/* Don't allow changing of locked mnt flags.
*
* No locks need to be held here while testing the various
* MNT_LOCK flags because those flags can never be cleared
* once they are set.
*/
if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) &&
!(mnt_flags & MNT_READONLY)) {
return -EPERM;
}
if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) &&
!(mnt_flags & MNT_NODEV)) {
return -EPERM;
}
if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) &&
!(mnt_flags & MNT_NOSUID)) {
return -EPERM;
}
if ((mnt->mnt.mnt_flags & MNT_LOCK_NOEXEC) &&
!(mnt_flags & MNT_NOEXEC)) {
return -EPERM;
}
if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) &&
((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK))) {
return -EPERM;
}

err = security_sb_remount(sb, data);
if (err)
return err;
Expand All @@ -1937,7 +1974,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
err = do_remount_sb(sb, flags, data, 0);
if (!err) {
lock_mount_hash();
mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK;
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
mnt->mnt.mnt_flags = mnt_flags;
touch_mnt_namespace(mnt->mnt_ns);
unlock_mount_hash();
Expand Down Expand Up @@ -2122,7 +2159,7 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,
*/
if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) {
flags |= MS_NODEV;
mnt_flags |= MNT_NODEV;
mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV;
}
}

Expand Down Expand Up @@ -2436,6 +2473,14 @@ long do_mount(const char *dev_name, const char *dir_name,
if (flags & MS_RDONLY)
mnt_flags |= MNT_READONLY;

/* The default atime for remount is preservation */
if ((flags & MS_REMOUNT) &&
((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME |
MS_STRICTATIME)) == 0)) {
mnt_flags &= ~MNT_ATIME_MASK;
mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK;
}

flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
MS_STRICTATIME);
Expand Down Expand Up @@ -2972,13 +3017,13 @@ static void *mntns_get(struct task_struct *task)
struct mnt_namespace *ns = NULL;
struct nsproxy *nsproxy;

rcu_read_lock();
nsproxy = task_nsproxy(task);
task_lock(task);
nsproxy = task->nsproxy;
if (nsproxy) {
ns = nsproxy->mnt_ns;
get_mnt_ns(ns);
}
rcu_read_unlock();
task_unlock(task);

return ns;
}
Expand Down
95 changes: 55 additions & 40 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ static const struct file_operations nfs_server_list_fops = {
.open = nfs_server_list_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.release = seq_release_net,
.owner = THIS_MODULE,
};

Expand All @@ -1226,7 +1226,7 @@ static const struct file_operations nfs_volume_list_fops = {
.open = nfs_volume_list_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.release = seq_release_net,
.owner = THIS_MODULE,
};

Expand All @@ -1236,27 +1236,16 @@ static const struct file_operations nfs_volume_list_fops = {
*/
static int nfs_server_list_open(struct inode *inode, struct file *file)
{
struct seq_file *m;
int ret;
struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
struct net *net = pid_ns->child_reaper->nsproxy->net_ns;

ret = seq_open(file, &nfs_server_list_ops);
if (ret < 0)
return ret;

m = file->private_data;
m->private = net;

return 0;
return seq_open_net(inode, file, &nfs_server_list_ops,
sizeof(struct seq_net_private));
}

/*
* set up the iterator to start reading from the server list and return the first item
*/
static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
{
struct nfs_net *nn = net_generic(m->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);

/* lock the list against modification */
spin_lock(&nn->nfs_client_lock);
Expand All @@ -1268,7 +1257,7 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
*/
static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
{
struct nfs_net *nn = net_generic(p->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);

return seq_list_next(v, &nn->nfs_client_list, pos);
}
Expand All @@ -1278,7 +1267,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
*/
static void nfs_server_list_stop(struct seq_file *p, void *v)
{
struct nfs_net *nn = net_generic(p->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);

spin_unlock(&nn->nfs_client_lock);
}
Expand All @@ -1289,7 +1278,7 @@ static void nfs_server_list_stop(struct seq_file *p, void *v)
static int nfs_server_list_show(struct seq_file *m, void *v)
{
struct nfs_client *clp;
struct nfs_net *nn = net_generic(m->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);

/* display header on line 1 */
if (v == &nn->nfs_client_list) {
Expand Down Expand Up @@ -1321,27 +1310,16 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
*/
static int nfs_volume_list_open(struct inode *inode, struct file *file)
{
struct seq_file *m;
int ret;
struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
struct net *net = pid_ns->child_reaper->nsproxy->net_ns;

ret = seq_open(file, &nfs_volume_list_ops);
if (ret < 0)
return ret;

m = file->private_data;
m->private = net;

return 0;
return seq_open_net(inode, file, &nfs_server_list_ops,
sizeof(struct seq_net_private));
}

/*
* set up the iterator to start reading from the volume list and return the first item
*/
static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
{
struct nfs_net *nn = net_generic(m->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);

/* lock the list against modification */
spin_lock(&nn->nfs_client_lock);
Expand All @@ -1353,7 +1331,7 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
*/
static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
{
struct nfs_net *nn = net_generic(p->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);

return seq_list_next(v, &nn->nfs_volume_list, pos);
}
Expand All @@ -1363,7 +1341,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
*/
static void nfs_volume_list_stop(struct seq_file *p, void *v)
{
struct nfs_net *nn = net_generic(p->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);

spin_unlock(&nn->nfs_client_lock);
}
Expand All @@ -1376,7 +1354,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
struct nfs_server *server;
struct nfs_client *clp;
char dev[8], fsid[17];
struct nfs_net *nn = net_generic(m->private, nfs_net_id);
struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);

/* display header on line 1 */
if (v == &nn->nfs_volume_list) {
Expand Down Expand Up @@ -1407,6 +1385,45 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
return 0;
}

int nfs_fs_proc_net_init(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct proc_dir_entry *p;

nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net);
if (!nn->proc_nfsfs)
goto error_0;

/* a file of servers with which we're dealing */
p = proc_create("servers", S_IFREG|S_IRUGO,
nn->proc_nfsfs, &nfs_server_list_fops);
if (!p)
goto error_1;

/* a file of volumes that we have mounted */
p = proc_create("volumes", S_IFREG|S_IRUGO,
nn->proc_nfsfs, &nfs_volume_list_fops);
if (!p)
goto error_2;
return 0;

error_2:
remove_proc_entry("servers", nn->proc_nfsfs);
error_1:
remove_proc_entry("fs/nfsfs", NULL);
error_0:
return -ENOMEM;
}

void nfs_fs_proc_net_exit(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);

remove_proc_entry("volumes", nn->proc_nfsfs);
remove_proc_entry("servers", nn->proc_nfsfs);
remove_proc_entry("fs/nfsfs", NULL);
}

/*
* initialise the /proc/fs/nfsfs/ directory
*/
Expand All @@ -1419,14 +1436,12 @@ int __init nfs_fs_proc_init(void)
goto error_0;

/* a file of servers with which we're dealing */
p = proc_create("servers", S_IFREG|S_IRUGO,
proc_fs_nfs, &nfs_server_list_fops);
p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers");
if (!p)
goto error_1;

/* a file of volumes that we have mounted */
p = proc_create("volumes", S_IFREG|S_IRUGO,
proc_fs_nfs, &nfs_volume_list_fops);
p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes");
if (!p)
goto error_2;
return 0;
Expand Down
3 changes: 2 additions & 1 deletion fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1840,11 +1840,12 @@ EXPORT_SYMBOL_GPL(nfs_net_id);
static int nfs_net_init(struct net *net)
{
nfs_clients_init(net);
return 0;
return nfs_fs_proc_net_init(net);
}

static void nfs_net_exit(struct net *net)
{
nfs_fs_proc_net_exit(net);
nfs_cleanup_cb_ident_idr(net);
}

Expand Down
9 changes: 9 additions & 0 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,16 @@ extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
#ifdef CONFIG_PROC_FS
extern int __init nfs_fs_proc_init(void);
extern void nfs_fs_proc_exit(void);
extern int nfs_fs_proc_net_init(struct net *net);
extern void nfs_fs_proc_net_exit(struct net *net);
#else
static inline int nfs_fs_proc_net_init(struct net *net)
{
return 0;
}
static inline void nfs_fs_proc_net_exit(struct net *net)
{
}
static inline int nfs_fs_proc_init(void)
{
return 0;
Expand Down
3 changes: 3 additions & 0 deletions fs/nfs/netns.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ struct nfs_net {
#endif
spinlock_t nfs_client_lock;
struct timespec boot_time;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_nfsfs;
#endif
};

extern int nfs_net_id;
Expand Down
1 change: 1 addition & 0 deletions fs/proc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ proc-y += version.o
proc-y += softirqs.o
proc-y += namespaces.o
proc-y += self.o
proc-y += thread_self.o
proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o
proc-$(CONFIG_NET) += proc_net.o
proc-$(CONFIG_PROC_KCORE) += kcore.o
Expand Down
Loading

0 comments on commit 77e40aa

Please sign in to comment.