]>
Commit | Line | Data |
---|---|---|
2289b8c5 SL |
1 | From 551220c9cfa90499cd0ee031bb3f97f8ebf6b818 Mon Sep 17 00:00:00 2001 |
2 | From: Randall Huang <huangrandall@google.com> | |
3 | Date: Thu, 11 Apr 2019 16:26:46 +0800 | |
4 | Subject: f2fs: fix to avoid accessing xattr across the boundary | |
5 | ||
6 | [ Upstream commit 2777e654371dd4207a3a7f4fb5fa39550053a080 ] | |
7 | ||
8 | When we traverse xattr entries via __find_xattr(), | |
9 | if the raw filesystem content is faked or any hardware failure occurs, | |
10 | out-of-bound error can be detected by KASAN. | |
11 | Fix the issue by introducing boundary check. | |
12 | ||
13 | [ 38.402878] c7 1827 BUG: KASAN: slab-out-of-bounds in f2fs_getxattr+0x518/0x68c | |
14 | [ 38.402891] c7 1827 Read of size 4 at addr ffffffc0b6fb35dc by task | |
15 | [ 38.402935] c7 1827 Call trace: | |
16 | [ 38.402952] c7 1827 [<ffffff900809003c>] dump_backtrace+0x0/0x6bc | |
17 | [ 38.402966] c7 1827 [<ffffff9008090030>] show_stack+0x20/0x2c | |
18 | [ 38.402981] c7 1827 [<ffffff900871ab10>] dump_stack+0xfc/0x140 | |
19 | [ 38.402995] c7 1827 [<ffffff9008325c40>] print_address_description+0x80/0x2d8 | |
20 | [ 38.403009] c7 1827 [<ffffff900832629c>] kasan_report_error+0x198/0x1fc | |
21 | [ 38.403022] c7 1827 [<ffffff9008326104>] kasan_report_error+0x0/0x1fc | |
22 | [ 38.403037] c7 1827 [<ffffff9008325000>] __asan_load4+0x1b0/0x1b8 | |
23 | [ 38.403051] c7 1827 [<ffffff90085fcc44>] f2fs_getxattr+0x518/0x68c | |
24 | [ 38.403066] c7 1827 [<ffffff90085fc508>] f2fs_xattr_generic_get+0xb0/0xd0 | |
25 | [ 38.403080] c7 1827 [<ffffff9008395708>] __vfs_getxattr+0x1f4/0x1fc | |
26 | [ 38.403096] c7 1827 [<ffffff9008621bd0>] inode_doinit_with_dentry+0x360/0x938 | |
27 | [ 38.403109] c7 1827 [<ffffff900862d6cc>] selinux_d_instantiate+0x2c/0x38 | |
28 | [ 38.403123] c7 1827 [<ffffff900861b018>] security_d_instantiate+0x68/0x98 | |
29 | [ 38.403136] c7 1827 [<ffffff9008377db8>] d_splice_alias+0x58/0x348 | |
30 | [ 38.403149] c7 1827 [<ffffff900858d16c>] f2fs_lookup+0x608/0x774 | |
31 | [ 38.403163] c7 1827 [<ffffff900835eacc>] lookup_slow+0x1e0/0x2cc | |
32 | [ 38.403177] c7 1827 [<ffffff9008367fe0>] walk_component+0x160/0x520 | |
33 | [ 38.403190] c7 1827 [<ffffff9008369ef4>] path_lookupat+0x110/0x2b4 | |
34 | [ 38.403203] c7 1827 [<ffffff900835dd38>] filename_lookup+0x1d8/0x3a8 | |
35 | [ 38.403216] c7 1827 [<ffffff900835eeb0>] user_path_at_empty+0x54/0x68 | |
36 | [ 38.403229] c7 1827 [<ffffff9008395f44>] SyS_getxattr+0xb4/0x18c | |
37 | [ 38.403241] c7 1827 [<ffffff9008084200>] el0_svc_naked+0x34/0x38 | |
38 | ||
39 | Signed-off-by: Randall Huang <huangrandall@google.com> | |
40 | [Jaegeuk Kim: Fix wrong ending boundary] | |
41 | Reviewed-by: Chao Yu <yuchao0@huawei.com> | |
42 | Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> | |
43 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
44 | --- | |
45 | fs/f2fs/xattr.c | 36 +++++++++++++++++++++++++++--------- | |
46 | fs/f2fs/xattr.h | 2 ++ | |
47 | 2 files changed, 29 insertions(+), 9 deletions(-) | |
48 | ||
49 | diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c | |
50 | index 409a637f7a92..88e30f7cf9e1 100644 | |
51 | --- a/fs/f2fs/xattr.c | |
52 | +++ b/fs/f2fs/xattr.c | |
53 | @@ -205,12 +205,17 @@ static inline const struct xattr_handler *f2fs_xattr_handler(int index) | |
54 | return handler; | |
55 | } | |
56 | ||
57 | -static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, | |
58 | - size_t len, const char *name) | |
59 | +static struct f2fs_xattr_entry *__find_xattr(void *base_addr, | |
60 | + void *last_base_addr, int index, | |
61 | + size_t len, const char *name) | |
62 | { | |
63 | struct f2fs_xattr_entry *entry; | |
64 | ||
65 | list_for_each_xattr(entry, base_addr) { | |
66 | + if ((void *)(entry) + sizeof(__u32) > last_base_addr || | |
67 | + (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) | |
68 | + return NULL; | |
69 | + | |
70 | if (entry->e_name_index != index) | |
71 | continue; | |
72 | if (entry->e_name_len != len) | |
73 | @@ -300,20 +305,22 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, | |
74 | const char *name, struct f2fs_xattr_entry **xe, | |
75 | void **base_addr, int *base_size) | |
76 | { | |
77 | - void *cur_addr, *txattr_addr, *last_addr = NULL; | |
78 | + void *cur_addr, *txattr_addr, *last_txattr_addr; | |
79 | + void *last_addr = NULL; | |
80 | nid_t xnid = F2FS_I(inode)->i_xattr_nid; | |
81 | - unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0; | |
82 | unsigned int inline_size = inline_xattr_size(inode); | |
83 | int err = 0; | |
84 | ||
85 | - if (!size && !inline_size) | |
86 | + if (!xnid && !inline_size) | |
87 | return -ENODATA; | |
88 | ||
89 | - *base_size = inline_size + size + XATTR_PADDING_SIZE; | |
90 | + *base_size = XATTR_SIZE(xnid, inode) + XATTR_PADDING_SIZE; | |
91 | txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), *base_size, GFP_NOFS); | |
92 | if (!txattr_addr) | |
93 | return -ENOMEM; | |
94 | ||
95 | + last_txattr_addr = (void *)txattr_addr + XATTR_SIZE(xnid, inode); | |
96 | + | |
97 | /* read from inline xattr */ | |
98 | if (inline_size) { | |
99 | err = read_inline_xattr(inode, ipage, txattr_addr); | |
100 | @@ -340,7 +347,11 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, | |
101 | else | |
102 | cur_addr = txattr_addr; | |
103 | ||
104 | - *xe = __find_xattr(cur_addr, index, len, name); | |
105 | + *xe = __find_xattr(cur_addr, last_txattr_addr, index, len, name); | |
106 | + if (!*xe) { | |
107 | + err = -EFAULT; | |
108 | + goto out; | |
109 | + } | |
110 | check: | |
111 | if (IS_XATTR_LAST_ENTRY(*xe)) { | |
112 | err = -ENODATA; | |
113 | @@ -584,7 +595,8 @@ static int __f2fs_setxattr(struct inode *inode, int index, | |
114 | struct page *ipage, int flags) | |
115 | { | |
116 | struct f2fs_xattr_entry *here, *last; | |
117 | - void *base_addr; | |
118 | + void *base_addr, *last_base_addr; | |
119 | + nid_t xnid = F2FS_I(inode)->i_xattr_nid; | |
120 | int found, newsize; | |
121 | size_t len; | |
122 | __u32 new_hsize; | |
123 | @@ -608,8 +620,14 @@ static int __f2fs_setxattr(struct inode *inode, int index, | |
124 | if (error) | |
125 | return error; | |
126 | ||
127 | + last_base_addr = (void *)base_addr + XATTR_SIZE(xnid, inode); | |
128 | + | |
129 | /* find entry with wanted name. */ | |
130 | - here = __find_xattr(base_addr, index, len, name); | |
131 | + here = __find_xattr(base_addr, last_base_addr, index, len, name); | |
132 | + if (!here) { | |
133 | + error = -EFAULT; | |
134 | + goto exit; | |
135 | + } | |
136 | ||
137 | found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1; | |
138 | ||
139 | diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h | |
140 | index dbcd1d16e669..2a4ecaf338ea 100644 | |
141 | --- a/fs/f2fs/xattr.h | |
142 | +++ b/fs/f2fs/xattr.h | |
143 | @@ -74,6 +74,8 @@ struct f2fs_xattr_entry { | |
144 | entry = XATTR_NEXT_ENTRY(entry)) | |
145 | #define VALID_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer)) | |
146 | #define XATTR_PADDING_SIZE (sizeof(__u32)) | |
147 | +#define XATTR_SIZE(x,i) (((x) ? VALID_XATTR_BLOCK_SIZE : 0) + \ | |
148 | + (inline_xattr_size(i))) | |
149 | #define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \ | |
150 | VALID_XATTR_BLOCK_SIZE) | |
151 | ||
152 | -- | |
153 | 2.20.1 | |
154 |