[PATCH 7/7] nss: Add new framework for hiding the ERANGE protocol internally
Florian Weimer
fweimer@redhat.com
Mon Nov 24 19:48:22 GMT 2025
And a new implementation of group member list merging that implements
member deduplication using a stringtable.
It is necessary to update nss/tst-nss-test4 to reflect the
deduplication of member lists.
---
include/set-freeres.h | 4 +-
malloc/set-freeres.c | 3 +-
nss/Makefile | 11 +++
nss/getgrgid.c | 14 ++-
nss/getgrgid_r.c | 20 ++---
nss/getgrnam.c | 14 ++-
nss/getgrnam_r.c | 23 +++--
nss/nss_generic.h | 71 +++++++++++++++
nss/nss_generic_get.c | 61 +++++++++++++
nss/nss_generic_get_r.c | 39 +++++++++
nss/nss_generic_lookup.c | 51 +++++++++++
nss/nss_generic_next.c | 60 +++++++++++++
nss/nss_generic_nscd.c | 62 ++++++++++++++
nss/nss_generic_storage.h | 29 +++++++
nss/nss_getX.c | 53 ++++++++++++
nss/nss_getX_r.c | 45 ++++++++++
nss/nss_getXinfo.c | 64 ++++++++++++++
nss/nss_getgrXinfo.c | 176 ++++++++++++++++++++++++++++++++++++++
nss/tst-nss-test4.c | 12 +--
19 files changed, 763 insertions(+), 49 deletions(-)
create mode 100644 nss/nss_generic_get.c
create mode 100644 nss/nss_generic_get_r.c
create mode 100644 nss/nss_generic_lookup.c
create mode 100644 nss/nss_generic_next.c
create mode 100644 nss/nss_generic_nscd.c
create mode 100644 nss/nss_generic_storage.h
create mode 100644 nss/nss_getX.c
create mode 100644 nss/nss_getX_r.c
create mode 100644 nss/nss_getXinfo.c
create mode 100644 nss/nss_getgrXinfo.c
diff --git a/include/set-freeres.h b/include/set-freeres.h
index 0fb25827ec..e64c4fd510 100644
--- a/include/set-freeres.h
+++ b/include/set-freeres.h
@@ -73,6 +73,8 @@ extern void __nss_module_freeres (void) attribute_hidden;
extern void __nss_action_freeres (void) attribute_hidden;
/* From nss/nss_database.c */
extern void __nss_database_freeres (void) attribute_hidden;
+/* From nss/nss-generic.h, */
+void __nss_generic_freeres (void) attribute_hidden;
/* From libio/genops.c */
extern int _IO_cleanup (void) attribute_hidden;;
/* From dlfcn/dlerror.c */
@@ -107,8 +109,6 @@ extern printf_arginfo_size_function ** __libc_reg_printf_freemem_ptr
extern printf_va_arg_function ** __libc_reg_type_freemem_ptr
attribute_hidden;
/* From nss/getXXbyYY.c */
-extern char * __libc_getgrgid_freemem_ptr attribute_hidden;
-extern char * __libc_getgrnam_freemem_ptr attribute_hidden;
extern char * __libc_getpwnam_freemem_ptr attribute_hidden;
extern char * __libc_getpwuid_freemem_ptr attribute_hidden;
extern char * __libc_getspnam_freemem_ptr attribute_hidden;
diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
index 2ac1df3c1c..740c726f30 100644
--- a/malloc/set-freeres.c
+++ b/malloc/set-freeres.c
@@ -131,6 +131,7 @@ __libc_freeres (void)
call_function_static_weak (__nss_module_freeres);
call_function_static_weak (__nss_action_freeres);
call_function_static_weak (__nss_database_freeres);
+ call_function_static_weak (__nss_generic_freeres);
_IO_cleanup ();
@@ -205,8 +206,6 @@ __libc_freeres (void)
call_free_static_weak (__libc_reg_printf_freemem_ptr);
call_free_static_weak (__libc_reg_type_freemem_ptr);
- call_free_static_weak (__libc_getgrgid_freemem_ptr);
- call_free_static_weak (__libc_getgrnam_freemem_ptr);
call_free_static_weak (__libc_getpwnam_freemem_ptr);
call_free_static_weak (__libc_getpwuid_freemem_ptr);
call_free_static_weak (__libc_getspnam_freemem_ptr);
diff --git a/nss/Makefile b/nss/Makefile
index 718c6f6b9d..53bc35719d 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -89,6 +89,17 @@ routines += \
getgrnam_r \
grp-merge \
initgroups \
+ nss_generic_copy \
+ nss_generic_dup \
+ nss_generic_get \
+ nss_generic_get_r \
+ nss_generic_lookup \
+ nss_generic_next \
+ nss_generic_nscd \
+ nss_getX \
+ nss_getX_r \
+ nss_getXinfo \
+ nss_getgrXinfo \
nss_group_members \
putgrent \
# routines
diff --git a/nss/getgrgid.c b/nss/getgrgid.c
index e81b3bee9f..918bb83c0a 100644
--- a/nss/getgrgid.c
+++ b/nss/getgrgid.c
@@ -17,12 +17,10 @@
#include <grp.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrgid
-#define DATABASE_NAME group
-#define ADD_PARAMS gid_t gid
-#define ADD_VARIABLES gid
-#define BUFLEN NSS_BUFLEN_GROUP
-
-#include "../nss/getXXbyYY.c"
+struct group *
+getgrgid (gid_t gid)
+{
+ return __nss_getX (nss_lookup_getgrgid, &gid);
+}
diff --git a/nss/getgrgid_r.c b/nss/getgrgid_r.c
index 12a8299749..72be20b07f 100644
--- a/nss/getgrgid_r.c
+++ b/nss/getgrgid_r.c
@@ -17,15 +17,13 @@
#include <grp.h>
-#include <grp-merge.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrgid
-#define DATABASE_NAME group
-#define ADD_PARAMS gid_t gid
-#define ADD_VARIABLES gid
-#define BUFLEN NSS_BUFLEN_GROUP
-#define DEEPCOPY_FN __copy_grp
-#define MERGE_FN __merge_grp
-
-#include <nss/getXXbyYY_r.c>
+int getgrgid_r (gid_t gid, struct group *grp,
+ char *buffer, size_t length, struct group **result)
+{
+ void *ptr = grp;
+ int ret = __nss_getX_r (nss_lookup_getgrgid, &gid, &ptr, buffer, length);
+ *result = ptr;
+ return ret;
+}
diff --git a/nss/getgrnam.c b/nss/getgrnam.c
index b24d564ea2..60f9e7472b 100644
--- a/nss/getgrnam.c
+++ b/nss/getgrnam.c
@@ -17,12 +17,10 @@
#include <grp.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrnam
-#define DATABASE_NAME group
-#define ADD_PARAMS const char *name
-#define ADD_VARIABLES name
-#define BUFLEN NSS_BUFLEN_GROUP
-
-#include "../nss/getXXbyYY.c"
+struct group *
+getgrnam (const char *name)
+{
+ return __nss_getX (nss_lookup_getgrnam, name);
+}
diff --git a/nss/getgrnam_r.c b/nss/getgrnam_r.c
index 69b088f9c4..a64fdda7b1 100644
--- a/nss/getgrnam_r.c
+++ b/nss/getgrnam_r.c
@@ -17,15 +17,14 @@
#include <grp.h>
-#include <grp-merge.h>
-
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrnam
-#define DATABASE_NAME group
-#define ADD_PARAMS const char *name
-#define ADD_VARIABLES name
-
-#define DEEPCOPY_FN __copy_grp
-#define MERGE_FN __merge_grp
-
-#include <nss/getXXbyYY_r.c>
+#include <nss_generic.h>
+
+int getgrnam_r(const char *name, struct group *grp,
+ char *buffer, size_t length, struct group **result)
+{
+ void *ptr = grp;
+ int ret = __nss_getX_r (nss_lookup_getgrnam, name,
+ &ptr, buffer, length);
+ *result = ptr;
+ return ret;
+}
diff --git a/nss/nss_generic.h b/nss/nss_generic.h
index 820169ca12..9fbe785cce 100644
--- a/nss/nss_generic.h
+++ b/nss/nss_generic.h
@@ -58,4 +58,75 @@ int __nss_generic_copy (enum nss_lookup_type lt, const void *source,
void *__nss_generic_dup (enum nss_lookup_type lt, const void *source)
attribute_hidden;
+/* Interpreted according to enum nss_lookup_type. Typically a string
+ or a pointer to an integer. */
+typedef const void *nss_lookup_key;
+
+/* Invokes the NSS module function SERVICE_FUNCTION of type LT using
+ KEY. The caller must initialization *RESULT to point to storage of
+ the appropriate NSS struct for LT. Upon return, the function
+ may write NULL to *RESULT and return with an error
+ (including triggering the ERANGE protocol), or leave *RESULT
+ unchanged and fill the struct with data, potentiallly using
+ LENGTH bytes at BUFFER for additional storage. */
+enum nss_status __nss_generic_get_r (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result,
+ char *buffer,
+ size_t length) attribute_hidden;
+
+/* Like __nss_generic_get_buffer, but handles the ERANGE protocol.
+ *RESULT is not read, but overwritten with a malloc-allocated
+ pointer or NULL. */
+enum nss_status __nss_generic_get (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result) attribute_hidden;
+
+_Bool __nss_generic_use_nscd (enum nss_lookup_type lt) attribute_hidden;
+int __nss_generic_get_nscd (enum nss_lookup_type, nss_lookup_key,
+ void **result) attribute_hidden;
+
+/* Returns the pointer to the first service lookup function for lookup
+ type LT, or NULL if there are service modules. Updates *NIP
+ accordingly. */
+void *__nss_generic_lookup (enum nss_lookup_type lt, nss_action_list *ni)
+ attribute_hidden;
+
+/* See __nss_next2 in <nsswitch.h>. The function names are selected
+ based on the type LT. */
+int __nss_generic_next (enum nss_lookup_type lt, nss_action_list *ni,
+ void *fctp, int status, int all_values)
+ attribute_hidden;
+
+/* Perform a group lookup with merging. On success, return zero and
+ write a malloc-allocated struct group pointer to *RESULT (positive
+ result) or NULL (negative result). On failure, return -1 and write
+ NULL to *RESULT, and set errno accordingly. */
+int __nss_getXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key, void **result) attribute_hidden;
+
+/* Like __nss_getXinfo, but LT must be nss_lookup_getgrgid or
+ nss_lookup_getgrnam. This function does not contact nscd and is
+ used in the implementation of __nss_getXInfo. */
+int __nss_getgrXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key, void **result) attribute_hidden;
+
+/* Implementation of the non-_r public interface. RESULT must be the
+ address of the global variable. It is freed before the lookup
+ starts. Returns NULL on failure, and a pointer to the NSS struct
+ appropriate for LT on success. Negative lookup returns NULL and
+ does not set errno. Other errors set errno. */
+void *__nss_getX (enum nss_lookup_type lt, nss_lookup_key key)
+ attribute_hidden;
+
+/* Implementation of the _r public interface. The caller must
+ initialize *RESULT with the address of the result structure passed
+ to the _r function. Return 0 on success and error code on
+ failure (including ERANGE). */
+int __nss_getX_r (enum nss_lookup_type lt, nss_lookup_key key,
+ void **result, char *buffer, size_t length)
+ attribute_hidden;
+
#endif /* NSS_GENERIC */
diff --git a/nss/nss_generic_get.c b/nss/nss_generic_get.c
new file mode 100644
index 0000000000..88dc99a143
--- /dev/null
+++ b/nss/nss_generic_get.c
@@ -0,0 +1,61 @@
+/* Implementation of the ERANGE protocol for service module functions.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_generic.h>
+#include <nss_generic_storage.h>
+#include <scratch_buffer.h>
+
+enum nss_status
+__nss_generic_get (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result)
+{
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+
+ *result = NULL;
+ while (true)
+ {
+ union nss_generic_storage storage;
+ void *ptr = &storage;
+ enum nss_status status = __nss_generic_get_r (lt, key,
+ service_function, &ptr,
+ buf.data, buf.length);
+ if (status == NSS_STATUS_TRYAGAIN && errno == ERANGE)
+ {
+ if (!scratch_buffer_grow (&buf))
+ return status;
+ }
+ else
+ {
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ if (ptr != NULL)
+ {
+ ptr = __nss_generic_dup (lt, ptr);
+ if (ptr == NULL)
+ status = NSS_STATUS_TRYAGAIN;
+ *result = ptr;
+ }
+ }
+ scratch_buffer_free (&buf);
+ return status;
+ }
+ }
+}
diff --git a/nss/nss_generic_get_r.c b/nss/nss_generic_get_r.c
new file mode 100644
index 0000000000..768a55f154
--- /dev/null
+++ b/nss/nss_generic_get_r.c
@@ -0,0 +1,39 @@
+/* Lookup-indendent call to an NSS service function.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_generic.h>
+
+enum nss_status
+__nss_generic_get_r (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void *service_function,
+ void **result,
+ char *buffer,
+ size_t length)
+{
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ return ((nss_getgrgid_r *) service_function)
+ (*(const gid_t *) key, *result, buffer, length, &errno);
+ case nss_lookup_getgrnam:
+ return ((nss_getgrnam_r *) service_function)
+ (key, *result, buffer, length, &errno);
+ }
+ __builtin_unreachable ();
+}
diff --git a/nss/nss_generic_lookup.c b/nss/nss_generic_lookup.c
new file mode 100644
index 0000000000..428144ddfd
--- /dev/null
+++ b/nss/nss_generic_lookup.c
@@ -0,0 +1,51 @@
+/* Type-generic lookup of the first NSS service module function.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <nss_generic.h>
+
+void *
+__nss_generic_lookup (enum nss_lookup_type lt, nss_action_list *ni)
+{
+ int database;
+ const char *fct_name;
+ const char *fct2_name = NULL;
+
+ switch (lt)
+ {
+#define DEFINE_LOOKUP(name, dbname, function) \
+ case nss_lookup_##name: \
+ database = NSS_DBSIDX_##dbname; \
+ fct_name = function; \
+ break;
+#include <nss-lookups.def>
+#undef DEFINE_LOOKUP
+ default:
+ abort ();
+ }
+
+ if (! __nss_database_get (database, ni))
+ return NULL;
+
+ assert (*ni != NULL);
+ void *fct;
+ if (__nss_lookup (ni, fct_name, fct2_name, &fct) == 0)
+ return fct;
+ else
+ return NULL;
+}
diff --git a/nss/nss_generic_next.c b/nss/nss_generic_next.c
new file mode 100644
index 0000000000..c0a6fb98c5
--- /dev/null
+++ b/nss/nss_generic_next.c
@@ -0,0 +1,60 @@
+/* Type-generic next NSS service module function lookup.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <nss_generic.h>
+
+int
+__nss_generic_next (enum nss_lookup_type lt, nss_action_list *ni,
+ void *fctp, int status, int all_values)
+{
+ /* Extract the database index and function name from <nss-lookups.def>. */
+ int database;
+ const char *fct_name;
+
+ switch (lt)
+ {
+#define DEFINE_LOOKUP(name, dbname, function) \
+ case nss_lookup_##name: \
+ database = NSS_DBSIDX_##dbname; \
+ fct_name = function; \
+ break;
+#include <nss-lookups.def>
+#undef DEFINE_LOOKUP
+ default:
+ abort ();
+ }
+
+ /* No secondary functions yet. */
+ const char *fct2_name = NULL;
+
+ int ret;
+ if (*ni == NULL)
+ {
+ /* First call. Ignore status and all_values. */
+ if (! __nss_database_get (database, ni))
+ return -1;
+
+ assert (*ni != NULL);
+ ret = __nss_lookup (ni, fct_name, fct2_name, fctp);
+ }
+ else
+ ret = __nss_next2 (ni, fct_name, fct2_name, fctp, status, all_values);
+
+ return ret;
+}
diff --git a/nss/nss_generic_nscd.c b/nss/nss_generic_nscd.c
new file mode 100644
index 0000000000..54306290e7
--- /dev/null
+++ b/nss/nss_generic_nscd.c
@@ -0,0 +1,62 @@
+/* Generic malloc-compatible version of nscd get functions.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nscd/nscd_proto.h>
+#include <nss_generic.h>
+#include <grp.h>
+
+_Bool
+__nss_generic_use_nscd (enum nss_lookup_type lt)
+{
+ switch (lt)
+ {
+#define DEFINE_LOOKUP(name, dbname, function) \
+ case nss_lookup_##name: \
+ return __nscd_use_database (NSS_DBSIDX_##dbname);
+#include <nss-lookups.def>
+#undef DEFINE_LOOKUP
+ }
+ __builtin_unreachable ();
+}
+
+int
+__nss_generic_get_nscd (enum nss_lookup_type lt, nss_lookup_key key,
+ void **result)
+{
+ if (!__nss_generic_use_nscd (lt))
+ return -1; /* Use NSS modules. */
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ {
+ struct group *ptr;
+ int ret = __nscd_getgrgid (*(const gid_t *) key, &ptr);
+ *result = ptr;
+ return ret;
+ }
+ case nss_lookup_getgrnam:
+ {
+ struct group *ptr;
+ int ret = __nscd_getgrnam (key, &ptr);
+ *result = ptr;
+ return ret;
+ }
+ }
+
+ __builtin_unreachable ();
+}
diff --git a/nss/nss_generic_storage.h b/nss/nss_generic_storage.h
new file mode 100644
index 0000000000..f91cef36e5
--- /dev/null
+++ b/nss/nss_generic_storage.h
@@ -0,0 +1,29 @@
+/* Type that can hold all NSS structs.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef NSS_GENERIC_STORAGE
+#define NSS_GENERIC_STORAGE
+
+#include <grp.h>
+
+union nss_generic_storage
+{
+ struct group grp;
+};
+
+#endif /* NSS_GENERIC_STORAGE */
diff --git a/nss/nss_getX.c b/nss/nss_getX.c
new file mode 100644
index 0000000000..4727400e31
--- /dev/null
+++ b/nss/nss_getX.c
@@ -0,0 +1,53 @@
+/* Type-generic implementation of public non-_r NSS legacy functions.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <libc-lock.h>
+#include <nss_generic.h>
+#include <set-freeres.h>
+#include <stdlib.h>
+
+static void *__nss_generic_allocation[nss_lookup_MAX];
+static __libc_lock_t __nss_generic_lock[nss_lookup_MAX];
+
+void *
+__nss_getX (enum nss_lookup_type lt, nss_lookup_key key)
+{
+ __libc_lock_lock (__nss_generic_lock[lt]);
+ free (__nss_generic_allocation[lt]);
+ __nss_generic_allocation[lt] = NULL;
+
+ /* Preserve errno (possibly 0) if ret == 0. */
+ int saved_errno = errno;
+ void *result;
+ int ret = __nss_getXinfo (lt, key, &result);
+ __nss_generic_allocation[lt] = result;
+
+ __libc_lock_unlock (__nss_generic_lock[lt]);
+ if (ret == 0)
+ __set_errno (saved_errno);
+
+ return result;
+}
+
+void
+__nss_generic_freeres (void)
+{
+ for (int i = 0; i < array_length (__nss_generic_allocation); ++i)
+ free (__nss_generic_allocation[i]);
+}
diff --git a/nss/nss_getX_r.c b/nss/nss_getX_r.c
new file mode 100644
index 0000000000..2de2975cae
--- /dev/null
+++ b/nss/nss_getX_r.c
@@ -0,0 +1,45 @@
+/* Type-generic implementation of public NSS get*_r functions.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <nss_generic.h>
+#include <stdlib.h>
+
+int
+__nss_getX_r (enum nss_lookup_type lt, nss_lookup_key key,
+ void **result, char *buffer, size_t length)
+{
+ void *storage = *result;
+ *result = NULL;
+
+ void *allocated;
+ int ret = __nss_getXinfo (lt, key, &allocated);
+ if (ret == 0)
+ {
+ if (allocated != NULL)
+ {
+ ret = __nss_generic_copy (lt, allocated, storage, buffer, length);
+ if (ret == 0)
+ *result = storage;
+ free (allocated);
+ }
+ return ret;
+ }
+ else
+ return errno;
+}
diff --git a/nss/nss_getXinfo.c b/nss/nss_getXinfo.c
new file mode 100644
index 0000000000..9ba0401b8b
--- /dev/null
+++ b/nss/nss_getXinfo.c
@@ -0,0 +1,64 @@
+/* Generic malloc-compatible version of NSS get functions.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_generic.h>
+
+int
+__nss_getXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key, void **result)
+{
+#if USE_NSCD
+ if (__nss_generic_use_nscd (lt)
+ && __nss_generic_get_nscd (lt, key, result) == 0)
+ return 0;
+#endif
+
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ case nss_lookup_getgrnam:
+ /* Group lookups are handled separately to implement merging. */
+ return __nss_getgrXinfo (lt, key, result);
+ default:
+ break;
+ }
+
+ nss_action_list nip = NULL;
+ void *fct;
+ int no_more = __nss_generic_next (lt, &nip, &fct, 0, 0);
+ enum nss_status status = NSS_STATUS_UNAVAIL;
+
+ *result = NULL;
+ while (no_more == 0)
+ {
+ void *ptr;
+ status = __nss_generic_get (lt, key, fct, &ptr);
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ free (*result);
+ *result = ptr;
+ }
+
+ no_more = __nss_generic_next (lt, &nip, &fct, status, 0);
+ }
+
+ if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
+ return 0;
+ else
+ return errno;
+}
diff --git a/nss/nss_getgrXinfo.c b/nss/nss_getgrXinfo.c
new file mode 100644
index 0000000000..6bac0d64e6
--- /dev/null
+++ b/nss/nss_getgrXinfo.c
@@ -0,0 +1,176 @@
+/* Implementation of the getgrXinfo family of functions.
+ Copyright (C) 1996-2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <errno.h>
+#include <grp.h>
+#include <nss_generic.h>
+#include <nss.h>
+#include <nss_group_members.h>
+#include <nsswitch.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stringtable.h>
+
+static inline bool
+__nss_status_has_result (enum nss_status status)
+{
+ return status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND;
+}
+
+static int
+__nss_status_to_posix_result (enum nss_status status)
+{
+ /* Treat NSS_STATUS_UNAVAIL like NSS_STATUS_NOTFOUND. Some NSS
+ modules return NSS_STATUS_UNAVAIL if they have disabled
+ themselves. */
+ if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND
+ || NSS_STATUS_UNAVAIL)
+ return 0;
+ else
+ {
+ /* Paranoia, to avoid endless loops. */
+ if (errno == ERANGE)
+ __set_errno (EINVAL);
+ return -1;
+ }
+}
+
+int
+__nss_getgrXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void **result)
+{
+ /* Default if no service modules are available. */
+ enum nss_status status = NSS_STATUS_UNAVAIL;
+ *result = NULL;
+
+ /* For merging. If table is not empty, then it supersedes the group
+ members in first_result. */
+ struct group *first_result = NULL;
+ struct nss_group_members table = { };
+ __nss_group_members_init (&table);
+
+ /* With better error reporting (especially from dlopen), we could
+ check for NSS initialization errors here and report them. */
+ nss_action_list nip;
+ void *fct = __nss_generic_lookup (lt, &nip);
+
+ int no_more = fct == NULL;
+ bool do_merge = false;
+ while (no_more == 0)
+ {
+ void *ptr;
+ enum nss_status status = __nss_generic_get (lt, key, fct, &ptr);
+ struct group *grp = ptr;
+
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ assert (grp != NULL);
+ /* We have result data. It may need to be merged. We only
+ support merging members of groups with identical names
+ and GID values. If we hit this, the grp result overrides
+ the first result. */
+ if (do_merge && first_result != NULL
+ && grp->gr_gid == first_result->gr_gid
+ && strcmp (grp->gr_name, first_result->gr_name) == 0)
+ {
+ /* Perform the merge. If no members have been merged yet,
+ process first_result as well. */
+ if ((table.T.count == 0
+ && !__nss_group_members_add (&table, first_result))
+ || !__nss_group_members_add (&table, grp))
+ {
+ __nss_group_members_free (&table);
+ free (grp);
+ free (first_result);
+ return -1;
+ }
+ free (grp);
+ }
+ else
+ {
+ /* No merge or different data. New result replaces
+ previous result. */
+ free (first_result);
+ first_result = grp;
+ __nss_group_members_free (&table);
+ }
+ }
+ else if (status == NSS_STATUS_TRYAGAIN)
+ {
+ free (first_result);
+ __nss_group_members_free (&table);
+ return -1;
+ }
+ else if (first_result != NULL)
+ {
+ /* If the result wasn't SUCCESS, use the stored data in
+ first_result/table and set the status back to
+ NSS_STATUS_SUCCESS to match the previous pass through
+ the loop.
+
+ * If the next action is CONTINUE, it will overwrite the value
+ currently in the buffer and return the new value.
+ * If the next action is RETURN, we'll return the previously-
+ acquired values.
+ * If the next action is MERGE, then it will be added to the
+ buffer saved from the previous source. */
+ status = NSS_STATUS_SUCCESS;
+ }
+
+ do_merge = (nss_next_action (nip, status) == NSS_ACTION_MERGE
+ && status == NSS_STATUS_SUCCESS);
+ no_more = __nss_generic_next (lt, &nip, &fct, status, 0);
+ }
+
+
+ if (table.T.count > 0)
+ {
+ /* We have something to merge. */
+ struct group *merged_allocated;
+ {
+ struct group merged = *first_result;
+ merged.gr_mem = __nss_group_members (&table);
+ if (merged.gr_mem == NULL)
+ {
+ __nss_group_members_free (&table);
+ free (first_result);
+ return -1;
+ }
+
+ /* Make a consolidated copy of the entire group information. */
+ merged_allocated = __nss_generic_dup (lt, &merged);
+
+ free (merged.gr_mem);
+ }
+
+ __nss_group_members_free (&table);
+ free (first_result);
+ first_result = merged_allocated;
+
+ if (first_result == NULL)
+ return -1;
+ }
+ else
+ /* No merging necessary. We can use first_result. */
+ __nss_group_members_free (&table);
+
+ *result = first_result;
+ return __nss_status_to_posix_result (status);
+}
diff --git a/nss/tst-nss-test4.c b/nss/tst-nss-test4.c
index 3d94c12d08..c2c0d3f5a1 100644
--- a/nss/tst-nss-test4.c
+++ b/nss/tst-nss-test4.c
@@ -100,9 +100,7 @@ do_test (void)
/* At least 3 service modules are needed to reproduce BZ#33361. */
__nss_configure_lookup ("group", "test1 [SUCCESS=merge] test2 files");
- /* Test increasing sizes of group_2 to see if we fail, starting with
- member_cnt == 1 to ensure we always check for no de-duplication
- e.g. { "foo", NULL } */
+ /* Test increasing sizes of group_2 to see if we fail. */
for (member_cnt = 1; member_cnt < array_length (group_2); member_cnt++)
{
verbose_printf ("Outer loop - member_cnt is %d\n", member_cnt);
@@ -127,15 +125,17 @@ do_test (void)
verbose_printf ("MERGED LIST of [%d] is %s\n", i, merge_1[i]);
}
- /* Add group_2 to the merge list */
- int group2_index = 0;
+ /* Add group_2 to the merge list. Skip the duplicate "foo"
+ group at the start. */
+ int group2_index = 1;
for (i = array_length (group_1) - 1;
i < array_length (group_1) - 1 + member_cnt; i++)
{
merge_1[i] = xasprintf ("%s", group_2[group2_index++]);
verbose_printf ("MERGED LIST of [%d] is %s\n", i, merge_1[i]);
}
- merge_1[array_length(group_1) - 1 + member_cnt]= NULL;
+ /* Skipping the "foo" group reduced the member count by 1. */
+ merge_1[array_length(group_1) - 1 + member_cnt - 1]= NULL;
align_mask = __alignof__ (struct group) - 1;
align_mem_mask = __alignof__ (char *) - 1;
--
2.52.0
More information about the Libc-alpha
mailing list