aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-01-10 10:20:35 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2012-01-10 10:20:35 -0500
commit3e25eb9c4bb649acdddb333d10774b640190f727 (patch)
treed51009557e95437dd7b7ef6b0f3a51aacccec743
parente4e11180dfa545233e5145919b75b7fac88638df (diff)
securityfs: fix object creation races
inode needs to be fully set up before we feed it to d_instantiate(). securityfs_create_file() does *not* do so; it sets ->i_fop and ->i_private only after we'd exposed the inode. Unfortunately, that's done fairly deep in call chain, so the amount of churn is considerable. Helper functions killed by substituting into their solitary call sites, dead code removed. We finally can bury default_file_ops, now that the final value of ->i_fop is available (and assigned) at the point where inode is allocated. Reviewed-by: James Morris <jmorris@namei.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--security/inode.c191
1 files changed, 50 insertions, 141 deletions
diff --git a/security/inode.c b/security/inode.c
index 90a70a67d835..43ce6e19015f 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -25,100 +25,6 @@
25static struct vfsmount *mount; 25static struct vfsmount *mount;
26static int mount_count; 26static int mount_count;
27 27
28/*
29 * TODO:
30 * I think I can get rid of these default_file_ops, but not quite sure...
31 */
32static ssize_t default_read_file(struct file *file, char __user *buf,
33 size_t count, loff_t *ppos)
34{
35 return 0;
36}
37
38static ssize_t default_write_file(struct file *file, const char __user *buf,
39 size_t count, loff_t *ppos)
40{
41 return count;
42}
43
44static int default_open(struct inode *inode, struct file *file)
45{
46 if (inode->i_private)
47 file->private_data = inode->i_private;
48
49 return 0;
50}
51
52static const struct file_operations default_file_ops = {
53 .read = default_read_file,
54 .write = default_write_file,
55 .open = default_open,
56 .llseek = noop_llseek,
57};
58
59static struct inode *get_inode(struct super_block *sb, umode_t mode, dev_t dev)
60{
61 struct inode *inode = new_inode(sb);
62
63 if (inode) {
64 inode->i_ino = get_next_ino();
65 inode->i_mode = mode;
66 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
67 switch (mode & S_IFMT) {
68 default:
69 init_special_inode(inode, mode, dev);
70 break;
71 case S_IFREG:
72 inode->i_fop = &default_file_ops;
73 break;
74 case S_IFDIR:
75 inode->i_op = &simple_dir_inode_operations;
76 inode->i_fop = &simple_dir_operations;
77
78 /* directory inodes start off with i_nlink == 2 (for "." entry) */
79 inc_nlink(inode);
80 break;
81 }
82 }
83 return inode;
84}
85
86/* SMP-safe */
87static int mknod(struct inode *dir, struct dentry *dentry,
88 umode_t mode, dev_t dev)
89{
90 struct inode *inode;
91 int error = -ENOMEM;
92
93 if (dentry->d_inode)
94 return -EEXIST;
95
96 inode = get_inode(dir->i_sb, mode, dev);
97 if (inode) {
98 d_instantiate(dentry, inode);
99 dget(dentry);
100 error = 0;
101 }
102 return error;
103}
104
105static int mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
106{
107 int res;
108
109 mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
110 res = mknod(dir, dentry, mode, 0);
111 if (!res)
112 inc_nlink(dir);
113 return res;
114}
115
116static int create(struct inode *dir, struct dentry *dentry, umode_t mode)
117{
118 mode = (mode & S_IALLUGO) | S_IFREG;
119 return mknod(dir, dentry, mode, 0);
120}
121
122static inline int positive(struct dentry *dentry) 28static inline int positive(struct dentry *dentry)
123{ 29{
124 return dentry->d_inode && !d_unhashed(dentry); 30 return dentry->d_inode && !d_unhashed(dentry);
@@ -145,38 +51,6 @@ static struct file_system_type fs_type = {
145 .kill_sb = kill_litter_super, 51 .kill_sb = kill_litter_super,
146}; 52};
147 53
148static int create_by_name(const char *name, umode_t mode,
149 struct dentry *parent,
150 struct dentry **dentry)
151{
152 int error = 0;
153
154 *dentry = NULL;
155
156 /* If the parent is not specified, we create it in the root.
157 * We need the root dentry to do this, which is in the super
158 * block. A pointer to that is in the struct vfsmount that we
159 * have around.
160 */
161 if (!parent)
162 parent = mount->mnt_root;
163
164 mutex_lock(&parent->d_inode->i_mutex);
165 *dentry = lookup_one_len(name, parent, strlen(name));
166 if (!IS_ERR(*dentry)) {
167 if (S_ISDIR(mode))
168 error = mkdir(parent->d_inode, *dentry, mode);
169 else
170 error = create(parent->d_inode, *dentry, mode);
171 if (error)
172 dput(*dentry);
173 } else
174 error = PTR_ERR(*dentry);
175 mutex_unlock(&parent->d_inode->i_mutex);
176
177 return error;
178}
179
180/** 54/**
181 * securityfs_create_file - create a file in the securityfs filesystem 55 * securityfs_create_file - create a file in the securityfs filesystem
182 * 56 *
@@ -209,31 +83,66 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
209 struct dentry *parent, void *data, 83 struct dentry *parent, void *data,
210 const struct file_operations *fops) 84 const struct file_operations *fops)
211{ 85{
212 struct dentry *dentry = NULL; 86 struct dentry *dentry;
87 int is_dir = S_ISDIR(mode);
88 struct inode *dir, *inode;
213 int error; 89 int error;
214 90
91 if (!is_dir) {
92 BUG_ON(!fops);
93 mode = (mode & S_IALLUGO) | S_IFREG;
94 }
95
215 pr_debug("securityfs: creating file '%s'\n",name); 96 pr_debug("securityfs: creating file '%s'\n",name);
216 97
217 error = simple_pin_fs(&fs_type, &mount, &mount_count); 98 error = simple_pin_fs(&fs_type, &mount, &mount_count);
218 if (error) { 99 if (error)
219 dentry = ERR_PTR(error); 100 return ERR_PTR(error);
220 goto exit; 101
102 if (!parent)
103 parent = mount->mnt_root;
104
105 dir = parent->d_inode;
106
107 mutex_lock(&dir->i_mutex);
108 dentry = lookup_one_len(name, parent, strlen(name));
109 if (IS_ERR(dentry))
110 goto out;
111
112 if (dentry->d_inode) {
113 error = -EEXIST;
114 goto out1;
221 } 115 }
222 116
223 error = create_by_name(name, mode, parent, &dentry); 117 inode = new_inode(dir->i_sb);
224 if (error) { 118 if (!inode) {
225 dentry = ERR_PTR(error); 119 error = -ENOMEM;
226 simple_release_fs(&mount, &mount_count); 120 goto out1;
227 goto exit;
228 } 121 }
229 122
230 if (dentry->d_inode) { 123 inode->i_ino = get_next_ino();
231 if (fops) 124 inode->i_mode = mode;
232 dentry->d_inode->i_fop = fops; 125 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
233 if (data) 126 inode->i_private = data;
234 dentry->d_inode->i_private = data; 127 if (is_dir) {
128 inode->i_op = &simple_dir_inode_operations;
129 inode->i_fop = &simple_dir_operations;
130 inc_nlink(inode);
131 inc_nlink(dir);
132 } else {
133 inode->i_fop = fops;
235 } 134 }
236exit: 135 d_instantiate(dentry, inode);
136 dget(dentry);
137 mutex_unlock(&dir->i_mutex);
138 return dentry;
139
140out1:
141 dput(dentry);
142 dentry = ERR_PTR(error);
143out:
144 mutex_unlock(&dir->i_mutex);
145 simple_release_fs(&mount, &mount_count);
237 return dentry; 146 return dentry;
238} 147}
239EXPORT_SYMBOL_GPL(securityfs_create_file); 148EXPORT_SYMBOL_GPL(securityfs_create_file);