Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 567418 - dev-lang/python-2.7.10-r1 ctypes module truncates 64-bit pointers
Summary: dev-lang/python-2.7.10-r1 ctypes module truncates 64-bit pointers
Status: RESOLVED INVALID
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: [OLD] Development (show other bugs)
Hardware: AMD64 Linux
: Normal major (vote)
Assignee: Python Gentoo Team
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-12-03 06:01 UTC by Stuart Longland
Modified: 2015-12-09 03:56 UTC (History)
0 users

See Also:
Package list:
Runtime testing required: ---


Attachments
emerge --info output (emerge-info.txt,7.04 KB, text/plain)
2015-12-03 06:01 UTC, Stuart Longland
Details
Test log (test.log,4.83 KB, text/plain)
2015-12-08 22:51 UTC, Mike Gilbert
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Stuart Longland 2015-12-03 06:01:52 UTC
Created attachment 418446 [details]
emerge --info output

Hi all,

Seems I've hit an issue with the Gentoo package for python-2.7.10-r1 and its 'ctypes' module.  When a structure is passed to a library function, the pointer is truncated to 32-bits, causing a crash in the driver module.

This is in particular been observed with the `python-mbus` library: https://github.com/rscada/python-mbus which is a ctypes wrapper around https://github.com/rscada/libmbus.  libmbus itself works fine, have it happily polling a Siemens UH50 thermal/pulse meter.

No hardware is needed to reproduce this bug in ctypes however.

RC=0 stuartl@rikishi ~/vrt/projects/metermaster/deps/python-mbus $ gdb --args /usr/bin/python /usr/bin/py.test 
GNU gdb (Gentoo 7.7.1 p1) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python...(no debugging symbols found)...done.
(gdb) r
Starting program: /usr/bin/python /usr/bin/py.test
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
process 5971 is executing new program: /usr/bin/python2.7
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
======================================================================== test session starts ========================================================================
platform linux2 -- Python 2.7.10 -- py-1.4.30 -- pytest-2.7.2
rootdir: /home/stuartl/vrt/projects/metermaster/deps/python-mbus, inifile: pytest.ini
plugins: cov
collected 16 items 

tests/test_MBus_connect.py 
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff19f5be1 in mbus_connect (handle=0x573e00a0) at mbus-protocol-aux.c:1589
1589    mbus-protocol-aux.c: No such file or directory.

I've verified that under Debian Wheezy 64-bit (running under LXC on the Gentoo host).
Comment 1 Stuart Longland 2015-12-08 10:47:14 UTC
Some further information, tried rebuilding libffi, cffi, python:

