diff options
author | David Rientjes <rientjes@google.com> | 2012-09-21 05:16:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-09-21 13:28:17 -0400 |
commit | 36048853c5257a7b6df346b83758ffa776a59e9f (patch) | |
tree | c8167eb505d2f8af213faa15acf27c6554783294 /fs/debugfs | |
parent | c46de2263f42fb4bbde411b9126f471e9343cb22 (diff) |
debugfs: fix race in u32_array_read and allocate array at open
u32_array_open() is racy when multiple threads read from a file with a
seek position of zero, i.e. when two or more simultaneous reads are
occurring after the non-seekable files are created. It is possible that
file->private_data is double-freed because the threads races between
kfree(file->private-data);
and
file->private_data = NULL;
The fix is to only do format_array_alloc() when the file is opened and
free it when it is closed.
Note that because the file has always been non-seekable, you can't open
it and read it multiple times anyway, so the data has always been
generated just once. The difference is that now it is generated at open
time rather than at the time of the first read, and that avoids the
race.
Reported-by: Dave Jones <davej@redhat.com>
Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Tested-by: Raghavendra <raghavendra.kt@linux.vnet.ibm.com>
Signed-off-by: David Rientjes <rientjes@google.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/debugfs')
-rw-r--r-- | fs/debugfs/file.c | 33 |
1 files changed, 11 insertions, 22 deletions
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 2340f6978d6e..a09d3c0aad68 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c | |||
@@ -526,12 +526,6 @@ struct array_data { | |||
526 | u32 elements; | 526 | u32 elements; |
527 | }; | 527 | }; |
528 | 528 | ||
529 | static int u32_array_open(struct inode *inode, struct file *file) | ||
530 | { | ||
531 | file->private_data = NULL; | ||
532 | return nonseekable_open(inode, file); | ||
533 | } | ||
534 | |||
535 | static size_t format_array(char *buf, size_t bufsize, const char *fmt, | 529 | static size_t format_array(char *buf, size_t bufsize, const char *fmt, |
536 | u32 *array, u32 array_size) | 530 | u32 *array, u32 array_size) |
537 | { | 531 | { |
@@ -573,26 +567,21 @@ static char *format_array_alloc(const char *fmt, u32 *array, | |||
573 | return ret; | 567 | return ret; |
574 | } | 568 | } |
575 | 569 | ||
576 | static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, | 570 | static int u32_array_open(struct inode *inode, struct file *file) |
577 | loff_t *ppos) | ||
578 | { | 571 | { |
579 | struct inode *inode = file->f_path.dentry->d_inode; | ||
580 | struct array_data *data = inode->i_private; | 572 | struct array_data *data = inode->i_private; |
581 | size_t size; | ||
582 | 573 | ||
583 | if (*ppos == 0) { | 574 | file->private_data = format_array_alloc("%u", data->array, |
584 | if (file->private_data) { | 575 | data->elements); |
585 | kfree(file->private_data); | 576 | if (!file->private_data) |
586 | file->private_data = NULL; | 577 | return -ENOMEM; |
587 | } | 578 | return nonseekable_open(inode, file); |
588 | 579 | } | |
589 | file->private_data = format_array_alloc("%u", data->array, | ||
590 | data->elements); | ||
591 | } | ||
592 | 580 | ||
593 | size = 0; | 581 | static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, |
594 | if (file->private_data) | 582 | loff_t *ppos) |
595 | size = strlen(file->private_data); | 583 | { |
584 | size_t size = strlen(file->private_data); | ||
596 | 585 | ||
597 | return simple_read_from_buffer(buf, len, ppos, | 586 | return simple_read_from_buffer(buf, len, ppos, |
598 | file->private_data, size); | 587 | file->private_data, size); |