Repository: python-pillow/Pillow

Issue:
[Feature Request] Making BMP pixel size limit configurable
Hello, I have been a happy user of Pillow for a long time, but new to the developer community. Thank you for making such amazing work available to the world. 

I am writing to ask if **the community would be open to the idea of making the hard-coded BMP [file size limit](https://github.com/python-pillow/Pillow/blob/5ce2fac3d6cfcf9af0f84502d9bd0c2dfcfcb398/src/PIL/BmpImagePlugin.py#L166) configurable by clients**. I need to work with large bmp files whose pixel counts exceed the limit, which does not appear configurable by design. Therefore, when I try opening the large bmp files using PIL, I get `OSError: Unsupported BMP Size`. It appears I need to modify the source code to lift the hard-coded limit.  

I understand, as documented, the motivation is to mitigate the risk with DOS attacks. However, I suppose there might be clients, just like me, that use PIL in an environment where there's a relatively limited DOS risk and are willing to accept the risk in any case.

If the idea makes sense, I'd love to work on a PR.

Look forward to your advice and thoughts.

FYI: the relevant code.

![image](https://user-images.githubusercontent.com/3280262/94590121-2c4b2d00-02c1-11eb-9596-96eed378d6b5.png)

source:

https://github.com/python-pillow/Pillow/blob/5ce2fac3d6cfcf9af0f84502d9bd0c2dfcfcb398/src/PIL/BmpImagePlugin.py#L166


Discussion:
Makes sense to me. `MAX_IMAGE_PIXELS * 2` is smaller than `2 ** 31`, so it's not like we would be loosening the restriction either.
What would be the action here, removing the check altogether, or making it configurable?
I would think the best way forward is to make it configurable, by replacing the check for a size of `2 ** 31` with the more standard `_decompression_bomb_check`.

Users could effectively configure it then by setting `Image.MAX_IMAGE_PIXELS`.
Thank you for sharing your advice about `_decompression_bomb_check`. Since the check is a function of `Image.MAX_IMAGE_PIXELS` that is already configurable, I think it sounds like a clean, natural solution. 

I think a path forward seems clear for us (@alicanb and I work together). We will send a PR soon.


 

Resolving patch:
diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py
--- a/src/PIL/BmpImagePlugin.py
+++ b/src/PIL/BmpImagePlugin.py
@@ -162,10 +162,6 @@ def _bitmap(self, header=0, offset=0):
             else (1 << file_info["bits"])
         )
 
-        # ------------------------------- Check abnormal values for DOS attacks
-        if file_info["width"] * file_info["height"] > 2 ** 31:
-            raise OSError("Unsupported BMP Size: (%dx%d)" % self.size)
-
         # ---------------------- Check bit depth for unusual unsupported values
         self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
         if self.mode is None:


Test patch:
diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py
--- a/Tests/test_decompression_bomb.py
+++ b/Tests/test_decompression_bomb.py
@@ -60,6 +60,10 @@ def test_exception_gif(self):
         with pytest.raises(Image.DecompressionBombError):
             Image.open("Tests/images/decompression_bomb.gif")
 
+    def test_exception_bmp(self):
+        with pytest.raises(Image.DecompressionBombError):
+            Image.open("Tests/images/bmp/b/reallybig.bmp")
+
 
 class TestDecompressionCrop:
     @classmethod