1449558950: Started emerge on: Dec 08, 2015 17:15:49
1449558950:  *** emerge --oneshot --verbose dev-libs/libffi dev-lang/python:2.7 dev-python/cffi
1449559136:  >>> emerge (1 of 3) dev-libs/libffi-3.0.13-r1 to /
1449559136:  === (1 of 3) Cleaning (dev-libs/libffi-3.0.13-r1::/usr/portage/dev-libs/libffi/libffi-3.0.13-r1.ebuild)
1449559137:  === (1 of 3) Compiling/Packaging (dev-libs/libffi-3.0.13-r1::/usr/portage/dev-libs/libffi/libffi-3.0.13-r1.ebuild)
1449559169:  === (1 of 3) Merging (dev-libs/libffi-3.0.13-r1::/usr/portage/dev-libs/libffi/libffi-3.0.13-r1.ebuild)
1449559212:  >>> AUTOCLEAN: dev-libs/libffi:0
1449559212:  === Unmerging... (dev-libs/libffi-3.0.13-r1)
1449559220:  >>> unmerge success: dev-libs/libffi-3.0.13-r1
1449559222:  === (1 of 3) Post-Build Cleaning (dev-libs/libffi-3.0.13-r1::/usr/portage/dev-libs/libffi/libffi-3.0.13-r1.ebuild)
1449559222:  ::: completed emerge (1 of 3) dev-libs/libffi-3.0.13-r1 to /
1449559222:  >>> emerge (2 of 3) dev-lang/python-2.7.10-r1 to /
1449559222:  === (2 of 3) Cleaning (dev-lang/python-2.7.10-r1::/usr/portage/dev-lang/python/python-2.7.10-r1.ebuild)
1449559223:  === (2 of 3) Compiling/Packaging (dev-lang/python-2.7.10-r1::/usr/portage/dev-lang/python/python-2.7.10-r1.ebuild)
1449559384:  === (2 of 3) Merging (dev-lang/python-2.7.10-r1::/usr/portage/dev-lang/python/python-2.7.10-r1.ebuild)
1449559402:  >>> AUTOCLEAN: dev-lang/python:2.7
1449559402:  === Unmerging... (dev-lang/python-2.7.10-r1)
1449559405:  >>> unmerge success: dev-lang/python-2.7.10-r1
1449559410:  === (2 of 3) Post-Build Cleaning (dev-lang/python-2.7.10-r1::/usr/portage/dev-lang/python/python-2.7.10-r1.ebuild)
1449559410:  ::: completed emerge (2 of 3) dev-lang/python-2.7.10-r1 to /
1449559410:  >>> emerge (3 of 3) dev-python/cffi-1.2.1 to /
1449559410:  === (3 of 3) Cleaning (dev-python/cffi-1.2.1::/usr/portage/dev-python/cffi/cffi-1.2.1.ebuild)
1449559410:  === (3 of 3) Compiling/Packaging (dev-python/cffi-1.2.1::/usr/portage/dev-python/cffi/cffi-1.2.1.ebuild)
1449559439:  === (3 of 3) Merging (dev-python/cffi-1.2.1::/usr/portage/dev-python/cffi/cffi-1.2.1.ebuild)
1449559442:  >>> AUTOCLEAN: dev-python/cffi:0
1449559442:  === Unmerging... (dev-python/cffi-0.8.6)
1449559444:  >>> unmerge success: dev-python/cffi-0.8.6
1449559448:  === (3 of 3) Post-Build Cleaning (dev-python/cffi-1.2.1::/usr/portage/dev-python/cffi/cffi-1.2.1.ebuild)
1449559448:  ::: completed emerge (3 of 3) dev-python/cffi-1.2.1 to /
1449559448:  *** Finished. Cleaning up...
1449559452:  *** exiting successfully.

The python interpreter still segfaults:

RC=0 stuartl@rikishi ~/vrt/projects/metermaster/deps/python-mbus $ gdb --args /usr/bin/python /usr/bin/py.test 
GNU gdb (Gentoo 7.7.1 p1) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python...(no debugging symbols found)...done.
(gdb) r
Starting program: /usr/bin/python /usr/bin/py.test
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
process 2427 is executing new program: /usr/bin/python2.7
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
================================================================== test session starts ==================================================================
platform linux2 -- Python 2.7.10 -- py-1.4.30 -- pytest-2.7.2
rootdir: /home/stuartl/vrt/projects/metermaster/deps/python-mbus, inifile: pytest.ini
plugins: cov
collected 16 items 

tests/test_MBus_connect.py 
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff19f5be1 in mbus_connect (handle=0x573e31c0) at mbus-protocol-aux.c:1589
1589    mbus-protocol-aux.c: No such file or directory.
(gdb) bt
#0  0x00007ffff19f5be1 in mbus_connect (handle=0x573e31c0) at mbus-protocol-aux.c:1589
#1  0x00007ffff1c4d8d4 in ffi_call_unix64 () from /usr/lib64/libffi.so.6
#2  0x00007ffff1c4d1ba in ffi_call () from /usr/lib64/libffi.so.6
#3  0x00007ffff1e62fcc in _ctypes_callproc () from /usr/lib64/python2.7/lib-dynload/_ctypes.so
#4  0x00007ffff1e5bb68 in ?? () from /usr/lib64/python2.7/lib-dynload/_ctypes.so
#5  0x00007ffff7a3c366 in PyObject_Call () from /usr/lib64/libpython2.7.so.1.0
#6  0x00007ffff7aedac8 in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#7  0x00007ffff7aef731 in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
Comment 2 Mike Gilbert gentoo-dev 2015-12-08 15:31:37 UTC
Does this occur with 2.7.11? 3.5.0?

Is there anything that indicates this is specific to Gentoo's python? I suspect you should probably seek help upstream.
Comment 3 Stuart Longland 2015-12-08 18:08:39 UTC
Haven't tried with Python3 (not sure if it's compatible), but I might give 2.7.11 a try later.

