Subject: bug#76876: logname output is often wrong when linked with glibc

From: Bruno Haible <[email-masked]>
Date: Sun, 09 Mar 2025 10:23:02 +0100

Paul Eggert wrote:
> In the meantime I installed the attached to Gnulib, to 
> document the incompatibility, which also occurs with getlogin_r.

These two patches actually work around the bugs.

With this, coreutils should be fine, since it already imports the 'getlogin'
module from gnulib.

2025-03-09  Bruno Haible  <[email-masked]>

	getlogin_r: Work around musl bug.
	* lib/getlogin_r.c (getlogin_r): Add implementation for Linux.
	* m4/getlogin_r.m4 (gl_FUNC_GETLOGIN_R): Test whether getlogin_r has the
	musl bug.
	* tests/test-getlogin_r.c (main): Add another test.
	* doc/posix-functions/getlogin_r.texi: Mention the workaround.

2025-03-09  Bruno Haible  <[email-masked]>

	getlogin: Work around musl bug.
	* lib/unistd.in.h (getlogin): Consider REPLACE_GETLOGIN.
	* lib/getlogin.c: Change license header to GPL.
	(getlogin): Add implementation for Linux.
	* m4/getlogin.m4 (gl_FUNC_GETLOGIN): Test whether getlogin works.
	* m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize REPLACE_GETLOGIN.
	* modules/unistd-h (Makefile.am): Substitute REPLACE_GETLOGIN.
	* modules/getlogin (Depends-on): Add readutmp.
	(configure.ac): Consider REPLACE_GETLOGIN.
	(License): Change to GPL.
	* tests/test-getlogin.c (main): Add another test.
	* doc/posix-functions/getlogin.texi: Mention the workaround.

>From 90840606e77dd24d87ad7f075d8f87c0ed814ef5 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email-masked]>
Date: Sun, 9 Mar 2025 10:16:58 +0100
Subject: [PATCH 1/2] getlogin: Work around musl bug.

* lib/unistd.in.h (getlogin): Consider REPLACE_GETLOGIN.
* lib/getlogin.c: Change license header to GPL.
(getlogin): Add implementation for Linux.
* m4/getlogin.m4 (gl_FUNC_GETLOGIN): Test whether getlogin works.
* m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize REPLACE_GETLOGIN.
* modules/unistd-h (Makefile.am): Substitute REPLACE_GETLOGIN.
* modules/getlogin (Depends-on): Add readutmp.
(configure.ac): Consider REPLACE_GETLOGIN.
(License): Change to GPL.
* tests/test-getlogin.c (main): Add another test.
* doc/posix-functions/getlogin.texi: Mention the workaround.
---
 ChangeLog                         | 15 +++++
 doc/posix-functions/getlogin.texi |  6 +-
 lib/getlogin.c                    | 99 +++++++++++++++++++++++++++++--
 lib/unistd.in.h                   | 14 ++++-
 m4/getlogin.m4                    | 49 ++++++++++++++-
 m4/unistd_h.m4                    |  3 +-
 modules/getlogin                  |  6 +-
 modules/unistd-h                  |  1 +
 tests/test-getlogin.c             |  6 ++
 9 files changed, 184 insertions(+), 15 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 89940ddd4c..6d2ad66c83 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2025-03-09  Bruno Haible  <[email-masked]>
+
+	getlogin: Work around musl bug.
+	* lib/unistd.in.h (getlogin): Consider REPLACE_GETLOGIN.
+	* lib/getlogin.c: Change license header to GPL.
+	(getlogin): Add implementation for Linux.
+	* m4/getlogin.m4 (gl_FUNC_GETLOGIN): Test whether getlogin works.
+	* m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize REPLACE_GETLOGIN.
+	* modules/unistd-h (Makefile.am): Substitute REPLACE_GETLOGIN.
+	* modules/getlogin (Depends-on): Add readutmp.
+	(configure.ac): Consider REPLACE_GETLOGIN.
+	(License): Change to GPL.
+	* tests/test-getlogin.c (main): Add another test.
+	* doc/posix-functions/getlogin.texi: Mention the workaround.
+
 2025-03-08  Paul Eggert  <[email-masked]>
 
 	getlogin: document musl bug
diff --git a/doc/posix-functions/getlogin.texi b/doc/posix-functions/getlogin.texi
index 0de026f2ee..0ccce336ce 100644
--- a/doc/posix-functions/getlogin.texi
+++ b/doc/posix-functions/getlogin.texi
@@ -15,14 +15,14 @@
 @item
 This function is not declared unless @code{_POSIX} is defined on some platforms:
 mingw.
+@item
+This function returns the value of the @env{LOGNAME} environment variable:
+musl libc 1.2.5.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
 @item
-This function returns the value of the @env{LOGNAME} environment variable:
-musl libc 1.2.5.
-@item
 This function returns an empty string even when standard input is a tty
 on some platforms:
 HP-UX 11.11.
diff --git a/lib/getlogin.c b/lib/getlogin.c
index 72c25acbc7..4772111567 100644
--- a/lib/getlogin.c
+++ b/lib/getlogin.c
@@ -3,19 +3,19 @@
    Copyright (C) 2010-2025 Free Software Foundation, Inc.
 
    This file is free software: you can redistribute it and/or modify
-   it under the terms of the GNU Lesser General Public License as
-   published by the Free Software Foundation; either version 2.1 of the
-   License, or (at your option) any later version.
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
 
    This file is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU Lesser General Public License for more details.
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public License
+   You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-/* Written by Bruno Haible, 2010.  */
+/* Written by Bruno Haible, 2010, 2025.  */
 
 #include <config.h>
 
@@ -30,15 +30,102 @@
 # define GetUserName GetUserNameA
 #endif
 
+#if defined __linux__ || defined __ANDROID__
+# include <fcntl.h>
+# include <pwd.h>
+# include <stdlib.h>
+# include <string.h>
+# include <sys/stat.h>
+# include "readutmp.h"
+#endif
+
 char *
 getlogin (void)
 {
 #if defined _WIN32 && ! defined __CYGWIN__
+  /* Native Windows platform.  */
   static char login_name[1024];
   DWORD sz = sizeof (login_name);
 
   if (GetUserName (login_name, &sz))
     return login_name;
+#elif defined __linux__ || defined __ANDROID__
+  /* Linux.  */
+  {
+    /* Read the login uid from the /proc file system.  */
+    int fd = open ("/proc/self/loginuid", O_RDONLY);
+    if (fd >= 0)
+      {
+        char buf[20 + 1];
+        int n = read (fd, buf, sizeof (buf) - 1);
+        if (n > 0)
+          {
+            buf[n] = '\0';
+            char *endptr;
+            unsigned long uid = strtoul (buf, &endptr, 10);
+            if (endptr == buf + n && uid != (uid_t) -1)
+              {
+                /* Convert the uid to a user name.  */
+                struct passwd *p = getpwuid (uid);
+                if (p != NULL && strlen (p->pw_name) < 64)
+                  {
+                    static char resultbuf[64];
+                    strcpy (resultbuf, p->pw_name);
+                    close (fd);
+                    return resultbuf;
+                  }
+              }
+          }
+        close (fd);
+      }
+  }
+  {
+    /* Find the tty connected to the current process.  */
+    char *tty = ttyname (STDIN_FILENO);
+    if (tty != NULL)
+      {
+        /* Try to see to which user it is allocated, via utmp.  */
+        if (strncmp (tty, "/dev/tty", 8) == 0)
+          {
+            idx_t n;
+            STRUCT_UTMP *entries;
+            if (read_utmp (UTMP_FILE, &n, &entries, READ_UTMP_USER_PROCESS)
+                == 0)
+              {
+                idx_t i;
+                for (i = 0; i < n; i++)
+                  if (strcmp (entries[i].ut_line, tty + 5) == 0)
+                    {
+                      if (strlen (entries[i].ut_user) < 64)
+                        {
+                          static char resultbuf[64];
+                          strcpy (resultbuf, entries[i].ut_user);
+                          free (entries);
+                          return resultbuf;
+                        }
+                      break;
+                    }
+                free (entries);
+              }
+          }
+        /* Fallback for systems which don't maintain an utmp database
+           or for ttys that are not recorded in that the utmp database:
+           Look at the owner of that tty.  */
+        struct stat statbuf;
+        if (stat (tty, &statbuf) >= 0)
+          {
+            uid_t uid = statbuf.st_uid;
+            /* Convert the uid to a user name.  */
+            struct passwd *p = getpwuid (uid);
+            if (p != NULL && strlen (p->pw_name) < 64)
+              {
+                static char resultbuf[64];
+                strcpy (resultbuf, p->pw_name);
+                return resultbuf;
+              }
+          }
+      }
+  }
 #endif
   return NULL;
 }
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index acabdf8c68..bfb4817726 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -1365,11 +1365,21 @@ _GL_WARN_ON_USE (gethostname, "gethostname is unportable - "
      ${LOGNAME-$USER}        on Unix platforms,
      $USERNAME               on native Windows platforms.
  */
-# if !@HAVE_DECL_GETLOGIN@
+# if @REPLACE_GETLOGIN@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define getlogin rpl_getlogin
+#  endif
+_GL_FUNCDECL_RPL (getlogin, char *, (void), );
+_GL_CXXALIAS_RPL (getlogin, char *, (void));
+# else
+#  if !@HAVE_DECL_GETLOGIN@
 _GL_FUNCDECL_SYS (getlogin, char *, (void), );
-# endif
+#  endif
 _GL_CXXALIAS_SYS (getlogin, char *, (void));
+# endif
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (getlogin);
+# endif
 #elif defined GNULIB_POSIXCHECK
 # undef getlogin
 # if HAVE_RAW_DECL_GETLOGIN
diff --git a/m4/getlogin.m4 b/m4/getlogin.m4
index e876fad5ea..7f1f07319d 100644
--- a/m4/getlogin.m4
+++ b/m4/getlogin.m4
@@ -1,5 +1,5 @@
 # getlogin.m4
-# serial 7
+# serial 8
 dnl Copyright (C) 2010-2025 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -16,6 +16,53 @@ AC_DEFUN([gl_FUNC_GETLOGIN]
   AC_CHECK_FUNCS_ONCE([getlogin])
   if test $ac_cv_func_getlogin = no; then
     HAVE_GETLOGIN=0
+  else
+    dnl On musl libc, getlogin returns getenv ("LOGNAME").
+    AC_REQUIRE([AC_CANONICAL_HOST])
+    AC_CACHE_CHECK([whether getlogin works],
+      [gl_cv_func_getlogin_works],
+      [
+        dnl Initial guess, used when cross-compiling.
+changequote(,)dnl
+        case "$host_os" in
+                              # Guess no on musl libc.
+          *-musl* | midipix*) gl_cv_func_getlogin_works="guessing no" ;;
+                              # Guess yes otherwise.
+          *)                  gl_cv_func_getlogin_works="guessing yes" ;;
+        esac
+changequote([,])dnl
+        AC_RUN_IFELSE(
+          [AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if !HAVE_DECL_GETLOGIN
+extern
+# ifdef __cplusplus
+"C"
+# endif
+char *getlogin (void);
+#endif
+int
+main (void)
+{
+  int result = 0;
+  char *buf;
+
+  putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke");
+  buf = getlogin ();
+  if (buf != NULL && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0)
+    result |= 1;
+  return result;
+}]])],
+          [gl_cv_func_getlogin_works=yes],
+          [gl_cv_func_getlogin_works=no],
+          [:])
+      ])
+    case "$gl_cv_func_getlogin_works" in
+      *yes) ;;
+      *) REPLACE_GETLOGIN=1 ;;
+    esac
   fi
 ])
 
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index 2c08d65e0d..6ec16286ef 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -1,5 +1,5 @@
 # unistd_h.m4
-# serial 96
+# serial 97
 dnl Copyright (C) 2006-2025 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -244,6 +244,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS]
   REPLACE_GETDOMAINNAME=0;           AC_SUBST([REPLACE_GETDOMAINNAME])
   REPLACE_GETDTABLESIZE=0;           AC_SUBST([REPLACE_GETDTABLESIZE])
   REPLACE_GETENTROPY=0;              AC_SUBST([REPLACE_GETENTROPY])
+  REPLACE_GETLOGIN=0;                AC_SUBST([REPLACE_GETLOGIN])
   REPLACE_GETLOGIN_R=0;              AC_SUBST([REPLACE_GETLOGIN_R])
   REPLACE_GETGROUPS=0;               AC_SUBST([REPLACE_GETGROUPS])
   REPLACE_GETPAGESIZE=0;             AC_SUBST([REPLACE_GETPAGESIZE])
diff --git a/modules/getlogin b/modules/getlogin
index 8725a7031e..7acee58a81 100644
--- a/modules/getlogin
+++ b/modules/getlogin
@@ -7,10 +7,12 @@ m4/getlogin.m4
 
 Depends-on:
 unistd-h
+readutmp        [test $HAVE_GETLOGIN = 0 || test $REPLACE_GETLOGIN = 1]
 
 configure.ac:
 gl_FUNC_GETLOGIN
-gl_CONDITIONAL([GL_COND_OBJ_GETLOGIN], [test $HAVE_GETLOGIN = 0])
+gl_CONDITIONAL([GL_COND_OBJ_GETLOGIN],
+               [test $HAVE_GETLOGIN = 0 || test $REPLACE_GETLOGIN = 1])
 gl_UNISTD_MODULE_INDICATOR([getlogin])
 AC_REQUIRE([gl_LIB_GETLOGIN])
 
@@ -26,7 +28,7 @@ Link:
 $(GETLOGIN_LIB)
 
 License:
-LGPLv2+
+GPL
 
 Maintainer:
 all
diff --git a/modules/unistd-h b/modules/unistd-h
index 1a7376efdb..0fdb6368b3 100644
--- a/modules/unistd-h
+++ b/modules/unistd-h
@@ -201,6 +201,7 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
 	      -e 's|@''REPLACE_GETDOMAINNAME''@|$(REPLACE_GETDOMAINNAME)|g' \
 	      -e 's|@''REPLACE_GETDTABLESIZE''@|$(REPLACE_GETDTABLESIZE)|g' \
 	      -e 's|@''REPLACE_GETENTROPY''@|$(REPLACE_GETENTROPY)|g' \
+	      -e 's|@''REPLACE_GETLOGIN''@|$(REPLACE_GETLOGIN)|g' \
 	      -e 's|@''REPLACE_GETLOGIN_R''@|$(REPLACE_GETLOGIN_R)|g' \
 	      -e 's|@''REPLACE_GETGROUPS''@|$(REPLACE_GETGROUPS)|g' \
 	      -e 's|@''REPLACE_GETPAGESIZE''@|$(REPLACE_GETPAGESIZE)|g' \
diff --git a/tests/test-getlogin.c b/tests/test-getlogin.c
index 1b980a152d..431844bd71 100644
--- a/tests/test-getlogin.c
+++ b/tests/test-getlogin.c
@@ -41,5 +41,11 @@ main (void)
 #endif
   test_getlogin_result (buf, err);
 
+  /* Check that getlogin() does not merely return getenv ("LOGNAME").  */
+  putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke");
+  buf = getlogin ();
+  ASSERT (!(buf != NULL
+            && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0));
+
   return test_exit_status;
 }
-- 
2.43.0

>From 0bca572030d26721cb81800ff4264b6f18dc7e40 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email-masked]>
Date: Sun, 9 Mar 2025 10:19:20 +0100
Subject: [PATCH 2/2] getlogin_r: Work around musl bug.

* lib/getlogin_r.c (getlogin_r): Add implementation for Linux.
* m4/getlogin_r.m4 (gl_FUNC_GETLOGIN_R): Test whether getlogin_r has the
musl bug.
* tests/test-getlogin_r.c (main): Add another test.
* doc/posix-functions/getlogin_r.texi: Mention the workaround.
---
 ChangeLog                           |  9 ++++
 doc/posix-functions/getlogin_r.texi |  6 +--
 lib/getlogin_r.c                    | 74 +++++++++++++++++++++++++++++
 m4/getlogin_r.m4                    | 20 +++++---
 tests/test-getlogin_r.c             |  5 ++
 5 files changed, 105 insertions(+), 9 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6d2ad66c83..d8eb0e8df7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2025-03-09  Bruno Haible  <[email-masked]>
+
+	getlogin_r: Work around musl bug.
+	* lib/getlogin_r.c (getlogin_r): Add implementation for Linux.
+	* m4/getlogin_r.m4 (gl_FUNC_GETLOGIN_R): Test whether getlogin_r has the
+	musl bug.
+	* tests/test-getlogin_r.c (main): Add another test.
+	* doc/posix-functions/getlogin_r.texi: Mention the workaround.
+
 2025-03-09  Bruno Haible  <[email-masked]>
 
 	getlogin: Work around musl bug.
diff --git a/doc/posix-functions/getlogin_r.texi b/doc/posix-functions/getlogin_r.texi
index daafd9eaa0..df145ecfe2 100644
--- a/doc/posix-functions/getlogin_r.texi
+++ b/doc/posix-functions/getlogin_r.texi
@@ -20,6 +20,9 @@
 This function returns a truncated result, instead of failing with error code
 @code{ERANGE}, when the buffer is not large enough, on some platforms:
 macOS 14.
+@item
+This function returns the value of the @env{LOGNAME} environment variable:
+musl libc 1.2.5.
 @end itemize
 
 Portability problems not fixed by Gnulib:
@@ -28,9 +31,6 @@
 This function has an incompatible declaration on some platforms:
 FreeBSD 12.0, MidnightBSD 2.0, Solaris 11.4 (when @code{_POSIX_PTHREAD_SEMANTICS} is not defined).
 @item
-This function returns the value of the @env{LOGNAME} environment variable:
-musl libc 1.2.5.
-@item
 This function fails even when standard input is a tty on some platforms:
 HP-UX 11.11.
 @item
diff --git a/lib/getlogin_r.c b/lib/getlogin_r.c
index 6a13e8e405..ade819940e 100644
--- a/lib/getlogin_r.c
+++ b/lib/getlogin_r.c
@@ -39,6 +39,14 @@ extern char *getlogin (void);
 # endif
 #endif
 
+#if defined __linux__ || defined __ANDROID__
+# include <fcntl.h>
+# include <pwd.h>
+# include <stdlib.h>
+# include <string.h>
+# include <sys/stat.h>
+#endif
+
 /* See unistd.in.h for documentation.  */
 int
 getlogin_r (char *name, size_t size)
@@ -64,6 +72,72 @@ getlogin_r (char *name, size_t size)
         return ENOENT;
     }
   return 0;
+#elif defined __linux__ || defined __ANDROID__
+  /* Linux.  */
+  {
+    /* Read the login uid from the /proc file system.  */
+    int fd = open ("/proc/self/loginuid", O_RDONLY);
+    if (fd >= 0)
+      {
+        char buf[20 + 1];
+        int n = read (fd, buf, sizeof (buf) - 1);
+        if (n > 0)
+          {
+            buf[n] = '\0';
+            char *endptr;
+            unsigned long uid = strtoul (buf, &endptr, 10);
+            if (endptr == buf + n && uid != (uid_t) -1)
+              {
+                /* Convert the uid to a user name.  */
+                struct passwd pbuf1;
+                char pbuf2[1024];
+                struct passwd *p;
+                if (getpwuid_r (uid, &pbuf1, pbuf2, sizeof (pbuf2), &p) == 0)
+                  {
+                    if (strlen (p->pw_name) < size)
+                      {
+                        strcpy (name, p->pw_name);
+                        close (fd);
+                        return 0;
+                      }
+                    return ERANGE;
+                  }
+              }
+          }
+        close (fd);
+      }
+  }
+  {
+    /* Find the tty connected to the current process.  */
+    char tty[1024];
+    if (ttyname_r (STDIN_FILENO, tty, sizeof (tty)) == 0)
+      {
+        /* We cannot use read_utmp here, since it is not multithread-safe.  */
+        /* Fallback for systems which don't maintain an utmp database
+           or for ttys that are not recorded in that the utmp database:
+           Look at the owner of that tty.  */
+        struct stat statbuf;
+        if (stat (tty, &statbuf) >= 0)
+          {
+            uid_t uid = statbuf.st_uid;
+            /* Convert the uid to a user name.  */
+            struct passwd pbuf1;
+            char pbuf2[1024];
+            struct passwd *p;
+            if (getpwuid_r (uid, &pbuf1, pbuf2, sizeof (pbuf2), &p) == 0)
+              {
+                if (strlen (p->pw_name) < size)
+                  {
+                    strcpy (name, p->pw_name);
+                    return 0;
+                  }
+                return ERANGE;
+              }
+          }
+      }
+  }
+  /* ENOENT is a reasonable errno value if getlogin returns NULL.  */
+  return ENOENT;
 #elif HAVE_GETLOGIN_R
   /* Platform with a getlogin_r() function.  */
   int ret = getlogin_r (name, size);
diff --git a/m4/getlogin_r.m4 b/m4/getlogin_r.m4
index 26664e1fda..5ac8a89ff8 100644
--- a/m4/getlogin_r.m4
+++ b/m4/getlogin_r.m4
@@ -1,5 +1,5 @@
 # getlogin_r.m4
-# serial 15
+# serial 16
 dnl Copyright (C) 2005-2007, 2009-2025 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -35,22 +35,26 @@ AC_DEFUN([gl_FUNC_GETLOGIN_R]
     HAVE_GETLOGIN_R=1
     dnl On Mac OS X 10.13 and OSF/1 5.1, getlogin_r returns a truncated result
     dnl if the buffer is not large enough.
+    dnl On musl libc, getlogin_r returns getenv ("LOGNAME").
     AC_REQUIRE([AC_CANONICAL_HOST])
-    AC_CACHE_CHECK([whether getlogin_r works with small buffers],
+    AC_CACHE_CHECK([whether getlogin_r works],
       [gl_cv_func_getlogin_r_works],
       [
         dnl Initial guess, used when cross-compiling.
 changequote(,)dnl
         case "$host_os" in
-                          # Guess no on Mac OS X, OSF/1.
-          darwin* | osf*) gl_cv_func_getlogin_r_works="guessing no" ;;
-                          # Guess yes otherwise.
-          *)              gl_cv_func_getlogin_r_works="guessing yes" ;;
+                              # Guess no on Mac OS X, OSF/1.
+          darwin* | osf*)     gl_cv_func_getlogin_r_works="guessing no" ;;
+                              # Guess no on musl libc.
+          *-musl* | midipix*) gl_cv_func_getlogin_r_works="guessing no" ;;
+                              # Guess yes otherwise.
+          *)                  gl_cv_func_getlogin_r_works="guessing yes" ;;
         esac
 changequote([,])dnl
         AC_RUN_IFELSE(
           [AC_LANG_SOURCE([[
 #include <stddef.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #if !HAVE_DECL_GETLOGIN_R
@@ -76,6 +80,10 @@ AC_DEFUN([gl_FUNC_GETLOGIN_R]
       if (getlogin_r (buf, n) == 0)
         result |= 4;
     }
+  putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke");
+  if (getlogin_r (buf, 100) == 0
+      && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0)
+    result |= 8;
   return result;
 }]])],
           [gl_cv_func_getlogin_r_works=yes],
diff --git a/tests/test-getlogin_r.c b/tests/test-getlogin_r.c
index 4deddfbb01..3674195e55 100644
--- a/tests/test-getlogin_r.c
+++ b/tests/test-getlogin_r.c
@@ -68,5 +68,10 @@ main (void)
     ASSERT (strcmp (hugebuf, buf) == 0);
   }
 
+  /* Check that getlogin_r() does not merely return getenv ("LOGNAME").  */
+  putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke");
+  err = getlogin_r (buf, sizeof buf);
+  ASSERT (!(err == 0 && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0));
+
   return test_exit_status;
 }
-- 
2.43.0