From a13609516ce1f8cf0c1fd5d5463adc7e9220edec Mon Sep 17 00:00:00 2001
From: Andrew Murray <radarhere@users.noreply.github.com>
Date: Mon, 30 Dec 2024 15:21:57 +1100
Subject: [PATCH] Check that _fp type is not DeferredError before use

---
 src/PIL/DcxImagePlugin.py    | 3 +++
 src/PIL/FliImagePlugin.py    | 3 +++
 src/PIL/GifImagePlugin.py    | 3 +++
 src/PIL/ImImagePlugin.py     | 3 +++
 src/PIL/Image.py             | 2 +-
 src/PIL/MpoImagePlugin.py    | 5 +++++
 src/PIL/PngImagePlugin.py    | 3 +++
 src/PIL/PsdImagePlugin.py    | 5 +++++
 src/PIL/SpiderImagePlugin.py | 3 +++
 src/PIL/TiffImagePlugin.py   | 4 +++-
 10 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py
index f67f27d73..aea661b9c 100644
--- a/src/PIL/DcxImagePlugin.py
+++ b/src/PIL/DcxImagePlugin.py
@@ -24,6 +24,7 @@ from __future__ import annotations
 
 from . import Image
 from ._binary import i32le as i32
+from ._util import DeferredError
 from .PcxImagePlugin import PcxImageFile
 
 MAGIC = 0x3ADE68B1  # QUIZ: what's this value, then?
@@ -66,6 +67,8 @@ class DcxImageFile(PcxImageFile):
     def seek(self, frame: int) -> None:
         if not self._seek_check(frame):
             return
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         self.frame = frame
         self.fp = self._fp
         self.fp.seek(self._offset[frame])
diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py
index b534b30ab..7c5bfeefa 100644
--- a/src/PIL/FliImagePlugin.py
+++ b/src/PIL/FliImagePlugin.py
@@ -22,6 +22,7 @@ from . import Image, ImageFile, ImagePalette
 from ._binary import i16le as i16
 from ._binary import i32le as i32
 from ._binary import o8
+from ._util import DeferredError
 
 #
 # decoder
@@ -134,6 +135,8 @@ class FliImageFile(ImageFile.ImageFile):
             self._seek(f)
 
     def _seek(self, frame: int) -> None:
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         if frame == 0:
             self.__frame = -1
             self._fp.seek(self.__rewind)
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
index 47022d584..2c48c747b 100644
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -45,6 +45,7 @@ from . import (
 from ._binary import i16le as i16
 from ._binary import o8
 from ._binary import o16le as o16
+from ._util import DeferredError
 
 if TYPE_CHECKING:
     from . import _imaging
@@ -167,6 +168,8 @@ class GifImageFile(ImageFile.ImageFile):
                 raise EOFError(msg) from e
 
     def _seek(self, frame: int, update_image: bool = True) -> None:
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         if frame == 0:
             # rewind
             self.__offset = 0
diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py
index b4215a0b1..2209177dc 100644
--- a/src/PIL/ImImagePlugin.py
+++ b/src/PIL/ImImagePlugin.py
@@ -31,6 +31,7 @@ import re
 from typing import IO, Any
 
 from . import Image, ImageFile, ImagePalette
+from ._util import DeferredError
 
 # --------------------------------------------------------------------
 # Standard tags
@@ -290,6 +291,8 @@ class ImImageFile(ImageFile.ImageFile):
     def seek(self, frame: int) -> None:
         if not self._seek_check(frame):
             return
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
 
         self.frame = frame
 
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
index dff3d063b..653195c8d 100644
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -604,7 +604,7 @@ class Image:
         return self
 
     def _close_fp(self):
-        if getattr(self, "_fp", False):
+        if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError):
             if self._fp != self.fp:
                 self._fp.close()
             self._fp = DeferredError(ValueError("Operation on closed image"))
diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py
index 71f89a09a..ea792e2fa 100644
--- a/src/PIL/MpoImagePlugin.py
+++ b/src/PIL/MpoImagePlugin.py
@@ -32,6 +32,7 @@ from . import (
     TiffImagePlugin,
 )
 from ._binary import o32le
+from ._util import DeferredError
 
 
 def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
@@ -125,11 +126,15 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
         self.readonly = 1
 
     def load_seek(self, pos: int) -> None:
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         self._fp.seek(pos)
 
     def seek(self, frame: int) -> None:
         if not self._seek_check(frame):
             return
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         self.fp = self._fp
         self.offset = self.__mpoffsets[frame]
 
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
index 4b97992a3..b44cf9665 100644
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -48,6 +48,7 @@ from ._binary import i32be as i32
 from ._binary import o8
 from ._binary import o16be as o16
 from ._binary import o32be as o32
+from ._util import DeferredError
 
 if TYPE_CHECKING:
     from . import _imaging
@@ -869,6 +870,8 @@ class PngImageFile(ImageFile.ImageFile):
 
     def _seek(self, frame: int, rewind: bool = False) -> None:
         assert self.png is not None
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
 
         self.dispose: _imaging.ImagingCore | None
         dispose_extent = None
diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py
index 8ff5e3908..fe6a5723e 100644
--- a/src/PIL/PsdImagePlugin.py
+++ b/src/PIL/PsdImagePlugin.py
@@ -27,6 +27,7 @@ from ._binary import i16be as i16
 from ._binary import i32be as i32
 from ._binary import si16be as si16
 from ._binary import si32be as si32
+from ._util import DeferredError
 
 MODES = {
     # (photoshop mode, bits) -> (pil mode, required channels)
@@ -148,6 +149,8 @@ class PsdImageFile(ImageFile.ImageFile):
     ) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]:
         layers = []
         if self._layers_position is not None:
+            if isinstance(self._fp, DeferredError):
+                raise self._fp.ex
             self._fp.seek(self._layers_position)
             _layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size))
             layers = _layerinfo(_layer_data, self._layers_size)
@@ -167,6 +170,8 @@ class PsdImageFile(ImageFile.ImageFile):
     def seek(self, layer: int) -> None:
         if not self._seek_check(layer):
             return
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
 
         # seek to given layer (1..max)
         try:
diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py
index 3a87d009a..3bd3510fa 100644
--- a/src/PIL/SpiderImagePlugin.py
+++ b/src/PIL/SpiderImagePlugin.py
@@ -40,6 +40,7 @@ import sys
 from typing import IO, TYPE_CHECKING, Any, cast
 
 from . import Image, ImageFile
+from ._util import DeferredError
 
 
 def isInt(f: Any) -> int:
@@ -178,6 +179,8 @@ class SpiderImageFile(ImageFile.ImageFile):
             raise EOFError(msg)
         if not self._seek_check(frame):
             return
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
         self.fp = self._fp
         self.fp.seek(self.stkoffset)
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
index 16c521bea..585bbc38b 100644
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -58,7 +58,7 @@ from ._binary import i32be as i32
 from ._binary import o8
 from ._deprecate import deprecate
 from ._typing import StrOrBytesPath
-from ._util import is_path
+from ._util import DeferredError, is_path
 from .TiffTags import TYPES
 
 if TYPE_CHECKING:
@@ -1212,6 +1212,8 @@ class TiffImageFile(ImageFile.ImageFile):
             self._im = None
 
     def _seek(self, frame: int) -> None:
+        if isinstance(self._fp, DeferredError):
+            raise self._fp.ex
         self.fp = self._fp
 
         while len(self._frame_pos) <= frame: