Re: Linux 3.3-rc5

From: Linus Torvalds
Date: Thu Mar 01 2012 - 11:23:16 EST


On Thu, Mar 1, 2012 at 6:47 AM, Mark Lord <kernel@xxxxxxxxxxxx> wrote:
>
> It's close (after adding a missing left-paren), but not 100% working yet.
> In particular, this command fails to get valid data:  i8kctl bios
>
>    ioctl(3, I8K_BIOS_VERSION, 0xbfc543c8)  = -1 EINVAL (Invalid argument)

Hmm. That makes no sense.

I8K_BIOS_VERSION is 0x80046980 - and it already matches on both x86-64
and x86-32. I just checked by compiling to asm. And the size is '4',
so it wouldn't trigger any changes by the sizemask thing either.

Ugh. But my patch was crap. It fixed up "arg", but it *should* have
fixed up "cmd".

Stupid.

So try *this* one instead. I also fixed up the missing parenthesis,
but didn't add the actual .compat function etc.

Does *this* work? And if not, can you do an strace, but use the "-e
raw=ioctl" option to make it show the ioctl numbers as raw hex to
verify..

Linus

Linus
drivers/char/i8k.c | 4 ++++
include/linux/i8k.h | 22 ++++++++++++++++------
2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index 40cc0cf2ded6..2baa921afd84 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -326,6 +326,10 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
if (!argp)
return -EINVAL;

+ /* We had some bad 64-bit confusion */
+ if (((cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) == 8)
+ cmd -= 4 << _IOC_SIZESHIFT;
+
switch (cmd) {
case I8K_BIOS_VERSION:
val = i8k_get_bios_version();
diff --git a/include/linux/i8k.h b/include/linux/i8k.h
index 1c45ba505115..650f4015c89c 100644
--- a/include/linux/i8k.h
+++ b/include/linux/i8k.h
@@ -20,14 +20,24 @@
#define I8K_PROC "/proc/i8k"
#define I8K_PROC_FMT "1.0"

+/*
+ * For hysterical raisins we expose the wrong size to user space
+ * in the ioctl numbering. The actual real data size is "int".
+ */
+#ifdef __KERNEL__
+#define borked_i8k_arg int
+#else
+#define borked_i8k_arg size_t
+#endif
+
#define I8K_BIOS_VERSION _IOR ('i', 0x80, int) /* broken: meant 4 bytes */
#define I8K_MACHINE_ID _IOR ('i', 0x81, int) /* broken: meant 16 bytes */
-#define I8K_POWER_STATUS _IOR ('i', 0x82, size_t)
-#define I8K_FN_STATUS _IOR ('i', 0x83, size_t)
-#define I8K_GET_TEMP _IOR ('i', 0x84, size_t)
-#define I8K_GET_SPEED _IOWR('i', 0x85, size_t)
-#define I8K_GET_FAN _IOWR('i', 0x86, size_t)
-#define I8K_SET_FAN _IOWR('i', 0x87, size_t)
+#define I8K_POWER_STATUS _IOR ('i', 0x82, borked_i8k_arg)
+#define I8K_FN_STATUS _IOR ('i', 0x83, borked_i8k_arg)
+#define I8K_GET_TEMP _IOR ('i', 0x84, borked_i8k_arg)
+#define I8K_GET_SPEED _IOWR('i', 0x85, borked_i8k_arg)
+#define I8K_GET_FAN _IOWR('i', 0x86, borked_i8k_arg)
+#define I8K_SET_FAN _IOWR('i', 0x87, borked_i8k_arg)

#define I8K_FAN_LEFT 1
#define I8K_FAN_RIGHT 0