btrfs: fix unaligned access in readdir
Subsystems (2)
David Sterba
maintainerPATCH 1/1 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]
--- fs/btrfs/inode.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
fs/btrfs/inode.c +12 -8
@@ -31,6 +31,7 @@#include <linux/uio.h> #include <linux/magic.h> #include <linux/iversion.h> +#include <asm/unaligned.h> #include "ctree.h" #include "disk-io.h" #include "transaction.h"
@@ -5905,11 +5906,13 @@ static int btrfs_filldir(void *addr, int entries, struct dir_context *ctx)struct dir_entry *entry = addr;char *name = (char *)(entry +1 ); - ctx->pos = entry->offset; -if (!dir_emit (ctx, name, entry->name_len, entry->ino, - entry->type)) + ctx->pos =get_unaligned (&entry->offset); +if (!dir_emit (ctx, name,get_unaligned (&entry->name_len), +get_unaligned (&entry->ino), +get_unaligned (&entry->type)))return 1 ; - addr +=sizeof (struct dir_entry ) + entry->name_len; + addr +=sizeof (struct dir_entry ) + +get_unaligned (&entry->name_len); ctx->pos++; }return 0 ;
@@ -5999,14 +6002,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) } entry = addr; - entry->name_len = name_len; +put_unaligned (name_len, &entry->name_len); name_ptr = (char *)(entry +1 );read_extent_buffer (leaf, name_ptr, (unsigned long )(di +1 ), 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); - entry->ino = location.objectid; - entry->offset = found_key.offset; +put_unaligned (location.objectid, &entry->ino); +put_unaligned (found_key.offset, &entry->offset); entries++; addr +=sizeof (struct dir_entry ) + name_len; total_len +=sizeof (struct dir_entry ) + name_len;
Signature · 1 line
2.16.2
Quoted 8 lines · David Sterba
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]
thanks,
liubo
Quoted 65 lines
Fixes 23b5ec74943 ("btrfs: fix readdir deadlock with pagefault")CC [email protected] # 4.14+Reported-and-tested-by René Rebe <[email protected]>Signed-off-by David Sterba <[email protected]>--- fs/btrfs/inode.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)fs/btrfs/inode.c +12
-9@@ -31,6 +31,7 @@#include <linux/uio.h> #include <linux/magic.h> #include <linux/iversion.h> +#include <asm/unaligned.h> #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -5905,11 +5906,13 @@ static int btrfs_filldir(void *addr, int entries, struct dir_context *ctx)struct dir_entry *entry = addr;char *name = (char *)(entry +1 ); - ctx->pos = entry->offset; -if (!dir_emit (ctx, name, entry->name_len, entry->ino, - entry->type)) + ctx->pos =get_unaligned (&entry->offset); +if (!dir_emit (ctx, name,get_unaligned (&entry->name_len), +get_unaligned (&entry->ino), +get_unaligned (&entry->type)))return 1 ; - addr +=sizeof (struct dir_entry ) + entry->name_len; + addr +=sizeof (struct dir_entry ) + +get_unaligned (&entry->name_len); ctx->pos++; }return 0 ;@@ -5999,14 +6002,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) } entry = addr; - entry->name_len = name_len; +put_unaligned (name_len, &entry->name_len); name_ptr = (char *)(entry +1 );read_extent_buffer (leaf, name_ptr, (unsigned long )(di +1 ), 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); - entry->ino = location.objectid; - entry->offset = found_key.offset; +put_unaligned (location.objectid, &entry->ino); +put_unaligned (found_key.offset, &entry->offset); entries++; addr +=sizeof (struct dir_entry ) + name_len; total_len +=sizeof (struct dir_entry ) + name_len; --2.16.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html