Repository: python-pillow/Pillow

Issue:
PPM "Channel value too large" error
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?

We are using PIL for reading PPM images of 1280x1024 or 1920x1080 pixels in RGB 24 bits with maxval of 255 in the header.

### What did you expect to happen?

We expect the PPM to open correctly.

### What actually happened?

The opening of PPM images failed due to a maxval error "Channel value too large for this mode"

### What are your OS, Python and Pillow versions?

* OS: Windows 10
* Python: 3.9
* Pillow: 9.4.0

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

When I first try to load my PPM, PIL return the channel value too large error with the value of 20458 with a maxval of 255. Then I searched in my PPM this value and there is no data higher than 255.
After investigated the issue, I saw that the concatenation of the half_block and the next block block give this:
-> end of previus block: ... 204 ]
-> half_block + block:    [20458 ...
-> expected:                   [204 58 ...

I fixed the issue locally by changing line 239:
```python
if half_token:
    block = half_token + block  # stitch half_token to new block
```
By
```python
if half_token:
    block = half_token + b' ' + block  # stitch half_token to new block
    half_token = False
```


Discussion:
Could you upload a copy of the image?
After searching a little bit more, my issue was because I create my PPM image in a C program using LF at the end of each line but then I launch the program with Windows so the EOL became CRLF and then pillow crash when I wanted to use show or save method.
I don't know if this CRLF is handle by pillow.

**Edit**
I have the same issue. Because my PPM file is internal to my company, I can't share it but I will try to replicate the bug with another PPM tomorrow.
Hmm. I don't think the issue should be CRLF. I would have thought that
https://github.com/python-pillow/Pillow/blob/8cc5396659c7a728eb4e50bc6671571df86cd346/src/PIL/PpmImagePlugin.py#L241
splits CRLF without a problem.
[output.zip](https://github.com/python-pillow/Pillow/files/10821798/output.zip)

Here my PPM images. First one, "mignon.ppm" can be open and show without any issue. The second one, "mignon1280.ppm" is the same image resize to 1280x1024 by Paint Shop Pro and can't be shown.

Code use to test:
```python
# Python39
from PIL import Image
im = Image.open("mignon1280.ppm")
im.show()
```
Thanks very much. I expect to include a fix for this in the next release of Pillow on April 1.
Amazing, did you have any idea how can we make a local fix because we use pillow to convert PPM images to PNG and we can’t really wait one month. If you don’t know, we will find another way make it !
This should serve as a temporary workaround.
```python
from PIL.PpmImagePlugin import PpmPlainDecoder
PpmPlainDecoder._read_block = lambda self: self.fd.read()

from PIL import Image
im = Image.open("mignon1280.ppm")
im.show()
```

Resolving patch:
diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py
--- a/src/PIL/PpmImagePlugin.py
+++ b/src/PIL/PpmImagePlugin.py
@@ -237,6 +237,7 @@ def _decode_blocks(self, maxval):
 
             if half_token:
                 block = half_token + block  # stitch half_token to new block
+                half_token = False
 
             tokens = block.split()
 


Test patch:
diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py
--- a/Tests/test_file_ppm.py
+++ b/Tests/test_file_ppm.py
@@ -256,6 +256,16 @@ def test_truncated_file(tmp_path):
             im.load()
 
 
+def test_not_enough_image_data(tmp_path):
+    path = str(tmp_path / "temp.ppm")
+    with open(path, "wb") as f:
+        f.write(b"P2 1 2 255 255")
+
+    with Image.open(path) as im:
+        with pytest.raises(ValueError):
+            im.load()
+
+
 @pytest.mark.parametrize("maxval", (b"0", b"65536"))
 def test_invalid_maxval(maxval, tmp_path):
     path = str(tmp_path / "temp.ppm")