As I mentioned, I tried running the same code inside an LXC instance of Debian Wheezy 64-bit, and did not suffer the segmentation fault.
Comment 4 Mike Gilbert gentoo-dev 2015-12-08 18:39:22 UTC
(In reply to Stuart Longland from comment #3)
> As I mentioned, I tried running the same code inside an LXC instance of
> Debian Wheezy 64-bit, and did not suffer the segmentation fault.

You said "I've verified that under Debian Wheezy 64-bit", but did not state what the results of said verification were. Thank you for clarifying.
Comment 5 Stuart Longland 2015-12-08 20:49:03 UTC
Fair enough, apologies for not being clear.

I did a test with Python 2.7.11:
RC=0 stuartl@rikishi ~/vrt/projects/metermaster/deps/python-mbus $ gdb --args /usr/bin/python /usr/bin/py.test 
GNU gdb (Gentoo 7.7.1 p1) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python...(no debugging symbols found)...done.
(gdb) run
Starting program: /usr/bin/python /usr/bin/py.test
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
process 27872 is executing new program: /usr/bin/python2.7
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
======================================================================== test session starts ========================================================================
platform linux2 -- Python 2.7.11 -- py-1.4.30 -- pytest-2.7.2
rootdir: /home/stuartl/vrt/projects/metermaster/deps/python-mbus, inifile: pytest.ini
plugins: cov
collected 16 items 

tests/test_MBus_connect.py 
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff19efbe1 in mbus_connect (handle=0x559cb390) at mbus-protocol-aux.c:1589
1589        return handle->open(handle);
(gdb) bt
#0  0x00007ffff19efbe1 in mbus_connect (handle=0x559cb390) at mbus-protocol-aux.c:1589
#1  0x00007ffff1c478d4 in ffi_call_unix64 () from /usr/lib64/libffi.so.6
#2  0x00007ffff1c471ba in ffi_call () from /usr/lib64/libffi.so.6
#3  0x00007ffff1e5cfcc in _ctypes_callproc () from /usr/lib64/python2.7/lib-dynload/_ctypes.so
#4  0x00007ffff1e55b68 in ?? () from /usr/lib64/python2.7/lib-dynload/_ctypes.so
#5  0x00007ffff7a37d86 in PyObject_Call () from /usr/lib64/libpython2.7.so.1.0
#6  0x00007ffff7aea1d2 in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#7  0x00007ffff7aec28b in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#8  0x00007ffff7aefcc8 in PyEval_EvalCodeEx () from /usr/lib64/libpython2.7.so.1.0
Comment 6 Stuart Longland 2015-12-08 20:55:44 UTC
For reference, this is what happens when I run the same code under Debian Wheezy:

RC=0 stuartl@sjl-lxc-wheezy ~/vrt/projects/metermaster/deps/python-mbus $ py.test
======================================================================== test session starts ========================================================================
platform linux2 -- Python 2.7.3 -- pytest-2.2.4
collected 14 items / 2 errors 

tests/test_MBus_init.py ...FF.........

============================================================================== ERRORS ===============================================================================
____________________________________________________________ ERROR collecting tests/test_MBus_connect.py ____________________________________________________________
tests/test_MBus_connect.py:7: in <module>
>   @pytest.fixture
E   AttributeError: 'module' object has no attribute 'fixture'
__________________________________________________________ ERROR collecting tests/test_MBus_disconnect.py ___________________________________________________________
tests/test_MBus_disconnect.py:7: in <module>
>   @pytest.fixture
E   AttributeError: 'module' object has no attribute 'fixture'
============================================================================= FAILURES ==============================================================================
______________________________________________________________________ test_device_nonexistent ______________________________________________________________________

    @pytest.mark.serial
    def test_device_nonexistent():
>       with pytest.raises(FileNotFoundError):
E   NameError: global name 'FileNotFoundError' is not defined

tests/test_MBus_init.py:36: NameError
________________________________________________________________________ test_device_serial _________________________________________________________________________

pytestconfig = <_pytest.config.Config object at 0x2b77410>

    @pytest.mark.serial
    def test_device_serial(pytestconfig):
        if '/dev/adjustme' == pytestconfig.getini('serialdevice'):
            pytest.skip("serial device not configured")
        with pytest.raises(TypeError):
>           foo = MBus.MBus(device=pytestconfig.getini('serialdevice'))

tests/test_MBus_init.py:45: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <mbus.MBus.MBus instance at 0x2f1ae60>

    def __init__(self, *args, **kwargs):
        """
            Constructor for MBus class.
    
            possible arguments are
            * device
            * host
            * libpath: path to libmbus (shared object or dll)
            * port: default 8888
            """
    
        import os
        from ctypes import cdll
    
        # check all given arguments for validity
        validargs = ('device','host','libpath','port')
        for arg in kwargs.keys():
            if arg not in validargs:
                raise TypeError("invalid argument")
    
        # set default values
        device = None
        host = None
        port = 8888
        libpath = None
    
        if 'device' in kwargs.keys():
             device = kwargs['device']
    
        if 'libpath' in kwargs.keys():
            libpath = kwargs['libpath']
    
        if 'host' in kwargs.keys():
            host = kwargs['host']
    
        if 'port' in kwargs.keys():
            if isinstance(kwargs['port'],int):
                if 65535 <= kwargs['port']:
                    raise ValueError("port number too high")
                if 0 > kwargs['port']:
                    raise ValueError("port number too low")
                port = kwargs['port']
            else:
                raise TypeError("port number not given as integer")
    
        if None == libpath:
            libpath = "/usr/local/lib/libmbus.so"
    
        self._libmbus = cdll.LoadLibrary(libpath)
    
        try:
            self._libmbus.mbus_get_current_version()
        except AttributeError:
            raise OSError("libmbus not found")
    
        if (None != device) and (None != host):
            raise BaseException("conflicting arguments 'device' and 'host' given")
    
        if (None == device) and (None == host):
            raise BaseException("Must provide either device or host keyword arguments")
    
    
        if device:
>           fd = os.open(device, os.O_RDONLY)
E           OSError: [Errno 2] No such file or directory: '/dev/ttyS0'

mbus/MBus.py:80: OSError
=========================================================== 2 failed, 12 passed, 2 error in 0.18 seconds ============================================================

OSError, because /dev/ttyS0 is on the host, I haven't figured out a way to expose my host's /dev/ttyS0 to the LXC guest.  However, the library doesn't segfault.
Comment 7 Mike Gilbert gentoo-dev 2015-12-08 22:11:51 UTC
I'll see if I can reproduce this.
Comment 8 Mike Gilbert gentoo-dev 2015-12-08 22:51:42 UTC
Created attachment 418814 [details]
Test log

I'm unable to reproduce the segfault here. A test log is attached.

I see you are running hardened. Do you experience this on a more vanilla Gentoo system? In my experience, PaX likes to make things segfault at random.
Comment 9 Stuart Longland 2015-12-08 23:23:26 UTC
Ahh okay, the plot thickens.  No I haven't tried with non-hardened.  All my AMD64/x86 systems run the hardened branch of Gentoo.

It certainly isn't a "random" segfault, it's segfaulting for a good reason: the pointer address is wrong.  I'm just trying to nut out why.

I might have a look at grabbing a couple of "fresh" stage3's.  One hardened (just in case there's something queer going on here), one not.
Comment 10 Mike Gilbert gentoo-dev 2015-12-08 23:48:09 UTC
(In reply to Stuart Longland from comment #9)
> It certainly isn't a "random" segfault, it's segfaulting for a good reason:
> the pointer address is wrong.  I'm just trying to nut out why.

I'm not sure how you have determined that the pointer is being truncated to 32-bits. That sounds rather far-fetched to me.
Comment 11 Mike Gilbert gentoo-dev 2015-12-08 23:54:32 UTC
(In reply to Stuart Longland from comment #9)
> I might have a look at grabbing a couple of "fresh" stage3's.  One hardened
> (just in case there's something queer going on here), one not.

The stage tarball is not very important; PaX is mainly a kernel feature, and I suspect it may be causing some issue here.
Comment 12 Stuart Longland 2015-12-09 02:58:18 UTC
(In reply to Mike Gilbert from comment #10)
> (In reply to Stuart Longland from comment #9)
> > It certainly isn't a "random" segfault, it's segfaulting for a good reason:
> > the pointer address is wrong.  I'm just trying to nut out why.
> 
> I'm not sure how you have determined that the pointer is being truncated to
> 32-bits. That sounds rather far-fetched to me.

Well, so far I've only ever seen addresses passed to the C code that would fit in a 32-bit address space.  Either the pointers are being truncated, or they're completely fictitious.

I'll admit truncation is an assumption on my part, it is possible that any old garbage is being passed back to the C code.

It is possible that Raditex Control (makers of libmbus, python-mbus) have cocked up their Python code and that it magically works on Debian.  I'm just trying to figure out why Gentoo and Debian have such different reactions to the same code.

(In reply to Mike Gilbert from comment #11)
> (In reply to Stuart Longland from comment #9)
> > I might have a look at grabbing a couple of "fresh" stage3's.  One hardened
> > (just in case there's something queer going on here), one not.
> 
> The stage tarball is not very important; PaX is mainly a kernel feature, and
> I suspect it may be causing some issue here.

As it's the same kernel running both (LXC container, so chroot on steroids), the kernel should have an identical effect on both instances of Python, correct?

I run the hardened stages mainly for the toolchain.  The kernels I run are vanilla mainline Linux kernels.
Comment 13 Stuart Longland 2015-12-09 03:18:04 UTC
Okay, some further digging.

Whatever is happening to that pointer, it's getting mangled badly.  I edited the example 'mbus-test-1.py' to print out the "handle" that gets passed when connecting:

diff --git a/examples/mbus-test-1.py b/examples/mbus-test-1.py
index dae8991..452306d 100755
--- a/examples/mbus-test-1.py
+++ b/examples/mbus-test-1.py
@@ -17,6 +17,7 @@ mbus = MBus(host="mbus-gw1", port=8888)
 
 if debug:
     print("mbus = " + str(mbus))
+    print 'handle = %r' % mbus.handle
 
 mbus.connect()


On Debian, I get this:
RC=0 stuartl@sjl-lxc-wheezy ~/vrt/projects/metermaster/deps/python-mbus/examples $ gdb --args /usr/bin/python mbus-test-1.py 
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/python...Reading symbols from /usr/lib/debug/usr/bin/python2.7...done.
done.
(gdb) break mbus_connect
Function "mbus_connect" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mbus_connect) pending.
(gdb) r
Starting program: /usr/bin/python mbus-test-1.py
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
mbus = <mbus.MBus.MBus instance at 0x7ffff7e77c20>
handle = 10224256

Breakpoint 1, mbus_connect (handle=0x9c0280) at mbus-protocol-aux.c:1583
1583        if (handle == NULL)

On Gentoo:

RC=0 stuartl@rikishi ~/vrt/projects/metermaster/deps/python-mbus/examples $ gdb --args /usr/bin/python mbus-test-1.py 
GNU gdb (Gentoo 7.7.1 p1) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python...(no debugging symbols found)...done.
(gdb) break mbus_connect
Function "mbus_connect" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mbus_connect) pending.
(gdb) r
Starting program: /usr/bin/python mbus-test-1.py
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
process 23397 is executing new program: /usr/bin/python2.7
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
mbus = <mbus.MBus.MBus instance at 0x7ffff7e4f3f8>
handle = 1434631936

Breakpoint 1, mbus_connect (handle=0x5582bf00) at mbus-protocol-aux.c:1582
1582    {
(gdb) 

Now the variable, from a Python perspective is just a plain integer, which is probably wrong: I've never dealt with ctypes before, if I've needed C code in Python I usually use the C extension API.  (I'm tempted to do that actually.)

The value is the return value from mbus_context_serial or mbus_context_tcp, which are both C functions that return a pointer.  It seems this pointer is getting mangled.

Running that code again on Gentoo, and this time setting a few more breakpoints:

RC=0 stuartl@rikishi ~/vrt/projects/metermaster/deps/python-mbus/examples $ gdb --args /usr/bin/python mbus-test-1.py 
GNU gdb (Gentoo 7.7.1 p1) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python...(no debugging symbols found)...done.
(gdb) break mbus_context_tcp
Function "mbus_context_tcp" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mbus_context_tcp) pending.
(gdb) break mbus_connect
Function "mbus_connect" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (mbus_connect) pending.
(gdb) r
Starting program: /usr/bin/python mbus-test-1.py
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
process 4275 is executing new program: /usr/bin/python2.7
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, mbus_context_tcp (host=0x7ffff7ea8f84 "mbus-gw1", port=43225) at mbus-protocol-aux.c:1523
1523    {
(gdb) s
1528        if ((handle = (mbus_handle *) malloc(sizeof(mbus_handle))) == NULL)
(gdb) s
1534        if ((tcp_data = (mbus_tcp_data *)malloc(sizeof(mbus_tcp_data))) == NULL)
(gdb) s
…
(gdb) s
1567        return handle;
(gdb) print handle
$1 = (mbus_handle *) 0x55555582bf00
(gdb) c
Continuing.
mbus = <mbus.MBus.MBus instance at 0x7ffff7e4f3f8>
handle = 1434631936

Breakpoint 2, mbus_connect (handle=0x5582bf00) at mbus-protocol-aux.c:1582
1582    {
(gdb) 

Sure looks like truncation to me.
Comment 14 Stuart Longland 2015-12-09 03:30:19 UTC
If I modify MBus.py:

diff --git a/mbus/MBus.py b/mbus/MBus.py
index 7f26165..466781f 100644
--- a/mbus/MBus.py
+++ b/mbus/MBus.py
@@ -2,7 +2,7 @@
 Python bindings for rSCADA libmbus.
 """
 
-from ctypes import c_int,c_char_p,addressof,pointer
+from ctypes import c_int,c_char_p,c_void_p,addressof,pointer
 
 from mbus.MBusFrame import MBusFrame
 from mbus.MBusFrameData import MBusFrameData
@@ -81,9 +81,13 @@ class MBus:
             if not os.isatty(fd):
                 raise TypeError(device+" is not a TTY")
             os.close(fd)
-            self.handle = self._libmbus.mbus_context_serial(device)
+            mbus_context_serial = self._libmbus.mbus_context_serial
+            mbus_context_serial.restype = c_void_p
+            self.handle = mbus_context_serial(device)
         elif host != None and port:
-            self.handle = self._libmbus.mbus_context_tcp(host)
+            mbus_context_tcp = self._libmbus.mbus_context_tcp
+            mbus_context_tcp.restype = c_void_p
+            self.handle = mbus_context_tcp(host)


I still get truncation:
mbus = <mbus.MBus.MBus instance at 0x7ffff7e4f3f8>
handle = 93824995231520

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff6101be1 in mbus_connect (handle=0x55830720) at mbus-protocol-aux.c:1589
1589        return handle->open(handle);
(gdb) print handle
$1 = (mbus_handle *) 0x55830720
Comment 15 Stuart Longland 2015-12-09 03:56:23 UTC
Okay, I think I see what's going on here now.

The bug is technically in python-mbus: it isn't declaring argument/return data types, and so Python assumes they are integers.  32-bit integers.

I started to get things moving when I made the following changes:
diff --git a/mbus/MBus.py b/mbus/MBus.py
index 7f26165..e12546a 100644
--- a/mbus/MBus.py
+++ b/mbus/MBus.py
@@ -2,7 +2,7 @@
 Python bindings for rSCADA libmbus.
 """
 
-from ctypes import c_int,c_char_p,addressof,pointer
+from ctypes import c_int,c_char_p,c_void_p,addressof,pointer
 
 from mbus.MBusFrame import MBusFrame
 from mbus.MBusFrameData import MBusFrameData
@@ -81,16 +81,22 @@ class MBus:
             if not os.isatty(fd):
                 raise TypeError(device+" is not a TTY")
             os.close(fd)
-            self.handle = self._libmbus.mbus_context_serial(device)
+            mbus_context_serial = self._libmbus.mbus_context_serial
+            mbus_context_serial.restype = c_void_p
+            self.handle = mbus_context_serial(device)
         elif host != None and port:
-            self.handle = self._libmbus.mbus_context_tcp(host)
+            mbus_context_tcp = self._libmbus.mbus_context_tcp
+            mbus_context_tcp.restype = c_void_p
+            self.handle = mbus_context_tcp(host)
 
     def connect(self):
         """
         Connect to MBus.
         """
         if self.handle:
-            if self._libmbus.mbus_connect(self.handle) == -1:
+            mbus_connect = self._libmbus.mbus_connect
+            mbus_connect.argtypes = [c_void_p]
+            if mbus_connect(self.handle) == -1:
                 raise Exception("libmbus.mbus_connect failed")
         else:
             raise Exception("Handle object not configure")

It worked on Debian because Debian used addresses in the lower 32-bits.  It only showed itself on Gentoo because on Gentoo, the addresses were allocated above that first 4GB of address space.

If everything gets allocated in that low area of memory, all is good, but if it moves higher, all hell breaks loose.  Perhaps this is a quirk of the hardened libc?

Since I know where the fault is now, I can mark this bug as invalid.  It might be a handy reference for others that might be looking at similar bugs.