diff options
author | Jeff Layton <jlayton@poochiereds.net> | 2014-04-30 09:31:47 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2014-05-21 13:18:05 -0400 |
commit | 4f73c7d342d57d065bdbc0995cb56d8d1701b0c0 (patch) | |
tree | 6933002e316475c879d702ffbb733a24ba4f0561 /fs/cifs/inode.c | |
parent | e284e53fdea1dfd66e73c239fa190685985ae465 (diff) |
cifs: fix potential races in cifs_revalidate_mapping
The handling of the CIFS_INO_INVALID_MAPPING flag is racy. It's possible
for two tasks to attempt to revalidate the mapping at the same time. The
first sees that CIFS_INO_INVALID_MAPPING is set. It clears the flag and
then calls invalidate_inode_pages2 to start shooting down the pagecache.
While that's going on, another task checks the flag and sees that it's
clear. It then ends up trusting the pagecache to satisfy a read when it
shouldn't.
Fix this by adding a bitlock to ensure that the clearing of the flag is
atomic with respect to the actual cache invalidation. Also, move the
other existing users of cifs_invalidate_mapping to use a new
cifs_zap_mapping() function that just sets the INVALID_MAPPING bit and
then uses the standard codepath to handle the invalidation.
Signed-off-by: Jeff Layton <jlayton@poochiereds.net>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs/inode.c')
-rw-r--r-- | fs/cifs/inode.c | 50 |
1 files changed, 41 insertions, 9 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ff420b275777..9ff8df8b4d84 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/stat.h> | 22 | #include <linux/stat.h> |
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/pagemap.h> | 24 | #include <linux/pagemap.h> |
25 | #include <linux/freezer.h> | ||
25 | #include <asm/div64.h> | 26 | #include <asm/div64.h> |
26 | #include "cifsfs.h" | 27 | #include "cifsfs.h" |
27 | #include "cifspdu.h" | 28 | #include "cifspdu.h" |
@@ -1762,29 +1763,60 @@ int | |||
1762 | cifs_invalidate_mapping(struct inode *inode) | 1763 | cifs_invalidate_mapping(struct inode *inode) |
1763 | { | 1764 | { |
1764 | int rc = 0; | 1765 | int rc = 0; |
1765 | struct cifsInodeInfo *cifs_i = CIFS_I(inode); | ||
1766 | |||
1767 | clear_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); | ||
1768 | 1766 | ||
1769 | if (inode->i_mapping && inode->i_mapping->nrpages != 0) { | 1767 | if (inode->i_mapping && inode->i_mapping->nrpages != 0) { |
1770 | rc = invalidate_inode_pages2(inode->i_mapping); | 1768 | rc = invalidate_inode_pages2(inode->i_mapping); |
1771 | if (rc) { | 1769 | if (rc) |
1772 | cifs_dbg(VFS, "%s: could not invalidate inode %p\n", | 1770 | cifs_dbg(VFS, "%s: could not invalidate inode %p\n", |
1773 | __func__, inode); | 1771 | __func__, inode); |
1774 | set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); | ||
1775 | } | ||
1776 | } | 1772 | } |
1777 | 1773 | ||
1778 | cifs_fscache_reset_inode_cookie(inode); | 1774 | cifs_fscache_reset_inode_cookie(inode); |
1779 | return rc; | 1775 | return rc; |
1780 | } | 1776 | } |
1781 | 1777 | ||
1778 | /** | ||
1779 | * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks | ||
1780 | * @word: long word containing the bit lock | ||
1781 | */ | ||
1782 | static int | ||
1783 | cifs_wait_bit_killable(void *word) | ||
1784 | { | ||
1785 | if (fatal_signal_pending(current)) | ||
1786 | return -ERESTARTSYS; | ||
1787 | freezable_schedule_unsafe(); | ||
1788 | return 0; | ||
1789 | } | ||
1790 | |||
1782 | int | 1791 | int |
1783 | cifs_revalidate_mapping(struct inode *inode) | 1792 | cifs_revalidate_mapping(struct inode *inode) |
1784 | { | 1793 | { |
1785 | if (test_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags)) | 1794 | int rc; |
1786 | return cifs_invalidate_mapping(inode); | 1795 | unsigned long *flags = &CIFS_I(inode)->flags; |
1787 | return 0; | 1796 | |
1797 | rc = wait_on_bit_lock(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, | ||
1798 | TASK_KILLABLE); | ||
1799 | if (rc) | ||
1800 | return rc; | ||
1801 | |||
1802 | if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { | ||
1803 | rc = cifs_invalidate_mapping(inode); | ||
1804 | if (rc) | ||
1805 | set_bit(CIFS_INO_INVALID_MAPPING, flags); | ||
1806 | } | ||
1807 | |||
1808 | clear_bit_unlock(CIFS_INO_LOCK, flags); | ||
1809 | smp_mb__after_clear_bit(); | ||
1810 | wake_up_bit(flags, CIFS_INO_LOCK); | ||
1811 | |||
1812 | return rc; | ||
1813 | } | ||
1814 | |||
1815 | int | ||
1816 | cifs_zap_mapping(struct inode *inode) | ||
1817 | { | ||
1818 | set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); | ||
1819 | return cifs_revalidate_mapping(inode); | ||
1788 | } | 1820 | } |
1789 | 1821 | ||
1790 | int cifs_revalidate_file_attr(struct file *filp) | 1822 | int cifs_revalidate_file_attr(struct file *filp) |