https://github.com/python/cpython/commit/5c75b50c9d416bf03f022aa2da1f2dbc550f80d1
commit: 5c75b50c9d416bf03f022aa2da1f2dbc550f80d1
branch: main
author: esadomer <[email protected]>
committer: ethanfurman <[email protected]>
date: 2026-10-19T12:49:26-07:00
summary:

gh-159322: NUL-terminate owner names in PAX base headers (GH-159329)

Co-authored-by: Ethan Furman <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-10-13-19-10-00.gh-issue-159322.hP6k3A.rst
M Lib/tarfile.py
M Lib/test/test_tarfile.py

diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index dc2d35fc27a3de..3c3375ca77cb41 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1139,7 +1139,7 @@ def create_pax_header(self, info, encoding):
         # be represented in ASCII encoding.
         for name, hname, length in (
                 ("name", "path", LENGTH_NAME), ("linkname", "linkpath", 
LENGTH_LINK),
-                ("uname", "uname", 32), ("gname", "gname", 32)):
+                ("uname", "uname", 31), ("gname", "gname", 31)):
 
             if hname in pax_headers:
                 # The pax header has priority.
@@ -1183,6 +1183,9 @@ def create_pax_header(self, info, encoding):
         else:
             buf = b""
 
+        # The owner names in the base header must be NUL-terminated.
+        info["uname"] = info["uname"][:31]
+        info["gname"] = info["gname"][:31]
         return buf + self._create_header(info, USTAR_FORMAT, "ascii", 
"replace")
 
     @classmethod
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 605e66b6c64c96..219a563a193be3 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -2560,6 +2560,43 @@ def test_dereference_hardlink(self):
 
 class PaxWriteTest(GNUWriteTest):
 
+    def test_100_byte_names(self):
+        for field, offset in (("name", 0), ("linkname", 157)):
+            with self.subTest(field=field):
+                name = "a" * 100
+                tarinfo = tarfile.TarInfo("test")
+                if field == "linkname":
+                    tarinfo.type = tarfile.SYMTYPE
+                setattr(tarinfo, field, name)
+                header = tarinfo.tobuf(format=tarfile.PAX_FORMAT)
+                self.assertEqual(len(header), tarfile.BLOCKSIZE)
+                self.assertEqual(header[offset:offset + 100],
+                                 name.encode("ascii"))
+                with tarfile.open(fileobj=io.BytesIO(header)) as tar:
+                    member = tar.next()
+                    self.assertEqual(getattr(member, field), name)
+                    self.assertEqual(member.pax_headers, {})
+
+    def test_owner_names(self):
+        for field, offset in (("uname", 265), ("gname", 297)):
+            for length in (0, 31, 32, 33, 40):
+                with self.subTest(field=field, length=length):
+                    name = "a" * length
+                    tarinfo = tarfile.TarInfo("test")
+                    setattr(tarinfo, field, name)
+                    header = tarinfo.tobuf(format=tarfile.PAX_FORMAT)
+                    # The base header must leave room for a NUL terminator.
+                    base_header = header[-tarfile.BLOCKSIZE:]
+                    self.assertEqual(base_header[offset:offset + 32],
+                                     name[:31].encode().ljust(32, b"\0"))
+                    with tarfile.open(fileobj=io.BytesIO(header)) as tar:
+                        member = tar.next()
+                        self.assertEqual(getattr(member, field), name)
+                        if length > 31:
+                            self.assertEqual(member.pax_headers[field], name)
+                        else:
+                            self.assertNotIn(field, member.pax_headers)
+
     def _test(self, name, link=None):
         # See GNUWriteTest.
         tarinfo = tarfile.TarInfo(name)
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-13-19-10-00.gh-issue-159322.hP6k3A.rst 
b/Misc/NEWS.d/next/Library/2026-10-13-19-10-00.gh-issue-159322.hP6k3A.rst
new file mode 100644
index 00000000000000..f9c3d1a98acc35
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-13-19-10-00.gh-issue-159322.hP6k3A.rst
@@ -0,0 +1,2 @@
+Fix :mod:`tarfile` to NUL-terminate user and group names in PAX archive
+headers and use extended headers for names longer than 31 characters.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to