Commit 92d32170 authored by David Sterba's avatar David Sterba

btrfs: fix unaligned access in readdir

The last update to readdir introduced a temporary buffer to store the
emitted readdir data, but as there are file names of variable length,
there's a lot of unaligned access.

This was observed on a sparc64 machine:

  Kernel unaligned access at TPC[102f3080] btrfs_real_readdir+0x51c/0x718 [btrfs]

Fixes: 23b5ec74 ("btrfs: fix readdir deadlock with pagefault")
CC: stable@vger.kernel.org # 4.14+
Reported-and-tested-by: default avatarRené Rebe <rene@exactcode.com>
Reviewed-by: default avatarLiu Bo <bo.liu@linux.alibaba.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 336a8bb8
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <asm/unaligned.h>
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "transaction.h" #include "transaction.h"
...@@ -5905,11 +5906,13 @@ static int btrfs_filldir(void *addr, int entries, struct dir_context *ctx) ...@@ -5905,11 +5906,13 @@ static int btrfs_filldir(void *addr, int entries, struct dir_context *ctx)
struct dir_entry *entry = addr; struct dir_entry *entry = addr;
char *name = (char *)(entry + 1); char *name = (char *)(entry + 1);
ctx->pos = entry->offset; ctx->pos = get_unaligned(&entry->offset);
if (!dir_emit(ctx, name, entry->name_len, entry->ino, if (!dir_emit(ctx, name, get_unaligned(&entry->name_len),
entry->type)) get_unaligned(&entry->ino),
get_unaligned(&entry->type)))
return 1; return 1;
addr += sizeof(struct dir_entry) + entry->name_len; addr += sizeof(struct dir_entry) +
get_unaligned(&entry->name_len);
ctx->pos++; ctx->pos++;
} }
return 0; return 0;
...@@ -5999,14 +6002,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ...@@ -5999,14 +6002,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
} }
entry = addr; entry = addr;
entry->name_len = name_len; put_unaligned(name_len, &entry->name_len);
name_ptr = (char *)(entry + 1); name_ptr = (char *)(entry + 1);
read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1), read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1),
name_len); name_len);
entry->type = btrfs_filetype_table[btrfs_dir_type(leaf, di)]; put_unaligned(btrfs_filetype_table[btrfs_dir_type(leaf, di)],
&entry->type);
btrfs_dir_item_key_to_cpu(leaf, di, &location); btrfs_dir_item_key_to_cpu(leaf, di, &location);
entry->ino = location.objectid; put_unaligned(location.objectid, &entry->ino);
entry->offset = found_key.offset; put_unaligned(found_key.offset, &entry->offset);
entries++; entries++;
addr += sizeof(struct dir_entry) + name_len; addr += sizeof(struct dir_entry) + name_len;
total_len += sizeof(struct dir_entry) + name_len; total_len += sizeof(struct dir_entry) + name_len;
......
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