9bb5d6
This is a custom downstream RHEL 8 patch which rebuilds three
9bb5d6
GLIBC_PRIVATE interfaces locally for use by libnss_files.so.2
9bb5d6
and libnss_compat.so.2.
9bb5d6
9bb5d6
The shared objects needs the following 3 functions:
9bb5d6
__nss_readline
9bb5d6
__nss_parse_line_result
9bb5d6
__nss_files_fopen (only requirement for libnss_compat.so.2)
9bb5d6
9bb5d6
They are implemented in:
9bb5d6
nss/nss_parse_line_result.c
9bb5d6
nss/nss_readline.c
9bb5d6
nss/nss_files_fopen.c
9bb5d6
9bb5d6
We create wrappers for those functions, recompile, and link directly
9bb5d6
into the shared objects:
9bb5d6
nss/nss_parse_line_result_int.c
9bb5d6
nss/nss_readline_int.c
9bb5d6
nss/nss_files_fopen_int.c
9bb5d6
9bb5d6
After building the new shared objects there are no longer any undefined
9bb5d6
global function references to __nss_readline@GLIBC_PRIVATE,
9bb5d6
__nss_parse_line_result@GLIBC_PRIVATE or
9bb5d6
__nss_files_fopen@GLIBC_PRIVATE.
9bb5d6
9bb5d6
Instead we see local function definitions in the shared object e.g.
9bb5d6
Symbol table '.symtab' contains 628 entries:
9bb5d6
...
9bb5d6
   486: 0000000000008ce0    92 FUNC    LOCAL  DEFAULT   15 __nss_parse_line_result
9bb5d6
...
9bb5d6
   494: 0000000000008b70    72 FUNC    LOCAL  DEFAULT   15 __nss_readline_seek
9bb5d6
...
9bb5d6
   497: 0000000000008bc0   279 FUNC    LOCAL  DEFAULT   15 __nss_readline
9bb5d6
...
9bb5d6
   510: 0000000000008ce0    82 FUNC    LOCAL  DEFAULT   15 __nss_files_fopen
9bb5d6
9bb5d6
The remaining GLIBC_PRIVATE references in the shared objects are all
9bb5d6
pre-existing and do not impact upgrade scenarios.
9bb5d6
9bb5d6
For reference the existing and present GLIBC_PRIVATE interfaces are:
9bb5d6
__libc_alloc_buffer_alloc_array@@GLIBC_PRIVATE
9bb5d6
__libc_alloc_buffer_copy_string@@GLIBC_PRIVATE
9bb5d6
__libc_alloc_buffer_create_failure@@GLIBC_PRIVATE
9bb5d6
__libc_dynarray_emplace_enlarge@@GLIBC_PRIVATE
9bb5d6
__libc_scratch_buffer_grow@@GLIBC_PRIVATE
9bb5d6
__resp@@GLIBC_PRIVATE
9bb5d6
_nss_files_parse_grent@@GLIBC_PRIVATE
9bb5d6
_nss_files_parse_pwent@@GLIBC_PRIVATE
9bb5d6
_nss_files_parse_sgent@@GLIBC_PRIVATE
9bb5d6
_nss_files_parse_spent@@GLIBC_PRIVATE
9bb5d6
errno@@GLIBC_PRIVATE
9bb5d6
__nss_database_lookup2@GLIBC_PRIVATE
9bb5d6
__nss_lookup_function@GLIBC_PRIVATE
9bb5d6
9bb5d6
Each was checked for existence in libc.so.6.
9bb5d6
9bb5d6
A small reproducer was used in testing this patch, included here:
9bb5d6
cat >> tst-rhbz1927040.c <
9bb5d6
#include <stdio.h>
9bb5d6
#include <stdlib.h>
9bb5d6
#include <sys/types.h>
9bb5d6
#include <errno.h>
9bb5d6
#include <pwd.h>
9bb5d6
#include <string.h>
9bb5d6
9bb5d6
int
9bb5d6
main (void)
9bb5d6
{
9bb5d6
  struct passwd *res;
9bb5d6
9bb5d6
  /* Only lookup via files.  */
9bb5d6
  printf ("INFO: Upgrade glibc, then press ENTER to see if libnss_files.so.2 loads.");
9bb5d6
  getchar ();
9bb5d6
9bb5d6
  /* Try to get one entry.  */
9bb5d6
  printf ("INFO: Looking up first password entry.\n");
9bb5d6
  setpwent ();
9bb5d6
  errno = 0;
9bb5d6
  res = getpwent ();
9bb5d6
  if (res == NULL && errno != 0)
9bb5d6
    {
9bb5d6
      printf ("FAIL: Could not get entry (%s).\n", strerror(errno));
9bb5d6
      exit (1);
9bb5d6
    }
9bb5d6
  printf ("INFO: First entry passwd.pw_name = \"%s\"\n", res->pw_name);
9bb5d6
  printf ("PASS: Call to getpwent succeeded.\n");
9bb5d6
  endpwent ();
9bb5d6
  exit (0);
9bb5d6
}
9bb5d6
EOF
9bb5d6
9bb5d6
Testing RHEL upgrade
9bb5d6
from: glibc-2.28-127.el8_3.2
9bb5d6
to: glibc-2.28-148.el8
9bb5d6
9bb5d6
./tst-rhbz1927040
9bb5d6
INFO: Upgrade glibc, then press ENTER to see if libnss_files.so.2 loads.
9bb5d6
INFO: Looking up first password entry.
9bb5d6
INFO: Result was NULL.
9bb5d6
PASS: Call to getpwent succeeded.
9bb5d6
9bb5d6
With LD_DEBUG=all you can observe:
9bb5d6
     22697:     /lib64/libnss_files.so.2: error: symbol lookup error: undefined symbol: __nss_files_fopen, version GLIBC_PRIVATE (fatal)
