summaryrefslogtreecommitdiffstats
path: root/queue-2.6.32/autofs-work-around-unhappy-compat-problem-on-x86-64.patch
diff options
context:
space:
mode:
Diffstat (limited to 'queue-2.6.32/autofs-work-around-unhappy-compat-problem-on-x86-64.patch')
-rw-r--r--queue-2.6.32/autofs-work-around-unhappy-compat-problem-on-x86-64.patch134
1 files changed, 134 insertions, 0 deletions
diff --git a/queue-2.6.32/autofs-work-around-unhappy-compat-problem-on-x86-64.patch b/queue-2.6.32/autofs-work-around-unhappy-compat-problem-on-x86-64.patch
new file mode 100644
index 0000000..0f603bf
--- /dev/null
+++ b/queue-2.6.32/autofs-work-around-unhappy-compat-problem-on-x86-64.patch
@@ -0,0 +1,134 @@
+From a32744d4abae24572eff7269bc17895c41bd0085 Mon Sep 17 00:00:00 2001
+From: Ian Kent <raven@themaw.net>
+Date: Wed, 22 Feb 2012 20:45:44 +0800
+Subject: autofs: work around unhappy compat problem on x86-64
+
+From: Ian Kent <raven@themaw.net>
+
+commit a32744d4abae24572eff7269bc17895c41bd0085 upstream.
+
+When the autofs protocol version 5 packet type was added in commit
+5c0a32fc2cd0 ("autofs4: add new packet type for v5 communications"), it
+obvously tried quite hard to be word-size agnostic, and uses explicitly
+sized fields that are all correctly aligned.
+
+However, with the final "char name[NAME_MAX+1]" array at the end, the
+actual size of the structure ends up being not very well defined:
+because the struct isn't marked 'packed', doing a "sizeof()" on it will
+align the size of the struct up to the biggest alignment of the members
+it has.
+
+And despite all the members being the same, the alignment of them is
+different: a "__u64" has 4-byte alignment on x86-32, but native 8-byte
+alignment on x86-64. And while 'NAME_MAX+1' ends up being a nice round
+number (256), the name[] array starts out a 4-byte aligned.
+
+End result: the "packed" size of the structure is 300 bytes: 4-byte, but
+not 8-byte aligned.
+
+As a result, despite all the fields being in the same place on all
+architectures, sizeof() will round up that size to 304 bytes on
+architectures that have 8-byte alignment for u64.
+
+Note that this is *not* a problem for 32-bit compat mode on POWER, since
+there __u64 is 8-byte aligned even in 32-bit mode. But on x86, 32-bit
+and 64-bit alignment is different for 64-bit entities, and as a result
+the structure that has exactly the same layout has different sizes.
+
+So on x86-64, but no other architecture, we will just subtract 4 from
+the size of the structure when running in a compat task. That way we
+will write the properly sized packet that user mode expects.
+
+Not pretty. Sadly, this very subtle, and unnecessary, size difference
+has been encoded in user space that wants to read packets of *exactly*
+the right size, and will refuse to touch anything else.
+
+Reported-and-tested-by: Thomas Meyer <thomas@m3y3r.de>
+Signed-off-by: Ian Kent <raven@themaw.net>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/autofs4/autofs_i.h | 1 +
+ fs/autofs4/dev-ioctl.c | 1 +
+ fs/autofs4/inode.c | 2 ++
+ fs/autofs4/waitq.c | 22 +++++++++++++++++++---
+ 4 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/fs/autofs4/autofs_i.h
++++ b/fs/autofs4/autofs_i.h
+@@ -125,6 +125,7 @@ struct autofs_sb_info {
+ int sub_version;
+ int min_proto;
+ int max_proto;
++ int compat_daemon;
+ unsigned long exp_timeout;
+ unsigned int type;
+ int reghost_enabled;
+--- a/fs/autofs4/dev-ioctl.c
++++ b/fs/autofs4/dev-ioctl.c
+@@ -389,6 +389,7 @@ static int autofs_dev_ioctl_setpipefd(st
+ sbi->pipefd = pipefd;
+ sbi->pipe = pipe;
+ sbi->catatonic = 0;
++ sbi->compat_daemon = is_compat_task();
+ }
+ out:
+ mutex_unlock(&sbi->wq_mutex);
+--- a/fs/autofs4/inode.c
++++ b/fs/autofs4/inode.c
+@@ -19,6 +19,7 @@
+ #include <linux/parser.h>
+ #include <linux/bitops.h>
+ #include <linux/magic.h>
++#include <linux/compat.h>
+ #include "autofs_i.h"
+ #include <linux/module.h>
+
+@@ -341,6 +342,7 @@ int autofs4_fill_super(struct super_bloc
+ set_autofs_type_indirect(&sbi->type);
+ sbi->min_proto = 0;
+ sbi->max_proto = 0;
++ sbi->compat_daemon = is_compat_task();
+ mutex_init(&sbi->wq_mutex);
+ spin_lock_init(&sbi->fs_lock);
+ sbi->queues = NULL;
+--- a/fs/autofs4/waitq.c
++++ b/fs/autofs4/waitq.c
+@@ -90,7 +90,24 @@ static int autofs4_write(struct file *fi
+
+ return (bytes > 0);
+ }
+-
++
++/*
++ * The autofs_v5 packet was misdesigned.
++ *
++ * The packets are identical on x86-32 and x86-64, but have different
++ * alignment. Which means that 'sizeof()' will give different results.
++ * Fix it up for the case of running 32-bit user mode on a 64-bit kernel.
++ */
++static noinline size_t autofs_v5_packet_size(struct autofs_sb_info *sbi)
++{
++ size_t pktsz = sizeof(struct autofs_v5_packet);
++#if defined(CONFIG_X86_64) && defined(CONFIG_COMPAT)
++ if (sbi->compat_daemon > 0)
++ pktsz -= 4;
++#endif
++ return pktsz;
++}
++
+ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
+ struct autofs_wait_queue *wq,
+ int type)
+@@ -147,8 +164,7 @@ static void autofs4_notify_daemon(struct
+ {
+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet;
+
+- pktsz = sizeof(*packet);
+-
++ pktsz = autofs_v5_packet_size(sbi);
+ packet->wait_queue_token = wq->wait_queue_token;
+ packet->len = wq->name.len;
+ memcpy(packet->name, wq->name.name, wq->name.len);