aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolai Stange <nicstange@gmail.com>2016-03-22 09:11:14 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-04-12 17:14:21 -0400
commit49d200deaa680501f19a247b1fffb29301e51d2b (patch)
tree3cf2574b0751ddaf8af47d1e1da1aaa232adad99
parent9fd4dcece43a53e5a9e65a973df5693702ee6401 (diff)
debugfs: prevent access to removed files' private data
Upon return of debugfs_remove()/debugfs_remove_recursive(), it might still be attempted to access associated private file data through previously opened struct file objects. If that data has been freed by the caller of debugfs_remove*() in the meanwhile, the reading/writing process would either encounter a fault or, if the memory address in question has been reassigned again, unrelated data structures could get overwritten. However, since debugfs files are seldomly removed, usually from module exit handlers only, the impact is very low. Currently, there are ~1000 call sites of debugfs_create_file() spread throughout the whole tree and touching all of those struct file_operations in order to make them file removal aware by means of checking the result of debugfs_use_file_start() from within their methods is unfeasible. Instead, wrap the struct file_operations by a lifetime managing proxy at file open: - In debugfs_create_file(), the original fops handed in has got stashed away in ->d_fsdata already. - In debugfs_create_file(), install a proxy file_operations factory, debugfs_full_proxy_file_operations, at ->i_fop. This proxy factory has got an ->open() method only. It carries out some lifetime checks and if successful, dynamically allocates and sets up a new struct file_operations proxy at ->f_op. Afterwards, it forwards to the ->open() of the original struct file_operations in ->d_fsdata, if any. The dynamically set up proxy at ->f_op has got a lifetime managing wrapper set for each of the methods defined in the original struct file_operations in ->d_fsdata. Its ->release()er frees the proxy again and forwards to the original ->release(), if any. In order not to mislead the VFS layer, it is strictly necessary to leave those fields blank in the proxy that have been NULL in the original struct file_operations also, i.e. aren't supported. This is why there is a need for dynamically allocated proxies. The choice made not to allocate a proxy instance for every dentry at file creation, but for every struct file object instantiated thereof is justified by the expected usage pattern of debugfs, namely that in general very few files get opened more than once at a time. The wrapper methods set in the struct file_operations implement lifetime managing by means of the SRCU protection facilities already in place for debugfs: They set up a SRCU read side critical section and check whether the dentry is still alive by means of debugfs_use_file_start(). If so, they forward the call to the original struct file_operation stored in ->d_fsdata, still under the protection of the SRCU read side critical section. This SRCU read side critical section prevents any pending debugfs_remove() and friends to return to their callers. Since a file's private data must only be freed after the return of debugfs_remove(), the ongoing proxied call is guarded against any file removal race. If, on the other hand, the initial call to debugfs_use_file_start() detects that the dentry is dead, the wrapper simply returns -EIO and does not forward the call. Note that the ->poll() wrapper is special in that its signature does not allow for the return of arbitrary -EXXX values and thus, POLLHUP is returned here. In order not to pollute debugfs with wrapper definitions that aren't ever needed, I chose not to define a wrapper for every struct file_operations method possible. Instead, a wrapper is defined only for the subset of methods which are actually set by any debugfs users. Currently, these are: ->llseek() ->read() ->write() ->unlocked_ioctl() ->poll() The ->release() wrapper is special in that it does not protect the original ->release() in any way from dead files in order not to leak resources. Thus, any ->release() handed to debugfs must implement file lifetime management manually, if needed. For only 33 out of a total of 434 releasers handed in to debugfs, it could not be verified immediately whether they access data structures that might have been freed upon a debugfs_remove() return in the meanwhile. Export debugfs_use_file_start() and debugfs_use_file_finish() in order to allow any ->release() to manually implement file lifetime management. For a set of common cases of struct file_operations implemented by the debugfs_core itself, future patches will incorporate file lifetime management directly within those in order to allow for their unproxied operation. Rename the original, non-proxying "debugfs_create_file()" to "debugfs_create_file_unsafe()" and keep it for future internal use by debugfs itself. Factor out code common to both into the new __debugfs_create_file(). Signed-off-by: Nicolai Stange <nicstange@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/debugfs/file.c157
-rw-r--r--fs/debugfs/inode.c70
-rw-r--r--fs/debugfs/internal.h6
-rw-r--r--include/linux/debugfs.h20
4 files changed, 226 insertions, 27 deletions
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 736ab3c988f2..6eb58a8ed03c 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -23,9 +23,12 @@
23#include <linux/atomic.h> 23#include <linux/atomic.h>
24#include <linux/device.h> 24#include <linux/device.h>
25#include <linux/srcu.h> 25#include <linux/srcu.h>
26#include <asm/poll.h>
26 27
27#include "internal.h" 28#include "internal.h"
28 29
30struct poll_table_struct;
31
29static ssize_t default_read_file(struct file *file, char __user *buf, 32static ssize_t default_read_file(struct file *file, char __user *buf,
30 size_t count, loff_t *ppos) 33 size_t count, loff_t *ppos)
31{ 34{
@@ -66,7 +69,7 @@ const struct file_operations debugfs_noop_file_operations = {
66 * debugfs_use_file_start() must be followed by a matching call 69 * debugfs_use_file_start() must be followed by a matching call
67 * to debugfs_use_file_finish(). 70 * to debugfs_use_file_finish().
68 */ 71 */
69static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) 72int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
70 __acquires(&debugfs_srcu) 73 __acquires(&debugfs_srcu)
71{ 74{
72 *srcu_idx = srcu_read_lock(&debugfs_srcu); 75 *srcu_idx = srcu_read_lock(&debugfs_srcu);
@@ -75,6 +78,7 @@ static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
75 return -EIO; 78 return -EIO;
76 return 0; 79 return 0;
77} 80}
81EXPORT_SYMBOL_GPL(debugfs_use_file_start);
78 82
79/** 83/**
80 * debugfs_use_file_finish - mark the end of file data access 84 * debugfs_use_file_finish - mark the end of file data access
@@ -85,10 +89,11 @@ static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
85 * debugfs_remove_recursive() blocked by a former call to 89 * debugfs_remove_recursive() blocked by a former call to
86 * debugfs_use_file_start() to proceed and return to its caller. 90 * debugfs_use_file_start() to proceed and return to its caller.
87 */ 91 */
88static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) 92void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
89{ 93{
90 srcu_read_unlock(&debugfs_srcu, srcu_idx); 94 srcu_read_unlock(&debugfs_srcu, srcu_idx);
91} 95}
96EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
92 97
93#define F_DENTRY(filp) ((filp)->f_path.dentry) 98#define F_DENTRY(filp) ((filp)->f_path.dentry)
94 99
@@ -131,6 +136,154 @@ const struct file_operations debugfs_open_proxy_file_operations = {
131 .open = open_proxy_open, 136 .open = open_proxy_open,
132}; 137};
133 138
139#define PROTO(args...) args
140#define ARGS(args...) args
141
142#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \
143static ret_type full_proxy_ ## name(proto) \
144{ \
145 const struct dentry *dentry = F_DENTRY(filp); \
146 const struct file_operations *real_fops = \
147 REAL_FOPS_DEREF(dentry); \
148 int srcu_idx; \
149 ret_type r; \
150 \
151 r = debugfs_use_file_start(dentry, &srcu_idx); \
152 if (likely(!r)) \
153 r = real_fops->name(args); \
154 debugfs_use_file_finish(srcu_idx); \
155 return r; \
156}
157
158FULL_PROXY_FUNC(llseek, loff_t, filp,
159 PROTO(struct file *filp, loff_t offset, int whence),
160 ARGS(filp, offset, whence));
161
162FULL_PROXY_FUNC(read, ssize_t, filp,
163 PROTO(struct file *filp, char __user *buf, size_t size,
164 loff_t *ppos),
165 ARGS(filp, buf, size, ppos));
166
167FULL_PROXY_FUNC(write, ssize_t, filp,
168 PROTO(struct file *filp, const char __user *buf, size_t size,
169 loff_t *ppos),
170 ARGS(filp, buf, size, ppos));
171
172FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
173 PROTO(struct file *filp, unsigned int cmd, unsigned long arg),
174 ARGS(filp, cmd, arg));
175
176static unsigned int full_proxy_poll(struct file *filp,
177 struct poll_table_struct *wait)
178{
179 const struct dentry *dentry = F_DENTRY(filp);
180 const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
181 int srcu_idx;
182 unsigned int r = 0;
183
184 if (debugfs_use_file_start(dentry, &srcu_idx)) {
185 debugfs_use_file_finish(srcu_idx);
186 return POLLHUP;
187 }
188
189 r = real_fops->poll(filp, wait);
190 debugfs_use_file_finish(srcu_idx);
191 return r;
192}
193
194static int full_proxy_release(struct inode *inode, struct file *filp)
195{
196 const struct dentry *dentry = F_DENTRY(filp);
197 const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
198 const struct file_operations *proxy_fops = filp->f_op;
199 int r = 0;
200
201 /*
202 * We must not protect this against removal races here: the
203 * original releaser should be called unconditionally in order
204 * not to leak any resources. Releasers must not assume that
205 * ->i_private is still being meaningful here.
206 */
207 if (real_fops->release)
208 r = real_fops->release(inode, filp);
209
210 replace_fops(filp, d_inode(dentry)->i_fop);
211 kfree((void *)proxy_fops);
212 fops_put(real_fops);
213 return 0;
214}
215
216static void __full_proxy_fops_init(struct file_operations *proxy_fops,
217 const struct file_operations *real_fops)
218{
219 proxy_fops->release = full_proxy_release;
220 if (real_fops->llseek)
221 proxy_fops->llseek = full_proxy_llseek;
222 if (real_fops->read)
223 proxy_fops->read = full_proxy_read;
224 if (real_fops->write)
225 proxy_fops->write = full_proxy_write;
226 if (real_fops->poll)
227 proxy_fops->poll = full_proxy_poll;
228 if (real_fops->unlocked_ioctl)
229 proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl;
230}
231
232static int full_proxy_open(struct inode *inode, struct file *filp)
233{
234 const struct dentry *dentry = F_DENTRY(filp);
235 const struct file_operations *real_fops = NULL;
236 struct file_operations *proxy_fops = NULL;
237 int srcu_idx, r;
238
239 r = debugfs_use_file_start(dentry, &srcu_idx);
240 if (r) {
241 r = -ENOENT;
242 goto out;
243 }
244
245 real_fops = REAL_FOPS_DEREF(dentry);
246 real_fops = fops_get(real_fops);
247 if (!real_fops) {
248 /* Huh? Module did not cleanup after itself at exit? */
249 WARN(1, "debugfs file owner did not clean up at exit: %pd",
250 dentry);
251 r = -ENXIO;
252 goto out;
253 }
254
255 proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL);
256 if (!proxy_fops) {
257 r = -ENOMEM;
258 goto free_proxy;
259 }
260 __full_proxy_fops_init(proxy_fops, real_fops);
261 replace_fops(filp, proxy_fops);
262
263 if (real_fops->open) {
264 r = real_fops->open(inode, filp);
265
266 if (filp->f_op != proxy_fops) {
267 /* No protection against file removal anymore. */
268 WARN(1, "debugfs file owner replaced proxy fops: %pd",
269 dentry);
270 goto free_proxy;
271 }
272 }
273
274 goto out;
275free_proxy:
276 kfree(proxy_fops);
277 fops_put(real_fops);
278out:
279 debugfs_use_file_finish(srcu_idx);
280 return r;
281}
282
283const struct file_operations debugfs_full_proxy_file_operations = {
284 .open = full_proxy_open,
285};
286
134static struct dentry *debugfs_create_mode(const char *name, umode_t mode, 287static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
135 struct dentry *parent, void *value, 288 struct dentry *parent, void *value,
136 const struct file_operations *fops, 289 const struct file_operations *fops,
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 2905dd160575..136f269f01de 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -300,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry)
300 return dentry; 300 return dentry;
301} 301}
302 302
303static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
304 struct dentry *parent, void *data,
305 const struct file_operations *proxy_fops,
306 const struct file_operations *real_fops)
307{
308 struct dentry *dentry;
309 struct inode *inode;
310
311 if (!(mode & S_IFMT))
312 mode |= S_IFREG;
313 BUG_ON(!S_ISREG(mode));
314 dentry = start_creating(name, parent);
315
316 if (IS_ERR(dentry))
317 return NULL;
318
319 inode = debugfs_get_inode(dentry->d_sb);
320 if (unlikely(!inode))
321 return failed_creating(dentry);
322
323 inode->i_mode = mode;
324 inode->i_private = data;
325
326 inode->i_fop = proxy_fops;
327 dentry->d_fsdata = (void *)real_fops;
328
329 d_instantiate(dentry, inode);
330 fsnotify_create(d_inode(dentry->d_parent), dentry);
331 return end_creating(dentry);
332}
333
303/** 334/**
304 * debugfs_create_file - create a file in the debugfs filesystem 335 * debugfs_create_file - create a file in the debugfs filesystem
305 * @name: a pointer to a string containing the name of the file to create. 336 * @name: a pointer to a string containing the name of the file to create.
@@ -330,33 +361,24 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
330 struct dentry *parent, void *data, 361 struct dentry *parent, void *data,
331 const struct file_operations *fops) 362 const struct file_operations *fops)
332{ 363{
333 struct dentry *dentry;
334 struct inode *inode;
335
336 if (!(mode & S_IFMT))
337 mode |= S_IFREG;
338 BUG_ON(!S_ISREG(mode));
339 dentry = start_creating(name, parent);
340
341 if (IS_ERR(dentry))
342 return NULL;
343
344 inode = debugfs_get_inode(dentry->d_sb);
345 if (unlikely(!inode))
346 return failed_creating(dentry);
347 364
348 inode->i_mode = mode; 365 return __debugfs_create_file(name, mode, parent, data,
349 inode->i_private = data; 366 fops ? &debugfs_full_proxy_file_operations :
367 &debugfs_noop_file_operations,
368 fops);
369}
370EXPORT_SYMBOL_GPL(debugfs_create_file);
350 371
351 inode->i_fop = fops ? &debugfs_open_proxy_file_operations 372struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
352 : &debugfs_noop_file_operations; 373 struct dentry *parent, void *data,
353 dentry->d_fsdata = (void *)fops; 374 const struct file_operations *fops)
375{
354 376
355 d_instantiate(dentry, inode); 377 return __debugfs_create_file(name, mode, parent, data,
356 fsnotify_create(d_inode(dentry->d_parent), dentry); 378 fops ? &debugfs_open_proxy_file_operations :
357 return end_creating(dentry); 379 &debugfs_noop_file_operations,
380 fops);
358} 381}
359EXPORT_SYMBOL_GPL(debugfs_create_file);
360 382
361/** 383/**
362 * debugfs_create_file_size - create a file in the debugfs filesystem 384 * debugfs_create_file_size - create a file in the debugfs filesystem
@@ -579,6 +601,7 @@ void debugfs_remove(struct dentry *dentry)
579 inode_unlock(d_inode(parent)); 601 inode_unlock(d_inode(parent));
580 if (!ret) 602 if (!ret)
581 simple_release_fs(&debugfs_mount, &debugfs_mount_count); 603 simple_release_fs(&debugfs_mount, &debugfs_mount_count);
604
582 synchronize_srcu(&debugfs_srcu); 605 synchronize_srcu(&debugfs_srcu);
583} 606}
584EXPORT_SYMBOL_GPL(debugfs_remove); 607EXPORT_SYMBOL_GPL(debugfs_remove);
@@ -657,6 +680,7 @@ void debugfs_remove_recursive(struct dentry *dentry)
657 if (!__debugfs_remove(child, parent)) 680 if (!__debugfs_remove(child, parent))
658 simple_release_fs(&debugfs_mount, &debugfs_mount_count); 681 simple_release_fs(&debugfs_mount, &debugfs_mount_count);
659 inode_unlock(d_inode(parent)); 682 inode_unlock(d_inode(parent));
683
660 synchronize_srcu(&debugfs_srcu); 684 synchronize_srcu(&debugfs_srcu);
661} 685}
662EXPORT_SYMBOL_GPL(debugfs_remove_recursive); 686EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h
index c7aaa5cb6685..bba52634b995 100644
--- a/fs/debugfs/internal.h
+++ b/fs/debugfs/internal.h
@@ -13,12 +13,14 @@
13#define _DEBUGFS_INTERNAL_H_ 13#define _DEBUGFS_INTERNAL_H_
14 14
15struct file_operations; 15struct file_operations;
16struct srcu_struct;
17 16
18/* declared over in file.c */ 17/* declared over in file.c */
19extern const struct file_operations debugfs_noop_file_operations; 18extern const struct file_operations debugfs_noop_file_operations;
20extern const struct file_operations debugfs_open_proxy_file_operations; 19extern const struct file_operations debugfs_open_proxy_file_operations;
20extern const struct file_operations debugfs_full_proxy_file_operations;
21 21
22extern struct srcu_struct debugfs_srcu; 22struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
23 struct dentry *parent, void *data,
24 const struct file_operations *fops);
23 25
24#endif /* _DEBUGFS_INTERNAL_H_ */ 26#endif /* _DEBUGFS_INTERNAL_H_ */
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index fcafe2d389f9..a63e6ea3321c 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -19,9 +19,11 @@
19#include <linux/seq_file.h> 19#include <linux/seq_file.h>
20 20
21#include <linux/types.h> 21#include <linux/types.h>
22#include <linux/compiler.h>
22 23
23struct device; 24struct device;
24struct file_operations; 25struct file_operations;
26struct srcu_struct;
25 27
26struct debugfs_blob_wrapper { 28struct debugfs_blob_wrapper {
27 void *data; 29 void *data;
@@ -41,6 +43,8 @@ struct debugfs_regset32 {
41 43
42extern struct dentry *arch_debugfs_dir; 44extern struct dentry *arch_debugfs_dir;
43 45
46extern struct srcu_struct debugfs_srcu;
47
44#if defined(CONFIG_DEBUG_FS) 48#if defined(CONFIG_DEBUG_FS)
45 49
46struct dentry *debugfs_create_file(const char *name, umode_t mode, 50struct dentry *debugfs_create_file(const char *name, umode_t mode,
@@ -65,6 +69,11 @@ struct dentry *debugfs_create_automount(const char *name,
65void debugfs_remove(struct dentry *dentry); 69void debugfs_remove(struct dentry *dentry);
66void debugfs_remove_recursive(struct dentry *dentry); 70void debugfs_remove_recursive(struct dentry *dentry);
67 71
72int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
73 __acquires(&debugfs_srcu);
74
75void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);
76
68struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, 77struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
69 struct dentry *new_dir, const char *new_name); 78 struct dentry *new_dir, const char *new_name);
70 79
@@ -173,6 +182,17 @@ static inline void debugfs_remove(struct dentry *dentry)
173static inline void debugfs_remove_recursive(struct dentry *dentry) 182static inline void debugfs_remove_recursive(struct dentry *dentry)
174{ } 183{ }
175 184
185static inline int debugfs_use_file_start(const struct dentry *dentry,
186 int *srcu_idx)
187 __acquires(&debugfs_srcu)
188{
189 return 0;
190}
191
192static inline void debugfs_use_file_finish(int srcu_idx)
193 __releases(&debugfs_srcu)
194{ }
195
176static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, 196static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
177 struct dentry *new_dir, char *new_name) 197 struct dentry *new_dir, char *new_name)
178{ 198{