seznam/fastrpc

python-fastrpc 7+ SegFault

Closed this issue · 10 comments

Next code reproduces segmentation fault. Tested on versions 7.0.1 and 7.0.2.

from fastrpc import ServerProxy
str(type(ServerProxy('http://sbox:2825/RPC2').bar))

GDB backtrace

#0  _PyObject_Str (v=<unknown at remote 0x7ffff6c18e00>) at ../Objects/object.c:423
        res = <optimized out>
        type_ok = <optimized out>
#1  PyObject_Str (v=<unknown at remote 0x7ffff6c18e00>) at ../Objects/object.c:451
        res = <type at remote 0x93cac0>
#2  string_new.lto_priv () at ../Objects/stringobject.c:3709
        x = <unknown at remote 0x7ffff6c18e00>
        kwlist = {0x6c8d61 "object", 0x0}
#3  0x00000000004ba865 in type_call.lto_priv () at ../Objects/typeobject.c:729
        obj = <optimized out>
#4  0x00000000004caaa1 in PyObject_Call (kw=<optimized out>, arg=<optimized out>, func=<optimized out>) at ../Objects/abstract.c:2529
        call = 0x4ba840 <type_call.lto_priv>
#5  do_call (nk=<optimized out>, na=<optimized out>, pp_stack=<optimized out>, func=<optimized out>) at ../Python/ceval.c:4251
        callargs = (<unknown at remote 0x7ffff6c18e00>,)
        kwdict = 0x0
#6  call_function (oparg=<optimized out>, pp_stack=<optimized out>) at ../Python/ceval.c:4056
        func = <type at remote 0x93cac0>
        w = <type at remote 0x93cac0>
        nk = -135697328
        n = -9552
        pfunc = 0x7ffff7ef0c48
#7  PyEval_EvalFrameEx () at ../Python/ceval.c:2679
        sp = 0x7ffff7ef0c50
        opcode = 4958272
#8  0x00000000004c87a1 in PyEval_EvalCodeEx () at ../Python/ceval.c:3265
        f = 0x0
        retval = <code at remote 0x7ffff7ee2a30>
        fastlocals = 0x4b3fa7 <vgetargskeywords.lto_priv+87>
        tstate = 0x9ae0a0
        u = <unknown at remote 0x1>
#9  0x00000000005030ef in PyEval_EvalCode (
    locals={'__builtins__': <module at remote 0x7ffff7fa7b08>, '__file__': 'test.py', '__package__': None, '__name__': '__main__', 'ServerProxy': <built-in function ServerProxy>, '__doc__': None}, 
    globals={'__builtins__': <module at remote 0x7ffff7fa7b08>, '__file__': 'test.py', '__package__': None, '__name__': '__main__', 'ServerProxy': <built-in function ServerProxy>, '__doc__': None}, 
    co=0x7ffff7ee2a30) at ../Python/ceval.c:667
No locals.
#10 run_mod.lto_priv () at ../Python/pythonrun.c:1371
        co = 0x7ffff7ee2a30
#11 0x00000000004f8c72 in PyRun_FileExFlags () at ../Python/pythonrun.c:1357
        ret = <unknown at remote 0x7fffffffe66f>
        mod = 0xa51508
        arena = 0x9da5c0
#12 0x00000000004f7d77 in PyRun_SimpleFileExFlags () at ../Python/pythonrun.c:949
        m = <module at remote 0x7ffff7f69cc8>
        d = {'__builtins__': <module at remote 0x7ffff7fa7b08>, '__file__': 'test.py', '__package__': None, '__name__': '__main__', 'ServerProxy': <built-in function ServerProxy>, '__doc__': None}
        v = <unknown at remote 0x1>
        ret = -6542
#13 0x00000000004982f2 in Py_Main () at ../Modules/main.c:640
        sts = 0
        command = 0x0
        filename = 0x7fffffffe66f "test.py"
        stdin_is_interactive = 1
        help = -135528320
        version = 1
        saw_unbuffered_flag = -6545
        cf = {cf_flags = 0}
#14 0x00007ffff6f12b45 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
No symbol table info available.
#15 0x0000000000497ca0 in _start ()
No symbol table info available.

Versions:

$ apt-cache policy python-fastrpc
python-fastrpc:
  Installed: 7.0.2
  Candidate: 7.0.2
  Version table:
 *** 7.0.2 0
        470 http://debian/ jessie-stable/main amd64 Packages
        460 http://debian/ jessie-testing/main amd64 Packages
        150 /var/lib/dpkg/status
     7.0.1 0
        470 http://debian/ jessie-stable/main amd64 Packages
        460 http://debian/ jessie-testing/main amd64 Packages
     6.0.2~1 0
        470 http://debian/ jessie-stable/main amd64 Packages
        460 http://debian/ jessie-testing/main amd64 Packages
     5.0.9 0
        470 http://debian/ jessie-stable/main amd64 Packages
        460 http://debian/ jessie-testing/main amd64 Packages
     5.0.8 0
        470 http://debian/ jessie-stable/main amd64 Packages
        460 http://debian/ jessie-testing/main amd64 Packages
