changeset 17314:ab5135d5ff83

getcwd: break fdopendir + save_cwd recursive loop (Bug#13516) Reported for OS X 10.8.2 by Assaf Gordon in <http://bugs.gnu.org/13516>. * lib/getcwd.c (HAVE_OPENAT_SUPPORT): Do not define if !HAVE_OPENAT && !HAVE_FDOPENDIR. * m4/getcwd-abort-bug.m4: Reformat to match test-getcwd.c so that they can be kept in sync more easily. Avoid PATH_MAX test on the Hurd. Sync from test-getcwd.c for errno tests after mkdir or chdir failure. * tests/test-getcwd.c (HAVE_OPENAT_SUPPORT): New macro, from lib/getcwd.c. (test_abort_bug): Do not test for the deep directory bug unless we have openat support. Avoid PATH_MAX test on the Hurd.
author Paul Eggert <eggert@cs.ucla.edu>
date Sun, 03 Feb 2013 21:28:30 -0800
parents a9f418d6d676
children c47552eff05b
files ChangeLog lib/getcwd.c m4/getcwd-abort-bug.m4 tests/test-getcwd.c
diffstat 4 files changed, 95 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
 2013-02-03  Paul Eggert  <eggert@cs.ucla.edu>
 
+	getcwd: break fdopendir + save_cwd recursive loop (Bug#13516)
+	Reported for OS X 10.8.2 by Assaf Gordon in
+	<http://bugs.gnu.org/13516>.
+	* lib/getcwd.c (HAVE_OPENAT_SUPPORT): Do not define if
+	!HAVE_OPENAT && !HAVE_FDOPENDIR.
+	* m4/getcwd-abort-bug.m4: Reformat to match test-getcwd.c
+	so that they can be kept in sync more easily.  Avoid PATH_MAX
+	test on the Hurd.  Sync from test-getcwd.c for errno tests after
+	mkdir or chdir failure.
+	* tests/test-getcwd.c (HAVE_OPENAT_SUPPORT): New macro, from
+	lib/getcwd.c.
+	(test_abort_bug): Do not test for the deep directory bug unless we
+	have openat support.  Avoid PATH_MAX test on the Hurd.
+
 	regex-tests, regex: fix bug: memset undeclared
 	* tests/test-regex.c: Don't include regex.h twice.  Include
 	string.h, to declare memset.  Christensen's report also mentioned
--- a/lib/getcwd.c
+++ b/lib/getcwd.c
@@ -28,9 +28,9 @@
 #include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
 
 /* If this host provides the openat function or if we're using the
-   gnulib replacement function, then enable code below to make getcwd
-   more efficient and robust.  */
-#if defined HAVE_OPENAT || defined GNULIB_OPENAT
+   gnulib replacement function with a native fdopendir, then enable
+   code below to make getcwd more efficient and robust.  */
+#if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
 # define HAVE_OPENAT_SUPPORT 1
 #else
 # define HAVE_OPENAT_SUPPORT 0
--- a/m4/getcwd-abort-bug.m4
+++ b/m4/getcwd-abort-bug.m4
@@ -58,16 +58,18 @@
 int
 main ()
 {
-  char const *dir_name = "confdir-14B---";
   char *cwd;
   size_t initial_cwd_len;
   int fail = 0;
-  size_t desired_depth;
-  size_t d;
 
   /* The bug is triggered when PATH_MAX < getpagesize (), so skip
      this relatively expensive and invasive test if that's not true.  */
-  if (getpagesize () <= PATH_MAX)
+#ifdef PATH_MAX
+  int bug_possible = PATH_MAX < getpagesize ();
+#else
+  int bug_possible = 0;
+#endif
+  if (! bug_possible)
     return 0;
 
   cwd = getcwd (NULL, 0);
@@ -76,35 +78,43 @@
 
   initial_cwd_len = strlen (cwd);
   free (cwd);
-  desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
-                   / (1 + strlen (dir_name)));
-  for (d = 0; d < desired_depth; d++)
+
+  if (1)
     {
-      if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
+      static char const dir_name[] = "confdir-14B---";
+      size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
+                              / sizeof dir_name);
+      size_t d;
+      for (d = 0; d < desired_depth; d++)
         {
-          fail = 3; /* Unable to construct deep hierarchy.  */
-          break;
+          if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
+            {
+              if (! (errno == ERANGE || errno == ENAMETOOLONG
+                     || errno == ENOENT))
+                fail = 3; /* Unable to construct deep hierarchy.  */
+              break;
+            }
         }
-    }
 
-  /* If libc has the bug in question, this invocation of getcwd
-     results in a failed assertion.  */
-  cwd = getcwd (NULL, 0);
-  if (cwd == NULL)
-    fail = 4; /* getcwd failed: it refuses to return a string longer
-                 than PATH_MAX.  */
-  free (cwd);
+      /* If libc has the bug in question, this invocation of getcwd
+         results in a failed assertion.  */
+      cwd = getcwd (NULL, 0);
+      if (cwd == NULL)
+        fail = 4; /* getcwd didn't assert, but it failed for a long name
+                     where the answer could have been learned.  */
+      free (cwd);
 
-  /* Call rmdir first, in case the above chdir failed.  */
-  rmdir (dir_name);
-  while (0 < d--)
-    {
-      if (chdir ("..") < 0)
+      /* Call rmdir first, in case the above chdir failed.  */
+      rmdir (dir_name);
+      while (0 < d--)
         {
-          fail = 5;
-          break;
+          if (chdir ("..") < 0)
+            {
+              fail = 5;
+              break;
+            }
+          rmdir (dir_name);
         }
-      rmdir (dir_name);
     }
 
   return fail;
--- a/tests/test-getcwd.c
+++ b/tests/test-getcwd.c
@@ -38,23 +38,29 @@
    trigger a bug in glibc's getcwd implementation before 2.4.90-10.  */
 #define TARGET_LEN (5 * 1024)
 
+#if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
+# define HAVE_OPENAT_SUPPORT 1
+#else
+# define HAVE_OPENAT_SUPPORT 0
+#endif
+
 /* Keep this test in sync with m4/getcwd-abort-bug.m4.  */
 static int
 test_abort_bug (void)
 {
-  char const *dir_name = "confdir-14B---";
   char *cwd;
   size_t initial_cwd_len;
   int fail = 0;
-  size_t desired_depth;
-  size_t d;
 
-#ifdef PATH_MAX
   /* The bug is triggered when PATH_MAX < getpagesize (), so skip
      this relatively expensive and invasive test if that's not true.  */
-  if (getpagesize () <= PATH_MAX)
+#ifdef PATH_MAX
+  int bug_possible = PATH_MAX < getpagesize ();
+#else
+  int bug_possible = 0;
+#endif
+  if (! bug_possible)
     return 0;
-#endif
 
   cwd = getcwd (NULL, 0);
   if (cwd == NULL)
@@ -62,36 +68,43 @@
 
   initial_cwd_len = strlen (cwd);
   free (cwd);
-  desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
-                   / (1 + strlen (dir_name)));
-  for (d = 0; d < desired_depth; d++)
+
+  if (HAVE_OPENAT_SUPPORT)
     {
-      if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
+      static char const dir_name[] = "confdir-14B---";
+      size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
+                              / sizeof dir_name);
+      size_t d;
+      for (d = 0; d < desired_depth; d++)
         {
-          if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
-            fail = 3; /* Unable to construct deep hierarchy.  */
-          break;
+          if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
+            {
+              if (! (errno == ERANGE || errno == ENAMETOOLONG
+                     || errno == ENOENT))
+                fail = 3; /* Unable to construct deep hierarchy.  */
+              break;
+            }
         }
-    }
 
-  /* If libc has the bug in question, this invocation of getcwd
-     results in a failed assertion.  */
-  cwd = getcwd (NULL, 0);
-  if (cwd == NULL)
-    fail = 4; /* getcwd didn't assert, but it failed for a long name
-                 where the answer could have been learned.  */
-  free (cwd);
+      /* If libc has the bug in question, this invocation of getcwd
+         results in a failed assertion.  */
+      cwd = getcwd (NULL, 0);
+      if (cwd == NULL)
+        fail = 4; /* getcwd didn't assert, but it failed for a long name
+                     where the answer could have been learned.  */
+      free (cwd);
 
-  /* Call rmdir first, in case the above chdir failed.  */
-  rmdir (dir_name);
-  while (0 < d--)
-    {
-      if (chdir ("..") < 0)
+      /* Call rmdir first, in case the above chdir failed.  */
+      rmdir (dir_name);
+      while (0 < d--)
         {
-          fail = 5;
-          break;
+          if (chdir ("..") < 0)
+            {
+              fail = 5;
+              break;
+            }
+          rmdir (dir_name);
         }
-      rmdir (dir_name);
     }
 
   return fail;