Compare commits

...

No commits in common. "imports/c8-beta/glibc-2.28-236.el8_9.1" and "c8" have entirely different histories.

21 changed files with 2826 additions and 1124 deletions

View File

@ -0,0 +1,112 @@
commit 849274d48fc59bfa6db3c713c8ced8026b20f3b7
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Nov 16 19:55:35 2023 +0100
elf: Fix force_first handling in dlclose (bug 30981)
The force_first parameter was ineffective because the dlclose'd
object was not necessarily the first in the maps array. Also
enable force_first handling unconditionally, regardless of namespace.
The initial object in a namespace should be destructed first, too.
The _dl_sort_maps_dfs function had early returns for relocation
dependency processing which broke force_first handling, too, and
this is fixed in this change as well.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 22225efb3226c3e1..16a39f5bf17b440f 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -182,6 +182,16 @@ _dl_close_worker (struct link_map *map, bool force)
}
assert (idx == nloaded);
+ /* Put the dlclose'd map first, so that its destructor runs first.
+ The map variable is NULL after a retry. */
+ if (map != NULL)
+ {
+ maps[map->l_idx] = maps[0];
+ maps[map->l_idx]->l_idx = map->l_idx;
+ maps[0] = map;
+ maps[0]->l_idx = 0;
+ }
+
/* Keep track of the lowest index link map we have covered already. */
int done_index = -1;
while (++done_index < nloaded)
@@ -255,9 +265,10 @@ _dl_close_worker (struct link_map *map, bool force)
}
}
- /* Sort the entries. We can skip looking for the binary itself which is
- at the front of the search list for the main namespace. */
- _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE), true);
+ /* Sort the entries. Unless retrying, the maps[0] object (the
+ original argument to dlclose) needs to remain first, so that its
+ destructor runs first. */
+ _dl_sort_maps (maps, nloaded, /* force_first */ map != NULL, true);
/* Call all termination functions at once. */
bool unload_any = false;
@@ -768,7 +779,11 @@ _dl_close_worker (struct link_map *map, bool force)
/* Recheck if we need to retry, release the lock. */
out:
if (dl_close_state == rerun)
- goto retry;
+ {
+ /* The map may have been deallocated. */
+ map = NULL;
+ goto retry;
+ }
dl_close_state = not_pending;
}
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index aeb79b40b45054c0..c17ac325eca658ef 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -260,13 +260,12 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
The below memcpy is not needed in the do_reldeps case here,
since we wrote back to maps[] during DFS traversal. */
if (maps_head == maps)
- return;
+ break;
}
assert (maps_head == maps);
- return;
}
-
- memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
+ else
+ memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
/* Skipping the first object at maps[0] is not valid in general,
since traversing along object dependency-links may "find" that
diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
index 4bf9052db16fb352..cf6453e9eb85ac65 100644
--- a/elf/dso-sort-tests-1.def
+++ b/elf/dso-sort-tests-1.def
@@ -56,14 +56,16 @@ output: b>a>{}<a<b
# relocation(dynamic) dependencies. While this is technically unspecified, the
# presumed reasonable practical behavior is for the destructor order to respect
# the static DT_NEEDED links (here this means the a->b->c->d order).
-# The older dynamic_sort=1 algorithm does not achieve this, while the DFS-based
-# dynamic_sort=2 algorithm does, although it is still arguable whether going
-# beyond spec to do this is the right thing to do.
+# The older dynamic_sort=1 algorithm originally did not achieve this,
+# but this was a bug in the way _dl_sort_maps was called from _dl_close_worker,
+# effectively disabling proper force_first handling.
+# The new dynamic_sort=2 algorithm shows the effect of the simpler force_first
+# handling: the a object is simply moved to the front.
# The below expected outputs are what the two algorithms currently produce
# respectively, for regression testing purposes.
tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
-output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
-output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
+output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<b<c<d<g<f<e];}
+output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<g<f<b<c<d<e];}
# Test that even in the presence of dependency loops involving dlopen'ed
# object, that object is initialized last (and not unloaded prematurely).

View File

@ -0,0 +1,35 @@
commit b893410be304ddcea0bd43f537a13e8b18d37cf2
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Nov 27 11:28:07 2023 +0100
elf: In _dl_relocate_object, skip processing if object is relocated
This is just a minor optimization. It also makes it more obvious that
_dl_relocate_object can be called multiple times.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 7a84b1fa8c3a7fdd..a80a54fb013adab5 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -165,6 +165,9 @@ void
_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
int reloc_mode, int consider_profiling)
{
+ if (l->l_relocated)
+ return;
+
struct textrels
{
caddr_t start;
@@ -202,9 +205,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
# define consider_symbind 0
#endif
- if (l->l_relocated)
- return;
-
/* If DT_BIND_NOW is set relocate all references in this object. We
do not do this if we are profiling, of course. */
// XXX Correct for auditing?

View File

@ -0,0 +1,121 @@
commit a74c2e1cbc8673dd7e97aae2f2705392e2ccc3f6
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Nov 27 11:28:10 2023 +0100
elf: Introduce the _dl_open_relocate_one_object function
It is extracted from dl_open_worker_begin.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/elf/dl-open.c b/elf/dl-open.c
index e82e53ff8b38fa11..1505fdb73088dcdb 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -466,6 +466,50 @@ activate_nodelete (struct link_map *new)
}
}
+/* Relocate the object L. *RELOCATION_IN_PROGRESS controls whether
+ the debugger is notified of the start of relocation processing. */
+static void
+_dl_open_relocate_one_object (struct dl_open_args *args, struct r_debug *r,
+ struct link_map *l, int reloc_mode,
+ bool *relocation_in_progress)
+{
+ if (l->l_real->l_relocated)
+ return;
+
+ if (!*relocation_in_progress)
+ {
+ /* Notify the debugger that relocations are about to happen. */
+ LIBC_PROBE (reloc_start, 2, args->nsid, r);
+ *relocation_in_progress = true;
+ }
+
+#ifdef SHARED
+ if (__glibc_unlikely (GLRO(dl_profile) != NULL))
+ {
+ /* If this here is the shared object which we want to profile
+ make sure the profile is started. We can find out whether
+ this is necessary or not by observing the `_dl_profile_map'
+ variable. If it was NULL but is not NULL afterwards we must
+ start the profiling. */
+ struct link_map *old_profile_map = GL(dl_profile_map);
+
+ _dl_relocate_object (l, l->l_scope, reloc_mode | RTLD_LAZY, 1);
+
+ if (old_profile_map == NULL && GL(dl_profile_map) != NULL)
+ {
+ /* We must prepare the profiling. */
+ _dl_start_profile ();
+
+ /* Prevent unloading the object. */
+ GL(dl_profile_map)->l_nodelete_active = true;
+ }
+ }
+ else
+#endif
+ _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
+}
+
+
/* struct dl_init_args and call_dl_init are used to call _dl_init with
exception handling disabled. */
struct dl_init_args
@@ -638,7 +682,7 @@ dl_open_worker_begin (void *a)
}
while (l != NULL);
- int relocation_in_progress = 0;
+ bool relocation_in_progress = false;
/* Perform relocation. This can trigger lazy binding in IFUNC
resolvers. For NODELETE mappings, these dependencies are not
@@ -649,44 +693,8 @@ dl_open_worker_begin (void *a)
are undefined anyway, so this is not a problem. */
for (unsigned int i = last; i-- > first; )
- {
- l = new->l_initfini[i];
-
- if (l->l_real->l_relocated)
- continue;
-
- if (! relocation_in_progress)
- {
- /* Notify the debugger that relocations are about to happen. */
- LIBC_PROBE (reloc_start, 2, args->nsid, r);
- relocation_in_progress = 1;
- }
-
-#ifdef SHARED
- if (__glibc_unlikely (GLRO(dl_profile) != NULL))
- {
- /* If this here is the shared object which we want to profile
- make sure the profile is started. We can find out whether
- this is necessary or not by observing the `_dl_profile_map'
- variable. If it was NULL but is not NULL afterwards we must
- start the profiling. */
- struct link_map *old_profile_map = GL(dl_profile_map);
-
- _dl_relocate_object (l, l->l_scope, reloc_mode | RTLD_LAZY, 1);
-
- if (old_profile_map == NULL && GL(dl_profile_map) != NULL)
- {
- /* We must prepare the profiling. */
- _dl_start_profile ();
-
- /* Prevent unloading the object. */
- GL(dl_profile_map)->l_nodelete_active = true;
- }
- }
- else
-#endif
- _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
- }
+ _dl_open_relocate_one_object (args, r, new->l_initfini[i], reloc_mode,
+ &relocation_in_progress);
/* This only performs the memory allocations. The actual update of
the scopes happens below, after failure is impossible. */

View File

@ -0,0 +1,223 @@
commit 78ca44da0160a0b442f0ca1f253e3360f044b2ec
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Nov 27 11:28:13 2023 +0100
elf: Relocate libc.so early during startup and dlmopen (bug 31083)
This makes it more likely that objects without dependencies can
use IFUNC resolvers in libc.so.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
elf/Makefile
(differences in test backports)
elf/rtld.c
(removal of prelink support upstream)
diff --git a/elf/Makefile b/elf/Makefile
index 634c3113227d64a6..6f0f36cdfe3961e8 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -386,6 +386,8 @@ tests += \
tst-nodelete2 \
tst-nodelete-dlclose \
tst-nodelete-opened \
+ tst-nodeps1 \
+ tst-nodeps2 \
tst-noload \
tst-null-argv \
tst-relsort1 \
@@ -740,6 +742,8 @@ modules-names = \
tst-nodelete-dlclose-dso \
tst-nodelete-dlclose-plugin \
tst-nodelete-opened-lib \
+ tst-nodeps1-mod \
+ tst-nodeps2-mod \
tst-null-argv-lib \
tst-relsort1mod1 \
tst-relsort1mod2 \
@@ -886,8 +890,13 @@ modules-execstack-yes = tst-execstack-mod
extra-test-objs += $(addsuffix .os,$(strip $(modules-names)))
# filtmod1.so has a special rule
-modules-names-nobuild := filtmod1 \
- tst-audit24bmod1 tst-audit24bmod2
+modules-names-nobuild += \
+ filtmod1 \
+ tst-audit24bmod1 \
+ tst-audit24bmod2 \
+ tst-nodeps1-mod \
+ tst-nodeps2-mod \
+ # modules-names-nobuild
tests += $(tests-static)
@@ -2697,3 +2706,19 @@ $(objpfx)tst-dlmopen-twice: $(libdl)
$(objpfx)tst-dlmopen-twice.out: \
$(objpfx)tst-dlmopen-twice-mod1.so \
$(objpfx)tst-dlmopen-twice-mod2.so
+
+# The object tst-nodeps1-mod.so has no explicit dependencies on libc.so.
+$(objpfx)tst-nodeps1-mod.so: $(objpfx)tst-nodeps1-mod.os
+ $(LINK.o) -nostartfiles -nostdlib -shared -o $@ $^
+tst-nodeps1.so-no-z-defs = yes
+# Link libc.so before the test module with the IFUNC resolver reference.
+LDFLAGS-tst-nodeps1 = $(common-objpfx)libc.so $(objpfx)tst-nodeps1-mod.so
+$(objpfx)tst-nodeps1: $(objpfx)tst-nodeps1-mod.so
+# Reuse the tst-nodeps1 module. Link libc.so before the test module
+# with the IFUNC resolver reference.
+$(objpfx)tst-nodeps2-mod.so: $(common-objpfx)libc.so \
+ $(objpfx)tst-nodeps1-mod.so $(objpfx)tst-nodeps2-mod.os
+ $(LINK.o) -Wl,--no-as-needed -nostartfiles -nostdlib -shared -o $@ $^
+$(objpfx)tst-nodeps2: $(libdl)
+$(objpfx)tst-nodeps2.out: \
+ $(objpfx)tst-nodeps1-mod.so $(objpfx)tst-nodeps2-mod.so
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 1505fdb73088dcdb..6508b0ea545440b8 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -692,6 +692,17 @@ dl_open_worker_begin (void *a)
them. However, such relocation dependencies in IFUNC resolvers
are undefined anyway, so this is not a problem. */
+ /* Ensure that libc is relocated first. This helps with the
+ execution of IFUNC resolvers in libc, and matters only to newly
+ created dlmopen namespaces. Do not do this for static dlopen
+ because libc has relocations against ld.so, which may not have
+ been relocated at this point. */
+#ifdef SHARED
+ if (GL(dl_ns)[args->nsid].libc_map != NULL)
+ _dl_open_relocate_one_object (args, r, GL(dl_ns)[args->nsid].libc_map,
+ reloc_mode, &relocation_in_progress);
+#endif
+
for (unsigned int i = last; i-- > first; )
_dl_open_relocate_one_object (args, r, new->l_initfini[i], reloc_mode,
&relocation_in_progress);
diff --git a/elf/rtld.c b/elf/rtld.c
index cd2cc4024a3581c2..502d2a1c58505d88 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2414,11 +2414,17 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
objects. We do not re-relocate the dynamic linker itself in this
loop because that could result in the GOT entries for functions we
call being changed, and that would break us. It is safe to relocate
- the dynamic linker out of order because it has no copy relocs (we
- know that because it is self-contained). */
+ the dynamic linker out of order because it has no copy relocations.
+ Likewise for libc, which is relocated early to ensure that IFUNC
+ resolvers in libc work. */
int consider_profiling = GLRO(dl_profile) != NULL;
+ if (GL(dl_ns)[LM_ID_BASE].libc_map != NULL)
+ _dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map,
+ GL(dl_ns)[LM_ID_BASE].libc_map->l_scope,
+ GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling);
+
/* If we are profiling we also must do lazy reloaction. */
GLRO(dl_lazy) |= consider_profiling;
diff --git a/elf/tst-nodeps1-mod.c b/elf/tst-nodeps1-mod.c
new file mode 100644
index 0000000000000000..45c8e3c631251a89
--- /dev/null
+++ b/elf/tst-nodeps1-mod.c
@@ -0,0 +1,25 @@
+/* Test module with no libc.so dependency and string function references.
+ Copyright (C) 2023 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 <string.h>
+
+/* Some references to libc symbols which are likely to have IFUNC
+ resolvers. If they do not, this module does not exercise bug 31083. */
+void *memcpy_pointer = memcpy;
+void *memmove_pointer = memmove;
+void *memset_pointer = memset;
diff --git a/elf/tst-nodeps1.c b/elf/tst-nodeps1.c
new file mode 100644
index 0000000000000000..1a8bde36cdb71446
--- /dev/null
+++ b/elf/tst-nodeps1.c
@@ -0,0 +1,23 @@
+/* Test initially loaded module with implicit libc.so dependency (bug 31083).
+ Copyright (C) 2023 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/>. */
+
+/* Testing happens before main. */
+int
+main (void)
+{
+}
diff --git a/elf/tst-nodeps2-mod.c b/elf/tst-nodeps2-mod.c
new file mode 100644
index 0000000000000000..4913feee9b56e0e1
--- /dev/null
+++ b/elf/tst-nodeps2-mod.c
@@ -0,0 +1 @@
+/* Empty test module which depends on tst-nodeps1-mod.so. */
diff --git a/elf/tst-nodeps2.c b/elf/tst-nodeps2.c
new file mode 100644
index 0000000000000000..0bdc8eeb8cba3a99
--- /dev/null
+++ b/elf/tst-nodeps2.c
@@ -0,0 +1,29 @@
+/* Test dlmopen with implicit libc.so dependency (bug 31083).
+ Copyright (C) 2023 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 <support/xdlfcn.h>
+
+static int
+do_test (void)
+{
+ void *handle = xdlmopen (LM_ID_NEWLM, "tst-nodeps2-mod.so", RTLD_NOW);
+ xdlclose (handle);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,41 @@
commit b3bee76c5f59498b9c189608f0a3132e2013fa1a
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Dec 8 09:51:34 2023 +0100
elf: Initialize GLRO(dl_lazy) before relocating libc in dynamic startup
GLRO(dl_lazy) is used to set the parameters for the early
_dl_relocate_object call, so the consider_profiling setting has to
be applied before the call.
Fixes commit 78ca44da0160a0b442f0ca1f253e3360f044b2ec ("elf: Relocate
libc.so early during startup and dlmopen (bug 31083)").
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
elf/rtld.c
(removal of prelink support upstream)
diff --git a/elf/rtld.c b/elf/rtld.c
index 502d2a1c58505d88..4f317a2a874e6af7 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2420,14 +2420,14 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
int consider_profiling = GLRO(dl_profile) != NULL;
+ /* If we are profiling we also must do lazy reloaction. */
+ GLRO(dl_lazy) |= consider_profiling;
+
if (GL(dl_ns)[LM_ID_BASE].libc_map != NULL)
_dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map,
GL(dl_ns)[LM_ID_BASE].libc_map->l_scope,
GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling);
- /* If we are profiling we also must do lazy reloaction. */
- GLRO(dl_lazy) |= consider_profiling;
-
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);
unsigned i = main_map->l_searchlist.r_nlist;

