lazytime v5 patch output
[ext4-patch-queue.git] / fix-potential-use-after-free-during-resize
blobea819e66a546f2b87cbe50a9f8c443be8ce0e381
1 ext4: fix potential use after free during resize
3 From: Dmitry Monakhov <dmonakhov@openvz.org>
5 We need some sort of synchronization while updating ->s_group_desc
6 because there are a lot of users which can access old ->s_group_desc
7 array after it was released.  It is reasonable to use lightweight
8 seqcount_t here instead of RCU.
10 Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
11 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
12 ---
13  fs/ext4/balloc.c | 14 ++++++++++----
14  fs/ext4/ext4.h   |  1 +
15  fs/ext4/resize.c |  4 ++++
16  fs/ext4/super.c  |  1 +
17  4 files changed, 16 insertions(+), 4 deletions(-)
19 diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
20 index 83a6f49..4746907 100644
21 --- a/fs/ext4/balloc.c
22 +++ b/fs/ext4/balloc.c
23 @@ -280,8 +280,10 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
24  {
25         unsigned int group_desc;
26         unsigned int offset;
27 +       unsigned int seq;
28         ext4_group_t ngroups = ext4_get_groups_count(sb);
29         struct ext4_group_desc *desc;
30 +       struct buffer_head *gd_bh;
31         struct ext4_sb_info *sbi = EXT4_SB(sb);
33         if (block_group >= ngroups) {
34 @@ -293,7 +295,11 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
36         group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
37         offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
38 -       if (!sbi->s_group_desc[group_desc]) {
39 +       do {
40 +               seq = read_seqcount_begin(&sbi->s_group_desc_seq);
41 +               gd_bh = sbi->s_group_desc[group_desc];
42 +       } while (unlikely(read_seqcount_retry(&sbi->s_group_desc_seq, seq)));
43 +       if (!gd_bh) {
44                 ext4_error(sb, "Group descriptor not loaded - "
45                            "block_group = %u, group_desc = %u, desc = %u",
46                            block_group, group_desc, offset);
47 @@ -301,10 +307,10 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
48         }
50         desc = (struct ext4_group_desc *)(
51 -               (__u8 *)sbi->s_group_desc[group_desc]->b_data +
52 -               offset * EXT4_DESC_SIZE(sb));
53 +               (__u8 *)gd_bh->b_data + offset * EXT4_DESC_SIZE(sb));
54         if (bh)
55 -               *bh = sbi->s_group_desc[group_desc];
56 +               *bh = gd_bh;
58         return desc;
59  }
61 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
62 index 4186ec8..84255c0 100644
63 --- a/fs/ext4/ext4.h
64 +++ b/fs/ext4/ext4.h
65 @@ -1190,6 +1190,7 @@ struct ext4_sb_info {
66         struct buffer_head * s_sbh;     /* Buffer containing the super block */
67         struct ext4_super_block *s_es;  /* Pointer to the super block in the buffer */
68         struct buffer_head **s_group_desc;
69 +       seqcount_t s_group_desc_seq;    /* protects s_group_desc updates */
70         unsigned int s_mount_opt;
71         unsigned int s_mount_opt2;
72         unsigned int s_mount_flags;
73 diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
74 index bf76f40..25e822d 100644
75 --- a/fs/ext4/resize.c
76 +++ b/fs/ext4/resize.c
77 @@ -854,8 +854,10 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
78         memcpy(n_group_desc, o_group_desc,
79                EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *));
80         n_group_desc[gdb_num] = gdb_bh;
81 +       write_seqcount_begin(&EXT4_SB(sb)->s_group_desc_seq);
82         EXT4_SB(sb)->s_group_desc = n_group_desc;
83         EXT4_SB(sb)->s_gdb_count++;
84 +       write_seqcount_end(&EXT4_SB(sb)->s_group_desc_seq);
85         kvfree(o_group_desc);
87         le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
88 @@ -907,8 +909,10 @@ static int add_new_gdb_meta_bg(struct super_block *sb,
89         memcpy(n_group_desc, o_group_desc,
90                EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *));
91         n_group_desc[gdb_num] = gdb_bh;
92 +       write_seqcount_begin(&EXT4_SB(sb)->s_group_desc_seq);
93         EXT4_SB(sb)->s_group_desc = n_group_desc;
94         EXT4_SB(sb)->s_gdb_count++;
95 +       write_seqcount_end(&EXT4_SB(sb)->s_group_desc_seq);
96         kvfree(o_group_desc);
97         BUFFER_TRACE(gdb_bh, "get_write_access");
98         err = ext4_journal_get_write_access(handle, gdb_bh);
99 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
100 index a22543d..6dbd05c 100644
101 --- a/fs/ext4/super.c
102 +++ b/fs/ext4/super.c
103 @@ -3871,6 +3871,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
104                         (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
105         db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
106                    EXT4_DESC_PER_BLOCK(sb);
107 +       seqcount_init(&sbi->s_group_desc_seq);
108         sbi->s_group_desc = ext4_kvmalloc(db_count *
109                                           sizeof(struct buffer_head *),
110                                           GFP_KERNEL);