$ apt-cache policy libfastrpc7
libfastrpc7:
  Installed: 7.0.1
  Candidate: 7.0.1
  Version table:
 *** 7.0.1 0
        470 http://debian/ jessie-stable/main amd64 Packages
        460 http://debian/ jessie-testing/main amd64 Packages
        150 /var/lib/dpkg/status

Same segfaults are in 6.0.2~1 and 5.1.0.

More info:

Program received signal SIGSEGV, Segmentation fault.
_PyObject_Str (v=<unknown at remote 0x7ffff6c18e00>) at ../Objects/object.c:423
423 ../Objects/object.c: No such file or directory.
(gdb) py-bt 
#7 (<unknown at remote 0x7ffff6c18e00>,)
Python Exception <type 'exceptions.AttributeError'> 'PyTupleObjectPtr' object has no attribute 'current_line': 
Error occurred in Python command: 'PyTupleObjectPtr' object has no attribute 'current_line'
mody commented

I'm not an expert, but commit f050f93 seems suspicious. Mainly the use o Py_TPFLAGS_HAVE_GC flag on static objects.

diff --git a/python/test/serverproxytest.py b/python/test/serverproxytest.py
index 16846f1..d2d1eb2 100755
--- a/python/test/serverproxytest.py
+++ b/python/test/serverproxytest.py
@@ -150,5 +150,10 @@ class ServerProxyTest(unittest.TestCase):
         else:
             self.assertIsInstance(client_hide_true.__dict__['last_call'], (str, unicode))

+    def test_reprstr(self):
+        client = fastrpc.ServerProxy(self.url)
+        self.assertTrue(str(type(client.foo)).startswith("<Method"))
+
+
 if __name__ == '__main__':
     unittest.main()

Okay, running with Py_DEBUG on python 2.7.11 reveals the init of frpc lib itself is broken

#0  0x00007ffff71422a8 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff714372a in abort () from /usr/lib/libc.so.6
#2  0x0000000000508593 in Py_FatalError (msg=0x595756 "UNREF invalid object") at Python/pythonrun.c:1694
#3  0x0000000000463133 in _Py_ForgetReference (op=0x7ffff6cd35a0 <FRPC::Python::DateTimeObject_Type>) at Objects/object.c:2242
#4  0x0000000000463187 in _Py_Dealloc (op=0x7ffff6cd35a0 <FRPC::Python::DateTimeObject_Type>) at Objects/object.c:2261
#5  0x00000000004fffc8 in PyModule_AddObject (m=0x7ffff6f02dc8, name=0x7ffff6ac8c14 "DateTime", o=0x7ffff6cd35a0 <FRPC::Python::DateTimeObject_Type>) at Python/modsupport.c:618
#6  0x00007ffff6ab76a4 in initfastrpc () at fastrpcmodule.cc:2256
#7  0x00000000004f91a5 in _PyImport_LoadDynamicModule (name=0x8d7d20 "fastrpc", pathname=0x868740 "/home/volca/fastrpcmodule.so", fp=0x86d240) at ./Python/importdl.c:53

This will need to be fixed before continuing any further.

Edit: Apparently this is just a side-effect of Py_DEBUG and nothing to worry about.

Okay, I found the problem. It was caused by commit 30c9524, which replaced ob_type in type objects. Before, those referenced themselves. Now they have NULL ob_type, which causes python to segv when doing this "if (Py_TYPE(v)->tp_str == NULL)" on the type, since Py_TYPE returns ob_type of the object (which itself is the Type object).

I'll implement a patch tomorrow, it needs to be replaced on all custom types. Similary to this:

--- a/python/fastrpcmodule.cc
+++ b/python/fastrpcmodule.cc
@@ -1384,7 +1384,7 @@ extern "C"

 static PyTypeObject Method_Type =
     {
-        PyVarObject_HEAD_INIT(NULL, 0)
+        PyVarObject_HEAD_INIT(&PyType_Type, 0)

@rembish Thanks, that needs a tweak though - the str(type(...)) will typically have the form of <type '...'>. I'll modify that and patch it in.

Great job!

dpkg-buildpackage -rfakeroot -D -us -uc
dpkg-buildpackage: source package python-fastrpc
dpkg-buildpackage: source version 7.0.2-1
dpkg-buildpackage: source changed by Luboš Vondra <lubos.vondra@firma.seznam.cz>
 dpkg-source --before-build python
dpkg-buildpackage: host architecture amd64
 fakeroot debian/rules clean
dh clean --with=python2,python3 --buildsystem=pybuild
   dh_testdir -O--buildsystem=pybuild
   dh_auto_clean -O--buildsystem=pybuild
I: pybuild base:170: python2.7 setup.py clean 
Traceback (most recent call last):
  File "setup.py", line 30, in <module>
    from fastrpc_version_info import version
ImportError: No module named fastrpc_version_info
E: pybuild pybuild:256: clean: plugin distutils failed with: exit code=1: python2.7 setup.py clean 
dh_auto_clean: pybuild --clean -i python{version} -p 2.7 --dir . returned exit code 13
make: *** [clean] Error 13
dpkg-buildpackage: error: fakeroot debian/rules clean gave error exit status 2
debuild: fatal error at line 1357:
dpkg-buildpackage -rfakeroot -D -us -uc failed

Please reopen and fix building process.

I'll fix that, but it does not have anything to do with this particular bug.