View File

@ -0,0 +1,83 @@
commit c00b984fcd53f679ca2dafcd1aee2c89836e6e73
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 29 08:28:31 2023 +0200
nscd: Skip unusable entries in first pass in prune_cache (bug 30800)
Previously, if an entry was marked unusable for any reason, but had
not timed out yet, the assert would trigger.
One way to get into such state is if a data change is detected during
re-validation of an entry. This causes the entry to be marked as not
usable. If exits nscd soon after that, then the clock jumps
backwards, and nscd restarted, the cache re-validation run after
startup triggers the removed assert.
The change is more complicated than just the removal of the assert
because entries marked as not usable should be garbage-collected in
the second pass. To make this happen, it is necessary to update some
book-keeping data.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/nscd/cache.c b/nscd/cache.c
index efe4214d953edb30..2fd3f78ebb567bbe 100644
--- a/nscd/cache.c
+++ b/nscd/cache.c
@@ -371,8 +371,11 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
serv2str[runp->type], str, dh->timeout);
}
- /* Check whether the entry timed out. */
- if (dh->timeout < now)
+ /* Check whether the entry timed out. Timed out entries
+ will be revalidated. For unusable records, it is still
+ necessary to record that the bucket needs to be scanned
+ again below. */
+ if (dh->timeout < now || !dh->usable)
{
/* This hash bucket could contain entries which need to
be looked at. */
@@ -384,7 +387,7 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
/* We only have to look at the data of the first entries
since the count information is kept in the data part
which is shared. */
- if (runp->first)
+ if (runp->first && dh->usable)
{
/* At this point there are two choices: we reload the
@@ -400,9 +403,6 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
{
/* Remove the value. */
dh->usable = false;
-
- /* We definitely have some garbage entries now. */
- any = true;
}
else
{
@@ -414,18 +414,15 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
time_t timeout = readdfcts[runp->type] (table, runp, dh);
next_timeout = MIN (next_timeout, timeout);
-
- /* If the entry has been replaced, we might need
- cleanup. */
- any |= !dh->usable;
}
}
+
+ /* If the entry has been replaced, we might need cleanup. */
+ any |= !dh->usable;
}
else
- {
- assert (dh->usable);
- next_timeout = MIN (next_timeout, dh->timeout);
- }
+ /* Entry has not timed out and is usable. */
+ next_timeout = MIN (next_timeout, dh->timeout);
run = runp->next;
}

View File

@ -0,0 +1,237 @@
commit d0f07f7df8d9758c838674b70144ac73bcbd1634
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue May 30 13:25:50 2023 +0200
elf: Make more functions available for binding during dlclose (bug 30425)
Previously, after destructors for a DSO have been invoked, ld.so refused
to bind against that DSO in all cases. Relax this restriction somewhat
if the referencing object is itself a DSO that is being unloaded. This
assumes that the symbol reference is not going to be stored anywhere.
The situation in the test case can arise fairly easily with C++ and
objects that are built with different optimization levels and therefore
define different functions with vague linkage.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
elf/Makefile
(usual test differences, link test with -ldl)
diff --git a/elf/Makefile b/elf/Makefile
index 6f0f36cdfe3961e8..ebf46a297d241d8f 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -362,6 +362,7 @@ tests += \
tst-big-note \
tst-debug1 \
tst-deep1 \
+ tst-dlclose-lazy \
tst-dlmodcount \
tst-dlmopen1 \
tst-dlmopen3 \
@@ -711,6 +712,8 @@ modules-names = \
tst-deep1mod2 \
tst-deep1mod3 \
tst-dlmopen1mod \
+ tst-dlclose-lazy-mod1 \
+ tst-dlclose-lazy-mod2 \
tst-dlmopen-dlerror-mod \
tst-dlmopen-gethostbyname-mod \
tst-dlmopen-twice-mod1 \
@@ -2707,6 +2710,12 @@ $(objpfx)tst-dlmopen-twice.out: \
$(objpfx)tst-dlmopen-twice-mod1.so \
$(objpfx)tst-dlmopen-twice-mod2.so
+LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed
+$(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
+$(objpfx)tst-dlclose-lazy: $(libdl)
+$(objpfx)tst-dlclose-lazy.out: \
+ $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
+
# The object tst-nodeps1-mod.so has no explicit dependencies on libc.so.
$(objpfx)tst-nodeps1-mod.so: $(objpfx)tst-nodeps1-mod.os
$(LINK.o) -nostartfiles -nostdlib -shared -o $@ $^
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 47acd134600b44b5..9e8f14b8483f5eba 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -380,8 +380,25 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable)
continue;
- /* Do not look into objects which are going to be removed. */
- if (map->l_removed)
+ /* Do not look into objects which are going to be removed,
+ except when the referencing object itself is being removed.
+
+ The second part covers the situation when an object lazily
+ binds to another object while running its destructor, but the
+ destructor of the other object has already run, so that
+ dlclose has set l_removed. It may not always be obvious how
+ to avoid such a scenario to programmers creating DSOs,
+ particularly if C++ vague linkage is involved and triggers
+ symbol interposition.
+
+ Accepting these to-be-removed objects makes the lazy and
+ BIND_NOW cases more similar. (With BIND_NOW, the symbol is
+ resolved early, before the destructor call, so the issue does
+ not arise.). Behavior matches the constructor scenario: the
+ implementation allows binding to symbols of objects whose
+ constructors have not run. In fact, not doing this would be
+ mostly incompatible with symbol interposition. */
+ if (map->l_removed && !(undef_map != NULL && undef_map->l_removed))
continue;
/* Print some debugging info if wanted. */
diff --git a/elf/tst-dlclose-lazy-mod1.c b/elf/tst-dlclose-lazy-mod1.c
new file mode 100644
index 0000000000000000..8439dc1925cc8b41
--- /dev/null
+++ b/elf/tst-dlclose-lazy-mod1.c
@@ -0,0 +1,36 @@
+/* Lazy binding during dlclose. Directly loaded module.
+ Copyright (C) 2023 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/>. */
+
+/* This function is called from exported_function below. It is only
+ defined in this module. The weak attribute mimics how G++
+ implements vague linkage for C++. */
+void __attribute__ ((weak))
+lazily_bound_exported_function (void)
+{
+}
+
+/* Called from tst-dlclose-lazy-mod2.so. */
+void
+exported_function (int call_it)
+{
+ if (call_it)
+ /* Previous to the fix this would crash when called during dlclose
+ since symbols from the DSO were no longer available for binding
+ (bug 30425) after the DSO started being closed by dlclose. */
+ lazily_bound_exported_function ();
+}
diff --git a/elf/tst-dlclose-lazy-mod2.c b/elf/tst-dlclose-lazy-mod2.c
new file mode 100644
index 0000000000000000..767f69ffdb23a685
--- /dev/null
+++ b/elf/tst-dlclose-lazy-mod2.c
@@ -0,0 +1,49 @@
+/* Lazy binding during dlclose. Indirectly loaded module.
+ Copyright (C) 2023 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 <stdio.h>
+#include <stdlib.h>
+
+void
+exported_function (int ignored)
+{
+ /* This function is interposed from tst-dlclose-lazy-mod1.so and
+ thus never called. */
+ abort ();
+}
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("info: tst-dlclose-lazy-mod2.so constructor called");
+
+ /* Trigger lazy binding to the definition in
+ tst-dlclose-lazy-mod1.so, but not for
+ lazily_bound_exported_function in that module. */
+ exported_function (0);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ puts ("info: tst-dlclose-lazy-mod2.so destructor called");
+
+ /* Trigger the lazily_bound_exported_function call in
+ exported_function in tst-dlclose-lazy-mod1.so. */
+ exported_function (1);
+}
diff --git a/elf/tst-dlclose-lazy.c b/elf/tst-dlclose-lazy.c
new file mode 100644
index 0000000000000000..976a6bb6f64fa981
--- /dev/null
+++ b/elf/tst-dlclose-lazy.c
@@ -0,0 +1,47 @@
+/* Test lazy binding during dlclose (bug 30425).
+ Copyright (C) 2023 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/>. */
+
+/* This test re-creates a situation that can arise naturally for C++
+ applications due to the use of vague linkage and differences in the
+ set of compiler-emitted functions. A function in
+ tst-dlclose-lazy-mod1.so (exported_function) interposes a function
+ in tst-dlclose-lazy-mod2.so. This function is called from the
+ destructor in tst-dlclose-lazy-mod2.so, after the destructor for
+ tst-dlclose-lazy-mod1.so has already completed. Prior to the fix
+ for bug 30425, this would lead to a lazy binding failure in
+ tst-dlclose-lazy-mod1.so because dlclose had already marked the DSO
+ as unavailable for binding (by setting l_removed). */
+
+#include <dlfcn.h>
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+int
+main (void)
+{
+ /* Load tst-dlclose-lazy-mod1.so, indirectly loading
+ tst-dlclose-lazy-mod2.so. */
+ void *handle = xdlopen ("tst-dlclose-lazy-mod1.so", RTLD_GLOBAL | RTLD_LAZY);
+
+ /* Invoke the destructor of tst-dlclose-lazy-mod2.so, which calls
+ into tst-dlclose-lazy-mod1.so after its destructor has been
+ called. */
+ xdlclose (handle);
+
+ return 0;
+}

View File

