@@ -258,6 +258,32 @@ def copyfileobj(src, dst, length=None, exception=OSError, bufsize=None):
258258 dst .write (buf )
259259 return
260260
261+ # Maximum number of bytes read in a single call when reading a member's
262+ # extended header (a GNU long name/link or a pax header). The size of such
263+ # a header is taken from the archive and is not trustworthy, so it is read in
264+ # bounded chunks to avoid a huge up-front allocation when a crafted or
265+ # truncated archive claims far more data than the file actually contains
266+ # (gh-151497).
267+ _EXTHEADER_READ_CHUNK = 1024 * 1024 # 1 MiB
268+
269+ def _safe_read (fileobj , size ):
270+ """Read up to *size* bytes from *fileobj* in bounded chunks.
271+
272+ Returns the same bytes as ``fileobj.read(size)`` would (including a short
273+ result at end of file), but limits pre-allocation, so an
274+ oversized size field in a crafted header cannot force a huge allocation.
275+ """
276+ if size <= _EXTHEADER_READ_CHUNK :
277+ return fileobj .read (size )
278+ chunks = []
279+ while size > 0 :
280+ chunk = fileobj .read (min (size , _EXTHEADER_READ_CHUNK ))
281+ if not chunk :
282+ break
283+ chunks .append (chunk )
284+ size -= len (chunk )
285+ return b"" .join (chunks )
286+
261287def _safe_print (s ):
262288 encoding = getattr (sys .stdout , 'encoding' , None )
263289 if encoding is not None :
@@ -524,7 +550,9 @@ def seek(self, pos=0):
524550 if pos - self .pos >= 0 :
525551 blocks , remainder = divmod (pos - self .pos , self .bufsize )
526552 for i in range (blocks ):
527- self .read (self .bufsize )
553+ data = self .read (self .bufsize )
554+ if not data :
555+ break
528556 self .read (remainder )
529557 else :
530558 raise StreamError ("seeking backwards is not allowed" )
@@ -1429,7 +1457,7 @@ def _proc_gnulong(self, tarfile):
14291457 """Process the blocks that hold a GNU longname
14301458 or longlink member.
14311459 """
1432- buf = tarfile .fileobj . read ( self ._block (self .size ))
1460+ buf = _safe_read ( tarfile .fileobj , self ._block (self .size ))
14331461
14341462 # Fetch the next header and process it.
14351463 try :
@@ -1485,7 +1513,7 @@ def _proc_pax(self, tarfile):
14851513 POSIX.1-2008.
14861514 """
14871515 # Read the header information.
1488- buf = tarfile .fileobj . read ( self ._block (self .size ))
1516+ buf = _safe_read ( tarfile .fileobj , self ._block (self .size ))
14891517
14901518 # A pax header stores supplemental information for either
14911519 # the following file (extended) or all following files
@@ -2510,7 +2538,8 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False,
25102538 tarinfo , unfiltered = self ._get_extract_tarinfo (
25112539 member , filter_function , path )
25122540 if tarinfo is not None :
2513- self ._extract_one (tarinfo , path , set_attrs , numeric_owner )
2541+ self ._extract_one (tarinfo , path , set_attrs , numeric_owner ,
2542+ filter_function = filter_function )
25142543
25152544 def _get_extract_tarinfo (self , member , filter_function , path ):
25162545 """Get (filtered, unfiltered) TarInfos from *member*
@@ -2782,6 +2811,9 @@ def makelink_with_filter(self, tarinfo, targetpath,
27822811 "makelink_with_filter: if filter_function is not None, "
27832812 + "extraction_root must also not be None" )
27842813 try :
2814+ filter_function (
2815+ unfiltered .replace (name = tarinfo .name , deep = False ),
2816+ extraction_root )
27852817 filtered = filter_function (unfiltered , extraction_root )
27862818 except _FILTER_ERRORS as cause :
27872819 raise LinkFallbackError (tarinfo , unfiltered .name ) from cause
0 commit comments