diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2007-02-12 00:52:10 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-12 09:48:29 -0800 |
commit | bc1fc6d88c646ea071de34250552051a63000d70 (patch) | |
tree | 37bf115305df3fefce7d0c68b531adfa92085d36 | |
parent | 79a81aef769f3a188988ad16032ccfc445cfaa13 (diff) | |
download | lwn-bc1fc6d88c646ea071de34250552051a63000d70.tar.gz lwn-bc1fc6d88c646ea071de34250552051a63000d70.zip |
[PATCH] ipc: save the ipc namespace while reading proc files
The problem we were assuming that current->nsproxy->ipc_ns would never
change while someone has our file in /proc/sysvipc/ file open. Given that
this can change with both unshare and by passing the file descriptor to
another process that assumption is occasionally wrong.
Therefore this patch causes /proc/sysvipc/* to cache the namespace and
increment it's count when we open the file and to decrement the count when
we close the file, ensuring consistent operation with no surprises.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Cc: Serge E. Hallyn <serue@us.ibm.com>
Cc: Herbert Poetzl <herbert@13thfloor.at>
Cc: Kirill Korotaev <dev@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | ipc/util.c | 58 |
1 files changed, 45 insertions, 13 deletions
diff --git a/ipc/util.c b/ipc/util.c index 0c97cb746160..115e9aac1366 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -738,14 +738,20 @@ int ipc_parse_version (int *cmd) #endif /* __ARCH_WANT_IPC_PARSE_VERSION */ #ifdef CONFIG_PROC_FS +struct ipc_proc_iter { + struct ipc_namespace *ns; + struct ipc_proc_iface *iface; +}; + static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) { - struct ipc_proc_iface *iface = s->private; + struct ipc_proc_iter *iter = s->private; + struct ipc_proc_iface *iface = iter->iface; struct kern_ipc_perm *ipc = it; loff_t p; struct ipc_ids *ids; - ids = current->nsproxy->ipc_ns->ids[iface->ids]; + ids = iter->ns->ids[iface->ids]; /* If we had an ipc id locked before, unlock it */ if (ipc && ipc != SEQ_START_TOKEN) @@ -772,12 +778,13 @@ static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) */ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) { - struct ipc_proc_iface *iface = s->private; + struct ipc_proc_iter *iter = s->private; + struct ipc_proc_iface *iface = iter->iface; struct kern_ipc_perm *ipc; loff_t p; struct ipc_ids *ids; - ids = current->nsproxy->ipc_ns->ids[iface->ids]; + ids = iter->ns->ids[iface->ids]; /* * Take the lock - this will be released by the corresponding @@ -806,21 +813,23 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) static void sysvipc_proc_stop(struct seq_file *s, void *it) { struct kern_ipc_perm *ipc = it; - struct ipc_proc_iface *iface = s->private; + struct ipc_proc_iter *iter = s->private; + struct ipc_proc_iface *iface = iter->iface; struct ipc_ids *ids; /* If we had a locked segment, release it */ if (ipc && ipc != SEQ_START_TOKEN) ipc_unlock(ipc); - ids = current->nsproxy->ipc_ns->ids[iface->ids]; + ids = iter->ns->ids[iface->ids]; /* Release the lock we took in start() */ mutex_unlock(&ids->mutex); } static int sysvipc_proc_show(struct seq_file *s, void *it) { - struct ipc_proc_iface *iface = s->private; + struct ipc_proc_iter *iter = s->private; + struct ipc_proc_iface *iface = iter->iface; if (it == SEQ_START_TOKEN) return seq_puts(s, iface->header); @@ -835,22 +844,45 @@ static struct seq_operations sysvipc_proc_seqops = { .show = sysvipc_proc_show, }; -static int sysvipc_proc_open(struct inode *inode, struct file *file) { +static int sysvipc_proc_open(struct inode *inode, struct file *file) +{ int ret; struct seq_file *seq; + struct ipc_proc_iter *iter; + + ret = -ENOMEM; + iter = kmalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + goto out; ret = seq_open(file, &sysvipc_proc_seqops); - if (!ret) { - seq = file->private_data; - seq->private = PDE(inode)->data; - } + if (ret) + goto out_kfree; + + seq = file->private_data; + seq->private = iter; + + iter->iface = PDE(inode)->data; + iter->ns = get_ipc_ns(current->nsproxy->ipc_ns); +out: return ret; +out_kfree: + kfree(iter); + goto out; +} + +static int sysvipc_proc_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct ipc_proc_iter *iter = seq->private; + put_ipc_ns(iter->ns); + return seq_release_private(inode, file); } static struct file_operations sysvipc_proc_fops = { .open = sysvipc_proc_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = sysvipc_proc_release, }; #endif /* CONFIG_PROC_FS */ |