@ -0,0 +1,27 @@
commit ecc7c3deb9f347649c2078fcc0f94d4cedf92d60
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jan 2 14:36:17 2024 +0100
libio: Check remaining buffer size in _IO_wdo_write (bug 31183)
The multibyte character needs to fit into the remaining buffer space,
not the already-written buffer space. Without the fix, we were never
moving the write pointer from the start of the buffer, always using
the single-character fallback buffer.
Fixes commit 04b76b5aa8b2d1d19066e42dd1 ("Don't error out writing
a multibyte character to an unbuffered stream (bug 17522)").
diff --git a/libio/wfileops.c b/libio/wfileops.c
index d3deb34ba058ca39..6a6421f8880f9356 100644
--- a/libio/wfileops.c
+++ b/libio/wfileops.c
@@ -57,7 +57,7 @@ _IO_wdo_write (FILE *fp, const wchar_t *data, size_t to_do)
char mb_buf[MB_LEN_MAX];
char *write_base, *write_ptr, *buf_end;
- if (fp->_IO_write_ptr - fp->_IO_write_base < sizeof (mb_buf))
+ if (fp->_IO_buf_end - fp->_IO_write_ptr < sizeof (mb_buf))
{
/* Make sure we have room for at least one multibyte
character. */

View File

@ -0,0 +1,347 @@
Avoid UAF in getcanonname (CVE-2023-4806)
When an NSS plugin only implements the _gethostbyname2_r and
_getcanonname_r callbacks, getaddrinfo could use memory that was freed
during tmpbuf resizing, through h_name in a previous query response.
The backing store for res->at->name when doing a query with
gethostbyname3_r or gethostbyname2_r is tmpbuf, which is reallocated in
gethosts during the query. For AF_INET6 lookup with AI_ALL |
AI_V4MAPPED, gethosts gets called twice, once for a v6 lookup and second
for a v4 lookup. In this case, if the first call reallocates tmpbuf
enough number of times, resulting in a malloc, th->h_name (that
res->at->name refers to) ends up on a heap allocated storage in tmpbuf.
Now if the second call to gethosts also causes the plugin callback to
return NSS_STATUS_TRYAGAIN, tmpbuf will get freed, resulting in a UAF
reference in res->at->name. This then gets dereferenced in the
getcanonname_r plugin call, resulting in the use after free.
Fix this by copying h_name over and freeing it at the end. This
resolves BZ #30843, which is assigned CVE-2023-4806. This is a minimal
RHEL-8-specific fix. Test case differences from upstream:
- The test module needs to explicitly link against libnss_files on
RHEL-8; upstream libnss_files is built into libc.so.
- Test module code was adapted to not use the upstream NSS module
convenience macros.
This change is adapted from the following commit from upstream:
commit 973fe93a5675c42798b2161c6f29c01b0e243994
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Fri Sep 15 13:51:12 2023 -0400
getaddrinfo: Fix use after free in getcanonname (CVE-2023-4806)
When an NSS plugin only implements the _gethostbyname2_r and
_getcanonname_r callbacks, getaddrinfo could use memory that was freed
during tmpbuf resizing, through h_name in a previous query response.
The backing store for res->at->name when doing a query with
gethostbyname3_r or gethostbyname2_r is tmpbuf, which is reallocated in
gethosts during the query. For AF_INET6 lookup with AI_ALL |
AI_V4MAPPED, gethosts gets called twice, once for a v6 lookup and second
for a v4 lookup. In this case, if the first call reallocates tmpbuf
enough number of times, resulting in a malloc, th->h_name (that
res->at->name refers to) ends up on a heap allocated storage in tmpbuf.
Now if the second call to gethosts also causes the plugin callback to
return NSS_STATUS_TRYAGAIN, tmpbuf will get freed, resulting in a UAF
reference in res->at->name. This then gets dereferenced in the
getcanonname_r plugin call, resulting in the use after free.
Fix this by copying h_name over and freeing it at the end. This
resolves BZ #30843, which is assigned CVE-2023-4806.
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
diff --git a/nss/Makefile b/nss/Makefile
index cfb255c6e7a3a4de..5829a2539306ddb5 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -66,7 +66,8 @@ xtests = bug-erange
tests-container = \
tst-nss-db-endpwent \
tst-nss-db-endgrent \
- tst-nss-gai-actions
+ tst-nss-gai-actions \
+ tst-nss-gai-hv2-canonname
# Tests which need libdl
ifeq (yes,$(build-shared))
@@ -132,7 +133,8 @@ routines += $(libnss_files-routines)
static-only-routines += $(libnss_files-routines)
tests-static += tst-nss-static
endif
-extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os
+extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os \
+ nss_test_gai_hv2_canonname.os
include ../Rules
@@ -169,12 +171,17 @@ rtld-tests-LDFLAGS += -Wl,--dynamic-list=nss_test.ver
libof-nss_test1 = extramodules
libof-nss_test2 = extramodules
libof-nss_test_errno = extramodules
+libof-nss_test_gai_hv2_canonname = extramodules
$(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(link-libc-deps)
$(build-module)
$(objpfx)/libnss_test2.so: $(objpfx)nss_test2.os $(link-libc-deps)
$(build-module)
$(objpfx)/libnss_test_errno.so: $(objpfx)nss_test_errno.os $(link-libc-deps)
$(build-module)
+$(objpfx)/libnss_test_gai_hv2_canonname.so: \
+ $(objpfx)nss_test_gai_hv2_canonname.os $(link-libc-deps) \
+ $(objpfx)/libnss_files.so
+ $(build-module)
$(objpfx)nss_test2.os : nss_test1.c
ifdef libnss_test1.so-version
$(objpfx)/libnss_test1.so$(libnss_test1.so-version): $(objpfx)/libnss_test1.so
@@ -187,10 +194,14 @@ endif
$(objpfx)/libnss_test_errno.so$(libnss_files.so-version): \
$(objpfx)/libnss_test_errno.so
$(make-link)
+$(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version): \
+ $(objpfx)/libnss_test_gai_hv2_canonname.so
+ $(make-link)
$(patsubst %,$(objpfx)%.out,$(tests)) : \
$(objpfx)/libnss_test1.so$(libnss_test1.so-version) \
$(objpfx)/libnss_test2.so$(libnss_test2.so-version) \
- $(objpfx)/libnss_test_errno.so$(libnss_files.so-version)
+ $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) \
+ $(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version)
ifeq (yes,$(have-thread-library))
$(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)
diff --git a/nss/nss_test_gai_hv2_canonname.c b/nss/nss_test_gai_hv2_canonname.c
new file mode 100644
index 0000000000000000..4195d7d24fdd5f6d
--- /dev/null
+++ b/nss/nss_test_gai_hv2_canonname.c
@@ -0,0 +1,64 @@
+/* NSS service provider that only provides gethostbyname2_r.
+ Copyright The GNU Toolchain Authors.
+ 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 <netdb.h>
+#include <nss.h>
+#include <stdlib.h>
+#include <string.h>
+#include "nss/tst-nss-gai-hv2-canonname.h"
+
+/* Catch misnamed and functions. */
+#pragma GCC diagnostic error "-Wmissing-prototypes"
+
+extern enum nss_status _nss_files_gethostbyname2_r (const char *, int,
+ struct hostent *, char *,
+ size_t, int *, int *);
+
+enum nss_status
+_nss_test_gai_hv2_canonname_gethostbyname2_r (const char *, int, struct hostent
+ *, char *, size_t, int *, int *);
+
+enum nss_status
+_nss_test_gai_hv2_canonname_getcanonname_r (const char *, char *, size_t, char
+ **, int *, int *);
+
+enum nss_status
+_nss_test_gai_hv2_canonname_gethostbyname2_r (const char *name, int af,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *herrnop)
+{
+ return _nss_files_gethostbyname2_r (name, af, result, buffer, buflen, errnop,
+ herrnop);
+}
+
+enum nss_status
+_nss_test_gai_hv2_canonname_getcanonname_r (const char *name, char *buffer,
+ size_t buflen, char **result,
+ int *errnop, int *h_errnop)
+{
+ /* We expect QUERYNAME, which is a small enough string that it shouldn't fail
+ the test. */
+ if (memcmp (QUERYNAME, name, sizeof (QUERYNAME))
+ || buflen < sizeof (QUERYNAME))
+ abort ();
+
+ strncpy (buffer, name, buflen);
+ *result = buffer;
+ return NSS_STATUS_SUCCESS;
+}
diff --git a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c
new file mode 100644
index 0000000000000000..d5f10c07d6a90773
--- /dev/null
+++ b/nss/tst-nss-gai-hv2-canonname.c
@@ -0,0 +1,63 @@
+/* Test NSS query path for plugins that only implement gethostbyname2
+ (#30843).
+ Copyright The GNU Toolchain Authors.
+ 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.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/xstdio.h>
+#include "nss/tst-nss-gai-hv2-canonname.h"
+
+#define PREPARE do_prepare
+
+static void do_prepare (int a, char **av)
+{
+ FILE *hosts = xfopen ("/etc/hosts", "w");
+ for (unsigned i = 2; i < 255; i++)
+ {
+ fprintf (hosts, "ff01::ff02:ff03:%u:2\ttest.example.com\n", i);
+ fprintf (hosts, "192.168.0.%u\ttest.example.com\n", i);
+ }
+ xfclose (hosts);
+}
+
+static int
+do_test (void)
+{
+ __nss_configure_lookup ("hosts", "test_gai_hv2_canonname");
+
+ struct addrinfo hints = {};
+ struct addrinfo *result = NULL;
+
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_CANONNAME;
+
+ int ret = getaddrinfo (QUERYNAME, NULL, &hints, &result);
+
+ if (ret != 0)
+ FAIL_EXIT1 ("getaddrinfo failed: %s\n", gai_strerror (ret));
+
+ TEST_COMPARE_STRING (result->ai_canonname, QUERYNAME);
+
+ freeaddrinfo(result);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nss/tst-nss-gai-hv2-canonname.h b/nss/tst-nss-gai-hv2-canonname.h
new file mode 100644
index 0000000000000000..14f2a9cb0867dff9
--- /dev/null
+++ b/nss/tst-nss-gai-hv2-canonname.h
@@ -0,0 +1 @@
+#define QUERYNAME "test.example.com"
diff --git a/nss/tst-nss-gai-hv2-canonname.root/postclean.req b/nss/tst-nss-gai-hv2-canonname.root/postclean.req
new file mode 100644
index 0000000000000000..e69de29bb2d1d643
diff --git a/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script
new file mode 100644
index 0000000000000000..31848b4a28524af6
--- /dev/null
+++ b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script
@@ -0,0 +1,2 @@
+cp $B/nss/libnss_test_gai_hv2_canonname.so $L/libnss_test_gai_hv2_canonname.so.2
+su
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 4fa963644af8b7d5..46046504a6858f2e 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -233,7 +233,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
}
array[i].next = array + i + 1;
}
- array[0].name = h->h_name;
array[count - 1].next = NULL;
*result = array;
@@ -287,6 +286,18 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
} \
*pat = addrmem; \
\
+ /* Store h_name so that it survives accidental deallocation when \
+ gethosts is called again and tmpbuf gets reallocated. */ \
+ if (h_name == NULL && th.h_name != NULL) \
+ { \
+ h_name = __strdup (th.h_name); \
+ if (h_name == NULL) \
+ { \
+ __resolv_context_put (res_ctx); \
+ result = -EAI_SYSTEM; \
+ goto free_and_return; \
+ } \
+ } \
if (localcanon != NULL && canon == NULL) \
{ \
canonbuf = __strdup (localcanon); \
@@ -323,15 +334,15 @@ typedef enum nss_status (*nss_getcanonname_r)
memory allocation failure. The returned string is allocated on the
heap; the caller has to free it. */
static char *
-getcanonname (service_user *nip, struct gaih_addrtuple *at, const char *name)
+getcanonname (service_user *nip, const char *hname, const char *name)
{
nss_getcanonname_r cfct = __nss_lookup_function (nip, "getcanonname_r");
char *s = (char *) name;
if (cfct != NULL)
{
char buf[256];
- if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf),
- &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS)
+ if (DL_CALL_FCT (cfct, (hname ?: name, buf, sizeof (buf), &s, &errno,
+ &h_errno)) != NSS_STATUS_SUCCESS)
/* If the canonical name cannot be determined, use the passed
string. */
s = (char *) name;
@@ -349,6 +360,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
struct gaih_addrtuple *at = NULL;
bool got_ipv6 = false;
const char *canon = NULL;
+ char *h_name = NULL;
const char *orig_name = name;
/* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -919,7 +931,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
if ((req->ai_flags & AI_CANONNAME) != 0
&& canon == NULL)
{
- canonbuf = getcanonname (nip, at, name);
+ canonbuf = getcanonname (nip, h_name, name);
if (canonbuf == NULL)
{
__resolv_context_enable_inet6
@@ -1169,6 +1181,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
free ((char *) name);
free (addrmem);
free (canonbuf);
+ free (h_name);
return result;
}

View File

@ -0,0 +1,22 @@
Work around in the test case, the fact that RHEL-8 NSS modules
infrastructure incorrectly allows merging in the hosts database. This
is a RHEL-8 only fix.
diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
index efca6cd1837a172a..c35e752896eceb2a 100644
--- a/nss/tst-nss-gai-actions.c
+++ b/nss/tst-nss-gai-actions.c
@@ -87,6 +87,13 @@ do_one_test (int action, int family, bool canon)
case ACTION_MERGE:
if (ret == 0)
{
+ if (hints.ai_flags == 0 && hints.ai_family == AF_INET)
+ {
+ printf ("***** RHEL-8 limitation: "
+ "NSS modules infrastructure incorrectly allows MERGE\n");
+ return;
+ }
+
char *formatted = support_format_addrinfo (ai, ret);
printf ("merge unexpectedly succeeded:\n %s\n", formatted);

View File

@ -0,0 +1,979 @@
commit 228cdb00a045ae3b68a91b35c7548bab6029446e
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Thu Mar 17 11:44:34 2022 +0530
Simplify allocations and fix merge and continue actions [BZ #28931]
Allocations for address tuples is currently a bit confusing because of
the pointer chasing through PAT, making it hard to observe the sequence
in which allocations have been made. Narrow scope of the pointer
chasing through PAT so that it is only used where necessary.
This also tightens actions behaviour with the hosts database in
getaddrinfo to comply with the manual text. The "continue" action
discards previous results and the "merge" action results in an immedate
lookup failure. Consequently, chaining of allocations across modules is
no longer necessary, thus opening up cleanup opportunities.
A test has been added that checks some combinations to ensure that they
work correctly.
Resolves: BZ #28931
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
(cherry picked from commit 1c37b8022e8763fedbb3f79c02e05c6acfe5a215)
Conflicts:
nss/Makefile
(Missing test cases)
sysdeps/posix/getaddrinfo.c
(RES_USE_INET6 still present in RHEL-8 and NSS module traversal rewrite
not in RHEL-8)
diff --git a/nss/Makefile b/nss/Makefile
index e8a7d9c7b3cefcdf..cfb255c6e7a3a4de 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -65,7 +65,8 @@ xtests = bug-erange
tests-container = \
tst-nss-db-endpwent \
- tst-nss-db-endgrent
+ tst-nss-db-endgrent \
+ tst-nss-gai-actions
# Tests which need libdl
ifeq (yes,$(build-shared))
diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
new file mode 100644
index 0000000000000000..efca6cd1837a172a
--- /dev/null
+++ b/nss/tst-nss-gai-actions.c
@@ -0,0 +1,149 @@
+/* Test continue and merge NSS actions for getaddrinfo.
+ Copyright The GNU Toolchain Authors.
+ 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 <dlfcn.h>
+#include <gnu/lib-names.h>
+#include <nss.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+enum
+{
+ ACTION_MERGE = 0,
+ ACTION_CONTINUE,
+};
+
+static const char *
+family_str (int family)
+{
+ switch (family)
+ {
+ case AF_UNSPEC:
+ return "AF_UNSPEC";
+ case AF_INET:
+ return "AF_INET";
+ default:
+ __builtin_unreachable ();
+ }
+}
+
+static const char *
+action_str (int action)
+{
+ switch (action)
+ {
+ case ACTION_MERGE:
+ return "merge";
+ case ACTION_CONTINUE:
+ return "continue";
+ default:
+ __builtin_unreachable ();
+ }
+}
+
+static void
+do_one_test (int action, int family, bool canon)
+{
+ struct addrinfo hints =
+ {
+ .ai_family = family,
+ };
+
+ struct addrinfo *ai;
+
+ if (canon)
+ hints.ai_flags = AI_CANONNAME;
+
+ printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n",
+ action_str (action), family_str (family),
+ canon ? "AI_CANONNAME" : "");
+
+ int ret = getaddrinfo ("example.org", "80", &hints, &ai);
+
+ switch (action)
+ {
+ case ACTION_MERGE:
+ if (ret == 0)
+ {
+ char *formatted = support_format_addrinfo (ai, ret);
+
+ printf ("merge unexpectedly succeeded:\n %s\n", formatted);
+ support_record_failure ();
+ free (formatted);
+ }
+ else
+ return;
+ case ACTION_CONTINUE:
+ {
+ char *formatted = support_format_addrinfo (ai, ret);
+
+ /* Verify that the result appears exactly once. */
+ const char *expected = "address: STREAM/TCP 192.0.0.1 80\n"
+ "address: DGRAM/UDP 192.0.0.1 80\n"
+ "address: RAW/IP 192.0.0.1 80\n";
+
+ const char *contains = strstr (formatted, expected);
+ const char *contains2 = NULL;
+
+ if (contains != NULL)
+ contains2 = strstr (contains + strlen (expected), expected);
+
+ if (contains == NULL || contains2 != NULL)
+ {
+ printf ("continue failed:\n%s\n", formatted);
+ support_record_failure ();
+ }
+
+ free (formatted);
+ break;
+ }
+ default:
+ __builtin_unreachable ();
+ }
+}
+
+static void
+do_one_test_set (int action)
+{
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files",
+ action_str (action));
+ __nss_configure_lookup ("hosts", buf);
+
+ do_one_test (action, AF_UNSPEC, false);
+ do_one_test (action, AF_INET, false);
+ do_one_test (action, AF_INET, true);
+}
+
+static int
+do_test (void)
+{
+ do_one_test_set (ACTION_CONTINUE);
+ do_one_test_set (ACTION_MERGE);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nss/tst-nss-gai-actions.root/etc/host.conf b/nss/tst-nss-gai-actions.root/etc/host.conf
new file mode 100644
index 0000000000000000..d1a59f73a90f2993
--- /dev/null
+++ b/nss/tst-nss-gai-actions.root/etc/host.conf
@@ -0,0 +1 @@
+multi on
diff --git a/nss/tst-nss-gai-actions.root/etc/hosts b/nss/tst-nss-gai-actions.root/etc/hosts
new file mode 100644
index 0000000000000000..50ce9774dc2c21d9
--- /dev/null
+++ b/nss/tst-nss-gai-actions.root/etc/hosts
@@ -0,0 +1,508 @@
+192.0.0.1 example.org
+192.0.0.2 example.org
+192.0.0.3 example.org
+192.0.0.4 example.org
+192.0.0.5 example.org
+192.0.0.6 example.org
+192.0.0.7 example.org
+192.0.0.8 example.org
+192.0.0.9 example.org
+192.0.0.10 example.org
+192.0.0.11 example.org
+192.0.0.12 example.org
+192.0.0.13 example.org
+192.0.0.14 example.org
+192.0.0.15 example.org
+192.0.0.16 example.org
+192.0.0.17 example.org
+192.0.0.18 example.org
+192.0.0.19 example.org
+192.0.0.20 example.org
+192.0.0.21 example.org
+192.0.0.22 example.org
+192.0.0.23 example.org
+192.0.0.24 example.org
+192.0.0.25 example.org
+192.0.0.26 example.org
+192.0.0.27 example.org
+192.0.0.28 example.org
+192.0.0.29 example.org
+192.0.0.30 example.org
+192.0.0.31 example.org
+192.0.0.32 example.org
+192.0.0.33 example.org
+192.0.0.34 example.org
+192.0.0.35 example.org
+192.0.0.36 example.org
+192.0.0.37 example.org
+192.0.0.38 example.org
+192.0.0.39 example.org
+192.0.0.40 example.org
+192.0.0.41 example.org
+192.0.0.42 example.org
+192.0.0.43 example.org
+192.0.0.44 example.org
+192.0.0.45 example.org
+192.0.0.46 example.org
+192.0.0.47 example.org
+192.0.0.48 example.org
+192.0.0.49 example.org
+192.0.0.50 example.org
+192.0.0.51 example.org
+192.0.0.52 example.org
+192.0.0.53 example.org
+192.0.0.54 example.org
+192.0.0.55 example.org
+192.0.0.56 example.org
+192.0.0.57 example.org
+192.0.0.58 example.org
+192.0.0.59 example.org
+192.0.0.60 example.org
+192.0.0.61 example.org
+192.0.0.62 example.org
+192.0.0.63 example.org
+192.0.0.64 example.org
+192.0.0.65 example.org
+192.0.0.66 example.org
+192.0.0.67 example.org
+192.0.0.68 example.org
+192.0.0.69 example.org
+192.0.0.70 example.org
+192.0.0.71 example.org
+192.0.0.72 example.org
+192.0.0.73 example.org
+192.0.0.74 example.org
+192.0.0.75 example.org
+192.0.0.76 example.org
+192.0.0.77 example.org
+192.0.0.78 example.org
+192.0.0.79 example.org
+192.0.0.80 example.org
+192.0.0.81 example.org
+192.0.0.82 example.org
+192.0.0.83 example.org
+192.0.0.84 example.org
+192.0.0.85 example.org
+192.0.0.86 example.org
+192.0.0.87 example.org
+192.0.0.88 example.org
+192.0.0.89 example.org
+192.0.0.90 example.org
+192.0.0.91 example.org
+192.0.0.92 example.org
+192.0.0.93 example.org
+192.0.0.94 example.org
+192.0.0.95 example.org
+192.0.0.96 example.org
+192.0.0.97 example.org
+192.0.0.98 example.org
+192.0.0.99 example.org
+192.0.0.100 example.org
+192.0.0.101 example.org
+192.0.0.102 example.org
+192.0.0.103 example.org
+192.0.0.104 example.org
+192.0.0.105 example.org
+192.0.0.106 example.org
+192.0.0.107 example.org
+192.0.0.108 example.org
+192.0.0.109 example.org
+192.0.0.110 example.org
+192.0.0.111 example.org
+192.0.0.112 example.org
+192.0.0.113 example.org
+192.0.0.114 example.org
+192.0.0.115 example.org
+192.0.0.116 example.org
+192.0.0.117 example.org
+192.0.0.118 example.org
+192.0.0.119 example.org
+192.0.0.120 example.org
+192.0.0.121 example.org
+192.0.0.122 example.org
+192.0.0.123 example.org
+192.0.0.124 example.org
+192.0.0.125 example.org
+192.0.0.126 example.org
+192.0.0.127 example.org
+192.0.0.128 example.org
+192.0.0.129 example.org
+192.0.0.130 example.org
+192.0.0.131 example.org
+192.0.0.132 example.org
+192.0.0.133 example.org
+192.0.0.134 example.org
+192.0.0.135 example.org
+192.0.0.136 example.org
+192.0.0.137 example.org
+192.0.0.138 example.org
+192.0.0.139 example.org
+192.0.0.140 example.org
+192.0.0.141 example.org
+192.0.0.142 example.org
+192.0.0.143 example.org
+192.0.0.144 example.org
+192.0.0.145 example.org
+192.0.0.146 example.org
+192.0.0.147 example.org
+192.0.0.148 example.org
+192.0.0.149 example.org
+192.0.0.150 example.org
+192.0.0.151 example.org
+192.0.0.152 example.org
+192.0.0.153 example.org
+192.0.0.154 example.org
+192.0.0.155 example.org
+192.0.0.156 example.org
+192.0.0.157 example.org
+192.0.0.158 example.org
+192.0.0.159 example.org
+192.0.0.160 example.org
+192.0.0.161 example.org
+192.0.0.162 example.org
+192.0.0.163 example.org
+192.0.0.164 example.org
+192.0.0.165 example.org
+192.0.0.166 example.org
+192.0.0.167 example.org
+192.0.0.168 example.org
+192.0.0.169 example.org
+192.0.0.170 example.org
+192.0.0.171 example.org
+192.0.0.172 example.org
+192.0.0.173 example.org
+192.0.0.174 example.org
+192.0.0.175 example.org
+192.0.0.176 example.org
+192.0.0.177 example.org
+192.0.0.178 example.org
+192.0.0.179 example.org
+192.0.0.180 example.org
+192.0.0.181 example.org
+192.0.0.182 example.org
+192.0.0.183 example.org
+192.0.0.184 example.org
+192.0.0.185 example.org
+192.0.0.186 example.org
+192.0.0.187 example.org
+192.0.0.188 example.org
+192.0.0.189 example.org
+192.0.0.190 example.org
+192.0.0.191 example.org
+192.0.0.192 example.org
+192.0.0.193 example.org
+192.0.0.194 example.org
+192.0.0.195 example.org
+192.0.0.196 example.org
+192.0.0.197 example.org
+192.0.0.198 example.org
+192.0.0.199 example.org
+192.0.0.200 example.org
+192.0.0.201 example.org
+192.0.0.202 example.org
+192.0.0.203 example.org
+192.0.0.204 example.org
+192.0.0.205 example.org
+192.0.0.206 example.org
+192.0.0.207 example.org
+192.0.0.208 example.org
+192.0.0.209 example.org
+192.0.0.210 example.org
+192.0.0.211 example.org
+192.0.0.212 example.org
+192.0.0.213 example.org
+192.0.0.214 example.org
+192.0.0.215 example.org
+192.0.0.216 example.org
+192.0.0.217 example.org
+192.0.0.218 example.org
+192.0.0.219 example.org
+192.0.0.220 example.org
+192.0.0.221 example.org
+192.0.0.222 example.org
+192.0.0.223 example.org
+192.0.0.224 example.org
+192.0.0.225 example.org
+192.0.0.226 example.org
+192.0.0.227 example.org
+192.0.0.228 example.org
+192.0.0.229 example.org
+192.0.0.230 example.org
+192.0.0.231 example.org
+192.0.0.232 example.org
+192.0.0.233 example.org
+192.0.0.234 example.org
+192.0.0.235 example.org
+192.0.0.236 example.org
+192.0.0.237 example.org
+192.0.0.238 example.org
+192.0.0.239 example.org
+192.0.0.240 example.org
+192.0.0.241 example.org
+192.0.0.242 example.org
+192.0.0.243 example.org
+192.0.0.244 example.org
+192.0.0.245 example.org
+192.0.0.246 example.org
+192.0.0.247 example.org
+192.0.0.248 example.org
+192.0.0.249 example.org
+192.0.0.250 example.org
+192.0.0.251 example.org
+192.0.0.252 example.org
+192.0.0.253 example.org
+192.0.0.254 example.org
+192.0.1.1 example.org
+192.0.1.2 example.org
+192.0.1.3 example.org
+192.0.1.4 example.org
+192.0.1.5 example.org
+192.0.1.6 example.org
+192.0.1.7 example.org
+192.0.1.8 example.org
+192.0.1.9 example.org
+192.0.1.10 example.org
+192.0.1.11 example.org
+192.0.1.12 example.org
+192.0.1.13 example.org
+192.0.1.14 example.org
+192.0.1.15 example.org
+192.0.1.16 example.org
+192.0.1.17 example.org
+192.0.1.18 example.org
+192.0.1.19 example.org
+192.0.1.20 example.org
+192.0.1.21 example.org
+192.0.1.22 example.org
+192.0.1.23 example.org
+192.0.1.24 example.org
+192.0.1.25 example.org
+192.0.1.26 example.org
+192.0.1.27 example.org
+192.0.1.28 example.org
+192.0.1.29 example.org
+192.0.1.30 example.org
+192.0.1.31 example.org
+192.0.1.32 example.org
+192.0.1.33 example.org
+192.0.1.34 example.org
+192.0.1.35 example.org
+192.0.1.36 example.org
+192.0.1.37 example.org
+192.0.1.38 example.org
+192.0.1.39 example.org
+192.0.1.40 example.org
+192.0.1.41 example.org
+192.0.1.42 example.org
+192.0.1.43 example.org
+192.0.1.44 example.org
+192.0.1.45 example.org
+192.0.1.46 example.org
+192.0.1.47 example.org
+192.0.1.48 example.org
+192.0.1.49 example.org
+192.0.1.50 example.org
+192.0.1.51 example.org
+192.0.1.52 example.org
+192.0.1.53 example.org
+192.0.1.54 example.org
+192.0.1.55 example.org
+192.0.1.56 example.org
+192.0.1.57 example.org
+192.0.1.58 example.org
+192.0.1.59 example.org
+192.0.1.60 example.org
+192.0.1.61 example.org
+192.0.1.62 example.org
+192.0.1.63 example.org
+192.0.1.64 example.org
+192.0.1.65 example.org
+192.0.1.66 example.org
+192.0.1.67 example.org
+192.0.1.68 example.org
+192.0.1.69 example.org
+192.0.1.70 example.org
+192.0.1.71 example.org
+192.0.1.72 example.org
+192.0.1.73 example.org
+192.0.1.74 example.org
+192.0.1.75 example.org
+192.0.1.76 example.org
+192.0.1.77 example.org
+192.0.1.78 example.org
+192.0.1.79 example.org
+192.0.1.80 example.org
+192.0.1.81 example.org
+192.0.1.82 example.org
+192.0.1.83 example.org
+192.0.1.84 example.org
+192.0.1.85 example.org
+192.0.1.86 example.org
+192.0.1.87 example.org
+192.0.1.88 example.org
+192.0.1.89 example.org
+192.0.1.90 example.org
+192.0.1.91 example.org
+192.0.1.92 example.org
+192.0.1.93 example.org
+192.0.1.94 example.org
+192.0.1.95 example.org
+192.0.1.96 example.org
+192.0.1.97 example.org
+192.0.1.98 example.org
+192.0.1.99 example.org
+192.0.1.100 example.org
+192.0.1.101 example.org
+192.0.1.102 example.org
+192.0.1.103 example.org
+192.0.1.104 example.org
+192.0.1.105 example.org
+192.0.1.106 example.org
+192.0.1.107 example.org
+192.0.1.108 example.org
+192.0.1.109 example.org
+192.0.1.110 example.org
+192.0.1.111 example.org
+192.0.1.112 example.org
+192.0.1.113 example.org
+192.0.1.114 example.org
+192.0.1.115 example.org
+192.0.1.116 example.org
+192.0.1.117 example.org
+192.0.1.118 example.org
+192.0.1.119 example.org
+192.0.1.120 example.org
+192.0.1.121 example.org
+192.0.1.122 example.org
+192.0.1.123 example.org
+192.0.1.124 example.org
+192.0.1.125 example.org
+192.0.1.126 example.org
+192.0.1.127 example.org
+192.0.1.128 example.org
+192.0.1.129 example.org
+192.0.1.130 example.org
+192.0.1.131 example.org
+192.0.1.132 example.org
+192.0.1.133 example.org
+192.0.1.134 example.org
+192.0.1.135 example.org
+192.0.1.136 example.org
+192.0.1.137 example.org
+192.0.1.138 example.org
+192.0.1.139 example.org
+192.0.1.140 example.org
+192.0.1.141 example.org
+192.0.1.142 example.org
+192.0.1.143 example.org
+192.0.1.144 example.org
+192.0.1.145 example.org
+192.0.1.146 example.org
+192.0.1.147 example.org
+192.0.1.148 example.org
+192.0.1.149 example.org
+192.0.1.150 example.org
+192.0.1.151 example.org
+192.0.1.152 example.org
+192.0.1.153 example.org
+192.0.1.154 example.org
+192.0.1.155 example.org
+192.0.1.156 example.org
+192.0.1.157 example.org
+192.0.1.158 example.org
+192.0.1.159 example.org
+192.0.1.160 example.org
+192.0.1.161 example.org
+192.0.1.162 example.org
+192.0.1.163 example.org
+192.0.1.164 example.org
+192.0.1.165 example.org
+192.0.1.166 example.org
+192.0.1.167 example.org
+192.0.1.168 example.org
+192.0.1.169 example.org
+192.0.1.170 example.org
+192.0.1.171 example.org
+192.0.1.172 example.org
+192.0.1.173 example.org
+192.0.1.174 example.org
+192.0.1.175 example.org
+192.0.1.176 example.org
+192.0.1.177 example.org
+192.0.1.178 example.org
+192.0.1.179 example.org
+192.0.1.180 example.org
+192.0.1.181 example.org
+192.0.1.182 example.org
+192.0.1.183 example.org
+192.0.1.184 example.org
+192.0.1.185 example.org
+192.0.1.186 example.org
+192.0.1.187 example.org
+192.0.1.188 example.org
+192.0.1.189 example.org
+192.0.1.190 example.org
+192.0.1.191 example.org
+192.0.1.192 example.org
+192.0.1.193 example.org
+192.0.1.194 example.org
+192.0.1.195 example.org
+192.0.1.196 example.org
+192.0.1.197 example.org
+192.0.1.198 example.org
+192.0.1.199 example.org
+192.0.1.200 example.org
+192.0.1.201 example.org
+192.0.1.202 example.org
+192.0.1.203 example.org
+192.0.1.204 example.org
+192.0.1.205 example.org
+192.0.1.206 example.org
+192.0.1.207 example.org
+192.0.1.208 example.org
+192.0.1.209 example.org
+192.0.1.210 example.org
+192.0.1.211 example.org
+192.0.1.212 example.org
+192.0.1.213 example.org
+192.0.1.214 example.org
+192.0.1.215 example.org
+192.0.1.216 example.org
+192.0.1.217 example.org
+192.0.1.218 example.org
+192.0.1.219 example.org
+192.0.1.220 example.org
+192.0.1.221 example.org
+192.0.1.222 example.org
+192.0.1.223 example.org
+192.0.1.224 example.org
+192.0.1.225 example.org
+192.0.1.226 example.org
+192.0.1.227 example.org
+192.0.1.228 example.org
+192.0.1.229 example.org
+192.0.1.230 example.org
+192.0.1.231 example.org
+192.0.1.232 example.org
+192.0.1.233 example.org
+192.0.1.234 example.org
+192.0.1.235 example.org
+192.0.1.236 example.org
+192.0.1.237 example.org
+192.0.1.238 example.org
+192.0.1.239 example.org
+192.0.1.240 example.org
+192.0.1.241 example.org
+192.0.1.242 example.org
+192.0.1.243 example.org
+192.0.1.244 example.org
+192.0.1.245 example.org
+192.0.1.246 example.org
+192.0.1.247 example.org
+192.0.1.248 example.org
+192.0.1.249 example.org
+192.0.1.250 example.org
+192.0.1.251 example.org
+192.0.1.252 example.org
+192.0.1.253 example.org
+192.0.1.254 example.org
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index fae3dea81f19dba6..4fa963644af8b7d5 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -474,11 +474,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (name != NULL)
{
- at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
- at->family = AF_UNSPEC;
- at->scopeid = 0;
- at->next = NULL;
-
if (req->ai_flags & AI_IDN)
{
char *out;
@@ -489,13 +484,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
malloc_name = true;
}
- if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+ uint32_t addr[4];
+ if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
{
+ at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+ at->scopeid = 0;
+ at->next = NULL;
+
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
- at->family = AF_INET;
+ {
+ memcpy (at->addr, addr, sizeof (at->addr));
+ at->family = AF_INET;
+ }
else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
{
- at->addr[3] = at->addr[0];
+ at->addr[3] = addr[0];
at->addr[2] = htonl (0xffff);
at->addr[1] = 0;
at->addr[0] = 0;
@@ -509,49 +512,62 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (req->ai_flags & AI_CANONNAME)
canon = name;
+
+ goto process_list;
}
- else if (at->family == AF_UNSPEC)
+
+ char *scope_delim = strchr (name, SCOPE_DELIMITER);
+ int e;
+
+ if (scope_delim == NULL)
+ e = inet_pton (AF_INET6, name, addr);
+ else
+ e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
+
+ if (e > 0)
{
- char *scope_delim = strchr (name, SCOPE_DELIMITER);
- int e;
- if (scope_delim == NULL)
- e = inet_pton (AF_INET6, name, at->addr);
+ at = alloca_account (sizeof (struct gaih_addrtuple),
+ alloca_used);
+ at->scopeid = 0;
+ at->next = NULL;
+
+ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+ {
+ memcpy (at->addr, addr, sizeof (at->addr));
+ at->family = AF_INET6;
+ }
+ else if (req->ai_family == AF_INET
+ && IN6_IS_ADDR_V4MAPPED (addr))
+ {
+ at->addr[0] = addr[3];
+ at->addr[1] = addr[1];
+ at->addr[2] = addr[2];
+ at->addr[3] = addr[3];
+ at->family = AF_INET;
+ }
else
- e = __inet_pton_length (AF_INET6, name, scope_delim - name,
- at->addr);
- if (e > 0)
{
- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
- at->family = AF_INET6;
- else if (req->ai_family == AF_INET
- && IN6_IS_ADDR_V4MAPPED (at->addr))
- {
- at->addr[0] = at->addr[3];
- at->family = AF_INET;
- }
- else
- {
- result = -EAI_ADDRFAMILY;
- goto free_and_return;
- }
-
- if (scope_delim != NULL
- && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
- scope_delim + 1,
- &at->scopeid) != 0)
- {
- result = -EAI_NONAME;
- goto free_and_return;
- }
+ result = -EAI_ADDRFAMILY;
+ goto free_and_return;
+ }
- if (req->ai_flags & AI_CANONNAME)
- canon = name;
+ if (scope_delim != NULL
+ && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+ scope_delim + 1,
+ &at->scopeid) != 0)
+ {
+ result = -EAI_NONAME;
+ goto free_and_return;
}
+
+ if (req->ai_flags & AI_CANONNAME)
+ canon = name;
+
+ goto process_list;
}
- if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
+ if ((req->ai_flags & AI_NUMERICHOST) == 0)
{
- struct gaih_addrtuple **pat = &at;
int no_data = 0;
int no_inet6_data = 0;
service_user *nip;
@@ -560,6 +576,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
int no_more;
struct resolv_context *res_ctx = NULL;
bool res_enable_inet6 = false;
+ bool do_merge = false;
/* If we do not have to look for IPv6 addresses or the canonical
name, use the simple, old functions, which do not support
@@ -596,7 +613,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
result = -EAI_MEMORY;
goto free_and_return;
}
- *pat = addrmem;
+ at = addrmem;
}
else
{
@@ -649,6 +666,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
}
struct gaih_addrtuple *addrfree = addrmem;
+ struct gaih_addrtuple **pat = &at;
+
for (int i = 0; i < air->naddrs; ++i)
{
socklen_t size = (air->family[i] == AF_INET
@@ -712,12 +731,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
free (air);
- if (at->family == AF_UNSPEC)
- {
- result = -EAI_NONAME;
- goto free_and_return;
- }
-
goto process_list;
}
else if (err == 0)
@@ -756,6 +769,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
while (!no_more)
{
+ /* Always start afresh; continue should discard previous results
+ and the hosts database does not support merge. */
+ at = NULL;
+ free (canonbuf);
+ free (addrmem);
+ canon = canonbuf = NULL;
+ addrmem = NULL;
+ got_ipv6 = false;
+
+ if (do_merge)
+ {
+ __set_h_errno (NETDB_INTERNAL);
+ __set_errno (EBUSY);
+ break;
+ }
+
no_data = 0;
nss_gethostbyname4_r fct4 = NULL;
@@ -768,12 +797,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
{
while (1)
{
- status = DL_CALL_FCT (fct4, (name, pat,
+ status = DL_CALL_FCT (fct4, (name, &at,
tmpbuf->data, tmpbuf->length,
&errno, &h_errno,
NULL));
if (status == NSS_STATUS_SUCCESS)
break;
+ /* gethostbyname4_r may write into AT, so reset it. */
+ at = NULL;
if (status != NSS_STATUS_TRYAGAIN
|| errno != ERANGE || h_errno != NETDB_INTERNAL)
{
@@ -800,7 +831,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
no_data = 1;
if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
- canon = (*pat)->name;
+ canon = at->name;
+
+ struct gaih_addrtuple **pat = &at;
while (*pat != NULL)
{
@@ -852,6 +885,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (fct != NULL)
{
+ struct gaih_addrtuple **pat = &at;
+
if (req->ai_family == AF_INET6
|| req->ai_family == AF_UNSPEC)
{
@@ -927,6 +962,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
break;
+ /* The hosts database does not support MERGE. */
+ if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+ do_merge = true;
+
if (nip->next == NULL)
no_more = -1;
else
@@ -960,7 +999,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
}
process_list:
- if (at->family == AF_UNSPEC)
+ if (at == NULL)
{
result = -EAI_NONAME;
goto free_and_return;

View File

@ -0,0 +1,157 @@
This patch was developed under embargo and cannot reference an upstream
commit. To find the associated commit please review the upstream git
log for CVE-2023-4911 to identify the relevant commits.
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Tue Sep 19 18:39:32 2023 -0400
tunables: Terminate if end of input is reached (CVE-2023-4911)
The string parsing routine may end up writing beyond bounds of tunestr
if the input tunable string is malformed, of the form name=name=val.
This gets processed twice, first as name=name=val and next as name=val,
resulting in tunestr being name=name=val:name=val, thus overflowing
tunestr.
Terminate the parsing loop at the first instance itself so that tunestr
does not overflow.
This also fixes up tst-env-setuid-tunables to actually handle failures
correct and add new tests to validate the fix for this CVE.
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
NEWS
(Dropped)
elf/tst-env-setuid-tunables.c
(Trivial conflict at HAVE_TUNABLES)
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 3c84809d44381241..2c878e08ea197b29 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -193,11 +193,7 @@ parse_tunables (char *tunestr, char *valstring)
/* If we reach the end of the string before getting a valid name-value
pair, bail out. */
if (p[len] == '\0')
- {
- if (__libc_enable_secure)
- tunestr[off] = '\0';
- return;
- }
+ break;
/* We did not find a valid name-value pair before encountering the
colon. */
@@ -257,9 +253,16 @@ parse_tunables (char *tunestr, char *valstring)
}
}
- if (p[len] != '\0')
- p += len + 1;
+ /* We reached the end while processing the tunable string. */
+ if (p[len] == '\0')
+ break;
+
+ p += len + 1;
}
+
+ /* Terminate tunestr before we leave. */
+ if (__libc_enable_secure)
+ tunestr[off] = '\0';
}
#endif
diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
index 0b9b075c40598c6f..8b0861c4ad853040 100644
--- a/elf/tst-env-setuid-tunables.c
+++ b/elf/tst-env-setuid-tunables.c
@@ -52,6 +52,8 @@ const char *teststrings[] =
"glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
"glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
"not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
+ "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
+ "glibc.malloc.check=2",
"glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
"glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
":glibc.malloc.garbage=2:glibc.malloc.check=1",
@@ -70,6 +72,8 @@ const char *resultstrings[] =
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
"glibc.malloc.mmap_threshold=4096",
"glibc.malloc.mmap_threshold=4096",
+ "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
+ "",
"",
"",
"",
@@ -84,11 +88,18 @@ test_child (int off)
const char *val = getenv ("GLIBC_TUNABLES");
#if HAVE_TUNABLES
+ printf (" [%d] GLIBC_TUNABLES is %s\n", off, val);
+ fflush (stdout);
if (val != NULL && strcmp (val, resultstrings[off]) == 0)
return 0;
if (val != NULL)
- printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
+ printf (" [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n",
+ off, val, resultstrings[off]);
+ else
+ printf (" [%d] GLIBC_TUNABLES environment variable absent\n", off);
+
+ fflush (stdout);
return 1;
#else
@@ -117,21 +128,26 @@ do_test (int argc, char **argv)
if (ret != 0)
exit (1);
- exit (EXIT_SUCCESS);
+ /* Special return code to make sure that the child executed all the way
+ through. */
+ exit (42);
}
else
{
- int ret = 0;
-
/* Spawn tests. */
for (int i = 0; i < array_length (teststrings); i++)
{
char buf[INT_BUFSIZE_BOUND (int)];
- printf ("Spawned test for %s (%d)\n", teststrings[i], i);
+ printf ("[%d] Spawned test for %s\n", i, teststrings[i]);
snprintf (buf, sizeof (buf), "%d\n", i);
+ fflush (stdout);
if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0)
- exit (1);
+ {
+ printf (" [%d] Failed to set GLIBC_TUNABLES: %m", i);
+ support_record_failure ();
+ continue;
+ }
int status = support_capture_subprogram_self_sgid (buf);
@@ -139,9 +155,14 @@ do_test (int argc, char **argv)
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
return EXIT_UNSUPPORTED;
- ret |= status;
+ if (WEXITSTATUS (status) != 42)
+ {
+ printf (" [%d] child failed with status %d\n", i,
+ WEXITSTATUS (status));
+ support_record_failure ();
+ }
}
- return ret;
+ return 0;
}
}

View File

@ -0,0 +1,203 @@
Author: Charles Fol <folcharles@gmail.com>
Date: Thu Mar 28 12:25:38 2024 -0300
iconv: ISO-2022-CN-EXT: fix out-of-bound writes when writing escape sequence (CVE-2024-2961)
ISO-2022-CN-EXT uses escape sequences to indicate character set changes
(as specified by RFC 1922). While the SOdesignation has the expected
bounds checks, neither SS2designation nor SS3designation have its;
allowing a write overflow of 1, 2, or 3 bytes with fixed values:
'$+I', '$+J', '$+K', '$+L', '$+M', or '$*H'.
Checked on aarch64-linux-gnu.
Co-authored-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/iconvdata/Makefile b/iconvdata/Makefile
index 646e2ccd11478646..c959758a90ed954f 100644
--- a/iconvdata/Makefile
+++ b/iconvdata/Makefile
@@ -75,7 +75,7 @@ ifeq (yes,$(build-shared))
tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
bug-iconv10 bug-iconv11 bug-iconv12 bug-iconv13 bug-iconv14 \
- bug-iconv15
+ bug-iconv15 tst-iconv-iso-2022-cn-ext
ifeq ($(have-thread-library),yes)
tests += bug-iconv3
endif
@@ -325,6 +325,8 @@ $(objpfx)bug-iconv14.out: $(addprefix $(objpfx), $(gconv-modules)) \
$(addprefix $(objpfx),$(modules.so))
$(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \
$(addprefix $(objpfx),$(modules.so))
+$(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \
+ $(addprefix $(objpfx),$(modules.so))
$(objpfx)iconv-test.out: run-iconv-test.sh \
$(addprefix $(objpfx), $(gconv-modules)) \
diff --git a/iconvdata/iso-2022-cn-ext.c b/iconvdata/iso-2022-cn-ext.c
index c21a7187b4d7808e..bd9493c12d95070b 100644
--- a/iconvdata/iso-2022-cn-ext.c
+++ b/iconvdata/iso-2022-cn-ext.c
@@ -575,6 +575,12 @@ DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
{ \
const char *escseq; \
\
+ if (outptr + 4 > outend) \
+ { \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ \
assert (used == CNS11643_2_set); /* XXX */ \
escseq = "*H"; \
*outptr++ = ESC; \
@@ -588,6 +594,12 @@ DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
{ \
const char *escseq; \
\
+ if (outptr + 4 > outend) \
+ { \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ \
assert ((used >> 5) >= 3 && (used >> 5) <= 7); \
escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; \
*outptr++ = ESC; \
diff --git a/iconvdata/tst-iconv-iso-2022-cn-ext.c b/iconvdata/tst-iconv-iso-2022-cn-ext.c
new file mode 100644
index 0000000000000000..96a8765fd5369681
--- /dev/null
+++ b/iconvdata/tst-iconv-iso-2022-cn-ext.c
@@ -0,0 +1,128 @@
+/* Verify ISO-2022-CN-EXT does not write out of the bounds.
+ Copyright (C) 2024 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 <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <iconv.h>
+#include <sys/mman.h>
+
+#include <support/xunistd.h>
+#include <support/check.h>
+#include <support/support.h>
+
+/* The test sets up a two memory page buffer with the second page marked
+ PROT_NONE to trigger a fault if the conversion writes beyond the exact
+ expected amount. Then we carry out various conversions and precisely
+ place the start of the output buffer in order to trigger a SIGSEGV if the
+ process writes anywhere between 1 and page sized bytes more (only one
+ PROT_NONE page is setup as a canary) than expected. These tests exercise
+ all three of the cases in ISO-2022-CN-EXT where the converter must switch
+ character sets and may run out of buffer space while doing the
+ operation. */
+
+static int
+do_test (void)
+{
+ iconv_t cd = iconv_open ("ISO-2022-CN-EXT", "UTF-8");
+ TEST_VERIFY_EXIT (cd != (iconv_t) -1);
+
+ char *ntf;
+ size_t ntfsize;
+ char *outbufbase;
+ {
+ int pgz = getpagesize ();
+ TEST_VERIFY_EXIT (pgz > 0);
+ ntfsize = 2 * pgz;
+
+ ntf = xmmap (NULL, ntfsize, PROT_READ | PROT_WRITE, MAP_PRIVATE
+ | MAP_ANONYMOUS, -1);
+ xmprotect (ntf + pgz, pgz, PROT_NONE);
+
+ outbufbase = ntf + pgz;
+ }
+
+ /* Check if SOdesignation escape sequence does not trigger an OOB write. */
+ {
+ char inbuf[] = "\xe4\xba\xa4\xe6\x8d\xa2";
+
+ for (int i = 0; i < 9; i++)
+ {
+ char *inp = inbuf;
+ size_t inleft = sizeof (inbuf) - 1;
+
+ char *outp = outbufbase - i;
+ size_t outleft = i;
+
+ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
+ == (size_t) -1);
+ TEST_COMPARE (errno, E2BIG);
+
+ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
+ }
+ }
+
+ /* Same as before for SS2designation. */
+ {
+ char inbuf[] = "㴽 \xe3\xb4\xbd";
+
+ for (int i = 0; i < 14; i++)
+ {
+ char *inp = inbuf;
+ size_t inleft = sizeof (inbuf) - 1;
+
+ char *outp = outbufbase - i;
+ size_t outleft = i;
+
+ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
+ == (size_t) -1);
+ TEST_COMPARE (errno, E2BIG);
+
+ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
+ }
+ }
+
+ /* Same as before for SS3designation. */
+ {
+ char inbuf[] = "劄 \xe5\x8a\x84";
+
+ for (int i = 0; i < 14; i++)
+ {
+ char *inp = inbuf;
+ size_t inleft = sizeof (inbuf) - 1;
+
+ char *outp = outbufbase - i;
+ size_t outleft = i;
+
+ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
+ == (size_t) -1);
+ TEST_COMPARE (errno, E2BIG);
+
+ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
+ }
+ }
+
+ TEST_VERIFY_EXIT (iconv_close (cd) != -1);
+
+ xmunmap (ntf, ntfsize);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -1,37 +0,0 @@
commit 7b5bfe77836442b9aeb75cc520f0d1eb7f82be67
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon May 18 15:21:04 2020 +0200
elf: Assert that objects are relocated before their constructors run
If we try to run constructors before relocation, this is always
a dynamic linker bug. An assert is easier to notice than a call
via an invalid function pointer (which may not even produce a valid
call stack).
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/elf/dl-init.c b/elf/dl-init.c
index 45405cd0563845b4..99ce531d7b326f5f 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -16,6 +16,7 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <assert.h>
#include <stddef.h>
#include <ldsodefs.h>
@@ -27,6 +28,11 @@ typedef void (*init_t) (int, char **, char **);
static void
call_init (struct link_map *l, int argc, char **argv, char **env)
{
+ /* If the object has not been relocated, this is a bug. The
+ function pointers are invalid in this case. (Executables do not
+ need relocation, and neither do proxy objects.) */
+ assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable);
+
if (l->l_init_called)
/* This object is all done. */
return;

View File

@ -1,237 +0,0 @@
commit 6f360366f7f76b158a0f4bf20d42f2854ad56264
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 27 11:36:44 2022 +0200
elf: Introduce to _dl_call_fini
This consolidates the destructor invocations from _dl_fini and
dlclose. Remove the micro-optimization that avoids
calling _dl_call_fini if they are no destructors (as dlclose is quite
expensive anyway). The debug log message is now printed
unconditionally.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Conflicts:
elf/dl-fini.c
(Missing ELF_INITFINI support downstream.)
sysdeps/generic/ldsodefs.h
(Missing dl_init_t declaration downstream.)
diff --git a/elf/Makefile b/elf/Makefile
index 634c3113227d64a6..040d82e243a80c0f 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -50,6 +50,7 @@ routines = \
# profiled libraries.
dl-routines = \
dl-call-libc-early-init \
+ dl-call_fini \
dl-close \
dl-debug \
dl-deps \
diff --git a/elf/dl-call_fini.c b/elf/dl-call_fini.c
new file mode 100644
index 0000000000000000..9e7ba10fa2a4df77
--- /dev/null
+++ b/elf/dl-call_fini.c
@@ -0,0 +1,50 @@
+/* Invoke DT_FINI and DT_FINI_ARRAY callbacks.
+ Copyright (C) 1996-2022 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 <ldsodefs.h>
+#include <sysdep.h>
+
+void
+_dl_call_fini (void *closure_map)
+{
+ struct link_map *map = closure_map;
+
+ /* When debugging print a message first. */
+ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS))
+ _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", map->l_name, map->l_ns);
+
+ /* Make sure nothing happens if we are called twice. */
+ map->l_init_called = 0;
+
+ ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY];
+ if (fini_array != NULL)
+ {
+ ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr
+ + fini_array->d_un.d_ptr);
+ size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
+ / sizeof (ElfW(Addr)));
+
+ while (sz-- > 0)
+ ((fini_t) array[sz]) ();
+ }
+
+ /* Next try the old-style destructor. */
+ ElfW(Dyn) *fini = map->l_info[DT_FINI];
+ if (fini != NULL)
+ DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr));
+}
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 22225efb3226c3e1..26ea51dfbadc5b85 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -35,11 +35,6 @@
#include <dl-unmap-segments.h>
-
-/* Type of the constructor functions. */
-typedef void (*fini_t) (void);
-
-
/* Special l_idx value used to indicate which objects remain loaded. */
#define IDX_STILL_USED -1
@@ -109,31 +104,6 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
return false;
}
-/* Invoke dstructors for CLOSURE (a struct link_map *). Called with
- exception handling temporarily disabled, to make errors fatal. */
-static void
-call_destructors (void *closure)
-{
- struct link_map *map = closure;
-
- if (map->l_info[DT_FINI_ARRAY] != NULL)
- {
- ElfW(Addr) *array =
- (ElfW(Addr) *) (map->l_addr
- + map->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
- unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
- / sizeof (ElfW(Addr)));
-
- while (sz-- > 0)
- ((fini_t) array[sz]) ();
- }
-
- /* Next try the old-style destructor. */
- if (map->l_info[DT_FINI] != NULL)
- DL_CALL_DT_FINI (map, ((void *) map->l_addr
- + map->l_info[DT_FINI]->d_un.d_ptr));
-}
-
void
_dl_close_worker (struct link_map *map, bool force)
{
@@ -279,17 +249,7 @@ _dl_close_worker (struct link_map *map, bool force)
half-cooked objects. Temporarily disable exception
handling, so that errors are fatal. */
if (imap->l_init_called)
- {
- /* When debugging print a message first. */
- if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS,
- 0))
- _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
- imap->l_name, nsid);
-
- if (imap->l_info[DT_FINI_ARRAY] != NULL
- || imap->l_info[DT_FINI] != NULL)
- _dl_catch_exception (NULL, call_destructors, imap);
- }
+ _dl_catch_exception (NULL, _dl_call_fini, imap);
#ifdef SHARED
/* Auditing checkpoint: we remove an object. */
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index e14259a3c8806e0d..2d34658d4c3a470c 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -20,11 +20,6 @@
#include <string.h>
#include <ldsodefs.h>
-
-/* Type of the constructor functions. */
-typedef void (*fini_t) (void);
-
-
void
_dl_fini (void)
{
@@ -115,38 +110,7 @@ _dl_fini (void)
if (l->l_init_called)
{
- /* Make sure nothing happens if we are called twice. */
- l->l_init_called = 0;
-
- /* Is there a destructor function? */
- if (l->l_info[DT_FINI_ARRAY] != NULL
- || l->l_info[DT_FINI] != NULL)
- {
- /* When debugging print a message first. */
- if (__builtin_expect (GLRO(dl_debug_mask)
- & DL_DEBUG_IMPCALLS, 0))
- _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
- DSO_FILENAME (l->l_name),
- ns);
-
- /* First see whether an array is given. */
- if (l->l_info[DT_FINI_ARRAY] != NULL)
- {
- ElfW(Addr) *array =
- (ElfW(Addr) *) (l->l_addr
- + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
- unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
- / sizeof (ElfW(Addr)));
- while (i-- > 0)
- ((fini_t) array[i]) ();
- }
-
- /* Next try the old-style destructor. */
- if (l->l_info[DT_FINI] != NULL)
- DL_CALL_DT_FINI
- (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
- }
-
+ _dl_call_fini (l);
#ifdef SHARED
/* Auditing checkpoint: another object closed. */
_dl_audit_objclose (l);
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 29bbde3e83e37d7e..0bad34d44a5685d9 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -93,6 +93,9 @@ typedef struct link_map *lookup_t;
: (__glibc_unlikely ((ref)->st_shndx == SHN_ABS) ? 0 \
: LOOKUP_VALUE_ADDRESS (map, map_set)) + (ref)->st_value)
+/* Type of a constructor function, in DT_FINI, DT_FINI_ARRAY. */
+typedef void (*fini_t) (void);
+
/* On some architectures a pointer to a function is not just a pointer
to the actual code of the function but rather an architecture
specific descriptor. */
@@ -1047,6 +1050,11 @@ extern void _dl_init (struct link_map *main_map, int argc, char **argv,
initializer functions have completed. */
extern void _dl_fini (void) attribute_hidden;
+/* Invoke the DT_FINI_ARRAY and DT_FINI destructors for MAP, which
+ must be a struct link_map *. Can be used as an argument to
+ _dl_catch_exception. */
+void _dl_call_fini (void *map) attribute_hidden;
+
/* Sort array MAPS according to dependencies of the contained objects.
If FORCE_FIRST, MAPS[0] keeps its place even if the dependencies
say otherwise. */

View File

@ -1,30 +0,0 @@
commit f6c8204fd7fabf0cf4162eaf10ccf23258e4d10e
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 22 13:56:25 2023 +0200
elf: Do not run constructors for proxy objects
Otherwise, the ld.so constructor runs for each audit namespace
and each dlmopen namespace.
diff --git a/elf/dl-init.c b/elf/dl-init.c
index 99ce531d7b326f5f..73c0259fbe6d19af 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -28,10 +28,14 @@ typedef void (*init_t) (int, char **, char **);
static void
call_init (struct link_map *l, int argc, char **argv, char **env)
{
+ /* Do not run constructors for proxy objects. */
+ if (l != l->l_real)
+ return;
+
/* If the object has not been relocated, this is a bug. The
function pointers are invalid in this case. (Executables do not
- need relocation, and neither do proxy objects.) */
- assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable);
+ need relocation.) */
+ assert (l->l_relocated || l->l_type == lt_executable);
if (l->l_init_called)
/* This object is all done. */

View File

@ -1,637 +0,0 @@
commit 6985865bc3ad5b23147ee73466583dd7fdf65892
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Sep 8 12:32:14 2023 +0200
elf: Always call destructors in reverse constructor order (bug 30785)
The current implementation of dlclose (and process exit) re-sorts the
link maps before calling ELF destructors. Destructor order is not the
reverse of the constructor order as a result: The second sort takes
relocation dependencies into account, and other differences can result
from ambiguous inputs, such as cycles. (The force_first handling in
_dl_sort_maps is not effective for dlclose.) After the changes in
this commit, there is still a required difference due to
dlopen/dlclose ordering by the application, but the previous
discrepancies went beyond that.
A new global (namespace-spanning) list of link maps,
_dl_init_called_list, is updated right before ELF constructors are
called from _dl_init.
In dl_close_worker, the maps variable, an on-stack variable length
array, is eliminated. (VLAs are problematic, and dlclose should not
call malloc because it cannot readily deal with malloc failure.)
Marking still-used objects uses the namespace list directly, with
next and next_idx replacing the done_index variable.
After marking, _dl_init_called_list is used to call the destructors
of now-unused maps in reverse destructor order. These destructors
can call dlopen. Previously, new objects do not have l_map_used set.
This had to change: There is no copy of the link map list anymore,
so processing would cover newly opened (and unmarked) mappings,
unloading them. Now, _dl_init (indirectly) sets l_map_used, too.
(dlclose is handled by the existing reentrancy guard.)
After _dl_init_called_list traversal, two more loops follow. The
processing order changes to the original link map order in the
namespace. Previously, dependency order was used. The difference
should not matter because relocation dependencies could already
reorder link maps in the old code.
The changes to _dl_fini remove the sorting step and replace it with
a traversal of _dl_init_called_list. The l_direct_opencount
decrement outside the loader lock is removed because it appears
incorrect: the counter manipulation could race with other dynamic
loader operations.
tst-audit23 needs adjustments to the changes in LA_ACT_DELETE
notifications. The new approach for checking la_activity should
make it clearer that la_activty calls come in pairs around namespace
updates.
The dependency sorting test cases need updates because the destructor
order is always the opposite order of constructor order, even with
relocation dependencies or cycles present.
There is a future cleanup opportunity to remove the now-constant
force_first and for_fini arguments from the _dl_sort_maps function.
Fixes commit 1df71d32fe5f5905ffd5d100e5e9ca8ad62 ("elf: Implement
force_first handling in _dl_sort_maps_dfs (bug 28937)").
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 26ea51dfbadc5b85..6b134f66628cfc03 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -137,30 +137,31 @@ _dl_close_worker (struct link_map *map, bool force)
bool any_tls = false;
const unsigned int nloaded = ns->_ns_nloaded;
- struct link_map *maps[nloaded];
- /* Run over the list and assign indexes to the link maps and enter
- them into the MAPS array. */
+ /* Run over the list and assign indexes to the link maps. */
int idx = 0;
for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next)
{
l->l_map_used = 0;
l->l_map_done = 0;
l->l_idx = idx;
- maps[idx] = l;
++idx;
}
assert (idx == nloaded);
- /* Keep track of the lowest index link map we have covered already. */
- int done_index = -1;
- while (++done_index < nloaded)
+ /* Keep marking link maps until no new link maps are found. */
+ for (struct link_map *l = ns->_ns_loaded; l != NULL; )
{
- struct link_map *l = maps[done_index];
+ /* next is reset to earlier link maps for remarking. */
+ struct link_map *next = l->l_next;
+ int next_idx = l->l_idx + 1; /* next->l_idx, but covers next == NULL. */
if (l->l_map_done)
- /* Already handled. */
- continue;
+ {
+ /* Already handled. */
+ l = next;
+ continue;
+ }
/* Check whether this object is still used. */
if (l->l_type == lt_loaded
@@ -170,7 +171,10 @@ _dl_close_worker (struct link_map *map, bool force)
acquire is sufficient and correct. */
&& atomic_load_acquire (&l->l_tls_dtor_count) == 0
&& !l->l_map_used)
- continue;
+ {
+ l = next;
+ continue;
+ }
/* We need this object and we handle it now. */
l->l_map_used = 1;
@@ -197,8 +201,11 @@ _dl_close_worker (struct link_map *map, bool force)
already processed it, then we need to go back
and process again from that point forward to
ensure we keep all of its dependencies also. */
- if ((*lp)->l_idx - 1 < done_index)
- done_index = (*lp)->l_idx - 1;
+ if ((*lp)->l_idx < next_idx)
+ {
+ next = *lp;
+ next_idx = next->l_idx;
+ }
}
}
@@ -218,44 +225,65 @@ _dl_close_worker (struct link_map *map, bool force)
if (!jmap->l_map_used)
{
jmap->l_map_used = 1;
- if (jmap->l_idx - 1 < done_index)
- done_index = jmap->l_idx - 1;
+ if (jmap->l_idx < next_idx)
+ {
+ next = jmap;
+ next_idx = next->l_idx;
+ }
}
}
}
- }
- /* Sort the entries. We can skip looking for the binary itself which is
- at the front of the search list for the main namespace. */
- _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE), true);
+ l = next;
+ }
- /* Call all termination functions at once. */
- bool unload_any = false;
- bool scope_mem_left = false;
- unsigned int unload_global = 0;
- unsigned int first_loaded = ~0;
- for (unsigned int i = 0; i < nloaded; ++i)
+ /* Call the destructors in reverse constructor order, and remove the
+ closed link maps from the list. */
+ for (struct link_map **init_called_head = &_dl_init_called_list;
+ *init_called_head != NULL; )
{
- struct link_map *imap = maps[i];
+ struct link_map *imap = *init_called_head;
- /* All elements must be in the same namespace. */
- assert (imap->l_ns == nsid);
-
- if (!imap->l_map_used)
+ /* _dl_init_called_list is global, to produce a global odering.
+ Ignore the other namespaces (and link maps that are still used). */
+ if (imap->l_ns != nsid || imap->l_map_used)
+ init_called_head = &imap->l_init_called_next;
+ else
{
assert (imap->l_type == lt_loaded && !imap->l_nodelete_active);
- /* Call its termination function. Do not do it for
- half-cooked objects. Temporarily disable exception
- handling, so that errors are fatal. */
- if (imap->l_init_called)
+ /* _dl_init_called_list is updated at the same time as
+ l_init_called. */
+ assert (imap->l_init_called);
+
+ if (imap->l_info[DT_FINI_ARRAY] != NULL
+ || imap->l_info[DT_FINI] != NULL)
_dl_catch_exception (NULL, _dl_call_fini, imap);
#ifdef SHARED
/* Auditing checkpoint: we remove an object. */
_dl_audit_objclose (imap);
#endif
+ /* Unlink this link map. */
+ *init_called_head = imap->l_init_called_next;
+ }
+ }
+
+
+ bool unload_any = false;
+ bool scope_mem_left = false;
+ unsigned int unload_global = 0;
+
+ /* For skipping un-unloadable link maps in the second loop. */
+ struct link_map *first_loaded = ns->_ns_loaded;
+ /* Iterate over the namespace to find objects to unload. Some
+ unloadable objects may not be on _dl_init_called_list due to
+ dlopen failure. */
+ for (struct link_map *imap = first_loaded; imap != NULL; imap = imap->l_next)
+ {
+ if (!imap->l_map_used)
+ {
/* This object must not be used anymore. */
imap->l_removed = 1;
@@ -266,8 +294,8 @@ _dl_close_worker (struct link_map *map, bool force)
++unload_global;
/* Remember where the first dynamically loaded object is. */
- if (i < first_loaded)
- first_loaded = i;
+ if (first_loaded == NULL)
+ first_loaded = imap;
}
/* Else imap->l_map_used. */
else if (imap->l_type == lt_loaded)
@@ -403,8 +431,8 @@ _dl_close_worker (struct link_map *map, bool force)
imap->l_loader = NULL;
/* Remember where the first dynamically loaded object is. */
- if (i < first_loaded)
- first_loaded = i;
+ if (first_loaded == NULL)
+ first_loaded = imap;
}
}
@@ -475,10 +503,11 @@ _dl_close_worker (struct link_map *map, bool force)
/* Check each element of the search list to see if all references to
it are gone. */
- for (unsigned int i = first_loaded; i < nloaded; ++i)
+ for (struct link_map *imap = first_loaded; imap != NULL; )
{
- struct link_map *imap = maps[i];
- if (!imap->l_map_used)
+ if (imap->l_map_used)
+ imap = imap->l_next;
+ else
{
assert (imap->l_type == lt_loaded);
@@ -686,7 +715,9 @@ _dl_close_worker (struct link_map *map, bool force)
if (imap == GL(dl_initfirst))
GL(dl_initfirst) = NULL;
+ struct link_map *next = imap->l_next;
free (imap);
+ imap = next;
}
}
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 2d34658d4c3a470c..25a7767d707721d5 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -23,116 +23,68 @@
void
_dl_fini (void)
{
- /* Lots of fun ahead. We have to call the destructors for all still
- loaded objects, in all namespaces. The problem is that the ELF
- specification now demands that dependencies between the modules
- are taken into account. I.e., the destructor for a module is
- called before the ones for any of its dependencies.
-
- To make things more complicated, we cannot simply use the reverse
- order of the constructors. Since the user might have loaded objects
- using `dlopen' there are possibly several other modules with its
- dependencies to be taken into account. Therefore we have to start
- determining the order of the modules once again from the beginning. */
-
- /* We run the destructors of the main namespaces last. As for the
- other namespaces, we pick run the destructors in them in reverse
- order of the namespace ID. */
-#ifdef SHARED
- int do_audit = 0;
- again:
-#endif
- for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
- {
- /* Protect against concurrent loads and unloads. */
- __rtld_lock_lock_recursive (GL(dl_load_lock));
-
- unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
- /* No need to do anything for empty namespaces or those used for
- auditing DSOs. */
- if (nloaded == 0
-#ifdef SHARED
- || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
-#endif
- )
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
- else
- {
+ /* Call destructors strictly in the reverse order of constructors.
+ This causes fewer surprises than some arbitrary reordering based
+ on new (relocation) dependencies. None of the objects are
+ unmapped, so applications can deal with this if their DSOs remain
+ in a consistent state after destructors have run. */
+
+ /* Protect against concurrent loads and unloads. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+ /* Ignore objects which are opened during shutdown. */
+ struct link_map *local_init_called_list = _dl_init_called_list;
+
+ for (struct link_map *l = local_init_called_list; l != NULL;
+ l = l->l_init_called_next)
+ /* Bump l_direct_opencount of all objects so that they
+ are not dlclose()ed from underneath us. */
+ ++l->l_direct_opencount;
+
+ /* After this point, everything linked from local_init_called_list
+ cannot be unloaded because of the reference counter update. */
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ /* Perform two passes: One for non-audit modules, one for audit
+ modules. This way, audit modules receive unload notifications
+ for non-audit objects, and the destructors for audit modules
+ still run. */
#ifdef SHARED
- _dl_audit_activity_nsid (ns, LA_ACT_DELETE);
+ int last_pass = GLRO(dl_naudit) > 0;
+ Lmid_t last_ns = -1;
+ for (int do_audit = 0; do_audit <= last_pass; ++do_audit)
#endif
-
- /* Now we can allocate an array to hold all the pointers and
- copy the pointers in. */
- struct link_map *maps[nloaded];
-
- unsigned int i;
- struct link_map *l;
- assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
- for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
- /* Do not handle ld.so in secondary namespaces. */
- if (l == l->l_real)
- {
- assert (i < nloaded);
-
- maps[i] = l;
- l->l_idx = i;
- ++i;
-
- /* Bump l_direct_opencount of all objects so that they
- are not dlclose()ed from underneath us. */
- ++l->l_direct_opencount;
- }
- assert (ns != LM_ID_BASE || i == nloaded);
- assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
- unsigned int nmaps = i;
-
- /* Now we have to do the sorting. We can skip looking for the
- binary itself which is at the front of the search list for
- the main namespace. */
- _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true);
-
- /* We do not rely on the linked list of loaded object anymore
- from this point on. We have our own list here (maps). The
- various members of this list cannot vanish since the open
- count is too high and will be decremented in this loop. So
- we release the lock so that some code which might be called
- from a destructor can directly or indirectly access the
- lock. */
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
-
- /* 'maps' now contains the objects in the right order. Now
- call the destructors. We have to process this array from
- the front. */
- for (i = 0; i < nmaps; ++i)
- {
- struct link_map *l = maps[i];
-
- if (l->l_init_called)
- {
- _dl_call_fini (l);
+ for (struct link_map *l = local_init_called_list; l != NULL;
+ l = l->l_init_called_next)
+ {
#ifdef SHARED
- /* Auditing checkpoint: another object closed. */
- _dl_audit_objclose (l);
+ if (GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing != do_audit)
+ continue;
+
+ /* Avoid back-to-back calls of _dl_audit_activity_nsid for the
+ same namespace. */
+ if (last_ns != l->l_ns)
+ {
+ if (last_ns >= 0)
+ _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT);
+ _dl_audit_activity_nsid (l->l_ns, LA_ACT_DELETE);
+ last_ns = l->l_ns;
+ }
#endif
- }
- /* Correct the previous increment. */
- --l->l_direct_opencount;
- }
+ /* There is no need to re-enable exceptions because _dl_fini
+ is not called from a context where exceptions are caught. */
+ _dl_call_fini (l);
#ifdef SHARED
- _dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT);
+ /* Auditing checkpoint: another object closed. */
+ _dl_audit_objclose (l);
#endif
- }
- }
+ }
#ifdef SHARED
- if (! do_audit && GLRO(dl_naudit) > 0)
- {
- do_audit = 1;
- goto again;
- }
+ if (last_ns >= 0)
+ _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT);
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
_dl_debug_printf ("\nruntime linker statistics:\n"
diff --git a/elf/dl-init.c b/elf/dl-init.c
index 73c0259fbe6d19af..eb6c83d180c3f1a1 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -24,6 +24,7 @@
/* Type of the initializer. */
typedef void (*init_t) (int, char **, char **);
+struct link_map *_dl_init_called_list;
static void
call_init (struct link_map *l, int argc, char **argv, char **env)
@@ -45,6 +46,21 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
dependency. */
l->l_init_called = 1;
+ /* Help an already-running dlclose: The just-loaded object must not
+ be removed during the current pass. (No effect if no dlclose in
+ progress.) */
+ l->l_map_used = 1;
+
+ /* Record execution before starting any initializers. This way, if
+ the initializers themselves call dlopen, their ELF destructors
+ will eventually be run before this object is destructed, matching
+ that their ELF constructors have run before this object was
+ constructed. _dl_fini uses this list for audit callbacks, so
+ register objects on the list even if they do not have a
+ constructor. */
+ l->l_init_called_next = _dl_init_called_list;
+ _dl_init_called_list = l;
+
/* Check for object which constructors we do not run here. */
if (__builtin_expect (l->l_name[0], 'a') == '\0'
&& l->l_type == lt_executable)
diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
index 4bf9052db16fb352..61dc54f8ae06d465 100644
--- a/elf/dso-sort-tests-1.def
+++ b/elf/dso-sort-tests-1.def
@@ -53,21 +53,14 @@ tst-dso-ordering10: {}->a->b->c;soname({})=c
output: b>a>{}<a<b
# Complex example from Bugzilla #15311, under-linked and with circular
-# relocation(dynamic) dependencies. While this is technically unspecified, the
-# presumed reasonable practical behavior is for the destructor order to respect
-# the static DT_NEEDED links (here this means the a->b->c->d order).
-# The older dynamic_sort=1 algorithm does not achieve this, while the DFS-based
-# dynamic_sort=2 algorithm does, although it is still arguable whether going
-# beyond spec to do this is the right thing to do.
-# The below expected outputs are what the two algorithms currently produce
-# respectively, for regression testing purposes.
+# relocation(dynamic) dependencies. For both sorting algorithms, the
+# destruction order is the reverse of the construction order, and
+# relocation dependencies are not taken into account.
tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
-output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
-output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
+output: {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<e<a<b<c<d];}
# Test that even in the presence of dependency loops involving dlopen'ed
# object, that object is initialized last (and not unloaded prematurely).
-# Final destructor order is indeterminate due to the cycle.
+# Final destructor order is the opposite of constructor order.
tst-bz28937: {+a;+b;-b;+c;%c};a->a1;a->a2;a2->a;b->b1;c->a1;c=>a1
-output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a<a2<c<a1
-output(glibc.rtld.dynamic_sort=2): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a2<a<c<a1
+output: {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<c<a<a1<a2
diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
index 4904cf1340a97ee1..f40760bd702e87c1 100644
--- a/elf/tst-audit23.c
+++ b/elf/tst-audit23.c
@@ -98,6 +98,8 @@ do_test (int argc, char *argv[])
char *lname;
uintptr_t laddr;
Lmid_t lmid;
+ uintptr_t cookie;
+ uintptr_t namespace;
bool closed;
} objs[max_objs] = { [0 ... max_objs-1] = { .closed = false } };
size_t nobjs = 0;
@@ -117,6 +119,9 @@ do_test (int argc, char *argv[])
size_t buffer_length = 0;
while (xgetline (&buffer, &buffer_length, out))
{
+ *strchrnul (buffer, '\n') = '\0';
+ printf ("info: subprocess output: %s\n", buffer);
+
if (startswith (buffer, "la_activity: "))
{
uintptr_t cookie;
@@ -125,29 +130,26 @@ do_test (int argc, char *argv[])
&cookie);
TEST_COMPARE (r, 2);
- /* The cookie identifies the object at the head of the link map,
- so we only add a new namespace if it changes from the previous
- one. This works since dlmopen is the last in the test body. */
- if (cookie != last_act_cookie && last_act_cookie != -1)
- TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
-
if (this_act == LA_ACT_ADD && acts[nacts] != cookie)
{
+ /* The cookie identifies the object at the head of the
+ link map, so we only add a new namespace if it
+ changes from the previous one. This works since
+ dlmopen is the last in the test body. */
+ if (cookie != last_act_cookie && last_act_cookie != -1)
+ TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
+
acts[nacts++] = cookie;
last_act_cookie = cookie;
}
- /* The LA_ACT_DELETE is called in the reverse order of LA_ACT_ADD
- at program termination (if the tests adds a dlclose or a library
- with extra dependencies this will need to be adapted). */
+ /* LA_ACT_DELETE is called multiple times for each
+ namespace, depending on destruction order. */
else if (this_act == LA_ACT_DELETE)
- {
- last_act_cookie = acts[--nacts];
- TEST_COMPARE (acts[nacts], cookie);
- acts[nacts] = 0;
- }
+ last_act_cookie = cookie;
else if (this_act == LA_ACT_CONSISTENT)
{
TEST_COMPARE (cookie, last_act_cookie);
+ last_act_cookie = -1;
/* LA_ACT_DELETE must always be followed by an la_objclose. */
if (last_act == LA_ACT_DELETE)
@@ -179,6 +181,8 @@ do_test (int argc, char *argv[])
objs[nobjs].lname = lname;
objs[nobjs].laddr = laddr;
objs[nobjs].lmid = lmid;
+ objs[nobjs].cookie = cookie;
+ objs[nobjs].namespace = last_act_cookie;
objs[nobjs].closed = false;
nobjs++;
@@ -201,6 +205,12 @@ do_test (int argc, char *argv[])
if (strcmp (lname, objs[i].lname) == 0 && lmid == objs[i].lmid)
{
TEST_COMPARE (objs[i].closed, false);
+ TEST_COMPARE (objs[i].cookie, cookie);
+ if (objs[i].namespace == -1)
+ /* No LA_ACT_ADD before the first la_objopen call. */
+ TEST_COMPARE (acts[0], last_act_cookie);
+ else
+ TEST_COMPARE (objs[i].namespace, last_act_cookie);
objs[i].closed = true;
break;
}
@@ -209,11 +219,7 @@ do_test (int argc, char *argv[])
/* la_objclose should be called after la_activity(LA_ACT_DELETE) for
the closed object's namespace. */
TEST_COMPARE (last_act, LA_ACT_DELETE);
- if (!seen_first_objclose)
- {
- TEST_COMPARE (last_act_cookie, cookie);
- seen_first_objclose = true;
- }
+ seen_first_objclose = true;
}
}
diff --git a/include/link.h b/include/link.h
index 041ff5f753a9ee11..a37b2cf3133eefd5 100644
--- a/include/link.h
+++ b/include/link.h
@@ -276,6 +276,10 @@ struct link_map
/* List of object in order of the init and fini calls. */
struct link_map **l_initfini;
+ /* Linked list of objects in reverse ELF constructor execution
+ order. Head of list is stored in _dl_init_called_list. */
+ struct link_map *l_init_called_next;
+
/* List of the dependencies introduced through symbol binding. */
struct link_map_reldeps
{
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 0bad34d44a5685d9..906447e677a4ca40 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1046,6 +1046,10 @@ extern int _dl_check_map_versions (struct link_map *map, int verbose,
extern void _dl_init (struct link_map *main_map, int argc, char **argv,
char **env) attribute_hidden;
+/* List of ELF objects in reverse order of their constructor
+ invocation. */
+extern struct link_map *_dl_init_called_list attribute_hidden;
+
/* Call the finalizer functions of all shared objects whose
initializer functions have completed. */
extern void _dl_fini (void) attribute_hidden;

View File

@ -1,141 +0,0 @@
commit 53df2ce6885da3d0e89e87dca7b095622296014f
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Sep 8 13:02:06 2023 +0200
elf: Remove unused l_text_end field from struct link_map
It is a left-over from commit 52a01100ad011293197637e42b5be1a479a2
("elf: Remove ad-hoc restrictions on dlopen callers [BZ #22787]").
When backporting commmit 6985865bc3ad5b23147ee73466583dd7fdf65892
("elf: Always call destructors in reverse constructor order
(bug 30785)"), we can move the l_init_called_next field to this
place, so that the internal GLIBC_PRIVATE ABI does not change.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
elf/dl-load.h
(Missing commit "Avoid "inline" after return type in function
definitions.")
elf/rtld.c
(Missing rtld_setup_main_map function. Re-did the l_text_end
removal from scratch.)
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 0b45e6e3db31c70d..52dc564af9e95878 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1176,7 +1176,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
/* Now process the load commands and map segments into memory.
This is responsible for filling in:
- l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr
+ l_map_start, l_map_end, l_addr, l_contiguous, l_phdr
*/
errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
maplength, has_holes, loader);
diff --git a/elf/dl-load.h b/elf/dl-load.h
index 66ea2e9237ab6321..ebf2604e044c3bde 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -82,14 +82,11 @@ struct loadcmd
/* This is a subroutine of _dl_map_segments. It should be called for each
load command, some time after L->l_addr has been set correctly. It is
- responsible for setting up the l_text_end and l_phdr fields. */
-static void __always_inline
+ responsible for setting the l_phdr fields */
+static __always_inline void
_dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
const struct loadcmd *c)
{
- if (c->prot & PROT_EXEC)
- l->l_text_end = l->l_addr + c->mapend;
-
if (l->l_phdr == 0
&& c->mapoff <= header->e_phoff
&& ((size_t) (c->mapend - c->mapstart + c->mapoff)
@@ -102,7 +99,7 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
/* This is a subroutine of _dl_map_object_from_fd. It is responsible
for filling in several fields in *L: l_map_start, l_map_end, l_addr,
- l_contiguous, l_text_end, l_phdr. On successful return, all the
+ l_contiguous, l_phdr. On successful return, all the
segments are mapped (or copied, or whatever) from the file into their
final places in the address space, with the correct page permissions,
and any bss-like regions already zeroed. It returns a null pointer
diff --git a/elf/rtld.c b/elf/rtld.c
index cd2cc4024a3581c2..c2edd0bfdc27f207 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -472,7 +472,6 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin;
GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
- GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext;
/* Copy the TLS related data if necessary. */
#ifndef DONT_USE_BOOTSTRAP_MAP
# if NO_TLS_OFFSET != 0
@@ -1520,7 +1519,6 @@ dl_main (const ElfW(Phdr) *phdr,
}
main_map->l_map_end = 0;
- main_map->l_text_end = 0;
/* Perhaps the executable has no PT_LOAD header entries at all. */
main_map->l_map_start = ~0;
/* And it was opened directly. */
@@ -1591,8 +1589,6 @@ dl_main (const ElfW(Phdr) *phdr,
allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz;
if (main_map->l_map_end < allocend)
main_map->l_map_end = allocend;
- if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end)
- main_map->l_text_end = allocend;
}
break;
@@ -1641,8 +1637,6 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
= (char *) main_map->l_tls_initimage + main_map->l_addr;
if (! main_map->l_map_end)
main_map->l_map_end = ~0;
- if (! main_map->l_text_end)
- main_map->l_text_end = ~0;
if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name)
{
/* We were invoked directly, so the program might not have a
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index d2b35a080b57c183..352e992c578e416f 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -52,9 +52,6 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
l->l_addr = ph->p_vaddr;
if (ph->p_vaddr + ph->p_memsz >= l->l_map_end)
l->l_map_end = ph->p_vaddr + ph->p_memsz;
- if ((ph->p_flags & PF_X)
- && ph->p_vaddr + ph->p_memsz >= l->l_text_end)
- l->l_text_end = ph->p_vaddr + ph->p_memsz;
}
else
/* There must be no TLS segment. */
@@ -63,7 +60,6 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso);
l->l_addr = l->l_map_start - l->l_addr;
l->l_map_end += l->l_addr;
- l->l_text_end += l->l_addr;
l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr);
elf_get_dynamic_info (l, dyn_temp);
_dl_setup_hash (l);
diff --git a/include/link.h b/include/link.h
index a37b2cf3133eefd5..88683e7a747b86e0 100644
--- a/include/link.h
+++ b/include/link.h
@@ -251,8 +251,6 @@ struct link_map
/* Start and finish of memory map for this object. l_map_start
need not be the same as l_addr. */
ElfW(Addr) l_map_start, l_map_end;
- /* End of the executable part of the mapping. */
- ElfW(Addr) l_text_end;
/* Default array for 'l_scope'. */
struct r_scope_elem *l_scope_mem[4];

View File

@ -1,35 +0,0 @@
commit d3ba6c1333b10680ce5900a628108507d9d4b844
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Sep 11 09:17:52 2023 +0200
elf: Move l_init_called_next to old place of l_text_end in link map
This preserves all member offsets and the GLIBC_PRIVATE ABI
for backporting.
diff --git a/include/link.h b/include/link.h
index 88683e7a747b86e0..a464dd8e86cf89d0 100644
--- a/include/link.h
+++ b/include/link.h
@@ -252,6 +252,10 @@ struct link_map
need not be the same as l_addr. */
ElfW(Addr) l_map_start, l_map_end;
+ /* Linked list of objects in reverse ELF constructor execution
+ order. Head of list is stored in _dl_init_called_list. */
+ struct link_map *l_init_called_next;
+
/* Default array for 'l_scope'. */
struct r_scope_elem *l_scope_mem[4];
/* Size of array allocated for 'l_scope'. */
@@ -274,10 +278,6 @@ struct link_map
/* List of object in order of the init and fini calls. */
struct link_map **l_initfini;
- /* Linked list of objects in reverse ELF constructor execution
- order. Head of list is stored in _dl_init_called_list. */
- struct link_map *l_init_called_next;
-
/* List of the dependencies introduced through symbol binding. */
struct link_map_reldeps
{

View File

@ -0,0 +1,187 @@
commit bd77dd7e73e3530203be1c52c8a29d08270cb25d
Author: Florian Weimer <fweimer@redhat.com>
Date: Wed Sep 13 14:10:56 2023 +0200
CVE-2023-4527: Stack read overflow with large TCP responses in no-aaaa mode
Without passing alt_dns_packet_buffer, __res_context_search can only
store 2048 bytes (what fits into dns_packet_buffer). However,
the function returns the total packet size, and the subsequent
DNS parsing code in _nss_dns_gethostbyname4_r reads beyond the end
of the stack-allocated buffer.
Fixes commit f282cdbe7f436c75864e5640a4 ("resolv: Implement no-aaaa
stub resolver option") and bug 30842.
Conflicts:
resolv/nss_dns/dns-host.c
(missing dns_packet_buffer cleanup downstream)
diff --git a/resolv/Makefile b/resolv/Makefile
index ab8ad49b5318ad41..4f4eaf060443c128 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -58,6 +58,7 @@ tests += \
tst-resolv-edns \
tst-resolv-network \
tst-resolv-noaaaa \
+ tst-resolv-noaaaa-vc \
tst-resolv-nondecimal \
tst-resolv-res_init-multi \
tst-resolv-search \
@@ -202,6 +203,7 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \
$(objpfx)tst-resolv-res_init-thread: $(libdl) $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
+$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index ff0a0b6f7f1f4703..f678c7d7caa3a026 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -392,7 +392,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
else
{
n = __res_context_search (ctx, name, C_IN, T_A,
- host_buffer.buf->buf, 2048, NULL,
+ host_buffer.buf->buf, 2048, &host_buffer.ptr,
NULL, NULL, NULL, NULL);
if (n >= 0)
status = gaih_getanswer_noaaaa (host_buffer.buf, n,
diff --git a/resolv/tst-resolv-noaaaa-vc.c b/resolv/tst-resolv-noaaaa-vc.c
new file mode 100644
index 0000000000000000..9f5aebd99f2d74a2
--- /dev/null
+++ b/resolv/tst-resolv-noaaaa-vc.c
@@ -0,0 +1,129 @@
+/* Test the RES_NOAAAA resolver option with a large response.
+ Copyright (C) 2022-2023 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 <netdb.h>
+#include <resolv.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+/* Used to keep track of the number of queries. */
+static volatile unsigned int queries;
+
+/* If true, add a large TXT record at the start of the answer section. */
+static volatile bool stuff_txt;
+
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ /* If not using TCP, just force its use. */
+ if (!ctx->tcp)
+ {
+ struct resolv_response_flags flags = {.tc = true};
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ return;
+ }
+
+ /* The test needs to send four queries, the first three are used to
+ grow the NSS buffer via the ERANGE handshake. */
+ ++queries;
+ TEST_VERIFY (queries <= 4);
+
+ /* AAAA queries are supposed to be disabled. */
+ TEST_COMPARE (qtype, T_A);
+ TEST_COMPARE (qclass, C_IN);
+ TEST_COMPARE_STRING (qname, "example.com");
+
+ struct resolv_response_flags flags = {};
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+
+ resolv_response_section (b, ns_s_an);
+
+ if (stuff_txt)
+ {
+ resolv_response_open_record (b, qname, qclass, T_TXT, 60);
+ int zero = 0;
+ for (int i = 0; i <= 15000; ++i)
+ resolv_response_add_data (b, &zero, sizeof (zero));
+ resolv_response_close_record (b);
+ }
+
+ for (int i = 0; i < 200; ++i)
+ {
+ resolv_response_open_record (b, qname, qclass, qtype, 60);
+ char ipv4[4] = {192, 0, 2, i + 1};
+ resolv_response_add_data (b, &ipv4, sizeof (ipv4));
+ resolv_response_close_record (b);
+ }
+}
+
+static int
+do_test (void)
+{
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response
+ });
+
+ _res.options |= RES_NOAAAA;
+
+ for (int do_stuff_txt = 0; do_stuff_txt < 2; ++do_stuff_txt)
+ {
+ queries = 0;
+ stuff_txt = do_stuff_txt;
+
+ struct addrinfo *ai = NULL;
+ int ret;
+ ret = getaddrinfo ("example.com", "80",
+ &(struct addrinfo)
+ {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ }, &ai);
+
+ char *expected_result;
+ {
+ struct xmemstream mem;
+ xopen_memstream (&mem);
+ for (int i = 0; i < 200; ++i)
+ fprintf (mem.out, "address: STREAM/TCP 192.0.2.%d 80\n", i + 1);
+ xfclose_memstream (&mem);
+ expected_result = mem.buffer;
+ }
+
+ check_addrinfo ("example.com", ai, ret, expected_result);
+
+ free (expected_result);
+ freeaddrinfo (ai);
+ }
+
+ resolv_test_end (obj);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -132,7 +132,7 @@ end \
Summary: The GNU libc libraries
Name: glibc
Version: %{glibcversion}
Release: %{glibcrelease}.1
Release: %{glibcrelease}.13
# In general, GPLv2+ is used by programs, LGPLv2+ is used for
# libraries.
@ -1047,12 +1047,21 @@ Patch854: glibc-rh2180462-1.patch
Patch855: glibc-rh2180462-2.patch
Patch856: glibc-rh2180462-3.patch
Patch857: glibc-rh2180462-4.patch
Patch858: glibc-rh2233338-1.patch
Patch859: glibc-rh2233338-2.patch
Patch860: glibc-rh2233338-3.patch
Patch861: glibc-rh2233338-4.patch
Patch862: glibc-rh2233338-5.patch
Patch863: glibc-rh2233338-6.patch
# (Reverted fixes for rh2233338 were here.)
Patch864: glibc-rh2234714.patch
Patch865: glibc-RHEL-2435.patch
Patch866: glibc-RHEL-2435-2.patch
Patch867: glibc-RHEL-2423.patch
Patch868: glibc-RHEL-3036.patch
Patch869: glibc-RHEL-21522-1.patch
Patch870: glibc-RHEL-21522-2.patch
Patch871: glibc-RHEL-21522-3.patch
Patch872: glibc-RHEL-21522-4.patch
Patch873: glibc-RHEL-21519.patch
Patch874: glibc-RHEL-22441.patch
Patch875: glibc-RHEL-22846.patch
Patch876: glibc-RHEL-22847.patch
Patch877: glibc-RHEL-32475.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -2883,6 +2892,42 @@ fi
%files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
%changelog
* Tue Apr 16 2024 Florian Weimer <fweimer@redhat.com> - 2.28-236.13
- CVE-2024-2961: Out of bounds write in iconv conversion to ISO-2022-CN-EXT (RHEL-32475)
* Mon Jan 29 2024 Florian Weimer <fweimer@redhat.com> - 2.28-236.12
- Re-enable output buffering for wide stdio streams (RHEL-22847)
* Mon Jan 29 2024 Florian Weimer <fweimer@redhat.com> - 2.28-236.11
- Avoid lazy binding failures during dlclose (RHEL-22846)
* Fri Jan 26 2024 Florian Weimer <fweimer@redhat.com> - 2.28-236.10
- nscd: Skip unusable entries in first pass in prune_cache (RHEL-22441)
* Fri Jan 26 2024 Florian Weimer <fweimer@redhat.com> - 2.28-236.9
- Fix force-first handling in dlclose (RHEL-21519)
* Fri Jan 26 2024 Florian Weimer <fweimer@redhat.com> - 2.28-236.8
- Improve compatibility between underlinking and IFUNC resolvers (RHEL-21522)
* Wed Sep 20 2023 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-236.7
- CVE-2023-4911 glibc: buffer overflow in ld.so leading to privilege escalation (RHEL-3036)
* Tue Sep 19 2023 Carlos O'Donell <carlos@redhat.com> - 2.28-236.6
- Revert: Always call destructors in reverse constructor order (#2233338)
* Tue Sep 19 2023 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-236.5
- CVE-2023-4806 glibc: potential use-after-free in getaddrinfo (RHEL-2423)
* Tue Sep 19 2023 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-236.4
- CVE-2023-4813: Work around RHEL-8 limitation in test (RHEL-2435)
* Fri Sep 15 2023 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-236.3
- CVE-2023-4813: potential use-after-free in gaih_inet (RHEL-2435)
* Wed Sep 13 2023 Florian Weimer <fweimer@redhat.com> - 2.28-236.2
- CVE-2023-4527: Stack read overflow in getaddrinfo in no-aaaa mode (#2234714)
* Mon Sep 11 2023 Florian Weimer <fweimer@redhat.com> - 2.28-236.1
- Always call destructors in reverse constructor order (#2233338)