Hi all,

GNU tar extracting a member "a/./b" fails with ENOENT.
It is due to a bug in the emulation of openat2(), more
specifically when processing dotlike components,
resulting in openat() being called on "". Please see
the proposed attached patch.

Thanks,
Kinan
From 5bb08e9ea80308572779f00a8b41ab2b21b3f658 Mon Sep 17 00:00:00 2001
From: Kinan Al-Falakh <[email protected]>
Date: Tue, 16 Jun 2026 23:38:02 +0200
Subject: [PATCH] openat2: fix trailing-slash dotlike final component

do_openat2 rewrites a dotlike component to "." before opening it,
by setting f to 2 and storing '.' at e[-f]. When such a final
component has a trailing slash, h == 2 as well. So that store and the
following e[-h] = '\0' terminator hit the same byte: the NUL overwrites
the '.', and openat() is given "" and fails with ENOENT. This breaks,
e.g. GNU tar extracting a member named "a/./b" on hosts that use the
emulation.

* lib/openat2.c (do_openat2): Store the dotlike '.' after the e[-h] =
  '\0' terminator.
---
 lib/openat2.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/lib/openat2.c b/lib/openat2.c
index 2be62c921c..62f5d39791 100644
--- a/lib/openat2.c
+++ b/lib/openat2.c
@@ -316,13 +316,6 @@ do_openat2 (int *fd, char const *filename,
         }
       else
         {
-          if (dotlike)
-            {
-              /* This is empty or the last component, and acts like ".".
-                 Use "." regardless of whether it was "" or "." or "..".  */
-              f = sizeof ".";
-              e[-f] = '.';
-            }
 
           /* Open the current component, as either an internal directory or
              the final open.  Do not follow symlinks.  */
@@ -332,6 +325,20 @@ do_openat2 (int *fd, char const *filename,
                            : flags)
                           | O_NOFOLLOW | (eh ? O_DIRECTORY : 0));
           e[-h] = '\0';
+
+          if (dotlike)
+            {
+              /* This is empty or the last component, and acts like ".".
+                 Use "." regardless of whether it was "" or "." or "..".
+
+                 Write '.' after the NUL above so that when f == h, the
+                 terminator does not overwrite the '.', which would
+                 make openat() see "". */
+
+              f = sizeof ".";
+              e[-f] = '.';
+            }
+
           int subfd = openat (*fd, &e[-f], subflags, mode);
 
           if (subfd < 0)
-- 
2.52.0

Reply via email to