Repository: python-pillow/Pillow

Issue:
pillow Image.crop function cause python.exe crash
So here is what i encountered:
assume i have an image file named " bmg1.bmp", if i run the script below

``` python
from PIL import Image
aoi = (1, 1, 10, 10) # an example of aoi
img = Image.open('bmg1.bmp')
img2 = img.crop(aoi)
img2.show()
```

everything is fine , but if I run:

``` python
from PIL import Image
aoi = (1, 1, 10, 10)
img = Image.open('bmg1.bmp')
img = img.crop(aoi)
img.show()
```

same error happens if i do:

``` python
from PIL import Image
aoi = (10, 10, 15, 15)
img = Image.open('bmg1.bmp').crop(aoi)
img.show()
```

the python.exe will crash, i am not sure if this is CPython's bug or pillow's bug. 

i doubt the crop function seems to return a Image object that is different from the original one, thus run show() on it gives errors, while does not on the original one.
both python 27 and python 34 have this problem, hope that will be resolved soon.

thanks!



Discussion:
What version/platform are you running?  Can you share the image that is causing the issues?

windows 7
python 2.7.8, and python 3.4.2, for both python versions
VERSION = '1.1.7'  # PIL version
PILLOW_VERSION = '2.7.0'  # Pillow

the image is shared on dropbox 
https://www.dropbox.com/s/eannbvbgiwskq7e/bmg1.bmp?dl=0

I can reproduce on Windows 7 with Python 2.7.8 and Pillow 2.7.0:

``` python
from PIL import Image
aoi = (1, 1, 10, 10)
img = Image.open('bmg1.bmp')
img = img.crop(aoi)
img.show()
```

Wrapping `try`/`except` doesn't catch anything.

It works fine with hopper.png.

Strangely, if I stick `import ipdb; ipdb.set_trace()` into [line 64 at the top of `Viewer`'s `show()`](https://github.com/python-pillow/Pillow/blob/master/PIL/ImageShow.py#L64) to step through with [ipdb](http://www.gregaker.net/2012/apr/05/debugging-python-with-pdb-or-ipdb/) (the IPython debugger), it works fine for both images (and both give `Exception AttributeError: "'NoneType' object has no attribute 'path'" in <function _remove at 0x0000000001E0ADD8> ignored` when the test script exits).

Problem also exists in Pillow 2.3.0, 2.4.0, 2.5.0, 2.6.2.

Windows event log:

```
Faulting application name: python.exe, version: 0.0.0.0, time stamp: 0x53b1ee02
Faulting module name: MSVCR90.dll, version: 9.0.30729.6161, time stamp: 0x4dace4e7
Exception code: 0xc0000005
Fault offset: 0x000000000001e28f
Faulting process id: 0x12e0
Faulting application start time: 0x01d030e16b080e1e
Faulting application path: c:\Python27\python.exe
Faulting module path: C:\Windows\WinSxS\amd64_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_08e61857a83bc251\MSVCR90.dll
Report Id: a9e681a6-9cd4-11e4-a980-005056c00002
```

```
Fault bucket , type 0
Event Name: APPCRASH
Response: Not available
Cab Id: 0

Problem signature:
P1: python.exe
P2: 0.0.0.0
P3: 53b1ee02
P4: MSVCR90.dll
P5: 9.0.30729.6161
P6: 4dace4e7
P7: c0000005
P8: 000000000001e28f
P9: 
P10: 

```

It's not doing it on linux here, and there's nothing fishy about the BMP that I can see. 

Is there a way to get a stack trace from the crash?  At least if we could get back to where we're calling into MSVCR90.dll.

The crash produces three files:
- WER993E.tmp.WERInternalMetadata.xml
- WERB171.tmp.appcompat.txt
- WERB1C0.tmp.mdmp

The xml is mainly the APPCRASH info above. The txt shows things like exe and dll versions. TThe mdpmp is a binary "mini dump file" abnd probably what is needed. Windows debugging tools are needed, and need to point to a Windows symbol server to interpret. I can't get the debugging tools installed right now. Would you like me to email them over?

It crashes at https://github.com/python-pillow/Pillow/blob/master/libImaging/Paste.c#L55
Looks like `imIn->image` is pointing to a bad pointer.
Locals:

```
+       imOut   0x0000000001fad7f0 {mode=0x0000000001fad7f0 "L" type=0 depth=0 ...} ImagingMemoryInstance *
-       imIn    0x0000000001fad850 {mode=0x0000000001fad850 "L" type=0 depth=0 ...} ImagingMemoryInstance *
+       mode    0x0000000001fad850 "L"  char [7]
        type    0   int
        depth   0   int
        bands   1   int
        xsize   2560    int
        ysize   1920    int
+       palette 0x0000000000000000 {mode=0x0000000000000000 <Bad Ptr> palette=0x0000000000000007 <Bad Ptr> cache=??? ...}   ImagingPaletteInstance *
+       image8  0x0000000001e80170  unsigned char * *
+       image32 0x0000000000000000  int * *
-       image   0x0000000001e80170  char * *
+           0x0000000002e3fa36 <Bad Ptr>    char *
+       block   0x0000000000000000 <Bad Ptr>    char *
        pixelsize   1   int
        linesize    2560    int
        destroy 0x00000000024426b0 ImagingDestroyMap(ImagingMemoryInstance *)   void (ImagingMemoryInstance *)*
        dx  0   int
        dy  0   int
        sx  1   int
        sy  1   int
        xsize   9   int
        ysize   9   int
        pixelsize   1   int
        y   0   int
```

Call stack:

```
    msvcr90.dll!000000006239e28f()  
    [Frames below may be incorrect and/or missing, no symbols loaded for msvcr90.dll]   
>   _imaging.pyd!paste(ImagingMemoryInstance * imOut=0x0000000001fbdb50, ImagingMemoryInstance * imIn=0x0000000001fbdcd0, int dx=0, int dy=0, int sx=1, int sy=1, int xsize=9, int ysize=9, int pixelsize=1)  Line 55 + 0x4b bytes  C
    _imaging.pyd!ImagingPaste(ImagingMemoryInstance * imOut=0x0000000001fbdb50, ImagingMemoryInstance * imIn=0x0000000001fbdcd0, ImagingMemoryInstance * imMask=0x0000000000000000, int dx0=0, int dy0=0, int dx1=2559, int dy1=1919)  Line 260 C
    _imaging.pyd!ImagingCrop(ImagingMemoryInstance * imIn=0x0000000001fbdcd0, int sx0=1, int sy0=1, int sx1=10, int sy1=10)  Line 55 + 0x32 bytes   C
    _imaging.pyd!_crop(ImagingObject * self=0x0000000001d3f270, _object * args=0x000000000286c9b0)  Line 812 + 0x24 bytes   C
    python27.dll!PyCFunction_Call(_object * func=0x0000000000000001, _object * arg=0x00000000005352f0, _object * kw=0x00000000028b3dc8)  Line 81 + 0x8 bytes    C
    python27.dll!call_function(_object * * * pp_stack=0x0000000000000000, int oparg=131)  Line 4033 + 0xaf bytes    C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000028918b8, int throwflag=-1968928)  Line 2683   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000001f510b0, _object * globals=0x0000000000000001, _object * locals=0x0000000000000000, _object * * args=0x0000000000000000, int argcount=1, _object * * kws=0x00000000026b0f18, int kwcount=0, _object * * defs=0x0000000000000000, int defcount=0, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!fast_function(_object * func=0x0000000000000001, _object * * * pp_stack=0x0000000001f3fc88, int n=42325832, int na=30971336, int nk=0)  Line 4133  C
    python27.dll!call_function(_object * * * pp_stack=0x0000000000000000, int oparg=131)  Line 4054 + 0x16 bytes    C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000026b0d68, int throwflag=40679200)  Line 2683   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000001f482b0, _object * globals=0x0000000000000001, _object * locals=0x00000000026cb720, _object * * args=0x000000000288d450, int argcount=1, _object * * kws=0x00000000028958f8, int kwcount=1, _object * * defs=0x00000000026cb720, int defcount=2, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!fast_function(_object * func=0x0000000000000001, _object * * * pp_stack=0x0000000001f3fc88, int n=42320056, int na=503955056, int nk=1)  Line 4133 C
    python27.dll!call_function(_object * * * pp_stack=0x0000000000000000, int oparg=131)  Line 4054 + 0x16 bytes    C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x0000000002895768, int throwflag=-880)  Line 2683   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x00000000028897b0, _object * globals=0x0000000000000002, _object * locals=0x0000000000000000, _object * * args=0x000000000288d4b0, int argcount=2, _object * * kws=0x0000000002895580, int kwcount=0, _object * * defs=0x0000000000000000, int defcount=0, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!fast_function(_object * func=0x0000000000000002, _object * * * pp_stack=0x00000000028a1888, int n=42692680, int na=503946761, int nk=0)  Line 4133 C
    python27.dll!call_function(_object * * * pp_stack=0x0000000000000000, int oparg=131)  Line 4054 + 0x16 bytes    C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000028953d8, int throwflag=42513104)  Line 2683   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x00000000028899b0, _object * globals=0x0000000000000002, _object * locals=0x0000000000000002, _object * * args=0x0000000000000001, int argcount=2, _object * * kws=0x00000000028a0330, int kwcount=2, _object * * defs=0x0000000000000000, int defcount=0, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!function_call(_object * func=0x00000000028b70b8, _object * arg=0x000000000289a908, _object * kw=0x0000000000000009)  Line 531 + 0x3b bytes C
    python27.dll!PyObject_Call(_object * func=0x000000000289a908, _object * arg=0x0000000002899bf8, _object * kw=0x0000000000000000)  Line 2530 C
    python27.dll!ext_do_call(_object * func=0x00000000028b70b8, _object * * * pp_stack=0x0000000000000002, int flags=42701304, int na=2, int nk=0)  Line 4346 + 0xe bytes   C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000028b9048, int throwflag=42598472)  Line 2721   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000002878c30, _object * globals=0x0000000000000002, _object * locals=0x0000000000000002, _object * * args=0x000000000022f170, int argcount=2, _object * * kws=0x000000000287ac90, int kwcount=2, _object * * defs=0x0000000000000000, int defcount=0, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!function_call(_object * func=0x00000000028baeb8, _object * arg=0x00000000028a1188, _object * kw=0x0000000000000009)  Line 531 + 0x3b bytes C
    python27.dll!PyObject_Call(_object * func=0x00000000028a1188, _object * arg=0x0000000002899e18, _object * kw=0x0000000000000000)  Line 2530 C
    python27.dll!ext_do_call(_object * func=0x00000000028baeb8, _object * * * pp_stack=0x0000000000000002, int flags=41261976, int na=2, int nk=1)  Line 4346 + 0xe bytes   C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000027599d0, int throwflag=42446584)  Line 2721   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x00000000028789b0, _object * globals=0x0000000000000002, _object * locals=0x0000000000000001, _object * * args=0x000000001e0a4406, int argcount=2, _object * * kws=0x000000000289aa20, int kwcount=1, _object * * defs=0x000000000289e1e8, int defcount=1, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!function_call(_object * func=0x00000000028badd8, _object * arg=0x000000000289a688, _object * kw=0x0000000000000009)  Line 531 + 0x3b bytes C
    python27.dll!PyObject_Call(_object * func=0x000000000289a688, _object * arg=0x0000000002892bf8, _object * kw=0x0000000000000000)  Line 2530 C
    python27.dll!ext_do_call(_object * func=0x00000000028badd8, _object * * * pp_stack=0x0000000000000002, int flags=42558896, int na=2, int nk=0)  Line 4346 + 0xe bytes   C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000028963f8, int throwflag=42302792)  Line 2721   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000001f51d30, _object * globals=0x0000000000000001, _object * locals=0x0000000000000002, _object * * args=0x000000001e29a6e8, int argcount=1, _object * * kws=0x0000000001f2ff60, int kwcount=2, _object * * defs=0x0000000002857d48, int defcount=1, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!function_call(_object * func=0x000000000285df28, _object * arg=0x0000000001f3afd0, _object * kw=0x0000000000000009)  Line 531 + 0x3b bytes C
    python27.dll!PyObject_Call(_object * func=0x0000000001f3afd0, _object * arg=0x0000000002892ae8, _object * kw=0x0000000000000000)  Line 2530 C
    python27.dll!ext_do_call(_object * func=0x000000000285df28, _object * * * pp_stack=0x0000000000000002, int flags=42374504, int na=1, int nk=0)  Line 4346 + 0xe bytes   C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000028693c8, int throwflag=32986120)  Line 2721   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000001f51cb0, _object * globals=0x0000000000000001, _object * locals=0x0000000000000000, _object * * args=0x0000000000000001, int argcount=1, _object * * kws=0x00000000026b0b48, int kwcount=2, _object * * defs=0x0000000000000000, int defcount=0, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!fast_function(_object * func=0x0000000000000001, _object * * * pp_stack=0x0000000000000000, int n=42327736, int na=2292056, int nk=2)  Line 4133   C
    python27.dll!call_function(_object * * * pp_stack=0x0000000000000000, int oparg=131)  Line 4054 + 0x16 bytes    C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x00000000026b09a8, int throwflag=41175136)  Line 2683   C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000001f4fa30, _object * globals=0x0000000000000001, _object * locals=0x0000000002744860, _object * * args=0x0000000001f45030, int argcount=1, _object * * kws=0x0000000001e6ac18, int kwcount=0, _object * * defs=0x0000000002744860, int defcount=2, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!fast_function(_object * func=0x0000000000000001, _object * * * pp_stack=0x0000000001f3fc88, int n=42324824, int na=503989254, int nk=0)  Line 4133 C
    python27.dll!call_function(_object * * * pp_stack=0x0000000000000000, int oparg=131)  Line 4054 + 0x16 bytes    C
    python27.dll!PyEval_EvalFrameEx(_frame * f=0x0000000001e6aa98, int throwflag=5474707)  Line 2683    C
    python27.dll!PyEval_EvalCodeEx(PyCodeObject * co=0x0000000001d28e30, _object * globals=0x000000006238f904, _object * locals=0x0000000001d28e30, _object * * args=0x00000000005350d0, int argcount=0, _object * * kws=0x0000000000000000, int kwcount=0, _object * * defs=0x0000000000000000, int defcount=0, _object * closure=0x0000000000000000)  Line 3265 + 0xa bytes   C
    python27.dll!PyEval_EvalCode(PyCodeObject * co=0x000000006240c660, _object * globals=0x0000000000538993, _object * locals=0x0000000001d79268)  Line 673 C
    python27.dll!run_mod(_mod * mod=0x000000006240c660, const char * filename=0x0000000001eb4290, _object * globals=0x0000000000000001, _object * locals=0x000000001e14562e, PyCompilerFlags * flags=0x000000000022ff38, _arena * arena=0x00000000005367e0)  Line 1372  C
    python27.dll!PyRun_FileExFlags(_iobuf * fp=0x0000000000538993, const char * filename=0x0000000001d781f8, int start=30904936, _object * globals=0x0000000001d78330, _object * locals=0x0000000001d79268, int closeit=1, PyCompilerFlags * flags=0x000000000022ff38)  Line 1358   C
    python27.dll!PyRun_SimpleFileExFlags(_iobuf * fp=0x0000000000000001, const char * filename=0x0000000000538993, int closeit=1648412256, PyCompilerFlags * flags=0x000000000022ff38)  Line 950 + 0x2a bytes   C
    python27.dll!PyRun_AnyFileExFlags(_iobuf * fp=0x0000000000538960, const char * filename=0x0000000000000001, int closeit=0, PyCompilerFlags * flags=0x0000000000538993)  Line 754    C
    python27.dll!Py_Main(int argc=0, char * * argv=0x000000ed00000000)  Line 643 + 0x24 bytes   C
    python.exe!__tmainCRTStartup()  Line 586 + 0x19 bytes   C
    kernel32.dll!00007fff793a13d2()     
    ntdll.dll!00007fff7bbf03c4()    
```

Yikes

The Dropbox URL is no longer working. Any chance that a link can be provided to the test image again?

it doesn't matter what bmp, any  rgb valued .bmp image gives this error.

@hugovk Can you check to see if the BMP changes in 3.3.0 have fixed this?

I won't be able to check Windows until tomorrow. Can anyone else check it?

Still crashes with git-master.

I think `Tests/images/bmp/g/pal8-0.bmp` triggers the bug. I'm currently unable to get anything out of appveyor with it. 

It looks like there's a use after free on the core image structure, but from my traces of it, it doesn't appear that the memory is actually freed. It shouldn't happen, because we're keeping a reference to the core image structure in the new `_ImageCrop` object.

The problem appears to go away if we make a copy of the core image object in `_ImageCrop.__init__` rather than a pointer to it. Copying the image removes the entire point of having a lazy crop in the first place. 

(not that it makes a huge amount of sense anyway -- it's the one lazy operation other than load, and it requires the initial image to be loaded anyway.  I think this is one place where the complexity is not worth it. )

Also, cropping a `P` image didn't retain the palette, and didn't return a PyAccess object where appropriate. 


Resolving patch:
diff --git a/PIL/Image.py b/PIL/Image.py
--- a/PIL/Image.py
+++ b/PIL/Image.py
@@ -1020,10 +1020,7 @@ def crop(self, box=None):
         4-tuple defining the left, upper, right, and lower pixel
         coordinate.
 
-        This is a lazy operation.  Changes to the source image may or
-        may not be reflected in the cropped image.  To break the
-        connection, call the :py:meth:`~PIL.Image.Image.load` method on
-        the cropped copy.
+        Note: Prior to Pillow 3.4.0, this was a lazy operation. 
 
         :param box: The crop rectangle, as a (left, upper, right, lower)-tuple.
         :rtype: :py:class:`~PIL.Image.Image`
@@ -1034,8 +1031,15 @@ def crop(self, box=None):
         if box is None:
             return self.copy()
 
-        # lazy operation
-        return _ImageCrop(self, box)
+        x0, y0, x1, y1 = map(int, map(round, box))
+
+        if x1 < x0:
+            x1 = x0
+        if y1 < y0:
+            y1 = y0
+
+        return self._new(self.im.crop(( x0, y0, x1, y1)))
+
 
     def draft(self, mode, size):
         """
@@ -1954,43 +1958,6 @@ def toqpixmap(self):
         return ImageQt.toqpixmap(self)
 
 
-# --------------------------------------------------------------------
-# Lazy operations
-
-class _ImageCrop(Image):
-
-    def __init__(self, im, box):
-
-        Image.__init__(self)
-
-        # Round to nearest integer, runs int(round(x)) when unpacking
-        x0, y0, x1, y1 = map(int, map(round, box))
-
-        if x1 < x0:
-            x1 = x0
-        if y1 < y0:
-            y1 = y0
-
-        self.mode = im.mode
-        self.size = x1-x0, y1-y0
-
-        self.__crop = x0, y0, x1, y1
-
-        self.im = im.im
-
-    def load(self):
-
-        # lazy evaluation!
-        if self.__crop:
-            self.im = self.im.crop(self.__crop)
-            self.__crop = None
-
-        if self.im:
-            return self.im.pixel_access(self.readonly)
-
-        # FIXME: future versions should optimize crop/paste
-        # sequences!
-
 
 # --------------------------------------------------------------------
 # Abstract handlers.


Test patch:
diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py
--- a/Tests/test_image_crop.py
+++ b/Tests/test_image_crop.py
@@ -66,6 +66,24 @@ def test_crop_float(self):
         # Assert
         self.assertEqual(cropped.size, (3, 5))
 
+    def test_crop_crash(self):
+        #Image.crop crashes prepatch with an access violation
+        #apparently a use after free on windows, see
+        #https://github.com/python-pillow/Pillow/issues/1077
+        
+        test_img = 'Tests/images/bmp/g/pal8-0.bmp'
+        extents = (1,1,10,10)
+        #works prepatch
+        img = Image.open(test_img)
+        img2 = img.crop(extents)
+        img2.load()
+        
+        # fail prepatch
+        img = Image.open(test_img)
+        img = img.crop(extents)
+        img.load()
+
+
 
 if __name__ == '__main__':
     unittest.main()