Fix "journal superblock changes" so that needs_recovery flag is
[ext4-patch-queue.git] / dont-crash-when-truncating-encrypted-on-the-orphan-list
blobe515540053e182ea9ff6fb09d7ce811fb7b70d19
1 ext4: don't BUG when truncating encrypted inodes on the orphan list
3 Fix a BUG when the kernel tries to mount a file system constructed as
4 follows:
6 echo foo > foo.txt
7 mke2fs -Fq -t ext4 -O encrypt foo.img 100
8 debugfs -w foo.img << EOF
9 write foo.txt a
10 set_inode_field a i_flags 0x80800
11 set_super_value s_last_orphan 12
12 quit
13 EOF
15 root@kvm-xfstests:~# mount -o loop foo.img /mnt
16 [  160.238770] ------------[ cut here ]------------
17 [  160.240106] kernel BUG at /usr/projects/linux/ext4/fs/ext4/inode.c:3874!
18 [  160.240106] invalid opcode: 0000 [#1] SMP
19 [  160.240106] Modules linked in:
20 [  160.240106] CPU: 0 PID: 2547 Comm: mount Tainted: G        W       4.10.0-rc3-00034-gcdd33b941b67 #227
21 [  160.240106] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014
22 [  160.240106] task: f4518000 task.stack: f47b6000
23 [  160.240106] EIP: ext4_block_zero_page_range+0x1a7/0x2b4
24 [  160.240106] EFLAGS: 00010246 CPU: 0
25 [  160.240106] EAX: 00000001 EBX: f7be4b50 ECX: f47b7dc0 EDX: 00000007
26 [  160.240106] ESI: f43b05a8 EDI: f43babec EBP: f47b7dd0 ESP: f47b7dac
27 [  160.240106]  DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
28 [  160.240106] CR0: 80050033 CR2: bfd85b08 CR3: 34a00680 CR4: 000006f0
29 [  160.240106] Call Trace:
30 [  160.240106]  ext4_truncate+0x1e9/0x3e5
31 [  160.240106]  ext4_fill_super+0x286f/0x2b1e
32 [  160.240106]  ? set_blocksize+0x2e/0x7e
33 [  160.240106]  mount_bdev+0x114/0x15f
34 [  160.240106]  ext4_mount+0x15/0x17
35 [  160.240106]  ? ext4_calculate_overhead+0x39d/0x39d
36 [  160.240106]  mount_fs+0x58/0x115
37 [  160.240106]  vfs_kern_mount+0x4b/0xae
38 [  160.240106]  do_mount+0x671/0x8c3
39 [  160.240106]  ? _copy_from_user+0x70/0x83
40 [  160.240106]  ? strndup_user+0x31/0x46
41 [  160.240106]  SyS_mount+0x57/0x7b
42 [  160.240106]  do_int80_syscall_32+0x4f/0x61
43 [  160.240106]  entry_INT80_32+0x2f/0x2f
44 [  160.240106] EIP: 0xb76b919e
45 [  160.240106] EFLAGS: 00000246 CPU: 0
46 [  160.240106] EAX: ffffffda EBX: 08053838 ECX: 08052188 EDX: 080537e8
47 [  160.240106] ESI: c0ed0000 EDI: 00000000 EBP: 080537e8 ESP: bfa13660
48 [  160.240106]  DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b
49 [  160.240106] Code: 59 8b 00 a8 01 0f 84 09 01 00 00 8b 07 66 25 00 f0 66 3d 00 80 75 61 89 f8 e8 3e e2 ff ff 84 c0 74 56 83 bf 48 02 00 00 00 75 02 <0f> 0b 81 7d e8 00 10 00 00 74 02 0f 0b 8b 43 04 8b 53 08 31 c9
50 [  160.240106] EIP: ext4_block_zero_page_range+0x1a7/0x2b4 SS:ESP: 0068:f47b7dac
51 [  160.317241] ---[ end trace d6a773a375c810a5 ]---
53 The problem is that when the kernel tries to truncate an inode in
54 ext4_truncate(), it tries to clear any on-disk data beyond i_size.
55 Without the encryption key, it can't do that, and so it triggers a
56 BUG.
58 E2fsck does *not* provide this service, and in practice most file
59 systems have their orphan list processed by e2fsck, so to avoid
60 crashing, this patch skips this step if we don't have access to the
61 encryption key (which is the case when processing the orphan list; in
62 all other cases, we will have the encryption key, or the kernel
63 wouldn't have allowed the file to be opened).
65 An open question is whether the fact that e2fsck isn't clearing the
66 bytes beyond i_size causing problems --- and if we've lived with it
67 not doing it for so long, can we drop this from the kernel replay of
68 the orphan list in all cases (not just when we don't have the key for
69 encrypted inodes).
71 Addresses-Google-Bug: #35209576
73 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
74 ---
75  fs/ext4/inode.c | 4 ++++
76  1 file changed, 4 insertions(+)
78 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
79 index bc282f9d0969..f622d4a577e3 100644
80 --- a/fs/ext4/inode.c
81 +++ b/fs/ext4/inode.c
82 @@ -3944,6 +3944,10 @@ static int ext4_block_truncate_page(handle_t *handle,
83         unsigned blocksize;
84         struct inode *inode = mapping->host;
86 +       /* If we are processing an encrypted inode during orphan list handling */
87 +       if (ext4_encrypted_inode(inode) && !fscrypt_has_encryption_key(inode))
88 +               return 0;
90         blocksize = inode->i_sb->s_blocksize;
91         length = blocksize - (offset & (blocksize - 1));