9bb5d6
9bb5d6
Which is the indication that the upgrade caused the transient IdM lookup failure.
9bb5d6
9bb5d6
Running again succeeds:
9bb5d6
INFO: Upgrade glibc, then press ENTER to see if libnss_files.so.2 loads.
9bb5d6
INFO: Looking up first password entry.
9bb5d6
INFO: First entry passwd.pw_name = "root"
9bb5d6
PASS: Call to getpwent succeeded.
9bb5d6
9bb5d6
diff --git a/nss/Makefile b/nss/Makefile
9bb5d6
index 7359da38feb65618..d5c28a6b5ed3661c 100644
9bb5d6
--- a/nss/Makefile
9bb5d6
+++ b/nss/Makefile
9bb5d6
@@ -92,9 +92,19 @@ extra-libs-others	= $(extra-libs)
9bb5d6
 subdir-dirs = $(services:%=nss_%)
9bb5d6
 vpath %.c $(subdir-dirs) ../locale/programs ../intl
9bb5d6
 
9bb5d6
-
9bb5d6
+# In RHEL we add nss_readline, nss_parse_line_result, and
9bb5d6
+# nss_files_fopen to the libnss_files-routines in order to avoid the
9bb5d6
+# case where a long running process (having never used NSS) attemps to
9bb5d6
+# load an NSS module for the first time and that NSS module needs a
9bb5d6
+# newer GLIBC_PRIVATE interface.  In effect we must make the NSS modules
9bb5d6
+# self-sufficient and not rely on a GLIBC_PRIVATE interface.
9bb5d6
+# See: https://bugzilla.redhat.com/show_bug.cgi?id=1927040
9bb5d6
+# Note: We must recompile the objects to get the correct global symbol
9bb5d6
+#       references, which is why we have the *_int.c wrappers.
9bb5d6
 libnss_files-routines	:= $(addprefix files-,$(databases)) \
9bb5d6
-			   files-initgroups files-init
9bb5d6
+			   files-initgroups files-init \
9bb5d6
+			   nss_readline_int nss_parse_line_result_int \
9bb5d6
+			   nss_files_fopen_int
9bb5d6
 
9bb5d6
 libnss_db-dbs		:= $(addprefix db-,\
9bb5d6
 				       $(filter-out hosts network key alias,\
9bb5d6
@@ -104,8 +114,10 @@ libnss_db-routines	:= $(libnss_db-dbs) db-open db-init hash-string
9bb5d6
 generated		+= $(filter-out db-alias.c db-netgrp.c, \
9bb5d6
 					$(addsuffix .c,$(libnss_db-dbs)))
9bb5d6
 
9bb5d6
+# See note above regarding nss_files_fopen.
9bb5d6
 libnss_compat-routines	:= $(addprefix compat-,grp pwd spwd initgroups) \
9bb5d6
-			   nisdomain
9bb5d6
+			   nisdomain \
9bb5d6
+			   nss_files_fopen_int
9bb5d6
 
9bb5d6
 install-others		+= $(inst_vardbdir)/Makefile
9bb5d6
 
9bb5d6
diff --git a/nss/nss_files_fopen_int.c b/nss/nss_files_fopen_int.c
9bb5d6
new file mode 100644
9bb5d6
index 0000000000000000..fa518084fd609b52
9bb5d6
--- /dev/null
9bb5d6
+++ b/nss/nss_files_fopen_int.c
9bb5d6
@@ -0,0 +1,3 @@
9bb5d6
+/* Include a local internal copy of __nss_files_fopen to make the NSS
9bb5d6
+   module self-contained.  */
9bb5d6
+#include <nss_files_fopen.c>
9bb5d6
diff --git a/nss/nss_parse_line_result_int.c b/nss/nss_parse_line_result_int.c
9bb5d6
new file mode 100644
9bb5d6
index 0000000000000000..bc0ee7a251743c9a
9bb5d6
--- /dev/null
9bb5d6
+++ b/nss/nss_parse_line_result_int.c
9bb5d6
@@ -0,0 +1,3 @@
9bb5d6
+/* Include a local internal copy of __nss_parse_line_result to make the
9bb5d6
+   NSS module self-contained.  */
9bb5d6
+#include <nss_parse_line_result.c>
9bb5d6
diff --git a/nss/nss_readline_int.c b/nss/nss_readline_int.c
9bb5d6
new file mode 100644
9bb5d6
index 0000000000000000..0e7bd259733673c9
9bb5d6
--- /dev/null
9bb5d6
+++ b/nss/nss_readline_int.c
9bb5d6
@@ -0,0 +1,3 @@
9bb5d6
+/* Include a local internal copy of __nss_readline and
9bb5d6
+   __nss_readline_seek to make the NSS module self-contained.  */
9bb5d6
+#include <nss_readline.c>