[NumPy-Tickets] [NumPy] #1464: numpy.set_numeric_ops causes double free

NumPy Trac numpy-tickets@scipy....
Sun Apr 25 08:00:51 CDT 2010


#1464: numpy.set_numeric_ops causes double free
-----------------------------+----------------------------------------------
 Reporter:  mras             |       Owner:  somebody
     Type:  defect           |      Status:  new     
 Priority:  normal           |   Milestone:          
Component:  numpy.core       |     Version:  devel   
 Keywords:  set_numeric_ops  |  
-----------------------------+----------------------------------------------
 The following snippet (bug.py) causes a double free on exit (tested with
 Numpy v. 1.3.0 and the latest source code from SVN on Python 2.6.4):
 {{{
 def binfunc(x,y):
         return None

 import numpy
 print numpy.__version__
 oldopts = numpy.set_numeric_ops(add=binfunc)
 a = numpy.arange(16)
 print a+a
 }}}

 Output from gdb:
 {{{
 Starting program: /usr/bin/python bug.py
 [Thread debugging using libthread_db enabled]
 2.0.0.dev8355
 None
 *** glibc detected *** /usr/bin/python: corrupted double-linked list:
 0x082d8540 ***
 ^C
 Program received signal SIGINT, Interrupt.
 0x0012d422 in __kernel_vsyscall ()
 (gdb) bt
 #0  0x0012d422 in __kernel_vsyscall ()
 #1  0x00264bd3 in __lll_lock_wait_private () at
 ../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/lowlevellock.S:95
 #2  0x001fbf57 in _L_lock_9510 () from /lib/tls/i686/cmov/libc.so.6
 #3  0x001fa7c6 in *__GI___libc_free (mem=0x8302050) at malloc.c:3714
 #4  0x0012160f in _dl_scope_free (old=0x8302050) at dl-open.c:175
 #5  0x0011c327 in _dl_map_object_deps (map=<value optimized out>,
 preloads=<value optimized out>, npreloads=<value optimized out>,
 trace_mode=0, open_mode=-2147483648) at dl-deps.c:668
 #6  0x0012184e in dl_open_worker (a=0xbfffed38) at dl-open.c:326
 #7  0x0011d4e6 in _dl_catch_error (objname=<value optimized out>,
 errstring=<value optimized out>, mallocedp=<value optimized out>,
 operate=0x1216b0 <dl_open_worker>, args=0xbfffed38)
     at dl-error.c:178
 #8  0x00121200 in _dl_open (file=0x2aa85d "libgcc_s.so.1", mode=<value
 optimized out>, caller_dlopen=0x0, nsid=-2, argc=2, argv=0xbffff8c4,
 env=0xbffff8d0) at dl-open.c:615
 #9  0x0028df92 in do_dlopen (ptr=0xbfffeec8) at dl-libc.c:86
 #10 0x0011d4e6 in _dl_catch_error (objname=<value optimized out>,
 errstring=<value optimized out>, mallocedp=<value optimized out>,
 operate=0x28df30 <do_dlopen>, args=0xbfffeec8) at dl-error.c:178
 #11 0x0028e091 in dlerror_run (operate=<value optimized out>,
 args=0xbfffeec8) at dl-libc.c:47
 #12 0x0028e1ab in *__GI___libc_dlopen_mode (name=0x2aa85d "libgcc_s.so.1",
 mode=-2147483647) at dl-libc.c:160
 #13 0x0026bfb8 in init () at ../sysdeps/i386/backtrace.c:44
 #14 0x00139160 in pthread_once () at
 ../nptl/sysdeps/unix/sysv/linux/i386/pthread_once.S:122
 #15 0x0026c1ad in *__GI___backtrace (array=0xbffff49c, size=64) at
 ../sysdeps/i386/backtrace.c:121
 #16 0x001ebefb in __libc_message (do_abort=2, fmt=0x2af578 "*** glibc
 detected *** %s: %s: 0x%s ***\n") at
 ../sysdeps/unix/sysv/linux/libc_fatal.c:168
 #17 0x001f5ff1 in malloc_printerr (action=<value optimized out>,
 str=0x8302048 "\300\207\371\267!", ptr=0x82d8540) at malloc.c:6217
 #18 0x001f7a89 in _int_free (av=<value optimized out>, p=0x82d8398) at
 malloc.c:4912
 #19 0x001fa7cd in *__GI___libc_free (mem=0x82d83a0) at malloc.c:3716
 #20 0x081581b3 in code_dealloc (co=0xb7fd6020) at
 ../Objects/codeobject.c:270
 #21 0x081603d4 in func_dealloc (op=0xb7f82d84) at
 ../Objects/funcobject.c:454
 #22 0x0807fe6e in list_dealloc (op=0xb7f75dac) at
 ../Objects/listobject.c:306
 #23 0x080f8223 in PyInterpreterState_Clear (interp=0x8273008) at
 ../Python/pystate.c:107
 #24 0x080fa312 in Py_Finalize () at ../Python/pythonrun.c:476
 #25 0x0805c34b in Py_Main (argc=2, argv=0xbffff8c4) at
 ../Modules/main.c:625
 #26 0x0805baeb in main (argc=2, argv=0xbffff8c4) at ../Modules/python.c:23
 }}}

 Earlier on I got another trace (that I unfortunately neglected to save)
 where the double free was in Py_Finalize -> ... -> dict_dealloc ->
 dict_dealloc -> ufunc_dealloc. I believe that was caused by me calling
 numpy.set_numeric_ops(**oldopts) at the end and thus causing something
 else to be doublefree'd.

 I've investigated to the best of my knowledge and I think the cause of the
 problem is in the SET-macro in numpy/core/src/multiarray/number.c. If I am
 reading the docs for
 [http://docs.python.org/c-api/dict.html#PyDict_GetItemString] correctly it
 returns a borrowed reference (
 [http://docs.python.org/release/2.5.2/ext/refcountsInPython.html] )
 and the it's reference count should thus be incremented before being
 stored in the numerical operations array.

 I've tried to check other call sites of PyArray_SetNumericOps to see if I
 had misunderstood the semantics, but I think it's simply a bug.

 The below code seems to fix the problem, but as I am inexperienced at
 writing python extensions in C and with NumPy in particular I would
 appreciate some feedback.

 {{{
 Index: numpy/core/src/multiarray/number.c
 ===================================================================
 --- numpy/core/src/multiarray/number.c  (revision 8355)
 +++ numpy/core/src/multiarray/number.c  (working copy)
 @@ -30,6 +30,7 @@
          if (!(PyCallable_Check(temp))) {                  \
              return -1;                                    \
          }                                                 \
 +        Py_INCREF(temp);                                  \
          Py_XDECREF(n_ops.op);                             \
          n_ops.op = temp;                                  \
      }
 }}}

-- 
Ticket URL: <http://projects.scipy.org/numpy/ticket/1464>
NumPy <http://projects.scipy.org/numpy>
My example project


More information about the NumPy-Tickets mailing list