[New-bugs-announce] [issue23444] HCI Bluetooth socket bind error on an arm crosscompiler environment
Thomas Chiroux
report at bugs.python.org
Wed Feb 11 16:50:15 CET 2015
New submission from Thomas Chiroux:
This bug bellow occurs only on my crosscompiled environment on arm (marvell armada 166): arm-pxa168-linux-gnueabi
It does not seems to be a cross-compile issue: python compiles without problem and all unittests pass on the target device.
description and first clues
---------------------------
The problem is easyly reproducted using this script:
#!/usr/bin/python
import socket
sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
sock.bind((0,))
which raises the following exception when run on the target device:
Traceback (most recent call last):
File "./test_bt.py", line 4, in <module>
sock.bind((0,))
OSError: [Errno 22] Invalid argument
This does not give much clues, but strace does (i've filtered to display the
two significant parts of strace)
socket(PF_BLUETOOTH, SOCK_RAW|SOCK_CLOEXEC, 1) = 3
bind(3, {sa_family=AF_UNSPEC, sa_data="\0\0\360\35\251\266s\316(U\3\0\0\0"}, 6) = -1 EINVAL (Invalid argument)
(on a working environment, including arm, like a raspberry pi, strace gives the following result (and no traceback of course):
socket(PF_BLUETOOTH, SOCK_RAW|SOCK_CLOEXEC, 1) = 3
bind(3, {sa_family=AF_BLUETOOTH, sa_data="\0\0\0\0\0\0X\352\243\266\0\24\265\266"}, 6) = 0
So, on the armada166, between the socket creation and the bind we lost the socket family (AF_UNSPEC instead of AF_BLUETOOTH).
And That's why bind returns invalid argument.
socketmodule and PyArg_ParseTuple
---------------------------------
Now let's look at Modules/socketmodule.c:
After some digging, i've found that the problem is in getsockaddrarg, in the AF_BLUETOOTH / BTPROTO_HCI case
and more precisely on this line:
https://hg.python.org/cpython/file/ab2c023a9432/Modules/socketmodule.c#l1449
reproducted here:
if (!PyArg_ParseTuple(args, "i", &_BT_HCI_MEMB(addr, dev))) {
When we execute the PyArg_ParseTuple, the addr->hci_family is crunched (by zeros with my previous python sample).
At this same place, i've done the following test:
char buffer[8];
memset(buffer, 0x55, 8);
if (!PyArg_ParseTuple(args, "i", buffer) {
PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
"wrong format");
return 0;
}
printf("CL: %d %d %d %d %d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
memset(buffer, 0xAA, 8);
if (!PyArg_ParseTuple(args, "i", buffer+1) {
PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
"wrong format");
return 0;
}
printf("CL+1: %d %d %d %d %d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
memset(buffer, 0xBB, 8);
if (!PyArg_ParseTuple(args, "i", buffer+2) {
PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
"wrong format");
return 0;
}
printf("CL+2: %d %d %d %d %d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
memset(buffer, 0xcc, 8);
if (!PyArg_ParseTuple(args, "i", buffer+3) {
PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
"wrong format");
return 0;
}
printf("CL+3: %d %d %d %d %d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
and the result is:
CL: 0 0 0 0 85 85 85 85
CL+1: 0 0 0 0 170 170 170 170
CL+2: 0 0 0 0 187 187 187 187
CL+3: 0 0 0 0 204 204 204 204
(WTF ??)
in a working environnement (tested on raspberry B+ / python 3.4.2 locally compiled) result is what we should expect:
CL: 0 0 0 0 85 85 85 85
CL+1: 170 0 0 0 0 170 170 170
CL+2: 187 187 0 0 0 0 187 187
CL+3: 204 204 204 0 0 0 0 204
So on my box if PyArg_ParseTuple write on &addr->hci_dev if write 4 bytes from &addr->hci_family which is 2 bytes BEFORE &addr->hci_dev
At this time I can not understand how it's possible.
Remarks and patch
-----------------
Now I have several remarks and a working patch.
* remark/question 1: why does PyArg_ParseTuple parse an int when addr->hci_dev is an unsigned short ?
even in normal situation, when it works, writing on &addr->hci_dev overflow on the next two bytes which are btw addr->channel (more on that later)
* remark/question 2: i've tried to dig more deeply inside PyArg_ParseTuple and found another odd thing, but I did not try to change it without knowing what I do:
in Python/getargs.c, in convertsimple, int parsing result is not casted before returned:
here: https://hg.python.org/cpython/file/ab2c023a9432/Python/getargs.c#l690
(ival is a long). In all other cases (short, unsigned short, char, usigned char), they are casted before return.
[disclosure: i've tested to add the cast and relaunched my bind test, it did not change anything, but it's still strange for me]
* Now a working patch: here below and attached a working patch which results on a good socket bind, but now completely satisfiying:
--- Python-3.4.2/Modules/socketmodule.c 2014-10-08 10:18:15.000000000 +0200
+++ CC_PYTHON/Python-3.4.2/Modules/socketmodule.c 2015-02-11 15:42:35.173455634 +0100
@@ -1446,11 +1446,12 @@ getsockaddrarg(PySocketSockObject *s, Py
return 0;
#else
_BT_HCI_MEMB(addr, family) = AF_BLUETOOTH;
- if (!PyArg_ParseTuple(args, "i", &_BT_HCI_MEMB(addr, dev))) {
+ if (!PyArg_ParseTuple(args, "H", &_BT_HCI_MEMB(addr, dev))) {
PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
"wrong format");
return 0;
}
+ _BT_HCI_MEMB(addr, channel) = HCI_CHANNEL_RAW;
#endif
*len_ret = sizeof *addr;
return 1;
in short: I parse now an unsigned short instead of parsing an int which gives me a two bytes long elements which is stored well
on addr->hci_dev without overloading addr->hci_family.
But this modification alone is not enough: addr->hci_channel needed a good value.
that's why i added _BT_HCI_MEMB(addr, channel) = HCI_CHANNEL_RAW;
which sets addr->hci_channel to zero.
(without this line, any value could be here)
And that led me to another question/problem: how is hci_channel normally handled ?
It does not seems to be a valid parameter of bind; behaviour without the patch will
erase hci_channel while storing int value in hci_dev, so theorically we can assign
a well defined int value in our bind method to both assign the wanted value in
hci_dev and hci_channel, but it does not seems to be a proper way to do it.
----------
components: Extension Modules
files: bluetooth_bind_arm.patch
keywords: patch
messages: 235751
nosy: Thomas.Chiroux
priority: normal
severity: normal
status: open
title: HCI Bluetooth socket bind error on an arm crosscompiler environment
type: behavior
versions: Python 3.4
Added file: http://bugs.python.org/file38097/bluetooth_bind_arm.patch
_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue23444>
_______________________________________
More information about the New-bugs-announce
mailing list