Skip to content

HCI Bluetooth socket bind error on an arm crosscompiled environment #67632

Description

@thomaschiroux
BPO 23444
Files
  • bluetooth_bind_arm.patch: patch file
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2015-02-11.15:50:15.557>
    labels = ['extension-modules', 'type-bug']
    title = 'HCI Bluetooth socket bind error on an arm crosscompiled environment'
    updated_at = <Date 2015-02-11.16:05:05.047>
    user = 'https://bugs.python.org/ThomasChiroux'

    bugs.python.org fields:

    activity = <Date 2015-02-11.16:05:05.047>
    actor = 'Thomas.Chiroux'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Extension Modules']
    creation = <Date 2015-02-11.15:50:15.557>
    creator = 'Thomas.Chiroux'
    dependencies = []
    files = ['38097']
    hgrepos = []
    issue_num = 23444
    keywords = ['patch']
    message_count = 1.0
    messages = ['235751']
    nosy_count = 1.0
    nosy_names = ['Thomas.Chiroux']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = None
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue23444'
    versions = ['Python 3.4']

    Activity

    1. thomaschiroux commented on Feb 11, 2015

      thomaschirouxmannequin
      MannequinAuthor

      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.

    2. changed the title [-]HCI Bluetooth socket bind error on an arm crosscompiler environment[/-] [+]HCI Bluetooth socket bind error on an arm crosscompiled environment[/+] on Feb 11, 2015
    3. transferred this issue fromon Apr 10, 2022
    4. serhiy-storchaka commented on Apr 1, 2025

      @serhiy-storchaka
      Member

      It should be fixed by #131668.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions