Commit fe10e398 authored by Eric Biggers's avatar Eric Biggers Committed by Linus Torvalds

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: 1da177e4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+b890b3335a4d8c608963@syzkaller.appspotmail.com
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Reviewed-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ffe07513
...@@ -76,83 +76,99 @@ static char *le_type(struct reiserfs_key *key) ...@@ -76,83 +76,99 @@ static char *le_type(struct reiserfs_key *key)
} }
/* %k */ /* %k */
static void sprintf_le_key(char *buf, struct reiserfs_key *key) static int scnprintf_le_key(char *buf, size_t size, struct reiserfs_key *key)
{ {
if (key) if (key)
sprintf(buf, "[%d %d %s %s]", le32_to_cpu(key->k_dir_id), return scnprintf(buf, size, "[%d %d %s %s]",
le32_to_cpu(key->k_objectid), le_offset(key), le32_to_cpu(key->k_dir_id),
le_type(key)); le32_to_cpu(key->k_objectid), le_offset(key),
le_type(key));
else else
sprintf(buf, "[NULL]"); return scnprintf(buf, size, "[NULL]");
} }
/* %K */ /* %K */
static void sprintf_cpu_key(char *buf, struct cpu_key *key) static int scnprintf_cpu_key(char *buf, size_t size, struct cpu_key *key)
{ {
if (key) if (key)
sprintf(buf, "[%d %d %s %s]", key->on_disk_key.k_dir_id, return scnprintf(buf, size, "[%d %d %s %s]",
key->on_disk_key.k_objectid, reiserfs_cpu_offset(key), key->on_disk_key.k_dir_id,
cpu_type(key)); key->on_disk_key.k_objectid,
reiserfs_cpu_offset(key), cpu_type(key));
else else
sprintf(buf, "[NULL]"); return scnprintf(buf, size, "[NULL]");
} }
static void sprintf_de_head(char *buf, struct reiserfs_de_head *deh) static int scnprintf_de_head(char *buf, size_t size,
struct reiserfs_de_head *deh)
{ {
if (deh) if (deh)
sprintf(buf, return scnprintf(buf, size,
"[offset=%d dir_id=%d objectid=%d location=%d state=%04x]", "[offset=%d dir_id=%d objectid=%d location=%d state=%04x]",
deh_offset(deh), deh_dir_id(deh), deh_objectid(deh), deh_offset(deh), deh_dir_id(deh),
deh_location(deh), deh_state(deh)); deh_objectid(deh), deh_location(deh),
deh_state(deh));
else else
sprintf(buf, "[NULL]"); return scnprintf(buf, size, "[NULL]");
} }
static void sprintf_item_head(char *buf, struct item_head *ih) static int scnprintf_item_head(char *buf, size_t size, struct item_head *ih)
{ {
if (ih) { if (ih) {
strcpy(buf, char *p = buf;
(ih_version(ih) == KEY_FORMAT_3_6) ? "*3.6* " : "*3.5*"); char * const end = buf + size;
sprintf_le_key(buf + strlen(buf), &(ih->ih_key));
sprintf(buf + strlen(buf), ", item_len %d, item_location %d, " p += scnprintf(p, end - p, "%s",
"free_space(entry_count) %d", (ih_version(ih) == KEY_FORMAT_3_6) ?
ih_item_len(ih), ih_location(ih), ih_free_space(ih)); "*3.6* " : "*3.5*");
p += scnprintf_le_key(p, end - p, &ih->ih_key);
p += scnprintf(p, end - p,
", item_len %d, item_location %d, free_space(entry_count) %d",
ih_item_len(ih), ih_location(ih),
ih_free_space(ih));
return p - buf;
} else } else
sprintf(buf, "[NULL]"); return scnprintf(buf, size, "[NULL]");
} }
static void sprintf_direntry(char *buf, struct reiserfs_dir_entry *de) static int scnprintf_direntry(char *buf, size_t size,
struct reiserfs_dir_entry *de)
{ {
char name[20]; char name[20];
memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen); memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen);
name[de->de_namelen > 19 ? 19 : de->de_namelen] = 0; name[de->de_namelen > 19 ? 19 : de->de_namelen] = 0;
sprintf(buf, "\"%s\"==>[%d %d]", name, de->de_dir_id, de->de_objectid); return scnprintf(buf, size, "\"%s\"==>[%d %d]",
name, de->de_dir_id, de->de_objectid);
} }
static void sprintf_block_head(char *buf, struct buffer_head *bh) static int scnprintf_block_head(char *buf, size_t size, struct buffer_head *bh)
{ {
sprintf(buf, "level=%d, nr_items=%d, free_space=%d rdkey ", return scnprintf(buf, size,
B_LEVEL(bh), B_NR_ITEMS(bh), B_FREE_SPACE(bh)); "level=%d, nr_items=%d, free_space=%d rdkey ",
B_LEVEL(bh), B_NR_ITEMS(bh), B_FREE_SPACE(bh));
} }
static void sprintf_buffer_head(char *buf, struct buffer_head *bh) static int scnprintf_buffer_head(char *buf, size_t size, struct buffer_head *bh)
{ {
sprintf(buf, return scnprintf(buf, size,
"dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)", "dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)",
bh->b_bdev, bh->b_size, bh->b_bdev, bh->b_size,
(unsigned long long)bh->b_blocknr, atomic_read(&(bh->b_count)), (unsigned long long)bh->b_blocknr,
bh->b_state, bh->b_page, atomic_read(&(bh->b_count)),
buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE", bh->b_state, bh->b_page,
buffer_dirty(bh) ? "DIRTY" : "CLEAN", buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE",
buffer_locked(bh) ? "LOCKED" : "UNLOCKED"); buffer_dirty(bh) ? "DIRTY" : "CLEAN",
buffer_locked(bh) ? "LOCKED" : "UNLOCKED");
} }
static void sprintf_disk_child(char *buf, struct disk_child *dc) static int scnprintf_disk_child(char *buf, size_t size, struct disk_child *dc)
{ {
sprintf(buf, "[dc_number=%d, dc_size=%u]", dc_block_number(dc), return scnprintf(buf, size, "[dc_number=%d, dc_size=%u]",
dc_size(dc)); dc_block_number(dc), dc_size(dc));
} }
static char *is_there_reiserfs_struct(char *fmt, int *what) 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,55 +205,60 @@ static void prepare_error_buf(const char *fmt, va_list args)
char *fmt1 = fmt_buf; char *fmt1 = fmt_buf;
char *k; char *k;
char *p = error_buf; char *p = error_buf;
char * const end = &error_buf[sizeof(error_buf)];
int what; int what;
spin_lock(&error_lock); spin_lock(&error_lock);
strcpy(fmt1, fmt); if (WARN_ON(strscpy(fmt_buf, fmt, sizeof(fmt_buf)) < 0)) {
strscpy(error_buf, "format string too long", end - error_buf);
goto out_unlock;
}
while ((k = is_there_reiserfs_struct(fmt1, &what)) != NULL) { while ((k = is_there_reiserfs_struct(fmt1, &what)) != NULL) {
*k = 0; *k = 0;
p += vsprintf(p, fmt1, args); p += vscnprintf(p, end - p, fmt1, args);
switch (what) { switch (what) {
case 'k': case 'k':
sprintf_le_key(p, va_arg(args, struct reiserfs_key *)); p += scnprintf_le_key(p, end - p,
va_arg(args, struct reiserfs_key *));
break; break;
case 'K': case 'K':
sprintf_cpu_key(p, va_arg(args, struct cpu_key *)); p += scnprintf_cpu_key(p, end - p,
va_arg(args, struct cpu_key *));
break; break;
case 'h': case 'h':
sprintf_item_head(p, va_arg(args, struct item_head *)); p += scnprintf_item_head(p, end - p,
va_arg(args, struct item_head *));
break; break;
case 't': case 't':
sprintf_direntry(p, p += scnprintf_direntry(p, end - p,
va_arg(args, va_arg(args, struct reiserfs_dir_entry *));
struct reiserfs_dir_entry *));
break; break;
case 'y': case 'y':
sprintf_disk_child(p, p += scnprintf_disk_child(p, end - p,
va_arg(args, struct disk_child *)); va_arg(args, struct disk_child *));
break; break;
case 'z': case 'z':
sprintf_block_head(p, p += scnprintf_block_head(p, end - p,
va_arg(args, struct buffer_head *)); va_arg(args, struct buffer_head *));
break; break;
case 'b': case 'b':
sprintf_buffer_head(p, p += scnprintf_buffer_head(p, end - p,
va_arg(args, struct buffer_head *)); va_arg(args, struct buffer_head *));
break; break;
case 'a': case 'a':
sprintf_de_head(p, p += scnprintf_de_head(p, end - p,
va_arg(args, va_arg(args, struct reiserfs_de_head *));
struct reiserfs_de_head *));
break; break;
} }
p += strlen(p);
fmt1 = k + 2; fmt1 = k + 2;
} }
vsprintf(p, fmt1, args); p += vscnprintf(p, end - p, fmt1, args);
out_unlock:
spin_unlock(&error_lock); spin_unlock(&error_lock);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment