* src/install.c (initial_umask): New variable.
(strip): Set the umask to initial_umask before invoking 'strip' and then
reset it to zero after the child is created.
(main): Save the initial umask when setting it to zero.
* tests/install/strip-program.sh: Add a few invocations to test the
behavior.
* NEWS: Mention the bug fix.

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-39LQ2UOD.
---
 NEWS                           |  4 ++++
 src/install.c                  | 14 +++++++++++++-
 tests/install/strip-program.sh | 11 +++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 64f6b0bfc..a20815642 100644
--- a/NEWS
+++ b/NEWS
@@ -67,6 +67,10 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   after a tab character are accounted for correctly.
   [bugs introduced in coreutils-9.8]
 
+  'install --strip' sets the umask to the initial umask specified by the user
+  before invoking 'strip' instead of using a umask of zero.
+  [This bug was present in "the beginning".]
+
   'ls' no longer omits ACL indicators or capability coloring for subsequent
   files on the same file system after an unsupported security context
   lookup on a file without an ACL.
diff --git a/src/install.c b/src/install.c
index 1eae1a5ff..ffe2aee27 100644
--- a/src/install.c
+++ b/src/install.c
@@ -102,6 +102,9 @@ static bool dir_arg;
 /* Program used to strip binaries, "strip" is default */
 static char const *strip_program = "strip";
 
+/* The umask specified the user before we set it to zero.  */
+static mode_t initial_umask;
+
 /* For long options that have no equivalent short option, use a
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
@@ -509,11 +512,20 @@ strip (char const *name)
     safe_name = concat_name = file_name_concat (".", name, NULL);
   char const *const argv[] = { strip_program, safe_name, NULL };
 
+  /* Reset the umask to the one specified by the user, since some
+     'strip' implementations create files that can be read and/or
+     written to by other users.  */
+  umask (initial_umask);
+
   /* Run 'strip'.  */
   pid_t pid;
   int result = posix_spawnp (&pid, strip_program, NULL, attrp,
                              (char * const *) argv, environ);
 
+  /* Restore the umask to zero now that the child process has
+     inherited the initial umask.  */
+  umask (0);
+
   bool ok = false;
   if (result != 0)
     {
@@ -863,7 +875,7 @@ main (int argc, char **argv)
 
   cp_option_init (&x);
 
-  umask (0);
+  initial_umask = umask (0);
 
   int optc;
   while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options,
diff --git a/tests/install/strip-program.sh b/tests/install/strip-program.sh
index 08e6abb9b..e25c8399c 100755
--- a/tests/install/strip-program.sh
+++ b/tests/install/strip-program.sh
@@ -32,6 +32,17 @@ echo aBc > exp || framework_failure_
 ginstall src dest -s --strip-program=./b || fail=1
 compare exp dest || fail=1
 
+cat <<EOF >getumask || framework_failure_
+#!$SHELL
+umask > \$1
+EOF
+chmod a+x getumask || framework_failure_
+
+for u in 2 4 7 22 44 77; do
+  (umask $u && ginstall src dest -s --strip-program=./getumask) || fail=1
+  test "$(sed 's/^0*//' dest)" = $u || fail=1
+done
+
 # Check that install cleans up properly if strip fails.
 returns_ 1 ginstall src dest2 -s --strip-program=./FOO || fail=1
 test -e dest2 && fail=1
-- 
2.56.0


Reply via email to