diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2007-02-12 03:52:10 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-12 12:48:29 -0500 |
commit | bc1fc6d88c646ea071de34250552051a63000d70 (patch) | |
tree | 37bf115305df3fefce7d0c68b531adfa92085d36 | |
parent | 79a81aef769f3a188988ad16032ccfc445cfaa13 (diff) |
[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) | |||
738 | #endif /* __ARCH_WANT_IPC_PARSE_VERSION */ | 738 | #endif /* __ARCH_WANT_IPC_PARSE_VERSION */ |
739 | 739 | ||
740 | #ifdef CONFIG_PROC_FS | 740 | #ifdef CONFIG_PROC_FS |
741 | struct ipc_proc_iter { | ||
742 | struct ipc_namespace *ns; | ||
743 | struct ipc_proc_iface *iface; | ||
744 | }; | ||
745 | |||
741 | static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) | 746 | static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) |
742 | { | 747 | { |
743 | struct ipc_proc_iface *iface = s->private; | 748 | struct ipc_proc_iter *iter = s->private; |
749 | struct ipc_proc_iface *iface = iter->iface; | ||
744 | struct kern_ipc_perm *ipc = it; | 750 | struct kern_ipc_perm *ipc = it; |
745 | loff_t p; | 751 | loff_t p; |
746 | struct ipc_ids *ids; | 752 | struct ipc_ids *ids; |
747 | 753 | ||
748 | ids = current->nsproxy->ipc_ns->ids[iface->ids]; | 754 | ids = iter->ns->ids[iface->ids]; |
749 | 755 | ||
750 | /* If we had an ipc id locked before, unlock it */ | 756 | /* If we had an ipc id locked before, unlock it */ |
751 | if (ipc && ipc != SEQ_START_TOKEN) | 757 | if (ipc && ipc != SEQ_START_TOKEN) |
@@ -772,12 +778,13 @@ static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) | |||
772 | */ | 778 | */ |
773 | static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) | 779 | static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) |
774 | { | 780 | { |
775 | struct ipc_proc_iface *iface = s->private; | 781 | struct ipc_proc_iter *iter = s->private; |
782 | struct ipc_proc_iface *iface = iter->iface; | ||
776 | struct kern_ipc_perm *ipc; | 783 | struct kern_ipc_perm *ipc; |
777 | loff_t p; | 784 | loff_t p; |
778 | struct ipc_ids *ids; | 785 | struct ipc_ids *ids; |
779 | 786 | ||
780 | ids = current->nsproxy->ipc_ns->ids[iface->ids]; | 787 | ids = iter->ns->ids[iface->ids]; |
781 | 788 | ||
782 | /* | 789 | /* |
783 | * Take the lock - this will be released by the corresponding | 790 | * 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) | |||
806 | static void sysvipc_proc_stop(struct seq_file *s, void *it) | 813 | static void sysvipc_proc_stop(struct seq_file *s, void *it) |
807 | { | 814 | { |
808 | struct kern_ipc_perm *ipc = it; | 815 | struct kern_ipc_perm *ipc = it; |
809 | struct ipc_proc_iface *iface = s->private; | 816 | struct ipc_proc_iter *iter = s->private; |
817 | struct ipc_proc_iface *iface = iter->iface; | ||
810 | struct ipc_ids *ids; | 818 | struct ipc_ids *ids; |
811 | 819 | ||
812 | /* If we had a locked segment, release it */ | 820 | /* If we had a locked segment, release it */ |
813 | if (ipc && ipc != SEQ_START_TOKEN) | 821 | if (ipc && ipc != SEQ_START_TOKEN) |
814 | ipc_unlock(ipc); | 822 | ipc_unlock(ipc); |
815 | 823 | ||
816 | ids = current->nsproxy->ipc_ns->ids[iface->ids]; | 824 | ids = iter->ns->ids[iface->ids]; |
817 | /* Release the lock we took in start() */ | 825 | /* Release the lock we took in start() */ |
818 | mutex_unlock(&ids->mutex); | 826 | mutex_unlock(&ids->mutex); |
819 | } | 827 | } |
820 | 828 | ||
821 | static int sysvipc_proc_show(struct seq_file *s, void *it) | 829 | static int sysvipc_proc_show(struct seq_file *s, void *it) |
822 | { | 830 | { |
823 | struct ipc_proc_iface *iface = s->private; | 831 | struct ipc_proc_iter *iter = s->private; |
832 | struct ipc_proc_iface *iface = iter->iface; | ||
824 | 833 | ||
825 | if (it == SEQ_START_TOKEN) | 834 | if (it == SEQ_START_TOKEN) |
826 | return seq_puts(s, iface->header); | 835 | return seq_puts(s, iface->header); |
@@ -835,22 +844,45 @@ static struct seq_operations sysvipc_proc_seqops = { | |||
835 | .show = sysvipc_proc_show, | 844 | .show = sysvipc_proc_show, |
836 | }; | 845 | }; |
837 | 846 | ||
838 | static int sysvipc_proc_open(struct inode *inode, struct file *file) { | 847 | static int sysvipc_proc_open(struct inode *inode, struct file *file) |
848 | { | ||
839 | int ret; | 849 | int ret; |
840 | struct seq_file *seq; | 850 | struct seq_file *seq; |
851 | struct ipc_proc_iter *iter; | ||
852 | |||
853 | ret = -ENOMEM; | ||
854 | iter = kmalloc(sizeof(*iter), GFP_KERNEL); | ||
855 | if (!iter) | ||
856 | goto out; | ||
841 | 857 | ||
842 | ret = seq_open(file, &sysvipc_proc_seqops); | 858 | ret = seq_open(file, &sysvipc_proc_seqops); |
843 | if (!ret) { | 859 | if (ret) |
844 | seq = file->private_data; | 860 | goto out_kfree; |
845 | seq->private = PDE(inode)->data; | 861 | |
846 | } | 862 | seq = file->private_data; |
863 | seq->private = iter; | ||
864 | |||
865 | iter->iface = PDE(inode)->data; | ||
866 | iter->ns = get_ipc_ns(current->nsproxy->ipc_ns); | ||
867 | out: | ||
847 | return ret; | 868 | return ret; |
869 | out_kfree: | ||
870 | kfree(iter); | ||
871 | goto out; | ||
872 | } | ||
873 | |||
874 | static int sysvipc_proc_release(struct inode *inode, struct file *file) | ||
875 | { | ||
876 | struct seq_file *seq = file->private_data; | ||
877 | struct ipc_proc_iter *iter = seq->private; | ||
878 | put_ipc_ns(iter->ns); | ||
879 | return seq_release_private(inode, file); | ||
848 | } | 880 | } |
849 | 881 | ||
850 | static struct file_operations sysvipc_proc_fops = { | 882 | static struct file_operations sysvipc_proc_fops = { |
851 | .open = sysvipc_proc_open, | 883 | .open = sysvipc_proc_open, |
852 | .read = seq_read, | 884 | .read = seq_read, |
853 | .llseek = seq_lseek, | 885 | .llseek = seq_lseek, |
854 | .release = seq_release, | 886 | .release = sysvipc_proc_release, |
855 | }; | 887 | }; |
856 | #endif /* CONFIG_PROC_FS */ | 888 | #endif /* CONFIG_PROC_FS */ |