bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
From: Paul Eggert <eggert@cs.ucla.edu>
To: bug-gnulib@gnu.org
Cc: Paul Eggert <eggert@cs.ucla.edu>
Subject: [PATCH] fchmodat, lchmod: port to buggy Linux filesystems
Date: Thu, 13 Feb 2020 10:42:09 -0800	[thread overview]
Message-ID: <20200213184209.34020-1-eggert@cs.ucla.edu> (raw)

Problem reported by Florian Weimer in:
https://www.sourceware.org/ml/libc-alpha/2020-02/msg00534.html
* lib/fchmodat.c (fchmodat):
* lib/lchmod.c (lchmod):
Don’t assume that chmod on the O_PATH-opened fd will do
the right thing on a symbolic link.
* lib/fchmodat.c (fchmodat):
Don’t attempt to special-case
any flag value other than AT_SYMLINK_NOFOLLOW.
---
 ChangeLog      | 13 +++++++++++++
 lib/fchmodat.c | 33 ++++++++++++++++++++++++++-------
 lib/lchmod.c   | 27 ++++++++++++++++++++++-----
 3 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b5ef9b3a1..8bcf1bf0a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2020-02-13  Paul Eggert  <eggert@cs.ucla.edu>
+
+	fchmodat, lchmod: port to buggy Linux filesystems
+	Problem reported by Florian Weimer in:
+	https://www.sourceware.org/ml/libc-alpha/2020-02/msg00534.html
+	* lib/fchmodat.c (fchmodat):
+	* lib/lchmod.c (lchmod):
+	Don’t assume that chmod on the O_PATH-opened fd will do
+	the right thing on a symbolic link.
+	* lib/fchmodat.c (fchmodat):
+	Don’t attempt to special-case
+	any flag value other than AT_SYMLINK_NOFOLLOW.
+
 2020-02-11  Paul Eggert  <eggert@cs.ucla.edu>
 
 	lchmod: pacify Coverity CID 1491216
diff --git a/lib/fchmodat.c b/lib/fchmodat.c
index 87aa0d191..02e2da956 100644
--- a/lib/fchmodat.c
+++ b/lib/fchmodat.c
@@ -63,12 +63,31 @@ orig_fchmodat (int dir, char const *file, mode_t mode, int flags)
 int
 fchmodat (int dir, char const *file, mode_t mode, int flags)
 {
-  if (flags & AT_SYMLINK_NOFOLLOW)
+  if (flags == AT_SYMLINK_NOFOLLOW)
     {
-# ifdef O_PATH
+      struct stat st;
+
+# if defined O_PATH && defined AT_EMPTY_PATH
       int fd = openat (dir, file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
       if (fd < 0)
         return fd;
+
+      /* Use fstatat because fstat does not work on O_PATH descriptors
+         before Linux 3.6.  */
+      if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0)
+        {
+          int stat_errno = errno;
+          close (fd);
+          errno = stat_errno;
+          return -1;
+        }
+      if (S_ISLNK (st.st_mode))
+        {
+          close (fd);
+          errno = EOPNOTSUPP;
+          return -1;
+        }
+
       static char const fmt[] = "/proc/self/fd/%d";
       char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
       sprintf (buf, fmt, fd);
@@ -82,10 +101,8 @@ fchmodat (int dir, char const *file, mode_t mode, int flags)
           errno = chmod_errno;
           return chmod_result;
         }
-      /* /proc is not mounted; fall back on racy implementation.  */
-# endif
-
-      struct stat st;
+      /* /proc is not mounted.  */
+# else
       int fstatat_result = fstatat (dir, file, &st, AT_SYMLINK_NOFOLLOW);
       if (fstatat_result != 0)
         return fstatat_result;
@@ -94,7 +111,9 @@ fchmodat (int dir, char const *file, mode_t mode, int flags)
           errno = EOPNOTSUPP;
           return -1;
         }
-      flags &= ~AT_SYMLINK_NOFOLLOW;
+# endif
+      /* Fall back on chmod, despite the race.  */
+      flags = 0;
     }
 
   return orig_fchmodat (dir, file, mode, flags);
