aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2015-11-13 07:44:28 -0500
committerChris Mason <clm@fb.com>2016-02-11 10:01:59 -0500
commitbc4ef7592f657ae81b017207a1098817126ad4cb (patch)
treeea366899588fbf0d0917fb010f1fa58718a2b949
parent43d871f029dadfff1bab1ebe24c4268b86801ca5 (diff)
btrfs: properly set the termination value of ctx->pos in readdir
The value of ctx->pos in the last readdir call is supposed to be set to INT_MAX due to 32bit compatibility, unless 'pos' is intentially set to a larger value, then it's LLONG_MAX. There's a report from PaX SIZE_OVERFLOW plugin that "ctx->pos++" overflows (https://forums.grsecurity.net/viewtopic.php?f=1&t=4284), on a 64bit arch, where the value is 0x7fffffffffffffff ie. LLONG_MAX before the increment. We can get to that situation like that: * emit all regular readdir entries * still in the same call to readdir, bump the last pos to INT_MAX * next call to readdir will not emit any entries, but will reach the bump code again, finds pos to be INT_MAX and sets it to LLONG_MAX Normally this is not a problem, but if we call readdir again, we'll find 'pos' set to LLONG_MAX and the unconditional increment will overflow. The report from Victor at (http://thread.gmane.org/gmane.comp.file-systems.btrfs/49500) with debugging print shows that pattern: Overflow: e Overflow: 7fffffff Overflow: 7fffffffffffffff PAX: size overflow detected in function btrfs_real_readdir fs/btrfs/inode.c:5760 cicus.935_282 max, count: 9, decl: pos; num: 0; context: dir_context; CPU: 0 PID: 2630 Comm: polkitd Not tainted 4.2.3-grsec #1 Hardware name: Gigabyte Technology Co., Ltd. H81ND2H/H81ND2H, BIOS F3 08/11/2015 ffffffff81901608 0000000000000000 ffffffff819015e6 ffffc90004973d48 ffffffff81742f0f 0000000000000007 ffffffff81901608 ffffc90004973d78 ffffffff811cb706 0000000000000000 ffff8800d47359e0 ffffc90004973ed8 Call Trace: [<ffffffff81742f0f>] dump_stack+0x4c/0x7f [<ffffffff811cb706>] report_size_overflow+0x36/0x40 [<ffffffff812ef0bc>] btrfs_real_readdir+0x69c/0x6d0 [<ffffffff811dafc8>] iterate_dir+0xa8/0x150 [<ffffffff811e6d8d>] ? __fget_light+0x2d/0x70 [<ffffffff811dba3a>] SyS_getdents+0xba/0x1c0 Overflow: 1a [<ffffffff811db070>] ? iterate_dir+0x150/0x150 [<ffffffff81749b69>] entry_SYSCALL_64_fastpath+0x12/0x83 The jump from 7fffffff to 7fffffffffffffff happens when new dir entries are not yet synced and are processed from the delayed list. Then the code could go to the bump section again even though it might not emit any new dir entries from the delayed list. The fix avoids entering the "bump" section again once we've finished emitting the entries, both for synced and delayed entries. References: https://forums.grsecurity.net/viewtopic.php?f=1&t=4284 Reported-by: Victor <services@swwu.com> CC: stable@vger.kernel.org Signed-off-by: David Sterba <dsterba@suse.com> Tested-by: Holger Hoffstätte <holger.hoffstaette@googlemail.com> Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r--fs/btrfs/delayed-inode.c3
-rw-r--r--fs/btrfs/delayed-inode.h2
-rw-r--r--fs/btrfs/inode.c14
3 files changed, 16 insertions, 3 deletions
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 0be47e4b8136..b57daa895cea 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1689,7 +1689,7 @@ int btrfs_should_delete_dir_index(struct list_head *del_list,
1689 * 1689 *
1690 */ 1690 */
1691int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, 1691int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
1692 struct list_head *ins_list) 1692 struct list_head *ins_list, bool *emitted)
1693{ 1693{
1694 struct btrfs_dir_item *di; 1694 struct btrfs_dir_item *di;
1695 struct btrfs_delayed_item *curr, *next; 1695 struct btrfs_delayed_item *curr, *next;
@@ -1733,6 +1733,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
1733 1733
1734 if (over) 1734 if (over)
1735 return 1; 1735 return 1;
1736 *emitted = true;
1736 } 1737 }
1737 return 0; 1738 return 0;
1738} 1739}
diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
index f70119f25421..0167853c84ae 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -144,7 +144,7 @@ void btrfs_put_delayed_items(struct list_head *ins_list,
144int btrfs_should_delete_dir_index(struct list_head *del_list, 144int btrfs_should_delete_dir_index(struct list_head *del_list,
145 u64 index); 145 u64 index);
146int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, 146int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
147 struct list_head *ins_list); 147 struct list_head *ins_list, bool *emitted);
148 148
149/* for init */ 149/* for init */
150int __init btrfs_delayed_inode_init(void); 150int __init btrfs_delayed_inode_init(void);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index e4565456eb01..600bf0d9ceaf 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5716,6 +5716,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
5716 char *name_ptr; 5716 char *name_ptr;
5717 int name_len; 5717 int name_len;
5718 int is_curr = 0; /* ctx->pos points to the current index? */ 5718 int is_curr = 0; /* ctx->pos points to the current index? */
5719 bool emitted;
5719 5720
5720 /* FIXME, use a real flag for deciding about the key type */ 5721 /* FIXME, use a real flag for deciding about the key type */
5721 if (root->fs_info->tree_root == root) 5722 if (root->fs_info->tree_root == root)
@@ -5744,6 +5745,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
5744 if (ret < 0) 5745 if (ret < 0)
5745 goto err; 5746 goto err;
5746 5747
5748 emitted = false;
5747 while (1) { 5749 while (1) {
5748 leaf = path->nodes[0]; 5750 leaf = path->nodes[0];
5749 slot = path->slots[0]; 5751 slot = path->slots[0];
@@ -5823,6 +5825,7 @@ skip:
5823 5825
5824 if (over) 5826 if (over)
5825 goto nopos; 5827 goto nopos;
5828 emitted = true;
5826 di_len = btrfs_dir_name_len(leaf, di) + 5829 di_len = btrfs_dir_name_len(leaf, di) +
5827 btrfs_dir_data_len(leaf, di) + sizeof(*di); 5830 btrfs_dir_data_len(leaf, di) + sizeof(*di);
5828 di_cur += di_len; 5831 di_cur += di_len;
@@ -5835,11 +5838,20 @@ next:
5835 if (key_type == BTRFS_DIR_INDEX_KEY) { 5838 if (key_type == BTRFS_DIR_INDEX_KEY) {
5836 if (is_curr) 5839 if (is_curr)
5837 ctx->pos++; 5840 ctx->pos++;
5838 ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list); 5841 ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list, &emitted);
5839 if (ret) 5842 if (ret)
5840 goto nopos; 5843 goto nopos;
5841 } 5844 }
5842 5845
5846 /*
5847 * If we haven't emitted any dir entry, we must not touch ctx->pos as
5848 * it was was set to the termination value in previous call. We assume
5849 * that "." and ".." were emitted if we reach this point and set the
5850 * termination value as well for an empty directory.
5851 */
5852 if (ctx->pos > 2 && !emitted)
5853 goto nopos;
5854
5843 /* Reached end of directory/root. Bump pos past the last item. */ 5855 /* Reached end of directory/root. Bump pos past the last item. */
5844 ctx->pos++; 5856 ctx->pos++;
5845 5857