Repository: scipy/scipy

Issue:
BUG: segfault when slicing a CSR or CSC sparse matrix with slice start index > stop index
<!-- 
Thank you for taking the time to file a bug report. 
Please fill in the fields below, deleting the sections that 
don't apply to your issue. You can view the final output
by clicking the preview button above.
Note: This is a comment, and won't appear in the output.
-->
I noticed a segfault in SciPy that occurs when running the CuPy test suite with 1.4.0rc1. This seems to occur when using a slice with a start index greater than the number of rows to index the matrix.

#### Reproducing code example:
<!-- 
If you place your code between the triple backticks below, 
it will be rendered as a code block. 
-->

```Python
import numpy as np
import scipy.sparse

data = np.array([0, 1, 2, 3], np.float32)
indices = np.array([0, 1, 3, 2], 'i')
indptr = np.array([0, 2, 3, 4], 'i')
m = scipy.sparse.csr_matrix((data, indices, indptr), shape=(3, 4))

# invalid index that exceeds size of the first axis causes a segfault!
z = m[3:2]

# z = m[3]  # this case raises an IndexError as expected

```

#### Error message:
<!-- If any, paste the *full* error message inside a code block
as above (starting from line Traceback)
-->

```
Fatal Python error: Segmentation fault

Current thread 0x00007f28492c5700 (most recent call first):
  File "/home/lee8rx/miniconda3_new/envs/pyir/lib/python3.7/site-packages/scipy/sparse/compressed.py", line 789 in _get_submatrix
  File "/home/lee8rx/miniconda3_new/envs/pyir/lib/python3.7/site-packages/scipy/sparse/compressed.py", line 652 in _get_sliceXslice
  File "/home/lee8rx/miniconda3_new/envs/pyir/lib/python3.7/site-packages/scipy/sparse/_index.py", line 51 in __getitem__
```

#### Scipy/Numpy/Python version information:
<!-- You can simply run the following and paste the result in a code block
```
import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
```
-->

```
1.4.0rc1 1.17.2 sys.version_info(major=3, minor=7, micro=3, releaselevel='final', serial=0)
```


Discussion:
The same occurs for slices into the columns of CSC matrices. Specifically, this test case:
https://github.com/cupy/cupy/blob/4586fedac8254dcd71d97617f2f95f98254dbbcc/tests/cupyx_tests/scipy_tests/sparse_tests/test_csc.py#L1011-L1013

I get the same fault for 1.3.0 and 1.3.3, but it does not segfault on 1.2.2:
```IndexError: index out of bounds: 0 <= 3 <= 3, 0 <= 2 <= 3, 3 <= 2```

I think the issue was introduced with this fairly large commit (https://github.com/scipy/scipy/commit/f43c64c4fd913751764d324a1b85c895a051bced#diff-9635cc7755181c45093df4a7468b65eb) (from PR #7827).  I will take a look and see if I can find where the check should occur.


Resolving patch:
diff --git a/scipy/sparse/compressed.py b/scipy/sparse/compressed.py
--- a/scipy/sparse/compressed.py
+++ b/scipy/sparse/compressed.py
@@ -782,6 +782,11 @@ def _get_submatrix(self, major=None, minor=None, copy=False):
         M, N = self._swap(self.shape)
         i0, i1 = _process_slice(major, M)
         j0, j1 = _process_slice(minor, N)
+
+        if i0 == 'empty' or j0 == 'empty':
+            out = np.zeros([0, 0], dtype=self.dtype)
+            return self.__class__(out)
+
         if i0 == 0 and j0 == 0 and i1 == M and j1 == N:
             return self.copy() if copy else self
 
@@ -1275,6 +1280,8 @@ def _process_slice(sl, num):
         i0, i1, stride = sl.indices(num)
         if stride != 1:
             raise ValueError('slicing with step != 1 not supported')
+        if i0 > i1:
+            return 'empty', 'empty'
     elif isintlike(sl):
         if sl < 0:
             sl += num


Test patch:
diff --git a/scipy/sparse/tests/test_base.py b/scipy/sparse/tests/test_base.py
--- a/scipy/sparse/tests/test_base.py
+++ b/scipy/sparse/tests/test_base.py
@@ -2384,6 +2384,7 @@ def test_slicing_3(self):
 
         s_ = np.s_
         slices = [s_[:2], s_[1:2], s_[3:], s_[3::2],
+                  s_[15:20], s_[3:2],
                   s_[8:3:-1], s_[4::-2], s_[:5:-1],
                   0, 1, s_[:], s_[1:5], -1, -2, -5,
                   array(-1), np.int8(-3)]