diff options
author | Eric Biggers <ebiggers@google.com> | 2018-07-13 19:59:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-07-14 14:11:10 -0400 |
commit | fe10e398e860955bac4d28ec031b701d358465e4 (patch) | |
tree | 5f5e31e058796ca818dae8f4bef23afd5aaec062 /fs | |
parent | ffe075132af8b7967089c361e506d4fa747efd14 (diff) |
reiserfs: fix buffer overflow with long warning messages
ReiserFS prepares log messages into a 1024-byte buffer with no bounds
checks. Long messages, such as the "unknown mount option" warning when
userspace passes a crafted mount options string, overflow this buffer.
This causes KASAN to report a global-out-of-bounds write.
Fix it by truncating messages to the buffer size.
Link: http://lkml.kernel.org/r/20180707203621.30922-1-ebiggers3@gmail.com
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+b890b3335a4d8c608963@syzkaller.appspotmail.com
Signed-off-by: Eric Biggers <ebiggers@google.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/reiserfs/prints.c | 141 |
1 files changed, 81 insertions, 60 deletions
diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c index 7e288d97adcb..9fed1c05f1f4 100644 --- a/fs/reiserfs/prints.c +++ b/fs/reiserfs/prints.c | |||
@@ -76,83 +76,99 @@ static char *le_type(struct reiserfs_key *key) | |||
76 | } | 76 | } |
77 | 77 | ||
78 | /* %k */ | 78 | /* %k */ |
79 | static void sprintf_le_key(char *buf, struct reiserfs_key *key) | 79 | static int scnprintf_le_key(char *buf, size_t size, struct reiserfs_key *key) |
80 | { | 80 | { |
81 | if (key) | 81 | if (key) |
82 | sprintf(buf, "[%d %d %s %s]", le32_to_cpu(key->k_dir_id), | 82 | return scnprintf(buf, size, "[%d %d %s %s]", |
83 | le32_to_cpu(key->k_objectid), le_offset(key), | 83 | le32_to_cpu(key->k_dir_id), |
84 | le_type(key)); | 84 | le32_to_cpu(key->k_objectid), le_offset(key), |
85 | le_type(key)); | ||
85 | else | 86 | else |
86 | sprintf(buf, "[NULL]"); | 87 | return scnprintf(buf, size, "[NULL]"); |
87 | } | 88 | } |
88 | 89 | ||
89 | /* %K */ | 90 | /* %K */ |
90 | static void sprintf_cpu_key(char *buf, struct cpu_key *key) | 91 | static int scnprintf_cpu_key(char *buf, size_t size, struct cpu_key *key) |
91 | { | 92 | { |
92 | if (key) | 93 | if (key) |
93 | sprintf(buf, "[%d %d %s %s]", key->on_disk_key.k_dir_id, | 94 | return scnprintf(buf, size, "[%d %d %s %s]", |
94 | key->on_disk_key.k_objectid, reiserfs_cpu_offset(key), | 95 | key->on_disk_key.k_dir_id, |
95 | cpu_type(key)); | 96 | key->on_disk_key.k_objectid, |
97 | reiserfs_cpu_offset(key), cpu_type(key)); | ||
96 | else | 98 | else |
97 | sprintf(buf, "[NULL]"); | 99 | return scnprintf(buf, size, "[NULL]"); |
98 | } | 100 | } |
99 | 101 | ||
100 | static void sprintf_de_head(char *buf, struct reiserfs_de_head *deh) | 102 | static int scnprintf_de_head(char *buf, size_t size, |
103 | struct reiserfs_de_head *deh) | ||
101 | { | 104 | { |
102 | if (deh) | 105 | if (deh) |
103 | sprintf(buf, | 106 | return scnprintf(buf, size, |
104 | "[offset=%d dir_id=%d objectid=%d location=%d state=%04x]", | 107 | "[offset=%d dir_id=%d objectid=%d location=%d state=%04x]", |
105 | deh_offset(deh), deh_dir_id(deh), deh_objectid(deh), | 108 | deh_offset(deh), deh_dir_id(deh), |
106 | deh_location(deh), deh_state(deh)); | 109 | deh_objectid(deh), deh_location(deh), |
110 | deh_state(deh)); | ||
107 | else | 111 | else |
108 | sprintf(buf, "[NULL]"); | 112 | return scnprintf(buf, size, "[NULL]"); |
109 | 113 | ||
110 | } | 114 | } |
111 | 115 | ||
112 | static void sprintf_item_head(char *buf, struct item_head *ih) | 116 | static int scnprintf_item_head(char *buf, size_t size, struct item_head *ih) |
113 | { | 117 | { |
114 | if (ih) { | 118 | if (ih) { |
115 | strcpy(buf, | 119 | char *p = buf; |
116 | (ih_version(ih) == KEY_FORMAT_3_6) ? "*3.6* " : "*3.5*"); | 120 | char * const end = buf + size; |
117 | sprintf_le_key(buf + strlen(buf), &(ih->ih_key)); | 121 | |
118 | sprintf(buf + strlen(buf), ", item_len %d, item_location %d, " | 122 | p += scnprintf(p, end - p, "%s", |
119 | "free_space(entry_count) %d", | 123 | (ih_version(ih) == KEY_FORMAT_3_6) ? |
120 | ih_item_len(ih), ih_location(ih), ih_free_space(ih)); | 124 | "*3.6* " : "*3.5*"); |
125 | |||
126 | p += scnprintf_le_key(p, end - p, &ih->ih_key); | ||
127 | |||
128 | p += scnprintf(p, end - p, | ||
129 | ", item_len %d, item_location %d, free_space(entry_count) %d", | ||
130 | ih_item_len(ih), ih_location(ih), | ||
131 | ih_free_space(ih)); | ||
132 | return p - buf; | ||
121 | } else | 133 | } else |
122 | sprintf(buf, "[NULL]"); | 134 | return scnprintf(buf, size, "[NULL]"); |
123 | } | 135 | } |
124 | 136 | ||
125 | static void sprintf_direntry(char *buf, struct reiserfs_dir_entry *de) | 137 | static int scnprintf_direntry(char *buf, size_t size, |
138 | struct reiserfs_dir_entry *de) | ||
126 | { | 139 | { |
127 | char name[20]; | 140 | char name[20]; |
128 | 141 | ||
129 | memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen); | 142 | memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen); |
130 | name[de->de_namelen > 19 ? 19 : de->de_namelen] = 0; | 143 | name[de->de_namelen > 19 ? 19 : de->de_namelen] = 0; |
131 | sprintf(buf, "\"%s\"==>[%d %d]", name, de->de_dir_id, de->de_objectid); | 144 | return scnprintf(buf, size, "\"%s\"==>[%d %d]", |
145 | name, de->de_dir_id, de->de_objectid); | ||
132 | } | 146 | } |
133 | 147 | ||
134 | static void sprintf_block_head(char *buf, struct buffer_head *bh) | 148 | static int scnprintf_block_head(char *buf, size_t size, struct buffer_head *bh) |
135 | { | 149 | { |
136 | sprintf(buf, "level=%d, nr_items=%d, free_space=%d rdkey ", | 150 | return scnprintf(buf, size, |
137 | B_LEVEL(bh), B_NR_ITEMS(bh), B_FREE_SPACE(bh)); | 151 | "level=%d, nr_items=%d, free_space=%d rdkey ", |
152 | B_LEVEL(bh), B_NR_ITEMS(bh), B_FREE_SPACE(bh)); | ||
138 | } | 153 | } |
139 | 154 | ||
140 | static void sprintf_buffer_head(char *buf, struct buffer_head *bh) | 155 | static int scnprintf_buffer_head(char *buf, size_t size, struct buffer_head *bh) |
141 | { | 156 | { |
142 | sprintf(buf, | 157 | return scnprintf(buf, size, |
143 | "dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)", | 158 | "dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)", |
144 | bh->b_bdev, bh->b_size, | 159 | bh->b_bdev, bh->b_size, |
145 | (unsigned long long)bh->b_blocknr, atomic_read(&(bh->b_count)), | 160 | (unsigned long long)bh->b_blocknr, |
146 | bh->b_state, bh->b_page, | 161 | atomic_read(&(bh->b_count)), |
147 | buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE", | 162 | bh->b_state, bh->b_page, |
148 | buffer_dirty(bh) ? "DIRTY" : "CLEAN", | 163 | buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE", |
149 | buffer_locked(bh) ? "LOCKED" : "UNLOCKED"); | 164 | buffer_dirty(bh) ? "DIRTY" : "CLEAN", |
165 | buffer_locked(bh) ? "LOCKED" : "UNLOCKED"); | ||
150 | } | 166 | } |
151 | 167 | ||
152 | static void sprintf_disk_child(char *buf, struct disk_child *dc) | 168 | static int scnprintf_disk_child(char *buf, size_t size, struct disk_child *dc) |
153 | { | 169 | { |
154 | sprintf(buf, "[dc_number=%d, dc_size=%u]", dc_block_number(dc), | 170 | return scnprintf(buf, size, "[dc_number=%d, dc_size=%u]", |
155 | dc_size(dc)); | 171 | dc_block_number(dc), dc_size(dc)); |
156 | } | 172 | } |
157 | 173 | ||
158 | static char *is_there_reiserfs_struct(char *fmt, int *what) | 174 | static char *is_there_reiserfs_struct(char *fmt, int *what) |
@@ -189,55 +205,60 @@ static void prepare_error_buf(const char *fmt, va_list args) | |||
189 | char *fmt1 = fmt_buf; | 205 | char *fmt1 = fmt_buf; |
190 | char *k; | 206 | char *k; |
191 | char *p = error_buf; | 207 | char *p = error_buf; |
208 | char * const end = &error_buf[sizeof(error_buf)]; | ||
192 | int what; | 209 | int what; |
193 | 210 | ||
194 | spin_lock(&error_lock); | 211 | spin_lock(&error_lock); |
195 | 212 | ||
196 | strcpy(fmt1, fmt); | 213 | if (WARN_ON(strscpy(fmt_buf, fmt, sizeof(fmt_buf)) < 0)) { |
214 | strscpy(error_buf, "format string too long", end - error_buf); | ||
215 | goto out_unlock; | ||
216 | } | ||
197 | 217 | ||
198 | while ((k = is_there_reiserfs_struct(fmt1, &what)) != NULL) { | 218 | while ((k = is_there_reiserfs_struct(fmt1, &what)) != NULL) { |
199 | *k = 0; | 219 | *k = 0; |
200 | 220 | ||
201 | p += vsprintf(p, fmt1, args); | 221 | p += vscnprintf(p, end - p, fmt1, args); |
202 | 222 | ||
203 | switch (what) { | 223 | switch (what) { |
204 | case 'k': | 224 | case 'k': |
205 | sprintf_le_key(p, va_arg(args, struct reiserfs_key *)); | 225 | p += scnprintf_le_key(p, end - p, |
226 | va_arg(args, struct reiserfs_key *)); | ||
206 | break; | 227 | break; |
207 | case 'K': | 228 | case 'K': |
208 | sprintf_cpu_key(p, va_arg(args, struct cpu_key *)); | 229 | p += scnprintf_cpu_key(p, end - p, |
230 | va_arg(args, struct cpu_key *)); | ||
209 | break; | 231 | break; |
210 | case 'h': | 232 | case 'h': |
211 | sprintf_item_head(p, va_arg(args, struct item_head *)); | 233 | p += scnprintf_item_head(p, end - p, |
234 | va_arg(args, struct item_head *)); | ||
212 | break; | 235 | break; |
213 | case 't': | 236 | case 't': |
214 | sprintf_direntry(p, | 237 | p += scnprintf_direntry(p, end - p, |
215 | va_arg(args, | 238 | va_arg(args, struct reiserfs_dir_entry *)); |
216 | struct reiserfs_dir_entry *)); | ||
217 | break; | 239 | break; |
218 | case 'y': | 240 | case 'y': |
219 | sprintf_disk_child(p, | 241 | p += scnprintf_disk_child(p, end - p, |
220 | va_arg(args, struct disk_child *)); | 242 | va_arg(args, struct disk_child *)); |
221 | break; | 243 | break; |
222 | case 'z': | 244 | case 'z': |
223 | sprintf_block_head(p, | 245 | p += scnprintf_block_head(p, end - p, |
224 | va_arg(args, struct buffer_head *)); | 246 | va_arg(args, struct buffer_head *)); |
225 | break; | 247 | break; |
226 | case 'b': | 248 | case 'b': |
227 | sprintf_buffer_head(p, | 249 | p += scnprintf_buffer_head(p, end - p, |
228 | va_arg(args, struct buffer_head *)); | 250 | va_arg(args, struct buffer_head *)); |
229 | break; | 251 | break; |
230 | case 'a': | 252 | case 'a': |
231 | sprintf_de_head(p, | 253 | p += scnprintf_de_head(p, end - p, |
232 | va_arg(args, | 254 | va_arg(args, struct reiserfs_de_head *)); |
233 | struct reiserfs_de_head *)); | ||
234 | break; | 255 | break; |
235 | } | 256 | } |
236 | 257 | ||
237 | p += strlen(p); | ||
238 | fmt1 = k + 2; | 258 | fmt1 = k + 2; |
239 | } | 259 | } |
240 | vsprintf(p, fmt1, args); | 260 | p += vscnprintf(p, end - p, fmt1, args); |
261 | out_unlock: | ||
241 | spin_unlock(&error_lock); | 262 | spin_unlock(&error_lock); |
242 | 263 | ||
243 | } | 264 | } |