Repository: numba/numba

Issue:
List.insert() crashes for Numpy arrays
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [x] I have tried using the latest released version of Numba (most recent is
 visible in the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included a self contained code sample to reproduce the problem.
  i.e. it's possible to run as 'python bug.py'.

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->
This code segfaults for me (on Mac OS). Didn't see an obvious issue in insert's implementation with a quick look.
```python
import numpy as np
import numba

@numba.njit
def f():
    l = [np.ones(3)]
    l.insert(0, np.ones(3))
    return l

f()
```


Discussion:
I think I found the issue. The setitem call here decrefs the old value but it shouldn't. I'd be happy to submit a PR if this sounds correct.
https://github.com/numba/numba/blob/805e24fbd895d90634cca68c13f4c439609e9286/numba/cpython/listobj.py#L982
@ehsantn thank you for raising this. Will the suggested fix take care of the segfault? If so, please do prepare a PR so we can run it though CI? What kind of patch would you propose?

BTW: I can confirm I am able to reproduce this on OSX with Python 3.9 and current `HEAD` at `5fc9d3c56da4e4c6aef7189e588ce9c44263d4a6`.
Yes, it fixes the segfault. It's a small change. Will open a PR soon.
> Yes, it fixes the segfault. It's a small change. Will open a PR soon.

Thank you! 🙏

Resolving patch:
diff --git a/numba/cpython/listobj.py b/numba/cpython/listobj.py
--- a/numba/cpython/listobj.py
+++ b/numba/cpython/listobj.py
@@ -191,9 +191,10 @@ def clear_value(self, idx):
         # it's necessary for the dtor which just decref every slot on it.
         self.zfill(idx, self._builder.add(idx, idx.type(1)))
 
-    def setitem(self, idx, val, incref):
+    def setitem(self, idx, val, incref, decref_old_value=True):
         # Decref old data
-        self.decref_value(self.getitem(idx))
+        if decref_old_value:
+            self.decref_value(self.getitem(idx))
 
         ptr = self._gep(idx)
         data_item = self._datamodel.as_data(self._builder, val)
@@ -979,7 +980,7 @@ def list_insert(context, builder, sig, args):
     new_size = builder.add(n, one)
     inst.resize(new_size)
     inst.move(builder.add(index, one), index, builder.sub(n, index))
-    inst.setitem(index, value, incref=True)
+    inst.setitem(index, value, incref=True, decref_old_value=False)
 
     return context.get_dummy_value()
 


Test patch:
diff --git a/numba/tests/test_lists.py b/numba/tests/test_lists.py
--- a/numba/tests/test_lists.py
+++ b/numba/tests/test_lists.py
@@ -920,6 +920,15 @@ def pyfunc(con):
 
         self._check_element_equal(pyfunc)
 
+    def test_reflect_insert(self):
+        """make sure list.insert() doesn't crash for refcounted objects (see #7553)
+        """
+
+        def pyfunc(con):
+            con.insert(1, np.arange(4).astype(np.intp))
+
+        self._check_element_equal(pyfunc)
+
     def test_append(self):
         def pyfunc():
             con = []