Unaligned access fixes
authorDavid Miller <davem@davemloft.net>
Fri, 15 Feb 2008 16:19:58 +0000 (11:19 -0500)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Fri, 15 Feb 2008 16:19:58 +0000 (11:19 -0500)
The first problem is that these SETGET macros lose typing information,
and therefore can't see the 'packed' attribute and therefore take
unaligned access SIGBUS signals on sparc64 when trying to derefernce
the member.

The next problem is a similar issue in btrfs_name_hash().  This gets
passed things like &key.offset which is a member of a packed
structure, losing this packed'ness information btrfs_name_hash()
performs a potentially unaligned memory access, again resulting in a
SIGBUS.

ctree.h
dir-item.c
dir-test.c
hash.c
hash.h
hasher.c

diff --git a/ctree.h b/ctree.h
index e852ec2..25532f2 100644 (file)
--- a/ctree.h
+++ b/ctree.h
@@ -451,18 +451,16 @@ static inline void btrfs_set_##name(struct extent_buffer *eb,             \
 static inline u##bits btrfs_##name(struct extent_buffer *eb,           \
                                   type *s)                             \
 {                                                                      \
-       unsigned long offset = (unsigned long)s +                       \
-                               offsetof(type, member);                 \
-       __le##bits *tmp = (__le##bits *)(eb->data + offset);            \
-       return le##bits##_to_cpu(*tmp);                                 \
+       unsigned long offset = (unsigned long)s;                        \
+       type *p = (type *) (eb->data + offset);                         \
+       return le##bits##_to_cpu(p->member);                            \
 }                                                                      \
 static inline void btrfs_set_##name(struct extent_buffer *eb,          \
                                    type *s, u##bits val)               \
 {                                                                      \
-       unsigned long offset = (unsigned long)s +                       \
-                               offsetof(type, member);                 \
-       __le##bits *tmp = (__le##bits *)(eb->data + offset);            \
-       *tmp = cpu_to_le##bits(val);                                    \
+       unsigned long offset = (unsigned long)s;                        \
+       type *p = (type *) (eb->data + offset);                         \
+       p->member = cpu_to_le##bits(val);                               \
 }
 
 #define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits)             \
index 98628db..aaaad6a 100644 (file)
@@ -71,8 +71,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
 
        key.objectid = dir;
        btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
-       ret = btrfs_name_hash(name, name_len, &key.offset);
-       BUG_ON(ret);
+       key.offset = btrfs_name_hash(name, name_len);
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -122,8 +121,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
 
        key.objectid = dir;
        btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
-       ret = btrfs_name_hash(name, name_len, &key.offset);
-       BUG_ON(ret);
+       key.offset = btrfs_name_hash(name, name_len);
        path = btrfs_alloc_path();
        data_size = sizeof(*dir_item) + name_len;
        dir_item = insert_with_overflow(trans, root, path, &key, data_size,
@@ -196,8 +194,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
        key.objectid = dir;
        btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
 
-       ret = btrfs_name_hash(name, name_len, &key.offset);
-       BUG_ON(ret);
+       key.offset = btrfs_name_hash(name, name_len);
 
        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
        if (ret < 0)
@@ -258,8 +255,7 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
 
        key.objectid = dir;
        btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
-       ret = btrfs_name_hash(name, name_len, &key.offset);
-       BUG_ON(ret);
+       key.offset = btrfs_name_hash(name, name_len);
        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
        if (ret < 0)
                return ERR_PTR(ret);
index dc024a2..54ddee2 100644 (file)
@@ -129,8 +129,8 @@ error:
                                    struct btrfs_dir_item);
                found = (char *)(di + 1);
                found_len = btrfs_dir_name_len(di);
-               btrfs_name_hash(buf, strlen(buf), &myhash);
-               btrfs_name_hash(found, found_len, &foundhash);
+               myhash = btrfs_name_hash(buf, strlen(buf));
+               foundhash = btrfs_name_hash(found, found_len);
                if (myhash != foundhash)
                        goto fatal_release;
                btrfs_release_path(root, &path);
diff --git a/hash.c b/hash.c
index 75cac88..58f0be6 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -75,12 +75,13 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
                *buf++ = pad;
 }
 
-int btrfs_name_hash(const char *name, int len, u64 *hash_result)
+u64 btrfs_name_hash(const char *name, int len)
 {
        __u32   hash;
        __u32   minor_hash = 0;
        const char      *p;
        __u32           in[8], buf[2];
+       u64             hash_result;
 
        /* Initialize the default seed for the hash checksum functions */
        buf[0] = 0x67452301;
@@ -97,8 +98,8 @@ int btrfs_name_hash(const char *name, int len, u64 *hash_result)
        }
        hash = buf[0];
        minor_hash = buf[1];
-       *hash_result = buf[0];
-       *hash_result <<= 32;
-       *hash_result |= buf[1];
-       return 0;
+       hash_result = buf[0];
+       hash_result <<= 32;
+       hash_result |= buf[1];
+       return hash_result;
 }
diff --git a/hash.h b/hash.h
index d3be026..868ee17 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -18,5 +18,5 @@
 
 #ifndef __HASH__
 #define __HASH__
-int btrfs_name_hash(const char *name, int len, u64 *hash_result);
+u64 btrfs_name_hash(const char *name, int len);
 #endif
index fb4b0a5..b80407c 100644 (file)
--- a/hasher.c
+++ b/hasher.c
@@ -35,8 +35,7 @@ int main() {
                        continue;
                if (line[strlen(line)-1] == '\n')
                        line[strlen(line)-1] = '\0';
-               ret = btrfs_name_hash(line, strlen(line), &result);
-               BUG_ON(ret);
+               result = btrfs_name_hash(line, strlen(line));
                printf("hash returns %llu\n", (unsigned long long)result);
        }
        return 0;