Comment 63 for bug 32415

Revision history for this message
kisch (kisch) wrote : Re: Bluetooth Mouse and Keyboard Broken in Dapper/Edgy/Feisty

@Lothar (2006-10-14/15):
thanks for you strace log.
It proves that there are actually two diffent bugs, both related to hidd and/or the hidp kernel module.
A/ you observe a "connect timeout" from "hidd --connect ..."
B/ Zhares sees a "HID create error 14" from "hidd --connect ..."

Bug A seems to be related to certain dongle/device combinations, as Etienne states. It's not related to the platform - you see it on an Athlon, I see it on a PowerPC G4.

Bug B ONLY happens on 64bit PowerPC Apple G5 hardware. The HID device doesn't matter.

These bugs are really different. Your strace (bug A) shows hidd failing BEFORE it reaches the point where hidd executes ioctl(HIDPCONNADD), which would trigger bug B.

Bug B seems to be caused by a 64bit problem, just as Zhares assumed.

The ioctl which fails is defined in /usr/include/bluetooth/hidp.h:
#define HIDPCONNADD _IOW('H', 200, int)

... but the caller is supposed to pass not an int, but instead a pointer to a struct hidp_connadd_req, which is declared as:
{
        int ctrl_sock; /* Connected control socket */
        int intr_sock; /* Connected interrupt socket */
        uint16_t parser; /* Parser version */
        uint16_t rd_size; /* Report descriptor size */
        uint8_t *rd_data; /* Report descriptor data */
        uint8_t country;
... [remaining parts are arch-independently sized]
};

In the application the ioctl has to be called in this way (taken from bluez-utils/hidd/main.c):
        struct hidp_connadd_req req;
...
        err = ioctl(ctl, HIDPCONNADD, &req);

On a 64bit PowerPC system like a PowerMac G5, ubuntu uses a 64bit kernel and a mixed 32/64bit userspace. Specifically, hidd is compiled as a 32bit binary.
In a 64bit compile (kernel), the pointer "uint8_t *rd_data" is 8 bytes long, and it will also be aligned to a multiple of 8. Therefore the size of struct hidp_connadd_req will be larger in a 64bit compile than in a 32bit compile (hidd). I've attached a small test program to illustrate the difference -- try it:
gcc -m32 hidp_connadd_bug.c && ./a.out
gcc -m64 hidp_connadd_bug.c && ./a.out
The sizes of the struct are 160 vs. 168 bytes.

The hidp kernel module has this code in Linux/net/bluetooth/hidp/sock.c:

static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
        void __user *argp = (void __user *) arg;
        struct hidp_connadd_req ca;
...
        switch (cmd) {
        case HIDPCONNADD:
...
                if (copy_from_user(&ca, argp, sizeof(ca)))
                        return -EFAULT;
...

EFAULT is error 14, "bad address" (as observed).

The hidp kernel module tries to copy a structure hidp_conadd_req of a larger size (due to 64bit pointer size) than the user provided (due to 32bit pointer size). I suspect that this fails, so that the ioctl returns the error code, causing hidd to terminate.
Even if the copy succeeded, the struct would then be interpreted incorrectly by the hidp module (resulting in different error codes from the ioctl).

So I think that the root cause seems to be the definition of the bluetooth hidp ioctl API HIDPCONADD.
I'll try to prepare a patch for the kernel to verify this.

- Bernd Kischnick