diff --git a/lib/lchmod.c b/lib/lchmod.c
index 5fc658023..c7191c07d 100644
--- a/lib/lchmod.c
+++ b/lib/lchmod.c
@@ -37,10 +37,28 @@ lchmod (char const *file, mode_t mode)
 #if HAVE_FCHMODAT
   return fchmodat (AT_FDCWD, file, mode, AT_SYMLINK_NOFOLLOW);
 #else
-# if defined O_PATH && defined AT_FDCWD
+# if defined AT_FDCWD && defined O_PATH && defined AT_EMPTY_PATH
   int fd = openat (AT_FDCWD, file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
   if (fd < 0)
     return fd;
+
+  /* Use fstatat because fstat does not work on O_PATH descriptors
+     before Linux 3.6.  */
+  struct stat st;
+  if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0)
+    {
+      int stat_errno = errno;
+      close (fd);
+      errno = stat_errno;
+      return -1;
+    }
+  if (S_ISLNK (st.st_mode))
+    {
+      close (fd);
+      errno = EOPNOTSUPP;
+      return -1;
+    }
+
   static char const fmt[] = "/proc/self/fd/%d";
   char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
   sprintf (buf, fmt, fd);
@@ -54,10 +72,8 @@ lchmod (char const *file, mode_t mode)
       errno = chmod_errno;
       return chmod_result;
     }
-  /* /proc is not mounted; fall back on racy implementation.  */
-# endif
-
-# if HAVE_LSTAT
+  /* /proc is not mounted.  */
+# elif HAVE_LSTAT
   struct stat st;
   int lstat_result = lstat (file, &st);
   if (lstat_result != 0)
@@ -69,6 +85,7 @@ lchmod (char const *file, mode_t mode)
     }
 # endif
 
+  /* Fall back on chmod, despite the race.  */
   return chmod (file, mode);
 #endif
 }
-- 
2.24.1



             reply	other threads:[~2020-02-13 18:42 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-13 18:42 Paul Eggert [this message]
2020-02-14  3:29 ` [PATCH] fchmodat, lchmod: port to buggy Linux filesystems Bruno Haible
2020-02-16 17:24   ` Bruno Haible
2020-02-16 18:31     ` Paul Eggert
2020-02-16 18:58       ` Bruno Haible
2020-02-14  3:46 ` Bruno Haible
2020-02-14 21:02   ` Paul Eggert
2020-02-16 21:38     ` Bruno Haible
2020-02-16 22:28       ` Bruno Haible
2020-02-22 23:46       ` Bruno Haible
2020-02-23  8:15         ` Paul Eggert
2020-02-23 10:58           ` Bruno Haible
2020-02-23 23:56             ` Paul Eggert
2020-02-24  2:27               ` overriding glibc stub functions Bruno Haible
2020-02-24  7:44                 ` Paul Eggert
2020-02-23  1:35 ` [PATCH] fchmodat, lchmod: port to buggy Linux filesystems Bruno Haible
2020-02-23  7:22   ` Paul Eggert
2020-03-09 17:30 ` Pádraig Brady
2020-03-09 18:51   ` Paul Eggert
2020-03-09 23:45     ` Pádraig Brady
2020-03-10 11:52       ` Florian Weimer
2020-03-10 15:09         ` Kamil Dudka
2020-03-10 19:27         ` Pádraig Brady
2020-03-10 19:30           ` Florian Weimer
2020-03-11  8:03             ` Paul Eggert
2020-03-11  8:45               ` Florian Weimer
2020-03-11 16:04                 ` Paul Eggert
2020-03-11  8:25             ` Paul Eggert
2020-03-11  8:40               ` Florian Weimer
2020-03-11  9:04             ` Kamil Dudka
2020-03-11 16:36               ` Paul Eggert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://lists.gnu.org/mailman/listinfo/bug-gnulib

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200213184209.34020-1-eggert@cs.ucla.edu \
    --to=eggert@cs.ucla.edu \
    --cc=bug-gnulib@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).