import glibc-2.28-101.el8

This commit is contained in:
CentOS Sources 2020-04-28 05:34:53 -04:00 committed by Andrew Lukoshko
parent 9aaa9b05a8
commit 88b287be24
84 changed files with 13711 additions and 62 deletions

View File

@ -0,0 +1,49 @@
Backport of this Fedora Rawhide commit but split out into a distinct
patch.
commit 72195d44855ab96875f117acb75c37f98dcb26a9
Author: Carlos O'Donell <carlos@redhat.com>
Date: Thu Jun 6 23:58:21 2019 -0400
locale: Fix C.UTF-8 ranges.
The ellipsis range support only allows <Uxxxx> or <Uxxxxxxxx> as
valid unicode code points, otherwise it treats it as a symbol and
since we don't define the symbol the entire range is unused.
diff --git a/localedata/locales/C b/localedata/locales/C
index b2c2d1dc417cde69..30d9563213b8cb0f 100644
--- a/localedata/locales/C
+++ b/localedata/locales/C
@@ -43,21 +43,21 @@ order_start forward
<U0000>
..
<UFFFF>
-<U10000>
+<U00010000>
..
-<U1FFFF>
-<U20000>
+<U0001FFFF>
+<U00020000>
..
-<U2FFFF>
-<UE0000>
+<U0002FFFF>
+<U000E0000>
..
-<UEFFFF>
-<UF0000>
+<U000EFFFF>
+<U000F0000>
..
-<UFFFFF>
-<U100000>
+<U000FFFFF>
+<U00100000>
..
-<U10FFFF>
+<U0010FFFF>
UNDEFINED
order_end
END LC_COLLATE

View File

@ -0,0 +1,185 @@
commit 96cd0558bcd69481ccc42e1b392f0c0b36fce2b0
Author: Florian Weimer <fweimer@redhat.com>
Date: Wed Nov 28 19:59:45 2018 +0100
support: Add signal support to support_capture_subprocess_check
Signal zero does not terminate a process, so it is safe to use negative
values for signal numbers.
Adjust libio/tst-vtables-common.c to use this new functionality,
instead of determining the termination status for a signal indirectly.
diff --git a/libio/tst-vtables-common.c b/libio/tst-vtables-common.c
index 5e3101206919fa1b..85e246cd1131f8e8 100644
--- a/libio/tst-vtables-common.c
+++ b/libio/tst-vtables-common.c
@@ -380,21 +380,6 @@ without_compatibility_fflush (void *closure)
_exit (1);
}
-/* Exit status after abnormal termination. */
-static int termination_status;
-
-static void
-init_termination_status (void)
-{
- pid_t pid = xfork ();
- if (pid == 0)
- abort ();
- xwaitpid (pid, &termination_status, 0);
-
- TEST_VERIFY (WIFSIGNALED (termination_status));
- TEST_COMPARE (WTERMSIG (termination_status), SIGABRT);
-}
-
static void
check_for_termination (const char *name, void (*callback) (void *))
{
@@ -404,7 +389,7 @@ check_for_termination (const char *name, void (*callback) (void *))
shared->calls = 0;
struct support_capture_subprocess proc
= support_capture_subprocess (callback, NULL);
- support_capture_subprocess_check (&proc, name, termination_status,
+ support_capture_subprocess_check (&proc, name, -SIGABRT,
sc_allow_stderr);
const char *message
= "Fatal error: glibc detected an invalid stdio handle\n";
@@ -491,7 +476,6 @@ run_tests (bool initially_disabled)
shared = support_shared_allocate (sizeof (*shared));
shared->initially_disabled = initially_disabled;
- init_termination_status ();
if (initially_disabled)
{
diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
index d5eac84d09ae325f..2d2384e73df0d2d0 100644
--- a/support/capture_subprocess.h
+++ b/support/capture_subprocess.h
@@ -55,13 +55,16 @@ enum support_capture_allow
sc_allow_stderr = 0x04,
};
-/* Check that the subprocess exited with STATUS and that only the
- allowed outputs happened. ALLOWED is a combination of
- support_capture_allow flags. Report errors under the CONTEXT
- message. */
+/* Check that the subprocess exited and that only the allowed outputs
+ happened. If STATUS_OR_SIGNAL is nonnegative, it is the expected
+ (decoded) exit status of the process, as returned by WEXITSTATUS.
+ If STATUS_OR_SIGNAL is negative, -STATUS_OR_SIGNAL is the expected
+ termination signal, as returned by WTERMSIG. ALLOWED is a
+ combination of support_capture_allow flags. Report errors under
+ the CONTEXT message. */
void support_capture_subprocess_check (struct support_capture_subprocess *,
- const char *context, int status,
- int allowed)
+ const char *context,
+ int status_or_signal, int allowed)
__attribute__ ((nonnull (1, 2)));
#endif /* SUPPORT_CAPTURE_SUBPROCESS_H */
diff --git a/support/support_capture_subprocess_check.c b/support/support_capture_subprocess_check.c
index ff5ee89fb02599ae..8b4c352c96227b78 100644
--- a/support/support_capture_subprocess_check.c
+++ b/support/support_capture_subprocess_check.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
+#include <sys/wait.h>
static void
print_context (const char *context, bool *failed)
@@ -31,9 +32,22 @@ print_context (const char *context, bool *failed)
printf ("error: subprocess failed: %s\n", context);
}
+static void
+print_actual_status (struct support_capture_subprocess *proc)
+{
+ if (WIFEXITED (proc->status))
+ printf ("error: actual exit status: %d [0x%x]\n",
+ WEXITSTATUS (proc->status), proc->status);
+ else if (WIFSIGNALED (proc->status))
+ printf ("error: actual termination signal: %d [0x%x]\n",
+ WTERMSIG (proc->status), proc->status);
+ else
+ printf ("error: actual undecoded exit status: [0x%x]\n", proc->status);
+}
+
void
support_capture_subprocess_check (struct support_capture_subprocess *proc,
- const char *context, int status,
+ const char *context, int status_or_signal,
int allowed)
{
TEST_VERIFY ((allowed & sc_allow_none)
@@ -44,11 +58,28 @@ support_capture_subprocess_check (struct support_capture_subprocess *proc,
|| (allowed & sc_allow_stderr))));
bool failed = false;
- if (proc->status != status)
+ if (status_or_signal >= 0)
{
- print_context (context, &failed);
- printf ("error: expected exit status: %d\n", status);
- printf ("error: actual exit status: %d\n", proc->status);
+ /* Expect regular termination. */
+ if (!(WIFEXITED (proc->status)
+ && WEXITSTATUS (proc->status) == status_or_signal))
+ {
+ print_context (context, &failed);
+ printf ("error: expected exit status: %d\n", status_or_signal);
+ print_actual_status (proc);
+ }
+ }
+ else
+ {
+ /* status_or_signal < 0. Expect termination by signal. */
+ if (!(WIFSIGNALED (proc->status)
+ && WTERMSIG (proc->status) == -status_or_signal))
+ {
+ print_context (context, &failed);
+ printf ("error: expected termination signal: %d\n",
+ -status_or_signal);
+ print_actual_status (proc);
+ }
}
if (!(allowed & sc_allow_stdout) && proc->out.length != 0)
{
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
index 63b6699622f97fcc..99570879eedd65b1 100644
--- a/support/tst-support_capture_subprocess.c
+++ b/support/tst-support_capture_subprocess.c
@@ -285,15 +285,29 @@ do_multiple_tests (enum test_type type)
check_stream ("stdout", &result.out, test.out);
check_stream ("stderr", &result.err, test.err);
+
+ /* Allowed output for support_capture_subprocess_check. */
+ int check_allow = 0;
+ if (lengths[length_idx_stdout] > 0)
+ check_allow |= sc_allow_stdout;
+ if (lengths[length_idx_stderr] > 0)
+ check_allow |= sc_allow_stderr;
+ if (check_allow == 0)
+ check_allow = sc_allow_none;
+
if (test.signal != 0)
{
TEST_VERIFY (WIFSIGNALED (result.status));
TEST_VERIFY (WTERMSIG (result.status) == test.signal);
+ support_capture_subprocess_check (&result, "signal",
+ -SIGTERM, check_allow);
}
else
{
TEST_VERIFY (WIFEXITED (result.status));
TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
+ support_capture_subprocess_check (&result, "exit",
+ test.status, check_allow);
}
support_capture_subprocess_free (&result);
free (test.out);

View File

@ -0,0 +1,42 @@
commit e37c2cf299b61ce18f62852f6c5624c27829b610
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 31 18:48:43 2019 +0100
Move _dl_open_check to its original place in dl_open_worker
This reverts the non-test change from commit d0093c5cefb7f7a4143f
("Call _dl_open_check after relocation [BZ #24259]"), given that
the underlying bug has been fixed properly in commit 61b74477fa7f63
("Remove all loaded objects if dlopen fails, ignoring NODELETE
[BZ #20839]").
Tested on x86-64-linux-gnu, with and without --enable-cet.
Change-Id: I995a6cfb89f25d2b0cf5e606428c2a93eb48fc33
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 25838b073ac1edaf..e13968d4d7c4c83f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -619,6 +619,8 @@ dl_open_worker (void *a)
_dl_debug_state ();
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
+ _dl_open_check (new);
+
/* Print scope information. */
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
_dl_show_scope (new, 0);
@@ -699,12 +701,6 @@ dl_open_worker (void *a)
_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
}
- /* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE
- object when _dl_open_check throws an exception. Move it after
- relocation to avoid leaving the NODELETE object mapped without
- relocation. */
- _dl_open_check (new);
-
/* This only performs the memory allocations. The actual update of
the scopes happens below, after failure is impossible. */
resize_scopes (new);

View File

@ -0,0 +1,27 @@
commit 61a7c9df71ee4e6f94b56c20f0d37c6e17d5f284
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Dec 2 14:53:16 2019 +0100
elf/tst-dlopenfail: Disable --no-as-needed for tst-dlopenfailmod1.so
Otherwise, the shared object dependency which triggers the load
failure is dropped, invalidating the test.
diff --git a/elf/Makefile b/elf/Makefile
index bf7c41f38be42184..467e810e784bb96d 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1543,8 +1543,11 @@ LDFLAGS-tst-finilazyfailmod.so = \
$(objpfx)tst-dlopenfail: $(libdl)
$(objpfx)tst-dlopenfail.out: \
$(objpfx)tst-dlopenfailmod1.so $(objpfx)tst-dlopenfailmod2.so
-# Order matters here. tst-dlopenfaillinkmod.so's soname ensures
-# a run-time loader failure.
+# Order matters here. tst-dlopenfaillinkmod.so's soname ensures a
+# run-time loader failure. --as-needed breaks this test because
+# nothing actually references tst-dlopenfailmod2.so (with its soname
+# tst-dlopenfail-missingmod.so).
+LDFLAGS-tst-dlopenfailmod1.so = -Wl,--no-as-needed
$(objpfx)tst-dlopenfailmod1.so: \
$(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so
LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,328 @@
commit f8ed116aa574435c6e28260f21963233682d3b57
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Dec 13 10:18:46 2019 +0100
dlopen: Rework handling of pending NODELETE status
Commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23 ("Block signals during
the initial part of dlopen") was deemed necessary because of
read-modify-write operations like the one in add_dependency in
elf/dl-lookup.c. In the old code, we check for any kind of NODELETE
status and bail out:
/* Redo the NODELETE check, as when dl_load_lock wasn't held
yet this could have changed. */
if (map->l_nodelete != link_map_nodelete_inactive)
goto out;
And then set pending status (during relocation):
if (flags & DL_LOOKUP_FOR_RELOCATE)
map->l_nodelete = link_map_nodelete_pending;
else
map->l_nodelete = link_map_nodelete_active;
If a signal arrives during relocation and the signal handler, through
lazy binding, adds a global scope dependency on the same map, it will
set map->l_nodelete to link_map_nodelete_active. This will be
overwritten with link_map_nodelete_pending by the dlopen relocation
code.
To avoid such problems in relation to the l_nodelete member, this
commit introduces two flags for active NODELETE status (irrevocable)
and pending NODELETE status (revocable until activate_nodelete is
invoked). As a result, NODELETE processing in dlopen does not
introduce further reasons why lazy binding from signal handlers
is unsafe during dlopen, and a subsequent commit can remove signal
blocking from dlopen.
This does not address pre-existing issues (unrelated to the NODELETE
changes) which make lazy binding in a signal handler during dlopen
unsafe, such as the use of malloc in both cases.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 243a028c443173c1..fa7f3e8174576e46 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -197,7 +197,7 @@ _dl_close_worker (struct link_map *map, bool force)
/* Check whether this object is still used. */
if (l->l_type == lt_loaded
&& l->l_direct_opencount == 0
- && l->l_nodelete != link_map_nodelete_active
+ && !l->l_nodelete_active
/* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why
acquire is sufficient and correct. */
&& atomic_load_acquire (&l->l_tls_dtor_count) == 0
@@ -279,8 +279,7 @@ _dl_close_worker (struct link_map *map, bool force)
if (!used[i])
{
- assert (imap->l_type == lt_loaded
- && imap->l_nodelete != link_map_nodelete_active);
+ 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
@@ -820,7 +819,7 @@ _dl_close (void *_map)
before we took the lock. There is no way to detect this (see below)
so we proceed assuming this isn't the case. First see whether we
can remove the object at all. */
- if (__glibc_unlikely (map->l_nodelete == link_map_nodelete_active))
+ if (__glibc_unlikely (map->l_nodelete_active))
{
/* Nope. Do nothing. */
__rtld_lock_unlock_recursive (GL(dl_load_lock));
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 35a3f96a6296294a..01724a54f8840f9f 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -187,6 +187,28 @@ enter_unique_sym (struct unique_sym *table, size_t size,
table[idx].map = map;
}
+/* Mark MAP as NODELETE according to the lookup mode in FLAGS. During
+ initial relocation, NODELETE state is pending only. */
+static void
+mark_nodelete (struct link_map *map, int flags)
+{
+ if (flags & DL_LOOKUP_FOR_RELOCATE)
+ map->l_nodelete_pending = true;
+ else
+ map->l_nodelete_active = true;
+}
+
+/* Return true if MAP is marked as NODELETE according to the lookup
+ mode in FLAGS> */
+static bool
+is_nodelete (struct link_map *map, int flags)
+{
+ /* Non-pending NODELETE always counts. Pending NODELETE only counts
+ during initial relocation processing. */
+ return map->l_nodelete_active
+ || ((flags & DL_LOOKUP_FOR_RELOCATE) && map->l_nodelete_pending);
+}
+
/* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol
in the unique symbol table, creating a new entry if necessary.
Return the matching symbol in RESULT. */
@@ -311,8 +333,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
enter_unique_sym (entries, size,
new_hash, strtab + sym->st_name, sym, map);
- if (map->l_type == lt_loaded
- && map->l_nodelete == link_map_nodelete_inactive)
+ if (map->l_type == lt_loaded && !is_nodelete (map, flags))
{
/* Make sure we don't unload this object by
setting the appropriate flag. */
@@ -320,10 +341,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
_dl_debug_printf ("\
marking %s [%lu] as NODELETE due to unique symbol\n",
map->l_name, map->l_ns);
- if (flags & DL_LOOKUP_FOR_RELOCATE)
- map->l_nodelete = link_map_nodelete_pending;
- else
- map->l_nodelete = link_map_nodelete_active;
+ mark_nodelete (map, flags);
}
}
++tab->n_elements;
@@ -586,7 +604,7 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
dependencies may pick an dependency which can be dlclose'd, but
such IFUNC resolvers are undefined anyway. */
assert (map->l_type == lt_loaded);
- if (map->l_nodelete != link_map_nodelete_inactive)
+ if (is_nodelete (map, flags))
return 0;
struct link_map_reldeps *l_reldeps
@@ -694,17 +712,16 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
/* Redo the NODELETE check, as when dl_load_lock wasn't held
yet this could have changed. */
- if (map->l_nodelete != link_map_nodelete_inactive)
+ if (is_nodelete (map, flags))
goto out;
/* If the object with the undefined reference cannot be removed ever
just make sure the same is true for the object which contains the
definition. */
- if (undef_map->l_type != lt_loaded
- || (undef_map->l_nodelete != link_map_nodelete_inactive))
+ if (undef_map->l_type != lt_loaded || is_nodelete (map, flags))
{
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
- && map->l_nodelete == link_map_nodelete_inactive)
+ && !is_nodelete (map, flags))
{
if (undef_map->l_name[0] == '\0')
_dl_debug_printf ("\
@@ -716,11 +733,7 @@ marking %s [%lu] as NODELETE due to reference to %s [%lu]\n",
map->l_name, map->l_ns,
undef_map->l_name, undef_map->l_ns);
}
-
- if (flags & DL_LOOKUP_FOR_RELOCATE)
- map->l_nodelete = link_map_nodelete_pending;
- else
- map->l_nodelete = link_map_nodelete_active;
+ mark_nodelete (map, flags);
goto out;
}
@@ -746,17 +759,14 @@ marking %s [%lu] as NODELETE due to reference to %s [%lu]\n",
cannot be unloaded. This is semantically the correct
behavior. */
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
- && map->l_nodelete == link_map_nodelete_inactive)
+ && !is_nodelete (map, flags))
_dl_debug_printf ("\
marking %s [%lu] as NODELETE due to memory allocation failure\n",
map->l_name, map->l_ns);
- if (flags & DL_LOOKUP_FOR_RELOCATE)
- /* In case of non-lazy binding, we could actually
- report the memory allocation error, but for now, we
- use the conservative approximation as well. */
- map->l_nodelete = link_map_nodelete_pending;
- else
- map->l_nodelete = link_map_nodelete_active;
+ /* In case of non-lazy binding, we could actually report
+ the memory allocation error, but for now, we use the
+ conservative approximation as well. */
+ mark_nodelete (map, flags);
goto out;
}
else
diff --git a/elf/dl-open.c b/elf/dl-open.c
index c7ed85b7ee99a296..a382bfae8aa3a2f8 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -440,13 +440,17 @@ activate_nodelete (struct link_map *new)
NODELETE status for objects outside the local scope. */
for (struct link_map *l = GL (dl_ns)[new->l_ns]._ns_loaded; l != NULL;
l = l->l_next)
- if (l->l_nodelete == link_map_nodelete_pending)
+ if (l->l_nodelete_pending)
{
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
_dl_debug_printf ("activating NODELETE for %s [%lu]\n",
l->l_name, l->l_ns);
- l->l_nodelete = link_map_nodelete_active;
+ l->l_nodelete_active = true;
+
+ /* This is just a debugging aid, to indicate that
+ activate_nodelete has run for this map. */
+ l->l_nodelete_pending = false;
}
}
@@ -549,10 +553,10 @@ dl_open_worker (void *a)
if (__glibc_unlikely (mode & RTLD_NODELETE))
{
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)
- && new->l_nodelete == link_map_nodelete_inactive)
+ && !new->l_nodelete_active)
_dl_debug_printf ("marking %s [%lu] as NODELETE\n",
new->l_name, new->l_ns);
- new->l_nodelete = link_map_nodelete_active;
+ new->l_nodelete_active = true;
}
/* Finalize the addition to the global scope. */
@@ -568,7 +572,7 @@ dl_open_worker (void *a)
/* Schedule NODELETE marking for the directly loaded object if
requested. */
if (__glibc_unlikely (mode & RTLD_NODELETE))
- new->l_nodelete = link_map_nodelete_pending;
+ new->l_nodelete_pending = true;
/* Load that object's dependencies. */
_dl_map_object_deps (new, NULL, 0, 0,
@@ -680,7 +684,7 @@ dl_open_worker (void *a)
_dl_start_profile ();
/* Prevent unloading the object. */
- GL(dl_profile_map)->l_nodelete = link_map_nodelete_active;
+ GL(dl_profile_map)->l_nodelete_active = true;
}
}
else
@@ -879,9 +883,9 @@ no more namespaces available for dlmopen()"));
happens inside dl_open_worker. */
__libc_signal_restore_set (&args.original_signal_mask);
- /* All link_map_nodelete_pending objects should have been
- deleted at this point, which is why it is not necessary
- to reset the flag here. */
+ /* All l_nodelete_pending objects should have been deleted
+ at this point, which is why it is not necessary to reset
+ the flag here. */
}
else
__libc_signal_restore_set (&args.original_signal_mask);
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index ea286abaea0128d1..78ba7e76db9706cc 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -164,7 +164,7 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
{
l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val;
if (l->l_flags_1 & DF_1_NODELETE)
- l->l_nodelete = link_map_nodelete_pending;
+ l->l_nodelete_pending = true;
/* Only DT_1_SUPPORTED_MASK bits are supported, and we would like
to assert this, but we can't. Users have been setting
diff --git a/include/link.h b/include/link.h
index a277b77cad6b52b1..e90fa79a0b332087 100644
--- a/include/link.h
+++ b/include/link.h
@@ -79,22 +79,6 @@ struct r_search_path_struct
int malloced;
};
-/* Type used by the l_nodelete member. */
-enum link_map_nodelete
-{
- /* This link map can be deallocated. */
- link_map_nodelete_inactive = 0, /* Zero-initialized in _dl_new_object. */
-
- /* This link map cannot be deallocated. */
- link_map_nodelete_active,
-
- /* This link map cannot be deallocated after dlopen has succeded.
- dlopen turns this into link_map_nodelete_active. dlclose treats
- this intermediate state as link_map_nodelete_active. */
- link_map_nodelete_pending,
-};
-
-
/* Structure describing a loaded shared object. The `l_next' and `l_prev'
members form a chain of all the shared objects loaded at startup.
@@ -218,10 +202,17 @@ struct link_map
freed, ie. not allocated with
the dummy malloc in ld.so. */
- /* Actually of type enum link_map_nodelete. Separate byte due to
- a read in add_dependency in elf/dl-lookup.c outside the loader
- lock. Only valid for l_type == lt_loaded. */
- unsigned char l_nodelete;
+ /* NODELETE status of the map. Only valid for maps of type
+ lt_loaded. Lazy binding sets l_nodelete_active directly,
+ potentially from signal handlers. Initial loading of an
+ DF_1_NODELETE object set l_nodelete_pending. Relocation may
+ set l_nodelete_pending as well. l_nodelete_pending maps are
+ promoted to l_nodelete_active status in the final stages of
+ dlopen, prior to calling ELF constructors. dlclose only
+ refuses to unload l_nodelete_active maps, the pending status is
+ ignored. */
+ bool l_nodelete_active;
+ bool l_nodelete_pending;
#include <link_map.h>

View File

@ -0,0 +1,134 @@
commit f7649d5780aa4682393b9daedd653e4d9c12784c
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Dec 13 10:23:10 2019 +0100
dlopen: Do not block signals
Blocking signals causes issues with certain anti-malware solutions
which rely on an unblocked SIGSYS signal for system calls they
intercept.
This reverts commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23
("Block signals during the initial part of dlopen") and adds
comments related to async signal safety to active_nodelete and
its caller.
Note that this does not make lazy binding async-signal-safe with regards
to dlopen. It merely avoids introducing new async-signal-safety hazards
as part of the NODELETE changes.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/elf/dl-open.c b/elf/dl-open.c
index a382bfae8aa3a2f8..d834b89754d2b073 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -34,7 +34,6 @@
#include <atomic.h>
#include <libc-internal.h>
#include <array_length.h>
-#include <internal-signals.h>
#include <dl-dst.h>
#include <dl-prop.h>
@@ -53,10 +52,6 @@ struct dl_open_args
/* Namespace ID. */
Lmid_t nsid;
- /* Original signal mask. Used for unblocking signal handlers before
- running ELF constructors. */
- sigset_t original_signal_mask;
-
/* Original value of _ns_global_scope_pending_adds. Set by
dl_open_worker. Only valid if nsid is a real namespace
(non-negative). */
@@ -446,6 +441,9 @@ activate_nodelete (struct link_map *new)
_dl_debug_printf ("activating NODELETE for %s [%lu]\n",
l->l_name, l->l_ns);
+ /* The flag can already be true at this point, e.g. a signal
+ handler may have triggered lazy binding and set NODELETE
+ status immediately. */
l->l_nodelete_active = true;
/* This is just a debugging aid, to indicate that
@@ -520,16 +518,12 @@ dl_open_worker (void *a)
if (new == NULL)
{
assert (mode & RTLD_NOLOAD);
- __libc_signal_restore_set (&args->original_signal_mask);
return;
}
if (__glibc_unlikely (mode & __RTLD_SPROF))
- {
- /* This happens only if we load a DSO for 'sprof'. */
- __libc_signal_restore_set (&args->original_signal_mask);
- return;
- }
+ /* This happens only if we load a DSO for 'sprof'. */
+ return;
/* This object is directly loaded. */
++new->l_direct_opencount;
@@ -565,7 +559,6 @@ dl_open_worker (void *a)
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
- __libc_signal_restore_set (&args->original_signal_mask);
return;
}
@@ -709,6 +702,12 @@ dl_open_worker (void *a)
All memory allocations for new objects must have happened
before. */
+ /* Finalize the NODELETE status first. This comes before
+ update_scopes, so that lazy binding will not see pending NODELETE
+ state for newly loaded objects. There is a compiler barrier in
+ update_scopes which ensures that the changes from
+ activate_nodelete are visible before new objects show up in the
+ local scope. */
activate_nodelete (new);
/* Second stage after resize_scopes: Actually perform the scope
@@ -742,10 +741,6 @@ dl_open_worker (void *a)
if (mode & RTLD_GLOBAL)
add_to_global_resize (new);
- /* Unblock signals. Data structures are now consistent, and
- application code may run. */
- __libc_signal_restore_set (&args->original_signal_mask);
-
/* Run the initializer functions of new objects. Temporarily
disable the exception handler, so that lazy binding failures are
fatal. */
@@ -835,10 +830,6 @@ no more namespaces available for dlmopen()"));
args.argv = argv;
args.env = env;
- /* Recursive lazy binding during manipulation of the dynamic loader
- structures may result in incorrect behavior. */
- __libc_signal_block_all (&args.original_signal_mask);
-
struct dl_exception exception;
int errcode = _dl_catch_exception (&exception, dl_open_worker, &args);
@@ -879,16 +870,10 @@ no more namespaces available for dlmopen()"));
_dl_close_worker (args.map, true);
- /* Restore the signal mask. In the success case, this
- happens inside dl_open_worker. */
- __libc_signal_restore_set (&args.original_signal_mask);
-
/* All l_nodelete_pending objects should have been deleted
at this point, which is why it is not necessary to reset
the flag here. */
}
- else
- __libc_signal_restore_set (&args.original_signal_mask);
assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);

View File

@ -0,0 +1,27 @@
commit 5177d85b0c050a2333a0c4165c938dd422013d05
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Thu Jan 16 06:45:36 2020 -0800
Clear GL(dl_initfirst) when freeing its link_map memory [BZ# 25396]
We should clear GL(dl_initfirst) when freeing its link_map memory.
Tested on Fedora 31/x86-64 with CET.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
diff --git a/elf/dl-close.c b/elf/dl-close.c
index fa7f3e8174576e46..a9ecdff62dba88fb 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -749,6 +749,10 @@ _dl_close_worker (struct link_map *map, bool force)
if (imap->l_runpath_dirs.dirs != (void *) -1)
free (imap->l_runpath_dirs.dirs);
+ /* Clear GL(dl_initfirst) when freeing its link_map memory. */
+ if (imap == GL(dl_initfirst))
+ GL(dl_initfirst) = NULL;
+
free (imap);
}
}

View File

@ -0,0 +1,143 @@
commit a332bd1518af518c984fad73eba6f46dc5b2b2d4
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Jan 16 16:53:58 2020 +0100
elf: Add elf/tst-dlopenfail-2 [BZ #25396]
Without CET, a jump into a newly loaded object through an overwritten
link map often does not crash, it just executes some random code.
CET detects this in some cases because the function pointer does not
point to the start of a function in the replacement shared object,
so there is no ENDBR instruction.
The new test uses a small shared object and the existing dangling
link map to trigger the bug.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Conflicts:
elf/Makefile
(Test backport differences.)
diff --git a/elf/Makefile b/elf/Makefile
index 16a3e8dcda19b4ba..f1a16fe8ca594c57 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -192,7 +192,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail \
- tst-dlopenfail
+ tst-dlopenfail tst-dlopenfail-2
# reldep9
tests-internal += loadtest unload unload2 circleload1 \
neededtest neededtest2 neededtest3 neededtest4 \
@@ -301,7 +301,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-sonamemove-linkmod1 \
tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
tst-initlazyfailmod tst-finilazyfailmod \
- tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2
+ tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
+ tst-dlopenfailmod3
ifeq (yes,$(have-mtls-dialect-gnu2))
tests += tst-gnu2-tls1
@@ -1569,6 +1570,10 @@ $(objpfx)tst-dlopenfailmod1.so: \
$(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so
LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so
$(objpfx)tst-dlopenfailmod2.so: $(shared-thread-library)
+$(objpfx)tst-dlopenfail-2: $(libdl)
+$(objpfx)tst-dlopenfail.out: \
+ $(objpfx)tst-dlopenfailmod1.so $(objpfx)tst-dlopenfailmod2.so \
+ $(objpfx)tst-dlopenfailmod3.so
$(objpfx)tst-dlopen-nodelete-reloc: $(libdl)
$(objpfx)tst-dlopen-nodelete-reloc.out: \
diff --git a/elf/tst-dlopenfail-2.c b/elf/tst-dlopenfail-2.c
new file mode 100644
index 0000000000000000..35bbde64abbb6603
--- /dev/null
+++ b/elf/tst-dlopenfail-2.c
@@ -0,0 +1,59 @@
+/* Test unrelated dlopen after dlopen failure involving NODELETE.
+ Copyright (C) 2019-2020 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 <dlfcn.h>
+#include <errno.h>
+#include <gnu/lib-names.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static int
+do_test (void)
+{
+ /* This test uses libpthread as the canonical NODELETE module. If
+ libpthread is no longer NODELETE because it has been merged into
+ libc, the test needs to be updated. */
+ TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL);
+
+ /* This is expected to fail because of the missing dependency. */
+ puts ("info: attempting to load tst-dlopenfailmod1.so");
+ TEST_VERIFY (dlopen ("tst-dlopenfailmod1.so", RTLD_LAZY) == NULL);
+ const char *message = dlerror ();
+ TEST_COMPARE_STRING (message,
+ "tst-dlopenfail-missingmod.so:"
+ " cannot open shared object file:"
+ " No such file or directory");
+
+ /* Open a small shared object. With a dangling GL (dl_initfirst)
+ pointer, this is likely to crash because there is no longer any
+ mapped text segment there (bug 25396). */
+
+ puts ("info: attempting to load tst-dlopenfailmod3.so");
+ xdlclose (xdlopen ("tst-dlopenfailmod3.so", RTLD_NOW));
+
+ return 0;
+}
+
+/* Do not perturb the dangling link map. With M_PERTURB, the link map
+ appears to have l_init_called set, so there are no constructor
+ calls and no crashes. */
+#define TEST_NO_MALLOPT
+#include <support/test-driver.c>
diff --git a/elf/tst-dlopenfailmod3.c b/elf/tst-dlopenfailmod3.c
new file mode 100644
index 0000000000000000..636e971264292110
--- /dev/null
+++ b/elf/tst-dlopenfailmod3.c
@@ -0,0 +1,17 @@
+/* Empty module for the tst-dlopenfail-2 test.
+ Copyright (C) 2020 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/>. */

View File

@ -0,0 +1,33 @@
commit ca136bb0a36d0a7056c926bfe5126873566efe40
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 31 13:28:26 2019 +0100
Clarify purpose of assert in _dl_lookup_symbol_x
Only one of the currently defined flags is incompatible with versioned
symbol lookups, so it makes sense to check for that flag and not its
complement.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Gabriel F. T. Gomes <gabrielftg@linux.ibm.com>
Change-Id: I3384349cef90cfd91862ebc34a4053f0c0a99404
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 1d046caf017b582b..efbdb8deb3c0a9d4 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -792,11 +792,9 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
bump_num_relocations ();
- /* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK
- is allowed if we look up a versioned symbol. */
- assert (version == NULL
- || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK))
- == 0);
+ /* DL_LOOKUP_RETURN_NEWEST does not make sense for versioned
+ lookups. */
+ assert (version == NULL || !(flags & DL_LOOKUP_RETURN_NEWEST));
size_t i = 0;
if (__glibc_unlikely (skip_map != NULL))

View File

@ -0,0 +1,54 @@
commit 2a764c6ee848dfe92cb2921ed3b14085f15d9e79
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 31 13:23:06 2019 +0100
Enhance _dl_catch_exception to allow disabling exception handling
In some cases, it is necessary to introduce noexcept regions
where raised dynamic loader exceptions (e.g., from lazy binding)
are fatal, despite being nested in a code region with an active
exception handler. This change enhances _dl_catch_exception with
to provide such a capability. The existing function is reused,
so that it is not necessary to introduce yet another function with
a similar purpose.
Change-Id: Iec1bf642ff95a349fdde8040e9baf851ac7b8904
diff --git a/elf/dl-error-skeleton.c b/elf/dl-error-skeleton.c
index d5f418ab1848f0c4..9cb002ccfed2c7b4 100644
--- a/elf/dl-error-skeleton.c
+++ b/elf/dl-error-skeleton.c
@@ -173,6 +173,18 @@ int
_dl_catch_exception (struct dl_exception *exception,
void (*operate) (void *), void *args)
{
+ /* If exception is NULL, temporarily disable exception handling.
+ Exceptions during operate (args) are fatal. */
+ if (exception == NULL)
+ {
+ struct catch *const old = catch_hook;
+ catch_hook = NULL;
+ operate (args);
+ /* If we get here, the operation was successful. */
+ catch_hook = old;
+ return 0;
+ }
+
/* We need not handle `receiver' since setting a `catch' is handled
before it. */
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 95dc87519b80e0ec..cc2484033fe0d902 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -852,7 +852,9 @@ libc_hidden_proto (_dl_catch_error)
/* Call OPERATE (ARGS). If no error occurs, set *EXCEPTION to zero.
Otherwise, store a copy of the raised exception in *EXCEPTION,
- which has to be freed by _dl_exception_free. */
+ which has to be freed by _dl_exception_free. As a special case, if
+ EXCEPTION is null, call OPERATE (ARGS) with exception handling
+ disabled (so that exceptions are fatal). */
int _dl_catch_exception (struct dl_exception *exception,
void (*operate) (void *), void *args);
libc_hidden_proto (_dl_catch_exception)

View File

@ -0,0 +1,42 @@
commit fcb04b9aed26a737159ef7be9c5a6ad0994437dc
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 31 13:28:49 2019 +0100
Introduce DL_LOOKUP_FOR_RELOCATE flag for _dl_lookup_symbol_x
This will allow changes in dependency processing during non-lazy
binding, for more precise processing of NODELETE objects: During
initial relocation in dlopen, the fate of NODELETE objects is still
unclear, so objects which are depended upon by NODELETE objects
cannot immediately be marked as NODELETE.
Change-Id: Ic7b94a3f7c4719a00ca8e6018088567824da0658
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 053916eeae50467c..afeace4d3e49180c 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -248,7 +248,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
v = (version); \
_lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \
scope, v, _tc, \
- DL_LOOKUP_ADD_DEPENDENCY, NULL); \
+ DL_LOOKUP_ADD_DEPENDENCY \
+ | DL_LOOKUP_FOR_RELOCATE, NULL); \
l->l_lookup_cache.ret = (*ref); \
l->l_lookup_cache.value = _lr; })) \
: l)
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index cc2484033fe0d902..6c5298a80bff8e96 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -908,6 +908,9 @@ enum
DL_LOOKUP_RETURN_NEWEST = 2,
/* Set if dl_lookup* called with GSCOPE lock held. */
DL_LOOKUP_GSCOPE_LOCK = 4,
+ /* Set if dl_lookup is called for non-lazy relocation processing
+ from _dl_relocate_object in elf/dl-reloc.c. */
+ DL_LOOKUP_FOR_RELOCATE = 8,
};
/* Lookup versioned symbol. */

View File

@ -0,0 +1,343 @@
commit 79e0cd7b3c997e211fad44a81fd839dc5b2546e8
Author: Florian Weimer <fweimer@redhat.com>
Date: Wed Nov 27 16:20:47 2019 +0100
Lazy binding failures during dlopen/dlclose must be fatal [BZ #24304]
If a lazy binding failure happens during the execution of an ELF
constructor or destructor, the dynamic loader catches the error
and reports it using the dlerror mechanism. This is undesirable
because there could be other constructors and destructors that
need processing (which are skipped), and the process is in an
inconsistent state at this point. Therefore, we have to issue
a fatal dynamic loader error error and terminate the process.
Note that the _dl_catch_exception in _dl_open is just an inner catch,
to roll back some state locally. If called from dlopen, there is
still an outer catch, which is why calling _dl_init via call_dl_init
and a no-exception is required and cannot be avoiding by moving the
_dl_init call directly into _dl_open.
_dl_fini does not need changes because it does not install an error
handler, so errors are already fatal there.
Change-Id: I6b1addfe2e30f50a1781595f046f44173db9491a
Conflicts:
elf/Makefile
(Usual conflicts due to test backport differences.)
diff --git a/elf/Makefile b/elf/Makefile
index 74a240b3a68ff5e2..b752f6366400d221 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -191,7 +191,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
- tst-sonamemove-link tst-sonamemove-dlopen
+ tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail
# reldep9
tests-internal += loadtest unload unload2 circleload1 \
neededtest neededtest2 neededtest3 neededtest4 \
@@ -281,7 +281,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
tst-absolute-zero-lib tst-big-note-lib \
tst-sonamemove-linkmod1 \
- tst-sonamemove-runmod1 tst-sonamemove-runmod2
+ tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
+ tst-initlazyfailmod tst-finilazyfailmod
ifeq (yes,$(have-mtls-dialect-gnu2))
tests += tst-gnu2-tls1
@@ -1526,3 +1527,13 @@ tst-libc_dlvsym-static-ENV = \
$(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so
$(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
+
+$(objpfx)tst-initfinilazyfail: $(libdl)
+$(objpfx)tst-initfinilazyfail.out: \
+ $(objpfx)tst-initlazyfailmod.so $(objpfx)tst-finilazyfailmod.so
+# Override -z defs, so that we can reference an undefined symbol.
+# Force lazy binding for the same reason.
+LDFLAGS-tst-initlazyfailmod.so = \
+ -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
+LDFLAGS-tst-finilazyfailmod.so = \
+ -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
diff --git a/elf/dl-close.c b/elf/dl-close.c
index ecd6729704ea3294..88aeea25839a34e0 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -106,6 +106,30 @@ 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)
@@ -267,7 +291,8 @@ _dl_close_worker (struct link_map *map, bool force)
&& (imap->l_flags_1 & DF_1_NODELETE) == 0);
/* Call its termination function. Do not do it for
- half-cooked objects. */
+ half-cooked objects. Temporarily disable exception
+ handling, so that errors are fatal. */
if (imap->l_init_called)
{
/* When debugging print a message first. */
@@ -276,22 +301,9 @@ _dl_close_worker (struct link_map *map, bool force)
_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
imap->l_name, nsid);
- if (imap->l_info[DT_FINI_ARRAY] != NULL)
- {
- ElfW(Addr) *array =
- (ElfW(Addr) *) (imap->l_addr
- + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
- unsigned int sz = (imap->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 (imap->l_info[DT_FINI] != NULL)
- DL_CALL_DT_FINI (imap, ((void *) imap->l_addr
- + imap->l_info[DT_FINI]->d_un.d_ptr));
+ if (imap->l_info[DT_FINI_ARRAY] != NULL
+ || imap->l_info[DT_FINI] != NULL)
+ _dl_catch_exception (NULL, call_destructors, imap);
}
#ifdef SHARED
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 518a6cad699ec6d0..c9c0254ee74c4f4b 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -177,6 +177,23 @@ _dl_find_dso_for_object (const ElfW(Addr) addr)
}
rtld_hidden_def (_dl_find_dso_for_object);
+/* struct dl_init_args and call_dl_init are used to call _dl_init with
+ exception handling disabled. */
+struct dl_init_args
+{
+ struct link_map *new;
+ int argc;
+ char **argv;
+ char **env;
+};
+
+static void
+call_dl_init (void *closure)
+{
+ struct dl_init_args *args = closure;
+ _dl_init (args->new, args->argc, args->argv, args->env);
+}
+
static void
dl_open_worker (void *a)
{
@@ -506,8 +523,19 @@ TLS generation counter wrapped! Please report this."));
DL_STATIC_INIT (new);
#endif
- /* Run the initializer functions of new objects. */
- _dl_init (new, args->argc, args->argv, args->env);
+ /* Run the initializer functions of new objects. Temporarily
+ disable the exception handler, so that lazy binding failures are
+ fatal. */
+ {
+ struct dl_init_args init_args =
+ {
+ .new = new,
+ .argc = args->argc,
+ .argv = args->argv,
+ .env = args->env
+ };
+ _dl_catch_exception (NULL, call_dl_init, &init_args);
+ }
/* Now we can make the new map available in the global scope. */
if (mode & RTLD_GLOBAL)
diff --git a/elf/tst-finilazyfailmod.c b/elf/tst-finilazyfailmod.c
new file mode 100644
index 0000000000000000..2670bd1a9400d0ef
--- /dev/null
+++ b/elf/tst-finilazyfailmod.c
@@ -0,0 +1,27 @@
+/* Helper module for tst-initfinilazyfail: lazy binding failure in destructor.
+ Copyright (C) 2019 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/>. */
+
+/* An undefined function. Calling it will cause a lazy binding
+ failure. */
+void undefined_function (void);
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ undefined_function ();
+}
diff --git a/elf/tst-initfinilazyfail.c b/elf/tst-initfinilazyfail.c
new file mode 100644
index 0000000000000000..9b4a3d0c0ffbb7c6
--- /dev/null
+++ b/elf/tst-initfinilazyfail.c
@@ -0,0 +1,84 @@
+/* Test that lazy binding failures in constructors and destructors are fatal.
+ Copyright (C) 2019 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 <dlfcn.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static void
+test_constructor (void *closure)
+{
+ void *handle = dlopen ("tst-initlazyfailmod.so", RTLD_LAZY);
+ if (handle == NULL)
+ FAIL_EXIT (2, "dlopen did not terminate the process: %s", dlerror ());
+ else
+ FAIL_EXIT (2, "dlopen did not terminate the process (%p)", handle);
+}
+
+static void
+test_destructor (void *closure)
+{
+ void *handle = xdlopen ("tst-finilazyfailmod.so", RTLD_LAZY);
+ int ret = dlclose (handle);
+ const char *message = dlerror ();
+ if (message != NULL)
+ FAIL_EXIT (2, "dlclose did not terminate the process: %d, %s",
+ ret, message);
+ else
+ FAIL_EXIT (2, "dlopen did not terminate the process: %d", ret);
+}
+
+static int
+do_test (void)
+{
+ {
+ struct support_capture_subprocess proc
+ = support_capture_subprocess (test_constructor, NULL);
+ support_capture_subprocess_check (&proc, "constructor", 127,
+ sc_allow_stderr);
+ printf ("info: constructor failure output: [[%s]]\n", proc.err.buffer);
+ TEST_VERIFY (strstr (proc.err.buffer,
+ "tst-initfinilazyfail: symbol lookup error: ")
+ != NULL);
+ TEST_VERIFY (strstr (proc.err.buffer,
+ "tst-initlazyfailmod.so: undefined symbol:"
+ " undefined_function\n") != NULL);
+ support_capture_subprocess_free (&proc);
+ }
+
+ {
+ struct support_capture_subprocess proc
+ = support_capture_subprocess (test_destructor, NULL);
+ support_capture_subprocess_check (&proc, "destructor", 127,
+ sc_allow_stderr);
+ printf ("info: destructor failure output: [[%s]]\n", proc.err.buffer);
+ TEST_VERIFY (strstr (proc.err.buffer,
+ "tst-initfinilazyfail: symbol lookup error: ")
+ != NULL);
+ TEST_VERIFY (strstr (proc.err.buffer,
+ "tst-finilazyfailmod.so: undefined symbol:"
+ " undefined_function\n") != NULL);
+ support_capture_subprocess_free (&proc);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-initlazyfailmod.c b/elf/tst-initlazyfailmod.c
new file mode 100644
index 0000000000000000..36348b58d634d2bb
--- /dev/null
+++ b/elf/tst-initlazyfailmod.c
@@ -0,0 +1,27 @@
+/* Helper module for tst-initfinilazyfail: lazy binding failure in constructor.
+ Copyright (C) 2019 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/>. */
+
+/* An undefined function. Calling it will cause a lazy binding
+ failure. */
+void undefined_function (void);
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ undefined_function ();
+}

View File

@ -0,0 +1,308 @@
commit 440b7f8653e4ed8f6e1425145208050b795e9a6c
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 31 18:25:39 2019 +0100
Avoid late failure in dlopen in global scope update [BZ #25112]
The call to add_to_global in dl_open_worker happens after running ELF
constructors for new objects. At this point, proper recovery from
malloc failure would be quite complicated: We would have to run the
ELF destructors and close all opened objects, something that we
currently do not do.
Instead, this change splits add_to_global into two phases,
add_to_global_resize (which can raise an exception, called before ELF
constructors run), and add_to_global_update (which cannot, called
after ELF constructors). A complication arises due to recursive
dlopen: After the inner dlopen consumes some space, the pre-allocation
in the outer dlopen may no longer be sufficient. A new member in the
namespace structure, _ns_global_scope_pending_adds keeps track of the
maximum number of objects that need to be added to the global scope.
This enables the inner add_to_global_resize call to take into account
the needs of an outer dlopen.
Most code in the dynamic linker assumes that the number of global
scope entries fits into an unsigned int (matching the r_nlist member
of struct r_scop_elem). Therefore, change the type of
_ns_global_scope_alloc to unsigned int (from size_t), and add overflow
checks.
Change-Id: Ie08e2f318510d5a6a4bcb1c315f46791b5b77524
diff --git a/elf/dl-open.c b/elf/dl-open.c
index c9c0254ee74c4f4b..85db4f0ecb5f29ce 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -50,22 +50,38 @@ struct dl_open_args
struct link_map *map;
/* Namespace ID. */
Lmid_t nsid;
+
+ /* Original value of _ns_global_scope_pending_adds. Set by
+ dl_open_worker. Only valid if nsid is a real namespace
+ (non-negative). */
+ unsigned int original_global_scope_pending_adds;
+
/* Original parameters to the program and the current environment. */
int argc;
char **argv;
char **env;
};
+/* Called in case the global scope cannot be extended. */
+static void __attribute__ ((noreturn))
+add_to_global_resize_failure (struct link_map *new)
+{
+ _dl_signal_error (ENOMEM, new->l_libname->name, NULL,
+ N_ ("cannot extend global scope"));
+}
-static int
-add_to_global (struct link_map *new)
+/* Grow the global scope array for the namespace, so that all the new
+ global objects can be added later in add_to_global_update, without
+ risk of memory allocation failure. add_to_global_resize raises
+ exceptions for memory allocation errors. */
+static void
+add_to_global_resize (struct link_map *new)
{
- struct link_map **new_global;
- unsigned int to_add = 0;
- unsigned int cnt;
+ struct link_namespaces *ns = &GL (dl_ns)[new->l_ns];
/* Count the objects we have to put in the global scope. */
- for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
+ unsigned int to_add = 0;
+ for (unsigned int cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
if (new->l_searchlist.r_list[cnt]->l_global == 0)
++to_add;
@@ -83,47 +99,51 @@ add_to_global (struct link_map *new)
in an realloc() call. Therefore we allocate a completely new
array the first time we have to add something to the locale scope. */
- struct link_namespaces *ns = &GL(dl_ns)[new->l_ns];
+ if (__builtin_add_overflow (ns->_ns_global_scope_pending_adds, to_add,
+ &ns->_ns_global_scope_pending_adds))
+ add_to_global_resize_failure (new);
+
+ unsigned int new_size = 0; /* 0 means no new allocation. */
+ void *old_global = NULL; /* Old allocation if free-able. */
+
+ /* Minimum required element count for resizing. Adjusted below for
+ an exponential resizing policy. */
+ size_t required_new_size;
+ if (__builtin_add_overflow (ns->_ns_main_searchlist->r_nlist,
+ ns->_ns_global_scope_pending_adds,
+ &required_new_size))
+ add_to_global_resize_failure (new);
+
if (ns->_ns_global_scope_alloc == 0)
{
- /* This is the first dynamic object given global scope. */
- ns->_ns_global_scope_alloc
- = ns->_ns_main_searchlist->r_nlist + to_add + 8;
- new_global = (struct link_map **)
- malloc (ns->_ns_global_scope_alloc * sizeof (struct link_map *));
- if (new_global == NULL)
- {
- ns->_ns_global_scope_alloc = 0;
- nomem:
- _dl_signal_error (ENOMEM, new->l_libname->name, NULL,
- N_("cannot extend global scope"));
- return 1;
- }
+ if (__builtin_add_overflow (required_new_size, 8, &new_size))
+ add_to_global_resize_failure (new);
+ }
+ else if (required_new_size > ns->_ns_global_scope_alloc)
+ {
+ if (__builtin_mul_overflow (required_new_size, 2, &new_size))
+ add_to_global_resize_failure (new);
- /* Copy over the old entries. */
- ns->_ns_main_searchlist->r_list
- = memcpy (new_global, ns->_ns_main_searchlist->r_list,
- (ns->_ns_main_searchlist->r_nlist
- * sizeof (struct link_map *)));
+ /* The old array was allocated with our malloc, not the minimal
+ malloc. */
+ old_global = ns->_ns_main_searchlist->r_list;
}
- else if (ns->_ns_main_searchlist->r_nlist + to_add
- > ns->_ns_global_scope_alloc)
+
+ if (new_size > 0)
{
- /* We have to extend the existing array of link maps in the
- main map. */
- struct link_map **old_global
- = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list;
- size_t new_nalloc = ((ns->_ns_global_scope_alloc + to_add) * 2);
-
- new_global = (struct link_map **)
- malloc (new_nalloc * sizeof (struct link_map *));
+ size_t allocation_size;
+ if (__builtin_mul_overflow (new_size, sizeof (struct link_map *),
+ &allocation_size))
+ add_to_global_resize_failure (new);
+ struct link_map **new_global = malloc (allocation_size);
if (new_global == NULL)
- goto nomem;
+ add_to_global_resize_failure (new);
- memcpy (new_global, old_global,
- ns->_ns_global_scope_alloc * sizeof (struct link_map *));
+ /* Copy over the old entries. */
+ memcpy (new_global, ns->_ns_main_searchlist->r_list,
+ ns->_ns_main_searchlist->r_nlist * sizeof (struct link_map *));
- ns->_ns_global_scope_alloc = new_nalloc;
+ ns->_ns_global_scope_alloc = new_size;
ns->_ns_main_searchlist->r_list = new_global;
if (!RTLD_SINGLE_THREAD_P)
@@ -131,16 +151,28 @@ add_to_global (struct link_map *new)
free (old_global);
}
+}
+
+/* Actually add the new global objects to the global scope. Must be
+ called after add_to_global_resize. This function cannot fail. */
+static void
+add_to_global_update (struct link_map *new)
+{
+ struct link_namespaces *ns = &GL (dl_ns)[new->l_ns];
/* Now add the new entries. */
unsigned int new_nlist = ns->_ns_main_searchlist->r_nlist;
- for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
+ for (unsigned int cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
{
struct link_map *map = new->l_searchlist.r_list[cnt];
if (map->l_global == 0)
{
map->l_global = 1;
+
+ /* The array has been resized by add_to_global_resize. */
+ assert (new_nlist < ns->_ns_global_scope_alloc);
+
ns->_ns_main_searchlist->r_list[new_nlist++] = map;
/* We modify the global scope. Report this. */
@@ -149,10 +181,15 @@ add_to_global (struct link_map *new)
map->l_name, map->l_ns);
}
}
+
+ /* Some of the pending adds have been performed by the loop above.
+ Adjust the counter accordingly. */
+ unsigned int added = new_nlist - ns->_ns_main_searchlist->r_nlist;
+ assert (added <= ns->_ns_global_scope_pending_adds);
+ ns->_ns_global_scope_pending_adds -= added;
+
atomic_write_barrier ();
ns->_ns_main_searchlist->r_nlist = new_nlist;
-
- return 0;
}
/* Search link maps in all namespaces for the DSO that contains the object at
@@ -225,6 +262,10 @@ dl_open_worker (void *a)
args->nsid = call_map->l_ns;
}
+ /* Retain the old value, so that it can be restored. */
+ args->original_global_scope_pending_adds
+ = GL (dl_ns)[args->nsid]._ns_global_scope_pending_adds;
+
/* One might be tempted to assert that we are RT_CONSISTENT at this point, but that
may not be true if this is a recursive call to dlopen. */
_dl_debug_initialize (0, args->nsid);
@@ -266,7 +307,10 @@ dl_open_worker (void *a)
/* If the user requested the object to be in the global namespace
but it is not so far, add it now. */
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
- (void) add_to_global (new);
+ {
+ add_to_global_resize (new);
+ add_to_global_update (new);
+ }
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
@@ -523,6 +567,11 @@ TLS generation counter wrapped! Please report this."));
DL_STATIC_INIT (new);
#endif
+ /* Perform the necessary allocations for adding new global objects
+ to the global scope below, via add_to_global_update. */
+ if (mode & RTLD_GLOBAL)
+ add_to_global_resize (new);
+
/* Run the initializer functions of new objects. Temporarily
disable the exception handler, so that lazy binding failures are
fatal. */
@@ -539,10 +588,7 @@ TLS generation counter wrapped! Please report this."));
/* Now we can make the new map available in the global scope. */
if (mode & RTLD_GLOBAL)
- /* Move the object in the global namespace. */
- if (add_to_global (new) != 0)
- /* It failed. */
- return;
+ add_to_global_update (new);
#ifndef SHARED
/* We must be the static _dl_open in libc.a. A static program that
@@ -556,7 +602,6 @@ TLS generation counter wrapped! Please report this."));
new->l_name, new->l_ns, new->l_direct_opencount);
}
-
void *
_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
int argc, char *argv[], char *env[])
@@ -624,6 +669,19 @@ no more namespaces available for dlmopen()"));
_dl_unload_cache ();
#endif
+ /* Do this for both the error and success cases. The old value has
+ only been determined if the namespace ID was assigned (i.e., it
+ is not __LM_ID_CALLER). In the success case, we actually may
+ have consumed more pending adds than planned (because the local
+ scopes overlap in case of a recursive dlopen, the inner dlopen
+ doing some of the globalization work of the outer dlopen), so the
+ old pending adds value is larger than absolutely necessary.
+ Since it is just a conservative upper bound, this is harmless.
+ The top-level dlopen call will restore the field to zero. */
+ if (args.nsid >= 0)
+ GL (dl_ns)[args.nsid]._ns_global_scope_pending_adds
+ = args.original_global_scope_pending_adds;
+
/* See if an error occurred during loading. */
if (__glibc_unlikely (exception.errstring != NULL))
{
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 6c5298a80bff8e96..57fbefea3cb841e9 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -311,7 +311,14 @@ struct rtld_global
/* This is zero at program start to signal that the global scope map is
allocated by rtld. Later it keeps the size of the map. It might be
reset if in _dl_close if the last global object is removed. */
- size_t _ns_global_scope_alloc;
+ unsigned int _ns_global_scope_alloc;
+
+ /* During dlopen, this is the number of objects that still need to
+ be added to the global scope map. It has to be taken into
+ account when resizing the map, for future map additions after
+ recursive dlopen calls from ELF constructors. */
+ unsigned int _ns_global_scope_pending_adds;
+
/* Search table for unique objects. */
struct unique_sym_table
{

View File

@ -0,0 +1,490 @@
commit a509eb117fac1d764b15eba64993f4bdb63d7f3c
Author: Florian Weimer <fweimer@redhat.com>
Date: Wed Nov 27 16:37:17 2019 +0100
Avoid late dlopen failure due to scope, TLS slotinfo updates [BZ #25112]
This change splits the scope and TLS slotinfo updates in dlopen into
two parts: one to resize the data structures, and one to actually apply
the update. The call to add_to_global_resize in dl_open_worker is moved
before the demarcation point at which no further memory allocations are
allowed.
_dl_add_to_slotinfo is adjusted to make the list update optional. There
is some optimization possibility here because we could grow the slotinfo
list of arrays in a single call, one the largest TLS modid is known.
This commit does not fix the fatal meory allocation failure in
_dl_update_slotinfo. Ideally, this error during dlopen should be
recoverable.
The update order of scopes and TLS data structures is retained, although
it appears to be more correct to fully initialize TLS first, and then
expose symbols in the newly loaded objects via the scope update.
Tested on x86_64-linux-gnu.
Change-Id: I240c58387dabda3ca1bcab48b02115175fa83d6c
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 85db4f0ecb5f29ce..b330cff7d349224a 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -33,6 +33,7 @@
#include <stap-probe.h>
#include <atomic.h>
#include <libc-internal.h>
+#include <array_length.h>
#include <dl-dst.h>
#include <dl-prop.h>
@@ -214,6 +215,215 @@ _dl_find_dso_for_object (const ElfW(Addr) addr)
}
rtld_hidden_def (_dl_find_dso_for_object);
+/* Return true if NEW is found in the scope for MAP. */
+static size_t
+scope_has_map (struct link_map *map, struct link_map *new)
+{
+ size_t cnt;
+ for (cnt = 0; map->l_scope[cnt] != NULL; ++cnt)
+ if (map->l_scope[cnt] == &new->l_searchlist)
+ return true;
+ return false;
+}
+
+/* Return the length of the scope for MAP. */
+static size_t
+scope_size (struct link_map *map)
+{
+ size_t cnt;
+ for (cnt = 0; map->l_scope[cnt] != NULL; )
+ ++cnt;
+ return cnt;
+}
+
+/* Resize the scopes of depended-upon objects, so that the new object
+ can be added later without further allocation of memory. This
+ function can raise an exceptions due to malloc failure. */
+static void
+resize_scopes (struct link_map *new)
+{
+ /* If the file is not loaded now as a dependency, add the search
+ list of the newly loaded object to the scope. */
+ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+
+ /* If the initializer has been called already, the object has
+ not been loaded here and now. */
+ if (imap->l_init_called && imap->l_type == lt_loaded)
+ {
+ if (scope_has_map (imap, new))
+ /* Avoid duplicates. */
+ continue;
+
+ size_t cnt = scope_size (imap);
+ if (__glibc_unlikely (cnt + 1 >= imap->l_scope_max))
+ {
+ /* The l_scope array is too small. Allocate a new one
+ dynamically. */
+ size_t new_size;
+ struct r_scope_elem **newp;
+
+ if (imap->l_scope != imap->l_scope_mem
+ && imap->l_scope_max < array_length (imap->l_scope_mem))
+ {
+ /* If the current l_scope memory is not pointing to
+ the static memory in the structure, but the
+ static memory in the structure is large enough to
+ use for cnt + 1 scope entries, then switch to
+ using the static memory. */
+ new_size = array_length (imap->l_scope_mem);
+ newp = imap->l_scope_mem;
+ }
+ else
+ {
+ new_size = imap->l_scope_max * 2;
+ newp = (struct r_scope_elem **)
+ malloc (new_size * sizeof (struct r_scope_elem *));
+ if (newp == NULL)
+ _dl_signal_error (ENOMEM, "dlopen", NULL,
+ N_("cannot create scope list"));
+ }
+
+ /* Copy the array and the terminating NULL. */
+ memcpy (newp, imap->l_scope,
+ (cnt + 1) * sizeof (imap->l_scope[0]));
+ struct r_scope_elem **old = imap->l_scope;
+
+ imap->l_scope = newp;
+
+ if (old != imap->l_scope_mem)
+ _dl_scope_free (old);
+
+ imap->l_scope_max = new_size;
+ }
+ }
+ }
+}
+
+/* Second stage of resize_scopes: Add NEW to the scopes. Also print
+ debugging information about scopes if requested.
+
+ This function cannot raise an exception because all required memory
+ has been allocated by a previous call to resize_scopes. */
+static void
+update_scopes (struct link_map *new)
+{
+ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+ int from_scope = 0;
+
+ if (imap->l_init_called && imap->l_type == lt_loaded)
+ {
+ if (scope_has_map (imap, new))
+ /* Avoid duplicates. */
+ continue;
+
+ size_t cnt = scope_size (imap);
+ /* Assert that resize_scopes has sufficiently enlarged the
+ array. */
+ assert (cnt + 1 < imap->l_scope_max);
+
+ /* First terminate the extended list. Otherwise a thread
+ might use the new last element and then use the garbage
+ at offset IDX+1. */
+ imap->l_scope[cnt + 1] = NULL;
+ atomic_write_barrier ();
+ imap->l_scope[cnt] = &new->l_searchlist;
+
+ from_scope = cnt;
+ }
+
+ /* Print scope information. */
+ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
+ _dl_show_scope (imap, from_scope);
+ }
+}
+
+/* Call _dl_add_to_slotinfo with DO_ADD set to false, to allocate
+ space in GL (dl_tls_dtv_slotinfo_list). This can raise an
+ exception. The return value is true if any of the new objects use
+ TLS. */
+static bool
+resize_tls_slotinfo (struct link_map *new)
+{
+ bool any_tls = false;
+ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+
+ /* Only add TLS memory if this object is loaded now and
+ therefore is not yet initialized. */
+ if (! imap->l_init_called && imap->l_tls_blocksize > 0)
+ {
+ _dl_add_to_slotinfo (imap, false);
+ any_tls = true;
+ }
+ }
+ return any_tls;
+}
+
+/* Second stage of TLS update, after resize_tls_slotinfo. This
+ function does not raise any exception. It should only be called if
+ resize_tls_slotinfo returned true. */
+static void
+update_tls_slotinfo (struct link_map *new)
+{
+ unsigned int first_static_tls = new->l_searchlist.r_nlist;
+ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+
+ /* Only add TLS memory if this object is loaded now and
+ therefore is not yet initialized. */
+ if (! imap->l_init_called && imap->l_tls_blocksize > 0)
+ {
+ _dl_add_to_slotinfo (imap, true);
+
+ if (imap->l_need_tls_init
+ && first_static_tls == new->l_searchlist.r_nlist)
+ first_static_tls = i;
+ }
+ }
+
+ if (__builtin_expect (++GL(dl_tls_generation) == 0, 0))
+ _dl_fatal_printf (N_("\
+TLS generation counter wrapped! Please report this."));
+
+ /* We need a second pass for static tls data, because
+ _dl_update_slotinfo must not be run while calls to
+ _dl_add_to_slotinfo are still pending. */
+ for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+
+ if (imap->l_need_tls_init
+ && ! imap->l_init_called
+ && imap->l_tls_blocksize > 0)
+ {
+ /* For static TLS we have to allocate the memory here and
+ now, but we can delay updating the DTV. */
+ imap->l_need_tls_init = 0;
+#ifdef SHARED
+ /* Update the slot information data for at least the
+ generation of the DSO we are allocating data for. */
+
+ /* FIXME: This can terminate the process on memory
+ allocation failure. It is not possible to raise
+ exceptions from this context; to fix this bug,
+ _dl_update_slotinfo would have to be split into two
+ operations, similar to resize_scopes and update_scopes
+ above. This is related to bug 16134. */
+ _dl_update_slotinfo (imap->l_tls_modid);
+#endif
+
+ GL(dl_init_static_tls) (imap);
+ assert (imap->l_need_tls_init == 0);
+ }
+ }
+}
+
/* struct dl_init_args and call_dl_init are used to call _dl_init with
exception handling disabled. */
struct dl_init_args
@@ -431,133 +641,40 @@ dl_open_worker (void *a)
relocation. */
_dl_open_check (new);
- /* If the file is not loaded now as a dependency, add the search
- list of the newly loaded object to the scope. */
- bool any_tls = false;
- unsigned int first_static_tls = new->l_searchlist.r_nlist;
- for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
- {
- struct link_map *imap = new->l_searchlist.r_list[i];
- int from_scope = 0;
+ /* This only performs the memory allocations. The actual update of
+ the scopes happens below, after failure is impossible. */
+ resize_scopes (new);
- /* If the initializer has been called already, the object has
- not been loaded here and now. */
- if (imap->l_init_called && imap->l_type == lt_loaded)
- {
- struct r_scope_elem **runp = imap->l_scope;
- size_t cnt = 0;
-
- while (*runp != NULL)
- {
- if (*runp == &new->l_searchlist)
- break;
- ++cnt;
- ++runp;
- }
-
- if (*runp != NULL)
- /* Avoid duplicates. */
- continue;
-
- if (__glibc_unlikely (cnt + 1 >= imap->l_scope_max))
- {
- /* The 'r_scope' array is too small. Allocate a new one
- dynamically. */
- size_t new_size;
- struct r_scope_elem **newp;
-
-#define SCOPE_ELEMS(imap) \
- (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0]))
+ /* Increase the size of the GL (dl_tls_dtv_slotinfo_list) data
+ structure. */
+ bool any_tls = resize_tls_slotinfo (new);
- if (imap->l_scope != imap->l_scope_mem
- && imap->l_scope_max < SCOPE_ELEMS (imap))
- {
- new_size = SCOPE_ELEMS (imap);
- newp = imap->l_scope_mem;
- }
- else
- {
- new_size = imap->l_scope_max * 2;
- newp = (struct r_scope_elem **)
- malloc (new_size * sizeof (struct r_scope_elem *));
- if (newp == NULL)
- _dl_signal_error (ENOMEM, "dlopen", NULL,
- N_("cannot create scope list"));
- }
-
- memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0]));
- struct r_scope_elem **old = imap->l_scope;
-
- imap->l_scope = newp;
-
- if (old != imap->l_scope_mem)
- _dl_scope_free (old);
-
- imap->l_scope_max = new_size;
- }
-
- /* First terminate the extended list. Otherwise a thread
- might use the new last element and then use the garbage
- at offset IDX+1. */
- imap->l_scope[cnt + 1] = NULL;
- atomic_write_barrier ();
- imap->l_scope[cnt] = &new->l_searchlist;
-
- /* Print only new scope information. */
- from_scope = cnt;
- }
- /* Only add TLS memory if this object is loaded now and
- therefore is not yet initialized. */
- else if (! imap->l_init_called
- /* Only if the module defines thread local data. */
- && __builtin_expect (imap->l_tls_blocksize > 0, 0))
- {
- /* Now that we know the object is loaded successfully add
- modules containing TLS data to the slot info table. We
- might have to increase its size. */
- _dl_add_to_slotinfo (imap);
-
- if (imap->l_need_tls_init
- && first_static_tls == new->l_searchlist.r_nlist)
- first_static_tls = i;
-
- /* We have to bump the generation counter. */
- any_tls = true;
- }
-
- /* Print scope information. */
- if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
- _dl_show_scope (imap, from_scope);
- }
-
- /* Bump the generation number if necessary. */
- if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0))
- _dl_fatal_printf (N_("\
-TLS generation counter wrapped! Please report this."));
-
- /* We need a second pass for static tls data, because _dl_update_slotinfo
- must not be run while calls to _dl_add_to_slotinfo are still pending. */
- for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i)
- {
- struct link_map *imap = new->l_searchlist.r_list[i];
-
- if (imap->l_need_tls_init
- && ! imap->l_init_called
- && imap->l_tls_blocksize > 0)
- {
- /* For static TLS we have to allocate the memory here and
- now, but we can delay updating the DTV. */
- imap->l_need_tls_init = 0;
-#ifdef SHARED
- /* Update the slot information data for at least the
- generation of the DSO we are allocating data for. */
- _dl_update_slotinfo (imap->l_tls_modid);
-#endif
+ /* Perform the necessary allocations for adding new global objects
+ to the global scope below. */
+ if (mode & RTLD_GLOBAL)
+ add_to_global_resize (new);
- GL(dl_init_static_tls) (imap);
- assert (imap->l_need_tls_init == 0);
- }
- }
+ /* Demarcation point: After this, no recoverable errors are allowed.
+ All memory allocations for new objects must have happened
+ before. */
+
+ /* Second stage after resize_scopes: Actually perform the scope
+ update. After this, dlsym and lazy binding can bind to new
+ objects. */
+ update_scopes (new);
+
+ /* FIXME: It is unclear whether the order here is correct.
+ Shouldn't new objects be made available for binding (and thus
+ execution) only after there TLS data has been set up fully?
+ Fixing bug 16134 will likely make this distinction less
+ important. */
+
+ /* Second stage after resize_tls_slotinfo: Update the slotinfo data
+ structures. */
+ if (any_tls)
+ /* FIXME: This calls _dl_update_slotinfo, which aborts the process
+ on memory allocation failure. See bug 16134. */
+ update_tls_slotinfo (new);
/* Notify the debugger all new objects have been relocated. */
if (relocation_in_progress)
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index c87caf13d6a97ba4..a2def280b7096960 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -883,7 +883,7 @@ _dl_tls_get_addr_soft (struct link_map *l)
void
-_dl_add_to_slotinfo (struct link_map *l)
+_dl_add_to_slotinfo (struct link_map *l, bool do_add)
{
/* Now that we know the object is loaded successfully add
modules containing TLS data to the dtv info table. We
@@ -939,6 +939,9 @@ cannot create TLS data structures"));
}
/* Add the information into the slotinfo data structure. */
- listp->slotinfo[idx].map = l;
- listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
+ if (do_add)
+ {
+ listp->slotinfo[idx].map = l;
+ listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
+ }
}
diff --git a/elf/rtld.c b/elf/rtld.c
index 4ec26a79cbb0aa4f..0aa1a2a19f649e16 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2167,7 +2167,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
/* Add object to slot information data if necessasy. */
if (l->l_tls_blocksize != 0 && tls_init_tp_called)
- _dl_add_to_slotinfo (l);
+ _dl_add_to_slotinfo (l, true);
}
}
else
@@ -2215,7 +2215,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
/* Add object to slot information data if necessasy. */
if (l->l_tls_blocksize != 0 && tls_init_tp_called)
- _dl_add_to_slotinfo (l);
+ _dl_add_to_slotinfo (l, true);
}
HP_TIMING_NOW (stop);
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 57fbefea3cb841e9..c6b7e61badbfd513 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1135,8 +1135,15 @@ extern void *_dl_open (const char *name, int mode, const void *caller,
old scope, OLD can't be freed until no thread is using it. */
extern int _dl_scope_free (void *) attribute_hidden;
-/* Add module to slot information data. */
-extern void _dl_add_to_slotinfo (struct link_map *l) attribute_hidden;
+
+/* Add module to slot information data. If DO_ADD is false, only the
+ required memory is allocated. Must be called with GL
+ (dl_load_lock) acquired. If the function has already been called
+ for the link map L with !do_add, then this function will not raise
+ an exception, otherwise it is possible that it encounters a memory
+ allocation failure. */
+extern void _dl_add_to_slotinfo (struct link_map *l, bool do_add)
+ attribute_hidden;
/* Update slot information data for at least the generation of the
module with the given index. */

View File

@ -0,0 +1,619 @@
commit f63b73814f74032c0e5d0a83300e3d864ef905e5
Author: Florian Weimer <fweimer@redhat.com>
Date: Wed Nov 13 15:44:56 2019 +0100
Remove all loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]
This introduces a “pending NODELETE” state in the link map, which is
flipped to the persistent NODELETE state late in dlopen, via
activate_nodelete. During initial relocation, symbol binding
records pending NODELETE state only. dlclose ignores pending NODELETE
state. Taken together, this results that a partially completed dlopen
is rolled back completely because new NODELETE mappings are unloaded.
Tested on x86_64-linux-gnu and i386-linux-gnu.
Change-Id: Ib2a3d86af6f92d75baca65431d74783ee0dbc292
Conflicts:
elf/Makefile
(Usual conflicts due to test backport differences.)
diff --git a/elf/Makefile b/elf/Makefile
index b752f6366400d221..bf7c41f38be42184 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -191,7 +191,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
- tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail
+ tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail \
+ tst-dlopenfail
# reldep9
tests-internal += loadtest unload unload2 circleload1 \
neededtest neededtest2 neededtest3 neededtest4 \
@@ -282,7 +283,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-absolute-zero-lib tst-big-note-lib \
tst-sonamemove-linkmod1 \
tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
- tst-initlazyfailmod tst-finilazyfailmod
+ tst-initlazyfailmod tst-finilazyfailmod \
+ tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2
ifeq (yes,$(have-mtls-dialect-gnu2))
tests += tst-gnu2-tls1
@@ -1537,3 +1539,13 @@ LDFLAGS-tst-initlazyfailmod.so = \
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
LDFLAGS-tst-finilazyfailmod.so = \
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
+
+$(objpfx)tst-dlopenfail: $(libdl)
+$(objpfx)tst-dlopenfail.out: \
+ $(objpfx)tst-dlopenfailmod1.so $(objpfx)tst-dlopenfailmod2.so
+# Order matters here. tst-dlopenfaillinkmod.so's soname ensures
+# a run-time loader failure.
+$(objpfx)tst-dlopenfailmod1.so: \
+ $(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so
+LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so
+$(objpfx)tst-dlopenfailmod2.so: $(shared-thread-library)
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 88aeea25839a34e0..243a028c443173c1 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -168,14 +168,6 @@ _dl_close_worker (struct link_map *map, bool force)
char done[nloaded];
struct link_map *maps[nloaded];
- /* Clear DF_1_NODELETE to force object deletion. We don't need to touch
- l_tls_dtor_count because forced object deletion only happens when an
- error occurs during object load. Destructor registration for TLS
- non-POD objects should not have happened till then for this
- object. */
- if (force)
- map->l_flags_1 &= ~DF_1_NODELETE;
-
/* Run over the list and assign indexes to the link maps and enter
them into the MAPS array. */
int idx = 0;
@@ -205,7 +197,7 @@ _dl_close_worker (struct link_map *map, bool force)
/* Check whether this object is still used. */
if (l->l_type == lt_loaded
&& l->l_direct_opencount == 0
- && (l->l_flags_1 & DF_1_NODELETE) == 0
+ && l->l_nodelete != link_map_nodelete_active
/* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why
acquire is sufficient and correct. */
&& atomic_load_acquire (&l->l_tls_dtor_count) == 0
@@ -288,7 +280,7 @@ _dl_close_worker (struct link_map *map, bool force)
if (!used[i])
{
assert (imap->l_type == lt_loaded
- && (imap->l_flags_1 & DF_1_NODELETE) == 0);
+ && imap->l_nodelete != link_map_nodelete_active);
/* Call its termination function. Do not do it for
half-cooked objects. Temporarily disable exception
@@ -828,7 +820,7 @@ _dl_close (void *_map)
before we took the lock. There is no way to detect this (see below)
so we proceed assuming this isn't the case. First see whether we
can remove the object at all. */
- if (__glibc_unlikely (map->l_flags_1 & DF_1_NODELETE))
+ if (__glibc_unlikely (map->l_nodelete == link_map_nodelete_active))
{
/* Nope. Do nothing. */
__rtld_lock_unlock_recursive (GL(dl_load_lock));
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index efbdb8deb3c0a9d4..c5e5857fb1fe2808 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -192,9 +192,10 @@ enter_unique_sym (struct unique_sym *table, size_t size,
Return the matching symbol in RESULT. */
static void
do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
- const struct link_map *map, struct sym_val *result,
+ struct link_map *map, struct sym_val *result,
int type_class, const ElfW(Sym) *sym, const char *strtab,
- const ElfW(Sym) *ref, const struct link_map *undef_map)
+ const ElfW(Sym) *ref, const struct link_map *undef_map,
+ int flags)
{
/* We have to determine whether we already found a symbol with this
name before. If not then we have to add it to the search table.
@@ -222,7 +223,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
copy from the copy addressed through the
relocation. */
result->s = sym;
- result->m = (struct link_map *) map;
+ result->m = map;
}
else
{
@@ -311,9 +312,19 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
new_hash, strtab + sym->st_name, sym, map);
if (map->l_type == lt_loaded)
- /* Make sure we don't unload this object by
- setting the appropriate flag. */
- ((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE;
+ {
+ /* Make sure we don't unload this object by
+ setting the appropriate flag. */
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
+ && map->l_nodelete == link_map_nodelete_inactive)
+ _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to unique symbol\n",
+ map->l_name, map->l_ns);
+ if (flags & DL_LOOKUP_FOR_RELOCATE)
+ map->l_nodelete = link_map_nodelete_pending;
+ else
+ map->l_nodelete = link_map_nodelete_active;
+ }
}
++tab->n_elements;
@@ -525,8 +536,9 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
return 1;
case STB_GNU_UNIQUE:;
- do_lookup_unique (undef_name, new_hash, map, result, type_class,
- sym, strtab, ref, undef_map);
+ do_lookup_unique (undef_name, new_hash, (struct link_map *) map,
+ result, type_class, sym, strtab, ref,
+ undef_map, flags);
return 1;
default:
@@ -568,9 +580,13 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
if (undef_map == map)
return 0;
- /* Avoid references to objects which cannot be unloaded anyway. */
+ /* Avoid references to objects which cannot be unloaded anyway. We
+ do not need to record dependencies if this object goes away
+ during dlopen failure, either. IFUNC resolvers with relocation
+ dependencies may pick an dependency which can be dlclose'd, but
+ such IFUNC resolvers are undefined anyway. */
assert (map->l_type == lt_loaded);
- if ((map->l_flags_1 & DF_1_NODELETE) != 0)
+ if (map->l_nodelete != link_map_nodelete_inactive)
return 0;
struct link_map_reldeps *l_reldeps
@@ -678,16 +694,33 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
/* Redo the NODELETE check, as when dl_load_lock wasn't held
yet this could have changed. */
- if ((map->l_flags_1 & DF_1_NODELETE) != 0)
+ if (map->l_nodelete != link_map_nodelete_inactive)
goto out;
/* If the object with the undefined reference cannot be removed ever
just make sure the same is true for the object which contains the
definition. */
if (undef_map->l_type != lt_loaded
- || (undef_map->l_flags_1 & DF_1_NODELETE) != 0)
+ || (undef_map->l_nodelete != link_map_nodelete_inactive))
{
- map->l_flags_1 |= DF_1_NODELETE;
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
+ && map->l_nodelete == link_map_nodelete_inactive)
+ {
+ if (undef_map->l_name[0] == '\0')
+ _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to reference to main program\n",
+ map->l_name, map->l_ns);
+ else
+ _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to reference to %s [%lu]\n",
+ map->l_name, map->l_ns,
+ undef_map->l_name, undef_map->l_ns);
+ }
+
+ if (flags & DL_LOOKUP_FOR_RELOCATE)
+ map->l_nodelete = link_map_nodelete_pending;
+ else
+ map->l_nodelete = link_map_nodelete_active;
goto out;
}
@@ -712,7 +745,18 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
no fatal problem. We simply make sure the referenced object
cannot be unloaded. This is semantically the correct
behavior. */
- map->l_flags_1 |= DF_1_NODELETE;
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
+ && map->l_nodelete == link_map_nodelete_inactive)
+ _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to memory allocation failure\n",
+ map->l_name, map->l_ns);
+ if (flags & DL_LOOKUP_FOR_RELOCATE)
+ /* In case of non-lazy binding, we could actually
+ report the memory allocation error, but for now, we
+ use the conservative approximation as well. */
+ map->l_nodelete = link_map_nodelete_pending;
+ else
+ map->l_nodelete = link_map_nodelete_active;
goto out;
}
else
diff --git a/elf/dl-open.c b/elf/dl-open.c
index b330cff7d349224a..79c6e4c8ed1c9dfa 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -424,6 +424,40 @@ TLS generation counter wrapped! Please report this."));
}
}
+/* Mark the objects as NODELETE if required. This is delayed until
+ after dlopen failure is not possible, so that _dl_close can clean
+ up objects if necessary. */
+static void
+activate_nodelete (struct link_map *new, int mode)
+{
+ if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending)
+ {
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
+ _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
+ new->l_name, new->l_ns);
+ new->l_nodelete = link_map_nodelete_active;
+ }
+
+ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+ if (imap->l_nodelete == link_map_nodelete_pending)
+ {
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
+ _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
+ imap->l_name, imap->l_ns);
+
+ /* Only new objects should have set
+ link_map_nodelete_pending. Existing objects should not
+ have gained any new dependencies and therefore cannot
+ reach NODELETE status. */
+ assert (!imap->l_init_called || imap->l_type != lt_loaded);
+
+ imap->l_nodelete = link_map_nodelete_active;
+ }
+ }
+}
+
/* struct dl_init_args and call_dl_init are used to call _dl_init with
exception handling disabled. */
struct dl_init_args
@@ -493,12 +527,6 @@ dl_open_worker (void *a)
return;
}
- /* Mark the object as not deletable if the RTLD_NODELETE flags was passed.
- Do this early so that we don't skip marking the object if it was
- already loaded. */
- if (__glibc_unlikely (mode & RTLD_NODELETE))
- new->l_flags_1 |= DF_1_NODELETE;
-
if (__glibc_unlikely (mode & __RTLD_SPROF))
/* This happens only if we load a DSO for 'sprof'. */
return;
@@ -514,19 +542,37 @@ dl_open_worker (void *a)
_dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
new->l_name, new->l_ns, new->l_direct_opencount);
- /* If the user requested the object to be in the global namespace
- but it is not so far, add it now. */
+ /* If the user requested the object to be in the global
+ namespace but it is not so far, prepare to add it now. This
+ can raise an exception to do a malloc failure. */
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
+ add_to_global_resize (new);
+
+ /* Mark the object as not deletable if the RTLD_NODELETE flags
+ was passed. */
+ if (__glibc_unlikely (mode & RTLD_NODELETE))
{
- add_to_global_resize (new);
- add_to_global_update (new);
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)
+ && new->l_nodelete == link_map_nodelete_inactive)
+ _dl_debug_printf ("marking %s [%lu] as NODELETE\n",
+ new->l_name, new->l_ns);
+ new->l_nodelete = link_map_nodelete_active;
}
+ /* Finalize the addition to the global scope. */
+ if ((mode & RTLD_GLOBAL) && new->l_global == 0)
+ add_to_global_update (new);
+
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
return;
}
+ /* Schedule NODELETE marking for the directly loaded object if
+ requested. */
+ if (__glibc_unlikely (mode & RTLD_NODELETE))
+ new->l_nodelete = link_map_nodelete_pending;
+
/* Load that object's dependencies. */
_dl_map_object_deps (new, NULL, 0, 0,
mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
@@ -598,6 +644,14 @@ dl_open_worker (void *a)
int relocation_in_progress = 0;
+ /* Perform relocation. This can trigger lazy binding in IFUNC
+ resolvers. For NODELETE mappings, these dependencies are not
+ recorded because the flag has not been applied to the newly
+ loaded objects. This means that upon dlopen failure, these
+ NODELETE objects can be unloaded despite existing references to
+ them. However, such relocation dependencies in IFUNC resolvers
+ are undefined anyway, so this is not a problem. */
+
for (unsigned int i = nmaps; i-- > 0; )
{
l = maps[i];
@@ -627,7 +681,7 @@ dl_open_worker (void *a)
_dl_start_profile ();
/* Prevent unloading the object. */
- GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE;
+ GL(dl_profile_map)->l_nodelete = link_map_nodelete_active;
}
}
else
@@ -658,6 +712,8 @@ dl_open_worker (void *a)
All memory allocations for new objects must have happened
before. */
+ activate_nodelete (new, mode);
+
/* Second stage after resize_scopes: Actually perform the scope
update. After this, dlsym and lazy binding can bind to new
objects. */
@@ -817,6 +873,10 @@ no more namespaces available for dlmopen()"));
GL(dl_tls_dtv_gaps) = true;
_dl_close_worker (args.map, true);
+
+ /* All link_map_nodelete_pending objects should have been
+ deleted at this point, which is why it is not necessary
+ to reset the flag here. */
}
assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index 4b1ea7c4078ee947..ea286abaea0128d1 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -163,6 +163,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
if (info[VERSYMIDX (DT_FLAGS_1)] != NULL)
{
l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val;
+ if (l->l_flags_1 & DF_1_NODELETE)
+ l->l_nodelete = link_map_nodelete_pending;
/* Only DT_1_SUPPORTED_MASK bits are supported, and we would like
to assert this, but we can't. Users have been setting
diff --git a/elf/tst-dlopenfail.c b/elf/tst-dlopenfail.c
new file mode 100644
index 0000000000000000..ce3140c899562ca8
--- /dev/null
+++ b/elf/tst-dlopenfail.c
@@ -0,0 +1,79 @@
+/* Test dlopen rollback after failures involving NODELETE objects (bug 20839).
+ Copyright (C) 2019 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 <dlfcn.h>
+#include <errno.h>
+#include <gnu/lib-names.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static int
+do_test (void)
+{
+ /* This test uses libpthread as the canonical NODELETE module. If
+ libpthread is no longer NODELETE because it has been merged into
+ libc, the test needs to be updated. */
+ TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL);
+
+ /* This is expected to fail because of the missing dependency. */
+ puts ("info: attempting to load tst-dlopenfailmod1.so");
+ TEST_VERIFY (dlopen ("tst-dlopenfailmod1.so", RTLD_LAZY) == NULL);
+ const char *message = dlerror ();
+ TEST_COMPARE_STRING (message,
+ "tst-dlopenfail-missingmod.so:"
+ " cannot open shared object file:"
+ " No such file or directory");
+
+ /* Do not probe for the presence of libpthread at this point because
+ that might trigger relocation if bug 20839 is present, obscuring
+ a subsequent crash. */
+
+ /* This is expected to succeed. */
+ puts ("info: loading tst-dlopenfailmod2.so");
+ void *handle = xdlopen ("tst-dlopenfailmod2.so", RTLD_NOW);
+ xdlclose (handle);
+
+ /* libpthread should remain loaded. */
+ TEST_VERIFY (dlopen (LIBPTHREAD_SO, RTLD_LAZY | RTLD_NOLOAD) != NULL);
+ TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL);
+
+ /* We can make libpthread global, and then the symbol should become
+ available. */
+ TEST_VERIFY (dlopen (LIBPTHREAD_SO, RTLD_LAZY | RTLD_GLOBAL) != NULL);
+ TEST_VERIFY (dlsym (NULL, "pthread_create") != NULL);
+
+ /* sem_open is sufficiently complex to depend on relocations. */
+ void *(*sem_open_ptr) (const char *, int flag, ...)
+ = dlsym (NULL, "sem_open");
+ if (sem_open_ptr == NULL)
+ /* Hurd does not implement sem_open. */
+ puts ("warning: sem_open not found, further testing not possible");
+ else
+ {
+ errno = 0;
+ TEST_VERIFY (sem_open_ptr ("/", 0) == NULL);
+ TEST_COMPARE (errno, EINVAL);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-dlopenfaillinkmod.c b/elf/tst-dlopenfaillinkmod.c
new file mode 100644
index 0000000000000000..3b14b02bc9a12c0b
--- /dev/null
+++ b/elf/tst-dlopenfaillinkmod.c
@@ -0,0 +1,17 @@
+/* Empty module with a soname which is not available at run time.
+ Copyright (C) 2019 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/>. */
diff --git a/elf/tst-dlopenfailmod1.c b/elf/tst-dlopenfailmod1.c
new file mode 100644
index 0000000000000000..6ef48829899a5a64
--- /dev/null
+++ b/elf/tst-dlopenfailmod1.c
@@ -0,0 +1,36 @@
+/* Module which depends on two modules: one NODELETE, one missing.
+ Copyright (C) 2019 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/>. */
+
+/* Note: Due to the missing second module, this object cannot be
+ loaded at run time. */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/* Force linking against libpthread. */
+void *pthread_create_reference = pthread_create;
+
+/* The constructor will never be executed because the module cannot be
+ loaded. */
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("tst-dlopenfailmod1 constructor executed");
+ _exit (1);
+}
diff --git a/elf/tst-dlopenfailmod2.c b/elf/tst-dlopenfailmod2.c
new file mode 100644
index 0000000000000000..7d600386c13b98bd
--- /dev/null
+++ b/elf/tst-dlopenfailmod2.c
@@ -0,0 +1,29 @@
+/* Module which depends on on a NODELETE module, and can be loaded.
+ Copyright (C) 2019 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 <pthread.h>
+#include <stdio.h>
+
+/* Force linking against libpthread. */
+void *pthread_create_reference = pthread_create;
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("info: tst-dlopenfailmod2.so constructor invoked");
+}
diff --git a/include/link.h b/include/link.h
index 83b1c34b7b4db8f3..a277b77cad6b52b1 100644
--- a/include/link.h
+++ b/include/link.h
@@ -79,6 +79,21 @@ struct r_search_path_struct
int malloced;
};
+/* Type used by the l_nodelete member. */
+enum link_map_nodelete
+{
+ /* This link map can be deallocated. */
+ link_map_nodelete_inactive = 0, /* Zero-initialized in _dl_new_object. */
+
+ /* This link map cannot be deallocated. */
+ link_map_nodelete_active,
+
+ /* This link map cannot be deallocated after dlopen has succeded.
+ dlopen turns this into link_map_nodelete_active. dlclose treats
+ this intermediate state as link_map_nodelete_active. */
+ link_map_nodelete_pending,
+};
+
/* Structure describing a loaded shared object. The `l_next' and `l_prev'
members form a chain of all the shared objects loaded at startup.
@@ -203,6 +218,11 @@ struct link_map
freed, ie. not allocated with
the dummy malloc in ld.so. */
+ /* Actually of type enum link_map_nodelete. Separate byte due to
+ a read in add_dependency in elf/dl-lookup.c outside the loader
+ lock. Only valid for l_type == lt_loaded. */
+ unsigned char l_nodelete;
+
#include <link_map.h>
/* Collected information about own RPATH directories. */

View File

@ -0,0 +1,104 @@
commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Oct 31 19:30:19 2019 +0100
Block signals during the initial part of dlopen
Lazy binding in a signal handler that interrupts a dlopen sees
intermediate dynamic linker state. This has likely been always
unsafe, but with the new pending NODELETE state, this is clearly
incorrect. Other threads are excluded via the loader lock, but the
current thread is not. Blocking signals until right before ELF
constructors run is the safe thing to do.
Change-Id: Iad079080ebe7442c13313ba11dc2797953faef35
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 79c6e4c8ed1c9dfa..25838b073ac1edaf 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -34,6 +34,7 @@
#include <atomic.h>
#include <libc-internal.h>
#include <array_length.h>
+#include <internal-signals.h>
#include <dl-dst.h>
#include <dl-prop.h>
@@ -52,6 +53,10 @@ struct dl_open_args
/* Namespace ID. */
Lmid_t nsid;
+ /* Original signal mask. Used for unblocking signal handlers before
+ running ELF constructors. */
+ sigset_t original_signal_mask;
+
/* Original value of _ns_global_scope_pending_adds. Set by
dl_open_worker. Only valid if nsid is a real namespace
(non-negative). */
@@ -524,12 +529,16 @@ dl_open_worker (void *a)
if (new == NULL)
{
assert (mode & RTLD_NOLOAD);
+ __libc_signal_restore_set (&args->original_signal_mask);
return;
}
if (__glibc_unlikely (mode & __RTLD_SPROF))
- /* This happens only if we load a DSO for 'sprof'. */
- return;
+ {
+ /* This happens only if we load a DSO for 'sprof'. */
+ __libc_signal_restore_set (&args->original_signal_mask);
+ return;
+ }
/* This object is directly loaded. */
++new->l_direct_opencount;
@@ -565,6 +574,7 @@ dl_open_worker (void *a)
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
+ __libc_signal_restore_set (&args->original_signal_mask);
return;
}
@@ -745,6 +755,10 @@ dl_open_worker (void *a)
if (mode & RTLD_GLOBAL)
add_to_global_resize (new);
+ /* Unblock signals. Data structures are now consistent, and
+ application code may run. */
+ __libc_signal_restore_set (&args->original_signal_mask);
+
/* Run the initializer functions of new objects. Temporarily
disable the exception handler, so that lazy binding failures are
fatal. */
@@ -834,6 +848,10 @@ no more namespaces available for dlmopen()"));
args.argv = argv;
args.env = env;
+ /* Recursive lazy binding during manipulation of the dynamic loader
+ structures may result in incorrect behavior. */
+ __libc_signal_block_all (&args.original_signal_mask);
+
struct dl_exception exception;
int errcode = _dl_catch_exception (&exception, dl_open_worker, &args);
@@ -874,10 +892,16 @@ no more namespaces available for dlmopen()"));
_dl_close_worker (args.map, true);
+ /* Restore the signal mask. In the success case, this
+ happens inside dl_open_worker. */
+ __libc_signal_restore_set (&args.original_signal_mask);
+
/* All link_map_nodelete_pending objects should have been
deleted at this point, which is why it is not necessary
to reset the flag here. */
}
+ else
+ __libc_signal_restore_set (&args.original_signal_mask);
assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);

View File

@ -0,0 +1,545 @@
From 0d3905b110000463775b3fb189213833acaebf81 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 1 Jul 2019 12:23:10 -0700
Subject: Call _dl_open_check after relocation [BZ #24259]
This is a workaround for [BZ #20839] which doesn't remove the NODELETE
object when _dl_open_check throws an exception. Move it after relocation
in dl_open_worker to avoid leaving the NODELETE object mapped without
relocation.
[BZ #24259]
* elf/dl-open.c (dl_open_worker): Call _dl_open_check after
relocation.
* sysdeps/x86/Makefile (tests): Add tst-cet-legacy-5a,
tst-cet-legacy-5b, tst-cet-legacy-6a and tst-cet-legacy-6b.
(modules-names): Add tst-cet-legacy-mod-5a, tst-cet-legacy-mod-5b,
tst-cet-legacy-mod-5c, tst-cet-legacy-mod-6a, tst-cet-legacy-mod-6b
and tst-cet-legacy-mod-6c.
(CFLAGS-tst-cet-legacy-5a.c): New.
(CFLAGS-tst-cet-legacy-5b.c): Likewise.
(CFLAGS-tst-cet-legacy-mod-5a.c): Likewise.
(CFLAGS-tst-cet-legacy-mod-5b.c): Likewise.
(CFLAGS-tst-cet-legacy-mod-5c.c): Likewise.
(CFLAGS-tst-cet-legacy-6a.c): Likewise.
(CFLAGS-tst-cet-legacy-6b.c): Likewise.
(CFLAGS-tst-cet-legacy-mod-6a.c): Likewise.
(CFLAGS-tst-cet-legacy-mod-6b.c): Likewise.
(CFLAGS-tst-cet-legacy-mod-6c.c): Likewise.
($(objpfx)tst-cet-legacy-5a): Likewise.
($(objpfx)tst-cet-legacy-5a.out): Likewise.
($(objpfx)tst-cet-legacy-mod-5a.so): Likewise.
($(objpfx)tst-cet-legacy-mod-5b.so): Likewise.
($(objpfx)tst-cet-legacy-5b): Likewise.
($(objpfx)tst-cet-legacy-5b.out): Likewise.
(tst-cet-legacy-5b-ENV): Likewise.
($(objpfx)tst-cet-legacy-6a): Likewise.
($(objpfx)tst-cet-legacy-6a.out): Likewise.
($(objpfx)tst-cet-legacy-mod-6a.so): Likewise.
($(objpfx)tst-cet-legacy-mod-6b.so): Likewise.
($(objpfx)tst-cet-legacy-6b): Likewise.
($(objpfx)tst-cet-legacy-6b.out): Likewise.
(tst-cet-legacy-6b-ENV): Likewise.
* sysdeps/x86/tst-cet-legacy-5.c: New file.
* sysdeps/x86/tst-cet-legacy-5a.c: Likewise.
* sysdeps/x86/tst-cet-legacy-5b.c: Likewise.
* sysdeps/x86/tst-cet-legacy-6.c: Likewise.
* sysdeps/x86/tst-cet-legacy-6a.c: Likewise.
* sysdeps/x86/tst-cet-legacy-6b.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-5.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-5a.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-5b.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-5c.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-6.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-6a.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-6b.c: Likewise.
* sysdeps/x86/tst-cet-legacy-mod-6c.c: Likewise.
(cherry picked from commit d0093c5cefb7f7a4143f3bb03743633823229cc6)
diff --git a/elf/dl-open.c b/elf/dl-open.c
index f6c8ef1043..518a6cad69 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -292,8 +292,6 @@ dl_open_worker (void *a)
_dl_debug_state ();
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
- _dl_open_check (new);
-
/* Print scope information. */
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
_dl_show_scope (new, 0);
@@ -366,6 +364,12 @@ dl_open_worker (void *a)
_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
}
+ /* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE
+ object when _dl_open_check throws an exception. Move it after
+ relocation to avoid leaving the NODELETE object mapped without
+ relocation. */
+ _dl_open_check (new);
+
/* If the file is not loaded now as a dependency, add the search
list of the newly loaded object to the scope. */
bool any_tls = false;
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 337b0b63dc..43ad4a79ff 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -19,12 +19,17 @@ ifeq ($(subdir),elf)
sysdep-dl-routines += dl-cet
tests += tst-cet-legacy-1 tst-cet-legacy-2 tst-cet-legacy-2a \
- tst-cet-legacy-3 tst-cet-legacy-4
+ tst-cet-legacy-3 tst-cet-legacy-4 \
+ tst-cet-legacy-5a tst-cet-legacy-6a
ifneq (no,$(have-tunables))
-tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c
+tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c \
+ tst-cet-legacy-5b tst-cet-legacy-6b
endif
modules-names += tst-cet-legacy-mod-1 tst-cet-legacy-mod-2 \
- tst-cet-legacy-mod-4
+ tst-cet-legacy-mod-4 tst-cet-legacy-mod-5a \
+ tst-cet-legacy-mod-5b tst-cet-legacy-mod-5c \
+ tst-cet-legacy-mod-6a tst-cet-legacy-mod-6b \
+ tst-cet-legacy-mod-6c
CFLAGS-tst-cet-legacy-2.c += -fcf-protection=branch
CFLAGS-tst-cet-legacy-2a.c += -fcf-protection
@@ -35,6 +40,16 @@ CFLAGS-tst-cet-legacy-4.c += -fcf-protection=branch
CFLAGS-tst-cet-legacy-4a.c += -fcf-protection
CFLAGS-tst-cet-legacy-4b.c += -fcf-protection
CFLAGS-tst-cet-legacy-mod-4.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-5a.c += -fcf-protection
+CFLAGS-tst-cet-legacy-5b.c += -fcf-protection
+CFLAGS-tst-cet-legacy-mod-5a.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-mod-5b.c += -fcf-protection
+CFLAGS-tst-cet-legacy-mod-5c.c += -fcf-protection
+CFLAGS-tst-cet-legacy-6a.c += -fcf-protection
+CFLAGS-tst-cet-legacy-6b.c += -fcf-protection
+CFLAGS-tst-cet-legacy-mod-6a.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-mod-6b.c += -fcf-protection
+CFLAGS-tst-cet-legacy-mod-6c.c += -fcf-protection
$(objpfx)tst-cet-legacy-1: $(objpfx)tst-cet-legacy-mod-1.so \
$(objpfx)tst-cet-legacy-mod-2.so
@@ -44,6 +59,17 @@ $(objpfx)tst-cet-legacy-2a: $(objpfx)tst-cet-legacy-mod-2.so $(libdl)
$(objpfx)tst-cet-legacy-2a.out: $(objpfx)tst-cet-legacy-mod-1.so
$(objpfx)tst-cet-legacy-4: $(libdl)
$(objpfx)tst-cet-legacy-4.out: $(objpfx)tst-cet-legacy-mod-4.so
+$(objpfx)tst-cet-legacy-5a: $(libdl)
+$(objpfx)tst-cet-legacy-5a.out: $(objpfx)tst-cet-legacy-mod-5a.so \
+ $(objpfx)tst-cet-legacy-mod-5b.so
+$(objpfx)tst-cet-legacy-mod-5a.so: $(objpfx)tst-cet-legacy-mod-5c.so
+$(objpfx)tst-cet-legacy-mod-5b.so: $(objpfx)tst-cet-legacy-mod-5c.so
+$(objpfx)tst-cet-legacy-6a: $(libdl)
+$(objpfx)tst-cet-legacy-6a.out: $(objpfx)tst-cet-legacy-mod-6a.so \
+ $(objpfx)tst-cet-legacy-mod-6b.so
+$(objpfx)tst-cet-legacy-mod-6a.so: $(objpfx)tst-cet-legacy-mod-6c.so
+$(objpfx)tst-cet-legacy-mod-6b.so: $(objpfx)tst-cet-legacy-mod-6c.so
+LDFLAGS-tst-cet-legacy-mod-6c.so = -Wl,--enable-new-dtags,-z,nodelete
ifneq (no,$(have-tunables))
$(objpfx)tst-cet-legacy-4a: $(libdl)
$(objpfx)tst-cet-legacy-4a.out: $(objpfx)tst-cet-legacy-mod-4.so
@@ -54,6 +80,14 @@ tst-cet-legacy-4b-ENV = GLIBC_TUNABLES=glibc.tune.x86_shstk=on
$(objpfx)tst-cet-legacy-4c: $(libdl)
$(objpfx)tst-cet-legacy-4c.out: $(objpfx)tst-cet-legacy-mod-4.so
tst-cet-legacy-4c-ENV = GLIBC_TUNABLES=glibc.tune.x86_shstk=off
+$(objpfx)tst-cet-legacy-5b: $(libdl)
+$(objpfx)tst-cet-legacy-5b.out: $(objpfx)tst-cet-legacy-mod-5a.so \
+ $(objpfx)tst-cet-legacy-mod-5b.so
+tst-cet-legacy-5b-ENV = GLIBC_TUNABLES=glibc.tune.x86_ibt=off:glibc.tune.x86_shstk=off
+$(objpfx)tst-cet-legacy-6b: $(libdl)
+$(objpfx)tst-cet-legacy-6b.out: $(objpfx)tst-cet-legacy-mod-6a.so \
+ $(objpfx)tst-cet-legacy-mod-6b.so
+tst-cet-legacy-6b-ENV = GLIBC_TUNABLES=glibc.tune.x86_ibt=off:glibc.tune.x86_shstk=off
endif
endif
diff --git a/sysdeps/x86/tst-cet-legacy-5.c b/sysdeps/x86/tst-cet-legacy-5.c
new file mode 100644
index 0000000000..fbf640f664
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-5.c
@@ -0,0 +1,76 @@
+/* Check compatibility of CET-enabled executable with dlopened legacy
+ shared object.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+static void
+do_test_1 (const char *modname, bool fail)
+{
+ int (*fp) (void);
+ void *h;
+
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ if (fail)
+ {
+ const char *err = dlerror ();
+ if (strstr (err, "shadow stack isn't enabled") == NULL)
+ {
+ printf ("incorrect dlopen '%s' error: %s\n", modname,
+ dlerror ());
+ exit (1);
+ }
+
+ return;
+ }
+
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "test");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'test': %s\n", dlerror ());
+ exit (1);
+ }
+
+ if (fp () != 0)
+ {
+ puts ("test () != 0");
+ exit (1);
+ }
+
+ dlclose (h);
+}
+
+static int
+do_test (void)
+{
+ do_test_1 ("tst-cet-legacy-mod-5a.so", true);
+ do_test_1 ("tst-cet-legacy-mod-5b.so", false);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/x86/tst-cet-legacy-5a.c b/sysdeps/x86/tst-cet-legacy-5a.c
new file mode 100644
index 0000000000..fc5a609dff
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-5a.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-5b.c b/sysdeps/x86/tst-cet-legacy-5b.c
new file mode 100644
index 0000000000..fc5a609dff
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-5b.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-6.c b/sysdeps/x86/tst-cet-legacy-6.c
new file mode 100644
index 0000000000..9151225264
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-6.c
@@ -0,0 +1,76 @@
+/* Check compatibility of CET-enabled executable with dlopened legacy
+ shared object.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+static void
+do_test_1 (const char *modname, bool fail)
+{
+ int (*fp) (void);
+ void *h;
+
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ if (fail)
+ {
+ const char *err = dlerror ();
+ if (strstr (err, "shadow stack isn't enabled") == NULL)
+ {
+ printf ("incorrect dlopen '%s' error: %s\n", modname,
+ dlerror ());
+ exit (1);
+ }
+
+ return;
+ }
+
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "test");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'test': %s\n", dlerror ());
+ exit (1);
+ }
+
+ if (fp () != 0)
+ {
+ puts ("test () != 0");
+ exit (1);
+ }
+
+ dlclose (h);
+}
+
+static int
+do_test (void)
+{
+ do_test_1 ("tst-cet-legacy-mod-6a.so", true);
+ do_test_1 ("tst-cet-legacy-mod-6b.so", false);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/x86/tst-cet-legacy-6a.c b/sysdeps/x86/tst-cet-legacy-6a.c
new file mode 100644
index 0000000000..2d1546d36b
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-6a.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-6.c"
diff --git a/sysdeps/x86/tst-cet-legacy-6b.c b/sysdeps/x86/tst-cet-legacy-6b.c
new file mode 100644
index 0000000000..2d1546d36b
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-6b.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-6.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5.c b/sysdeps/x86/tst-cet-legacy-mod-5.c
new file mode 100644
index 0000000000..3c1071c2ef
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5.c
@@ -0,0 +1,31 @@
+/* Check compatibility of CET-enabled executable with dlopened legacy
+ shared object.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void foo (void);
+
+int
+test (void)
+{
+ foo ();
+ return 0;
+}
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5a.c b/sysdeps/x86/tst-cet-legacy-mod-5a.c
new file mode 100644
index 0000000000..daa43e4e8d
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5a.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-mod-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5b.c b/sysdeps/x86/tst-cet-legacy-mod-5b.c
new file mode 100644
index 0000000000..daa43e4e8d
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5b.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-mod-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5c.c b/sysdeps/x86/tst-cet-legacy-mod-5c.c
new file mode 100644
index 0000000000..e529a42ac0
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5c.c
@@ -0,0 +1,36 @@
+/* Check compatibility of CET-enabled executable with dlopened legacy
+ shared object.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+
+static int called = 0;
+
+static void
+__attribute__ ((constructor))
+init (void)
+{
+ called = 1;
+}
+
+void
+foo (void)
+{
+ if (!called)
+ abort ();
+}
diff --git a/sysdeps/x86/tst-cet-legacy-mod-6.c b/sysdeps/x86/tst-cet-legacy-mod-6.c
new file mode 100644
index 0000000000..3c1071c2ef
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-6.c
@@ -0,0 +1,31 @@
+/* Check compatibility of CET-enabled executable with dlopened legacy
+ shared object.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void foo (void);
+
+int
+test (void)
+{
+ foo ();
+ return 0;
+}
diff --git a/sysdeps/x86/tst-cet-legacy-mod-6a.c b/sysdeps/x86/tst-cet-legacy-mod-6a.c
new file mode 100644
index 0000000000..c89b8fe8ff
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-6a.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-mod-6.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-6b.c b/sysdeps/x86/tst-cet-legacy-mod-6b.c
new file mode 100644
index 0000000000..c89b8fe8ff
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-6b.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-mod-6.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-6c.c b/sysdeps/x86/tst-cet-legacy-mod-6c.c
new file mode 100644
index 0000000000..e529a42ac0
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-6c.c
@@ -0,0 +1,36 @@
+/* Check compatibility of CET-enabled executable with dlopened legacy
+ shared object.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+
+static int called = 0;
+
+static void
+__attribute__ ((constructor))
+init (void)
+{
+ called = 1;
+}
+
+void
+foo (void)
+{
+ if (!called)
+ abort ();
+}
diff --git a/sysdeps/x86/tst-cet-legacy-mod-6d.c b/sysdeps/x86/tst-cet-legacy-mod-6d.c
new file mode 100644
index 0000000000..eb233a1d10
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-6d.c
@@ -0,0 +1 @@
+#include "tst-cet-legacy-mod-6c.c"

View File

@ -0,0 +1,35 @@
commit 55f82d328d2dd1c7c13c1992f4b9bf9c95b57551
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
Date: Thu Apr 25 15:35:35 2019 +0100
aarch64: add STO_AARCH64_VARIANT_PCS and DT_AARCH64_VARIANT_PCS
STO_AARCH64_VARIANT_PCS is a non-visibility st_other flag for marking
symbols that reference functions that may follow a variant PCS with
different register usage convention from the base PCS.
DT_AARCH64_VARIANT_PCS is a dynamic tag that marks ELF modules that
have R_*_JUMP_SLOT relocations for symbols marked with
STO_AARCH64_VARIANT_PCS (i.e. have variant PCS calls via a PLT).
* elf/elf.h (STO_AARCH64_VARIANT_PCS): Define.
(DT_AARCH64_VARIANT_PCS): Define.
diff --git a/elf/elf.h b/elf/elf.h
index 7e2b072a7f75451c..74f7f479ce817040 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -2847,6 +2847,13 @@ enum
#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */
#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */
+/* AArch64 specific values for the Dyn d_tag field. */
+#define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5)
+#define DT_AARCH64_NUM 6
+
+/* AArch64 specific values for the st_other field. */
+#define STO_AARCH64_VARIANT_PCS 0x80
+
/* ARM relocs. */
#define R_ARM_NONE 0 /* No reloc */

View File

@ -0,0 +1,138 @@
commit 82bc69c012838a381c4167c156a06f4598f34227
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
Date: Thu Apr 25 15:35:35 2019 +0100
aarch64: handle STO_AARCH64_VARIANT_PCS
Avoid lazy binding of symbols that may follow a variant PCS with different
register usage convention from the base PCS.
Currently the lazy binding entry code does not preserve all the registers
required for AdvSIMD and SVE vector calls. Saving and restoring all
registers unconditionally may break existing binaries, even if they never
use vector calls, because of the larger stack requirement for lazy
resolution, which can be significant on an SVE system.
The solution is to mark all symbols in the symbol table that may follow
a variant PCS so the dynamic linker can handle them specially. In this
patch such symbols are always resolved at load time, not lazily.
So currently LD_AUDIT for variant PCS symbols are not supported, for that
the _dl_runtime_profile entry needs to be changed e.g. to unconditionally
save/restore all registers (but pass down arg and retval registers to
pltentry/exit callbacks according to the base PCS).
This patch also removes a __builtin_expect from the modified code because
the branch prediction hint did not seem useful.
* sysdeps/aarch64/dl-dtprocnum.h: New file.
* sysdeps/aarch64/dl-machine.h (DT_AARCH64): Define.
(elf_machine_runtime_setup): Handle DT_AARCH64_VARIANT_PCS.
(elf_machine_lazy_rel): Check STO_AARCH64_VARIANT_PCS and bind such
symbols at load time.
* sysdeps/aarch64/linkmap.h (struct link_map_machine): Add variant_pcs.
diff --git a/sysdeps/aarch64/dl-dtprocnum.h b/sysdeps/aarch64/dl-dtprocnum.h
new file mode 100644
index 0000000000000000..4ac2adf23458e02d
--- /dev/null
+++ b/sysdeps/aarch64/dl-dtprocnum.h
@@ -0,0 +1,21 @@
+/* Configuration of lookup functions. AArch64 version.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* Number of extra dynamic section entries for this architecture. By
+ default there are none. */
+#define DT_THISPROCNUM DT_AARCH64_NUM
diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
index 4935aa7c543876db..d4494852b32b8783 100644
--- a/sysdeps/aarch64/dl-machine.h
+++ b/sysdeps/aarch64/dl-machine.h
@@ -27,6 +27,9 @@
#include <dl-irel.h>
#include <cpu-features.c>
+/* Translate a processor specific dynamic tag to the index in l_info array. */
+#define DT_AARCH64(x) (DT_AARCH64_##x - DT_LOPROC + DT_NUM)
+
/* Return nonzero iff ELF header is compatible with the running host. */
static inline int __attribute__ ((unused))
elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
@@ -102,6 +105,10 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
}
}
+ /* Check if STO_AARCH64_VARIANT_PCS needs to be handled. */
+ if (l->l_info[DT_AARCH64 (VARIANT_PCS)])
+ l->l_mach.variant_pcs = 1;
+
return lazy;
}
@@ -388,10 +395,37 @@ elf_machine_lazy_rel (struct link_map *map,
/* Check for unexpected PLT reloc type. */
if (__builtin_expect (r_type == AARCH64_R(JUMP_SLOT), 1))
{
- if (__builtin_expect (map->l_mach.plt, 0) == 0)
- *reloc_addr += l_addr;
- else
- *reloc_addr = map->l_mach.plt;
+ if (map->l_mach.plt == 0)
+ {
+ /* Prelinking. */
+ *reloc_addr += l_addr;
+ return;
+ }
+
+ if (__glibc_unlikely (map->l_mach.variant_pcs))
+ {
+ /* Check the symbol table for variant PCS symbols. */
+ const Elf_Symndx symndx = ELFW (R_SYM) (reloc->r_info);
+ const ElfW (Sym) *symtab =
+ (const void *)D_PTR (map, l_info[DT_SYMTAB]);
+ const ElfW (Sym) *sym = &symtab[symndx];
+ if (__glibc_unlikely (sym->st_other & STO_AARCH64_VARIANT_PCS))
+ {
+ /* Avoid lazy resolution of variant PCS symbols. */
+ const struct r_found_version *version = NULL;
+ if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
+ {
+ const ElfW (Half) *vernum =
+ (const void *)D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+ version = &map->l_versions[vernum[symndx] & 0x7fff];
+ }
+ elf_machine_rela (map, reloc, sym, version, reloc_addr,
+ skip_ifunc);
+ return;
+ }
+ }
+
+ *reloc_addr = map->l_mach.plt;
}
else if (__builtin_expect (r_type == AARCH64_R(TLSDESC), 1))
{
diff --git a/sysdeps/aarch64/linkmap.h b/sysdeps/aarch64/linkmap.h
index 6852f343a1efd150..dd8597470c3d2174 100644
--- a/sysdeps/aarch64/linkmap.h
+++ b/sysdeps/aarch64/linkmap.h
@@ -20,4 +20,5 @@ struct link_map_machine
{
ElfW(Addr) plt; /* Address of .plt */
void *tlsdesc_table; /* Address of TLS descriptor hash table. */
+ int variant_pcs; /* If set, PLT calls may follow a variant PCS. */
};

View File

@ -0,0 +1,49 @@
commit 30ba0375464f34e4bf8129f3d3dc14d0c09add17
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
Date: Tue Jul 9 12:11:39 2019 +0100
aarch64: simplify the DT_AARCH64_VARIANT_PCS handling code
Remove unnecessary variant_pcs field: the dynamic tag can be checked
directly.
* sysdeps/aarch64/dl-machine.h (elf_machine_runtime_setup): Remove the
DT_AARCH64_VARIANT_PCS check.
(elf_machine_lazy_rel): Use l_info[DT_AARCH64 (VARIANT_PCS)].
* sysdeps/aarch64/linkmap.h (struct link_map_machine): Remove
variant_pcs.
diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
index d4494852b32b8783..b39eae4acf4086ee 100644
--- a/sysdeps/aarch64/dl-machine.h
+++ b/sysdeps/aarch64/dl-machine.h
@@ -105,10 +105,6 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
}
}
- /* Check if STO_AARCH64_VARIANT_PCS needs to be handled. */
- if (l->l_info[DT_AARCH64 (VARIANT_PCS)])
- l->l_mach.variant_pcs = 1;
-
return lazy;
}
@@ -402,7 +398,7 @@ elf_machine_lazy_rel (struct link_map *map,
return;
}
- if (__glibc_unlikely (map->l_mach.variant_pcs))
+ if (__glibc_unlikely (map->l_info[DT_AARCH64 (VARIANT_PCS)] != NULL))
{
/* Check the symbol table for variant PCS symbols. */
const Elf_Symndx symndx = ELFW (R_SYM) (reloc->r_info);
diff --git a/sysdeps/aarch64/linkmap.h b/sysdeps/aarch64/linkmap.h
index dd8597470c3d2174..6852f343a1efd150 100644
--- a/sysdeps/aarch64/linkmap.h
+++ b/sysdeps/aarch64/linkmap.h
@@ -20,5 +20,4 @@ struct link_map_machine
{
ElfW(Addr) plt; /* Address of .plt */
void *tlsdesc_table; /* Address of TLS descriptor hash table. */
- int variant_pcs; /* If set, PLT calls may follow a variant PCS. */
};

View File

@ -0,0 +1,25 @@
commit 6d750b18999b52ec74102c046cd27181f943bda8
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 1 14:06:24 2019 +0200
malloc: Remove unwanted leading whitespace in malloc_info [BZ #24867]
It was introduced in commit 6c8dbf00f536d78b1937b5af6f57be47fd376344
("Reformat malloc to gnu style.").
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit b0f6679bcd738ea244a14acd879d974901e56c8e)
diff --git a/malloc/malloc.c b/malloc/malloc.c
index e6a483d5cf7c4312..4fc7f175fe42d6c6 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -5518,7 +5518,7 @@ __malloc_info (int options, FILE *fp)
for (size_t i = 0; i < nsizes; ++i)
if (sizes[i].count != 0 && i != NFASTBINS)
- fprintf (fp, " \
+ fprintf (fp, "\
<size from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n",
sizes[i].from, sizes[i].to, sizes[i].total, sizes[i].count);

View File

@ -0,0 +1,35 @@
commit 91d5989356325759503311df67e750b358ef4148
Author: Niklas Hambüchen <mail@nh2.me>
Date: Thu Aug 8 22:02:27 2019 +0200
malloc: Fix missing accounting of top chunk in malloc_info [BZ #24026]
Fixes `<total type="rest" size="..."> incorrectly showing as 0 most
of the time.
The rest value being wrong is significant because to compute the
actual amount of memory handed out via malloc, the user must subtract
it from <system type="current" size="...">. That result being wrong
makes investigating memory fragmentation issues like
<https://bugzilla.redhat.com/show_bug.cgi?id=843478> close to
impossible.
(cherry picked from commit b6d2c4475d5abc05dd009575b90556bdd3c78ad0)
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 4fc7f175fe42d6c6..fcf480acdaea1b86 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -5433,6 +5433,12 @@ __malloc_info (int options, FILE *fp)
__libc_lock_lock (ar_ptr->mutex);
+ /* Account for top chunk. The top-most available chunk is
+ treated specially and is never in any bin. See "initial_top"
+ comments. */
+ avail = chunksize (ar_ptr->top);
+ nblocks = 1; /* Top always exists. */
+
for (size_t i = 0; i < NFASTBINS; ++i)
{
mchunkptr p = fastbin (ar_ptr, i);

View File

@ -0,0 +1,117 @@
commit 669ff911e2571f74a2668493e326ac9a505776bd
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Feb 8 12:46:19 2019 +0100
nptl: Avoid fork handler lock for async-signal-safe fork [BZ #24161]
Commit 27761a1042daf01987e7d79636d0c41511c6df3c ("Refactor atfork
handlers") introduced a lock, atfork_lock, around fork handler list
accesses. It turns out that this lock occasionally results in
self-deadlocks in malloc/tst-mallocfork2:
(gdb) bt
#0 __lll_lock_wait_private ()
at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:63
#1 0x00007f160c6f927a in __run_fork_handlers (who=(unknown: 209394016),
who@entry=atfork_run_prepare) at register-atfork.c:116
#2 0x00007f160c6b7897 in __libc_fork () at ../sysdeps/nptl/fork.c:58
#3 0x00000000004027d6 in sigusr1_handler (signo=<optimized out>)
at tst-mallocfork2.c:80
#4 sigusr1_handler (signo=<optimized out>) at tst-mallocfork2.c:64
#5 <signal handler called>
#6 0x00007f160c6f92e4 in __run_fork_handlers (who=who@entry=atfork_run_parent)
at register-atfork.c:136
#7 0x00007f160c6b79a2 in __libc_fork () at ../sysdeps/nptl/fork.c:152
#8 0x0000000000402567 in do_test () at tst-mallocfork2.c:156
#9 0x0000000000402dd2 in support_test_main (argc=1, argv=0x7ffc81ef1ab0,
config=config@entry=0x7ffc81ef1970) at support_test_main.c:350
#10 0x0000000000402362 in main (argc=<optimized out>, argv=<optimized out>)
at ../support/test-driver.c:168
If no locking happens in the single-threaded case (where fork is
expected to be async-signal-safe), this deadlock is avoided.
(pthread_atfork is not required to be async-signal-safe, so a fork
call from a signal handler interrupting pthread_atfork is not
a problem.)
diff --git a/nptl/register-atfork.c b/nptl/register-atfork.c
index bc797b7..80a1bec 100644
--- a/nptl/register-atfork.c
+++ b/nptl/register-atfork.c
@@ -107,13 +107,14 @@ __unregister_atfork (void *dso_handle)
}
void
-__run_fork_handlers (enum __run_fork_handler_type who)
+__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
{
struct fork_handler *runp;
if (who == atfork_run_prepare)
{
- lll_lock (atfork_lock, LLL_PRIVATE);
+ if (do_locking)
+ lll_lock (atfork_lock, LLL_PRIVATE);
size_t sl = fork_handler_list_size (&fork_handlers);
for (size_t i = sl; i > 0; i--)
{
@@ -133,7 +134,8 @@ __run_fork_handlers (enum __run_fork_handler_type who)
else if (who == atfork_run_parent && runp->parent_handler)
runp->parent_handler ();
}
- lll_unlock (atfork_lock, LLL_PRIVATE);
+ if (do_locking)
+ lll_unlock (atfork_lock, LLL_PRIVATE);
}
}
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index bd68f18..14b69a6 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -55,7 +55,7 @@ __libc_fork (void)
but our current fork implementation is not. */
bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
- __run_fork_handlers (atfork_run_prepare);
+ __run_fork_handlers (atfork_run_prepare, multiple_threads);
/* If we are not running multiple threads, we do not have to
preserve lock state. If fork runs from a signal handler, only
@@ -134,7 +134,7 @@ __libc_fork (void)
__rtld_lock_initialize (GL(dl_load_lock));
/* Run the handlers registered for the child. */
- __run_fork_handlers (atfork_run_child);
+ __run_fork_handlers (atfork_run_child, multiple_threads);
}
else
{
@@ -149,7 +149,7 @@ __libc_fork (void)
}
/* Run the handlers registered for the parent. */
- __run_fork_handlers (atfork_run_parent);
+ __run_fork_handlers (atfork_run_parent, multiple_threads);
}
return pid;
diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h
index a1c3b26..99ed760 100644
--- a/sysdeps/nptl/fork.h
+++ b/sysdeps/nptl/fork.h
@@ -52,9 +52,11 @@ enum __run_fork_handler_type
- atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
lock.
- atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
- lock. */
-extern void __run_fork_handlers (enum __run_fork_handler_type who)
- attribute_hidden;
+ lock.
+
+ Perform locking only if DO_LOCKING. */
+extern void __run_fork_handlers (enum __run_fork_handler_type who,
+ _Bool do_locking) attribute_hidden;
/* C library side function to register new fork handlers. */
extern int __register_atfork (void (*__prepare) (void),

View File

@ -0,0 +1,60 @@
commit 58d2672f64176fcb323859d3bd5240fb1cf8f25c
Author: Wilco Dijkstra <wdijkstr@arm.com>
Date: Fri May 10 16:38:21 2019 +0100
Fix tcache count maximum (BZ #24531)
The tcache counts[] array is a char, which has a very small range and thus
may overflow. When setting tcache_count tunable, there is no overflow check.
However the tunable must not be larger than the maximum value of the tcache
counts[] array, otherwise it can overflow when filling the tcache.
[BZ #24531]
* malloc/malloc.c (MAX_TCACHE_COUNT): New define.
(do_set_tcache_count): Only update if count is small enough.
* manual/tunables.texi (glibc.malloc.tcache_count): Document max value.
(cherry picked from commit 5ad533e8e65092be962e414e0417112c65d154fb)
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 723d393f529bdb4c..92239b3324584060 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2919,6 +2919,8 @@ typedef struct tcache_perthread_struct
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
+#define MAX_TCACHE_COUNT 127 /* Maximum value of counts[] entries. */
+
static __thread bool tcache_shutting_down = false;
static __thread tcache_perthread_struct *tcache = NULL;
@@ -5124,8 +5126,11 @@ static inline int
__always_inline
do_set_tcache_count (size_t value)
{
- LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
- mp_.tcache_count = value;
+ if (value <= MAX_TCACHE_COUNT)
+ {
+ LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
+ mp_.tcache_count = value;
+ }
return 1;
}
diff --git a/manual/tunables.texi b/manual/tunables.texi
index bb4819bdf1de273e..9dccf2ee7f8eec17 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -188,8 +188,8 @@ per-thread cache. The default (and maximum) value is 1032 bytes on
@deftp Tunable glibc.malloc.tcache_count
The maximum number of chunks of each size to cache. The default is 7.
-There is no upper limit, other than available system memory. If set
-to zero, the per-thread cache is effectively disabled.
+The upper limit is 127. If set to zero, the per-thread cache is effectively
+disabled.
The approximate maximum overhead of the per-thread cache is thus equal
to the number of bins times the chunk count in each bin times the size

View File

@ -0,0 +1,37 @@
commit 3640758943c856268bc12a3307838c2a65d2f9ea
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Feb 4 23:46:58 2019 +0000
Fix assertion in malloc.c:tcache_get.
One of the warnings that appears with -Wextra is "ordered comparison
of pointer with integer zero" in malloc.c:tcache_get, for the
assertion:
assert (tcache->entries[tc_idx] > 0);
Indeed, a "> 0" comparison does not make sense for
tcache->entries[tc_idx], which is a pointer. My guess is that
tcache->counts[tc_idx] is what's intended here, and this patch changes
the assertion accordingly.
Tested for x86_64.
* malloc/malloc.c (tcache_get): Compare tcache->counts[tc_idx]
with 0, not tcache->entries[tc_idx].
(cherry picked from commit 77dc0d8643aa99c92bf671352b0a8adde705896f)
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 92239b3324584060..998879aededf0d7c 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2948,7 +2948,7 @@ tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
assert (tc_idx < TCACHE_MAX_BINS);
- assert (tcache->entries[tc_idx] > 0);
+ assert (tcache->counts[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
e->key = NULL;

View File

@ -0,0 +1,92 @@
commit f88c59f4657ac2e0bab8f51f60022ecbe7f12e2e
Author: Wilco Dijkstra <wdijkstr@arm.com>
Date: Fri May 17 18:16:20 2019 +0100
Small tcache improvements
Change the tcache->counts[] entries to uint16_t - this removes
the limit set by char and allows a larger tcache. Remove a few
redundant asserts.
bench-malloc-thread with 4 threads is ~15% faster on Cortex-A72.
Reviewed-by: DJ Delorie <dj@redhat.com>
* malloc/malloc.c (MAX_TCACHE_COUNT): Increase to UINT16_MAX.
(tcache_put): Remove redundant assert.
(tcache_get): Remove redundant asserts.
(__libc_malloc): Check tcache count is not zero.
* manual/tunables.texi (glibc.malloc.tcache_count): Update maximum.
(cherry picked from commit 1f50f2ad854c84ead522bfc7331b46dbe6057d53)
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 998879aededf0d7c..e6a483d5cf7c4312 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -321,6 +321,10 @@ __malloc_assert (const char *assertion, const char *file, unsigned int line,
/* This is another arbitrary limit, which tunables can change. Each
tcache bin will hold at most this number of chunks. */
# define TCACHE_FILL_COUNT 7
+
+/* Maximum chunks in tcache bins for tunables. This value must fit the range
+ of tcache->counts[] entries, else they may overflow. */
+# define MAX_TCACHE_COUNT UINT16_MAX
#endif
@@ -2915,12 +2919,10 @@ typedef struct tcache_entry
time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
- char counts[TCACHE_MAX_BINS];
+ uint16_t counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
-#define MAX_TCACHE_COUNT 127 /* Maximum value of counts[] entries. */
-
static __thread bool tcache_shutting_down = false;
static __thread tcache_perthread_struct *tcache = NULL;
@@ -2930,7 +2932,6 @@ static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
- assert (tc_idx < TCACHE_MAX_BINS);
/* Mark this chunk as "in the tcache" so the test in _int_free will
detect a double free. */
@@ -2947,8 +2948,6 @@ static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
- assert (tc_idx < TCACHE_MAX_BINS);
- assert (tcache->counts[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
e->key = NULL;
@@ -3053,9 +3052,8 @@ __libc_malloc (size_t bytes)
DIAG_PUSH_NEEDS_COMMENT;
if (tc_idx < mp_.tcache_bins
- /*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
&& tcache
- && tcache->entries[tc_idx] != NULL)
+ && tcache->counts[tc_idx] > 0)
{
return tcache_get (tc_idx);
}
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 9dccf2ee7f8eec17..f6c49250e3889ddd 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -188,7 +188,7 @@ per-thread cache. The default (and maximum) value is 1032 bytes on
@deftp Tunable glibc.malloc.tcache_count
The maximum number of chunks of each size to cache. The default is 7.
-The upper limit is 127. If set to zero, the per-thread cache is effectively
+The upper limit is 65535. If set to zero, the per-thread cache is effectively
disabled.
The approximate maximum overhead of the per-thread cache is thus equal

View File

@ -0,0 +1,156 @@
commit 8692ebdb1259be60c545fa509d4852b26703777e
Author: David Newall <glibc@davidnewall.com>
Date: Mon Feb 4 13:35:11 2019 +0100
elf: Implement --preload option for the dynamic linker
diff --git a/elf/Makefile b/elf/Makefile
index 9cf5cd8dfd..db6a2a0c29 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -354,7 +354,8 @@ endif
ifeq (yes,$(build-shared))
ifeq ($(run-built-tests),yes)
-tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out
+tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out \
+ $(objpfx)tst-rtld-preload.out
endif
tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \
$(objpfx)check-localplt.out $(objpfx)check-initfini.out
@@ -883,6 +884,15 @@ $(objpfx)tst-rtld-load-self.out: tst-rtld-load-self.sh $(objpfx)ld.so
$(SHELL) $^ '$(test-wrapper)' '$(test-wrapper-env)' > $@; \
$(evaluate-test)
+tst-rtld-preload-OBJS = $(subst $(empty) ,:,$(strip $(preloadtest-preloads:=.so)))
+$(objpfx)tst-rtld-preload.out: tst-rtld-preload.sh $(objpfx)ld.so \
+ $(objpfx)preloadtest \
+ $(preloadtest-preloads:%=$(objpfx)%.so)
+ $(SHELL) $< $(objpfx)ld.so $(objpfx)preloadtest \
+ '$(test-wrapper)' '$(test-wrapper-env)' '$(run_program_env)' \
+ '$(rpath-link)' '$(tst-rtld-preload-OBJS)' > $@; \
+ $(evaluate-test)
+
$(objpfx)initfirst: $(libdl)
$(objpfx)initfirst.out: $(objpfx)firstobj.so
diff --git a/elf/rtld.c b/elf/rtld.c
index 5d97f41b7b..5a90e78ed6 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -826,15 +826,18 @@ static const char *library_path attribute_relro;
static const char *preloadlist attribute_relro;
/* Nonzero if information about versions has to be printed. */
static int version_info attribute_relro;
+/* The preload list passed as a command argument. */
+static const char *preloadarg attribute_relro;
/* The LD_PRELOAD environment variable gives list of libraries
separated by white space or colons that are loaded before the
executable's dependencies and prepended to the global scope list.
(If the binary is running setuid all elements containing a '/' are
ignored since it is insecure.) Return the number of preloads
- performed. */
+ performed. Ditto for --preload command argument. */
unsigned int
-handle_ld_preload (const char *preloadlist, struct link_map *main_map)
+handle_preload_list (const char *preloadlist, struct link_map *main_map,
+ const char *where)
{
unsigned int npreloads = 0;
const char *p = preloadlist;
@@ -858,7 +861,7 @@ handle_ld_preload (const char *preloadlist, struct link_map *main_map)
++p;
if (dso_name_valid_for_suid (fname))
- npreloads += do_preload (fname, main_map, "LD_PRELOAD");
+ npreloads += do_preload (fname, main_map, where);
}
return npreloads;
}
@@ -974,6 +977,13 @@ dl_main (const ElfW(Phdr) *phdr,
{
process_dl_audit (_dl_argv[2]);
+ _dl_skip_args += 2;
+ _dl_argc -= 2;
+ _dl_argv += 2;
+ }
+ else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
+ {
+ preloadarg = _dl_argv[2];
_dl_skip_args += 2;
_dl_argc -= 2;
_dl_argv += 2;
@@ -1006,7 +1016,8 @@ of this helper program; chances are you did not intend to run this program.\n\
variable LD_LIBRARY_PATH\n\
--inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\
in LIST\n\
- --audit LIST use objects named in LIST as auditors\n");
+ --audit LIST use objects named in LIST as auditors\n\
+ --preload LIST preload objects named in LIST\n");
++_dl_skip_args;
--_dl_argc;
@@ -1620,7 +1631,16 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
if (__glibc_unlikely (preloadlist != NULL))
{
HP_TIMING_NOW (start);
- npreloads += handle_ld_preload (preloadlist, main_map);
+ npreloads += handle_preload_list (preloadlist, main_map, "LD_PRELOAD");
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (diff, start, stop);
+ HP_TIMING_ACCUM_NT (load_time, diff);
+ }
+
+ if (__glibc_unlikely (preloadarg != NULL))
+ {
+ HP_TIMING_NOW (start);
+ npreloads += handle_preload_list (preloadarg, main_map, "--preload");
HP_TIMING_NOW (stop);
HP_TIMING_DIFF (diff, start, stop);
HP_TIMING_ACCUM_NT (load_time, diff);
diff --git a/elf/tst-rtld-preload.sh b/elf/tst-rtld-preload.sh
new file mode 100755
index 0000000000..f0c0ca11ba
--- /dev/null
+++ b/elf/tst-rtld-preload.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Test --preload argument ld.so.
+# Copyright (C) 2019 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
+# <http://www.gnu.org/licenses/>.
+
+set -e
+
+rtld=$1
+test_program=$2
+test_wrapper=$3
+test_wrapper_env=$4
+run_program_env=$5
+library_path=$6
+preload=$7
+
+echo "# [${test_wrapper}] [$rtld] [--library-path] [$library_path]" \
+ "[--preload] [$preload] [$test_program]"
+${test_wrapper_env} \
+${run_program_env} \
+${test_wrapper} $rtld --library-path "$library_path" \
+ --preload "$preload" $test_program 2>&1 && rc=0 || rc=$?
+echo "# exit status $rc"
+
+exit $rc

View File

@ -0,0 +1,168 @@
commit 561b0bec4448f0302cb4915bf67c919bde4a1c57
Author: DJ Delorie <dj@redhat.com>
Date: Fri Jul 6 01:10:41 2018 -0400
Add test-in-container infrastructure.
* Makefile (testroot.pristine): New rules to initialize the
test-in-container "testroot".
* Makerules (all-testsuite): Add tests-container.
* Rules (tests-expected): Add tests-container.
(binaries-all-tests): Likewise.
(tests-container): New, run these tests in the testroot container.
* support/links-dso-program-c.c: New.
* support/links-dso-program.cc: New.
* support/test-container.c: New.
* support/shell-container.c: New.
* support/echo-container.c: New.
* support/true-container.c: New.
* support/xmkdirp.c: New.
* support/xsymlink.c: New.
* support/support_paths.c: New.
* support/support.h: Add support paths prototypes.
* support/xunistd.h: Add xmkdirp () and xsymlink ().
* nss/tst-nss-test3.c: Convert to test-in-container.
* nss/tst-nss-test3.root/: New.
(note: support/ already present, not needed; sample test not included)
diff --git a/Makefile b/Makefile
index d3f25a5..3df55e6 100644
--- a/Makefile
+++ b/Makefile
@@ -340,6 +340,62 @@ define summarize-tests
@! egrep -q -v '^(X?PASS|XFAIL|UNSUPPORTED):' $(objpfx)$1
endef
+# The intention here is to do ONE install of our build into the
+# testroot.pristine/ directory, then rsync (internal to
+# support/test-container) that to testroot.root/ at the start of each
+# test. That way we can promise each test a "clean" install, without
+# having to do the install for each test.
+#
+# In addition, we have to copy some files (which we build) into this
+# root in addition to what glibc installs. For example, many tests
+# require additional programs including /bin/sh, /bin/true, and
+# /bin/echo, all of which we build below to limit library dependencies
+# to just those things in glibc and language support libraries which
+# we also copy into the into the rootfs. To determine what language
+# support libraries we need we build a "test" program in either C or
+# (if available) C++ just so we can copy in any shared objects
+# (which we do not build) that GCC-compiled programs depend on.
+
+
+ifeq (,$(CXX))
+LINKS_DSO_PROGRAM = links-dso-program-c
+else
+LINKS_DSO_PROGRAM = links-dso-program
+endif
+
+$(tests-container) $(addsuffix /tests,$(subdirs)) : \
+ $(objpfx)testroot.pristine/install.stamp
+$(objpfx)testroot.pristine/install.stamp :
+ test -d $(objpfx)testroot.pristine || \
+ mkdir $(objpfx)testroot.pristine
+ # We need a working /bin/sh for some of the tests.
+ test -d $(objpfx)testroot.pristine/bin || \
+ mkdir $(objpfx)testroot.pristine/bin
+ cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
+ cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
+ cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
+ # Copy these DSOs first so we can overwrite them with our own.
+ for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
+ $(objpfx)elf/$(rtld-installed-name) \
+ $(objpfx)testroot.pristine/bin/sh \
+ | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+ do \
+ test -d `dirname $(objpfx)testroot.pristine$$dso` || \
+ mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
+ $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
+ done
+ for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
+ $(objpfx)elf/$(rtld-installed-name) \
+ $(objpfx)support/$(LINKS_DSO_PROGRAM) \
+ | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+ do \
+ test -d `dirname $(objpfx)testroot.pristine$$dso` || \
+ mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
+ $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
+ done
+ $(MAKE) install DESTDIR=$(objpfx)testroot.pristine
+ touch $(objpfx)testroot.pristine/install.stamp
+
tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
tests: $(tests-special)
$(..)scripts/merge-test-results.sh -s $(objpfx) "" \
diff --git a/Makerules b/Makerules
index a10a0b4..5d6434c 100644
--- a/Makerules
+++ b/Makerules
@@ -1369,7 +1369,8 @@ xcheck: xtests
# The only difference between MODULE_NAME=testsuite and MODULE_NAME=nonlib is
# that almost all internal declarations from config.h, libc-symbols.h, and
# include/*.h are not available to 'testsuite' code, but are to 'nonlib' code.
-all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras))
+all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras) \
+ $(tests-container))
ifneq (,$(all-testsuite))
cpp-srcs-left = $(all-testsuite)
lib := testsuite
diff --git a/Rules b/Rules
index 706c8a7..5abb727 100644
--- a/Rules
+++ b/Rules
@@ -130,12 +130,14 @@ others: $(py-const)
ifeq ($(run-built-tests),no)
tests: $(addprefix $(objpfx),$(filter-out $(tests-unsupported), \
- $(tests) $(tests-internal)) \
+ $(tests) $(tests-internal) \
+ $(tests-container)) \
$(test-srcs)) $(tests-special) \
$(tests-printers-programs)
xtests: tests $(xtests-special)
else
tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) \
+ $(tests-container:%=$(objpfx)%.out) \
$(tests-special) $(tests-printers-out)
xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
endif
@@ -145,7 +147,8 @@ xtests-special-notdir = $(patsubst $(objpfx)%, %, $(xtests-special))
ifeq ($(run-built-tests),no)
tests-expected =
else
-tests-expected = $(tests) $(tests-internal) $(tests-printers)
+tests-expected = $(tests) $(tests-internal) $(tests-printers) \
+ $(tests-container)
endif
tests:
$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
@@ -158,7 +161,8 @@ xtests:
ifeq ($(build-programs),yes)
binaries-all-notests = $(others) $(sysdep-others)
-binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs)
+binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs) \
+ $(tests-container)
binaries-all = $(binaries-all-notests) $(binaries-all-tests)
binaries-static-notests = $(others-static)
binaries-static-tests = $(tests-static) $(xtests-static)
@@ -248,6 +252,17 @@ $(objpfx)%.out: /dev/null $(objpfx)% # Make it 2nd arg for canned sequence.
$(make-test-out) > $@; \
$(evaluate-test)
+
+# Any tests that require an isolated container (filesystem, network
+# and pid namespaces) in which to run, should be added to
+# tests-container.
+$(tests-container:%=$(objpfx)%.out): $(objpfx)%.out : $(if $(wildcard $(objpfx)%.files),$(objpfx)%.files,/dev/null) $(objpfx)%
+ $(test-wrapper-env) $(run-program-env) $(run-via-rtld-prefix) \
+ $(common-objpfx)support/test-container env $(run-program-env) $($*-ENV) \
+ $(host-test-program-cmd) $($*-ARGS) > $@; \
+ $(evaluate-test)
+
+
# tests-unsupported lists tests that we will not try to build at all in
# this configuration. Note this runs every time because it does not
# actually create its target. The dependency on Makefile is meant to

View File

@ -0,0 +1,48 @@
commit bd598da9f454bc1091b4ebe0303b07e6f96ca130
Author: Joseph Myers <joseph@codesourcery.com>
Date: Tue Dec 4 16:52:39 2018 +0000
Stop test-in-container trying to run other-OS binaries.
I noticed that, now that build-many-glibcs.py no longer copies glibc
sources, I was getting core dumps in my glibc source directories. The
cause appears to be, from the i686-gnu build:
for dso in ` env LD_TRACE_LOADED_OBJECTS=1 \
/scratch/jmyers/glibc-bot/build/glibcs/i686-gnu/glibc/elf/ld.so.1 \
/scratch/jmyers/glibc-bot/build/glibcs/i686-gnu/glibc/testroot.pristine/bin/sh \
[...]
Segmentation fault (core dumped)
In this case, the x86 architecture means the binary executes, but
dumps core rather than actually working.
Anything involving running the newly built glibc should only be done
ifeq ($(run-built-tests),yes). This patch conditions the relevant
part of the testroot setup accordingly.
Tested for x86_64, and with build-many-glibcs.py for i686-gnu.
* Makefile ($(objpfx)testroot.pristine/install.stamp): Do not run
dynamic linker unless [$(run-built-tests) = yes].
diff --git a/Makefile b/Makefile
index b4703e4..fd73d9b 100644
--- a/Makefile
+++ b/Makefile
@@ -374,6 +374,7 @@ $(objpfx)testroot.pristine/install.stamp :
cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
+ifeq ($(run-built-tests),yes)
# Copy these DSOs first so we can overwrite them with our own.
for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
$(objpfx)elf/$(rtld-installed-name) \
@@ -393,6 +394,7 @@ $(objpfx)testroot.pristine/install.stamp :
mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
$(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
done
+endif
$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
touch $(objpfx)testroot.pristine/install.stamp

View File

@ -0,0 +1,40 @@
commit 95da14dac04b494149290d85bc5306226e30839e
Author: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
Date: Mon Jul 22 16:30:45 2019 -0300
test-container: Avoid copying unintended system libraries
Some DSOs are distributed in hardware capability directories, e.g.
/usr/lib64/power7/libc.so.6
Whenever the processor is able to use one of these hardware-enabled
DSOs, testroot.pristine ends up with copies of glibc-provided libraries
from the system because it can't overwrite or remove them.
This patch avoids the unintended copies by executing ld.so with the same
arguments passed to each glibc test.
* Makefile (testroot.pristine/install.stamp): Execute ld.so with
the same arguments used in all tests.
diff --git a/Makefile b/Makefile
index dc5de7a..a4ed747 100644
--- a/Makefile
+++ b/Makefile
@@ -383,7 +383,7 @@ $(objpfx)testroot.pristine/install.stamp :
ifeq ($(run-built-tests),yes)
# Copy these DSOs first so we can overwrite them with our own.
for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
- $(objpfx)elf/$(rtld-installed-name) \
+ $(rtld-prefix) \
$(objpfx)testroot.pristine/bin/sh \
| grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
do \
@@ -392,7 +392,7 @@ ifeq ($(run-built-tests),yes)
$(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
done
for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
- $(objpfx)elf/$(rtld-installed-name) \
+ $(rtld-prefix) \
$(objpfx)support/$(LINKS_DSO_PROGRAM) \
| grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
do \

View File

@ -0,0 +1,31 @@
commit 35e038c1d2ccb3a75395662f9c4f28d85a61444f
Author: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
Date: Mon Jul 22 17:34:13 2019 -0300
test-container: Install with $(all-subdirs) [BZ #24794]
Whenever a sub-make is created, it inherits the variable subdirs from its
parent. This is also true when make check is called with a restricted
list of subdirs. In this scenario, make install is executed "partially"
and testroot.pristine ends up with an incomplete installation.
[BZ #24794]
* Makefile (testroot.pristine/install.stamp): Pass
subdirs='$(all-subdirs)' to make install.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/Makefile b/Makefile
index a4ed747..9fbf705 100644
--- a/Makefile
+++ b/Makefile
@@ -401,7 +401,8 @@ ifeq ($(run-built-tests),yes)
$(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
done
endif
- $(MAKE) install DESTDIR=$(objpfx)testroot.pristine
+ $(MAKE) install DESTDIR=$(objpfx)testroot.pristine \
+ subdirs='$(all-subdirs)'
touch $(objpfx)testroot.pristine/install.stamp
tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))

View File

@ -0,0 +1,56 @@
commit 7db1fe38de21831d53ceab9ae83493d8d1aec601
Author: Joseph Myers <joseph@codesourcery.com>
Date: Tue Oct 22 20:24:10 2019 +0000
Fix testroot.pristine creation copying dynamic linker.
This patch addresses an issue reported in
<https://sourceware.org/ml/libc-alpha/2019-07/msg00661.html> where the
creation of testroot.pristine, on encountering
LD_TRACE_LOADED_OBJECTS=1 of the form
libc.so.6 => /scratch/jmyers/glibc/mbs/obj/glibc-8-0-mips64-linux-gnu-x86_64-linux-gnu/default/libc.so.6 (0x772dd000)
/lib32/ld.so.1 => /scratch/jmyers/glibc/mbs/obj/glibc-8-0-mips64-linux-gnu-x86_64-linux-gnu/default/elf/ld.so.1 (0x7747b000)
tries to copy /lib32/ld.so.1 (which does not exist) into the testroot
instead of copying the path on the RHS of "=>", which does exist,
because the Makefile logic assumes that the path on such a line with
'/' should be copied, when if there are such paths on both the LHS and
the RHS of "=>", only the one on the RHS necessarily exists and so
only that should be copied. The patch follows the approach suggested
by DJ in <https://sourceware.org/ml/libc-alpha/2019-07/msg00662.html>,
with the suggestion from Andreas in
<https://sourceware.org/ml/libc-alpha/2019-10/msg00514.html> of a
single sed command in place of pipeline of grep and three sed
commands.
Tested for x86_64, with and without --enable-hardcoded-path-in-tests;
a previous version with multiple sed commands, implementing the same
logic, also tested for MIPS, with and without
--enable-hardcoded-path-in-tests, to confirm it fixes the original
problem.
Co-authored-by: DJ Delorie <dj@redhat.com>
diff --git a/Makefile b/Makefile
index d7e4be9..0711b97 100644
--- a/Makefile
+++ b/Makefile
@@ -564,7 +564,7 @@ ifeq ($(run-built-tests),yes)
for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
$(rtld-prefix) \
$(objpfx)testroot.pristine/bin/sh \
- | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+ | sed -n '/\//{s@.*=> /@/@;s/^[^/]*//;s/ .*//p;}'` ;\
do \
test -d `dirname $(objpfx)testroot.pristine$$dso` || \
mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
@@ -573,7 +573,7 @@ ifeq ($(run-built-tests),yes)
for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1 \
$(rtld-prefix) \
$(objpfx)support/$(LINKS_DSO_PROGRAM) \
- | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+ | sed -n '/\//{s@.*=> /@/@;s/^[^/]*//;s/ .*//p;}'` ;\
do \
test -d `dirname $(objpfx)testroot.pristine$$dso` || \
mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\

View File

@ -0,0 +1,56 @@
commit c7ac9caaae6f8d02d4e0c7618d4991324a084c66
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Mon May 13 13:57:37 2019 -0300
support: Export bindir path on support_path
Checked on x86_64-linux-gnu.
* support/Makefile (CFLAGS-support_paths.c): Add -DBINDIR_PATH.
* support/support.h (support_bindir_prefix): New variable.
* support/support_paths.c [BINDIR_PATH] (support_bindir_prefix):
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/support/Makefile b/support/Makefile
index 64044f6..fe416cd 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -179,7 +179,8 @@ CFLAGS-support_paths.c = \
-DOBJDIR_PATH=\"`cd $(objpfx)/..; pwd`\" \
-DOBJDIR_ELF_LDSO_PATH=\"`cd $(objpfx)/..; pwd`/elf/$(rtld-installed-name)\" \
-DINSTDIR_PATH=\"$(prefix)\" \
- -DLIBDIR_PATH=\"$(libdir)\"
+ -DLIBDIR_PATH=\"$(libdir)\" \
+ -DBINDIR_PATH=\"$(bindir)\"
ifeq (,$(CXX))
LINKS_DSO_PROGRAM = links-dso-program-c
diff --git a/support/support.h b/support/support.h
index 97fef2c..b162491 100644
--- a/support/support.h
+++ b/support/support.h
@@ -105,6 +105,8 @@ extern const char support_objdir_elf_ldso[];
extern const char support_install_prefix[];
/* Corresponds to the install's lib/ or lib64/ directory. */
extern const char support_libdir_prefix[];
+/* Corresponds to the install's bin/ directory. */
+extern const char support_bindir_prefix[];
extern ssize_t support_copy_file_range (int, off64_t *, int, off64_t *,
size_t, unsigned int);
diff --git a/support/support_paths.c b/support/support_paths.c
index 937e6e1..75634aa 100644
--- a/support/support_paths.c
+++ b/support/support_paths.c
@@ -57,3 +57,10 @@ const char support_libdir_prefix[] = LIBDIR_PATH;
#else
# error please -DLIBDIR_PATH=something in the Makefile
#endif
+
+#ifdef BINDIR_PATH
+/* Corresponds to the install's bin/ directory. */
+const char support_bindir_prefix[] = BINDIR_PATH;
+#else
+# error please -DBINDIR_PATH=something in the Makefile
+#endif

View File

@ -0,0 +1,46 @@
From 354e4c1adddb1da19c1043e3e5db61ee2148d912 Mon Sep 17 00:00:00 2001
From: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
Date: Wed, 24 Jul 2019 19:49:00 -0300
Subject: test-container: Install with $(sorted-subdirs) [BZ #24794]
Commit 35e038c1d2ccb3a75395662f9c4f28d85a61444f started to use an
incomplete list of subdirs based on $(all-subdirs) causing
testroot.pristine to miss files from nss.
Tested if the list of files in testroot.pristine remains the same.
[BZ #24794]
* Makeconfig (all-subdirs): Improved source comments.
* Makefile (testroot.pristine/install.stamp): Pass
subdirs='$(sorted-subdirs)' to make install.
diff --git a/Makeconfig b/Makeconfig
index 0e386fbc19..fd36c58c04 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -1267,9 +1267,9 @@ else
libsupport = $(common-objpfx)support/libsupport.a
endif
-# These are the subdirectories containing the library source. The order
-# is more or less arbitrary. The sorting step will take care of the
-# dependencies.
+# This is a partial list of subdirectories containing the library source.
+# The order is more or less arbitrary. The sorting step will take care of the
+# dependencies and generate sorted-subdirs dynamically.
all-subdirs = csu assert ctype locale intl catgets math setjmp signal \
stdlib stdio-common libio malloc string wcsmbs time dirent \
grp pwd posix io termios resource misc socket sysvipc gmon \
diff --git a/Makefile b/Makefile
index 9fbf705200..ac1125853b 100644
--- a/Makefile
+++ b/Makefile
@@ -402,7 +402,7 @@ ifeq ($(run-built-tests),yes)
done
endif
$(MAKE) install DESTDIR=$(objpfx)testroot.pristine \
- subdirs='$(all-subdirs)'
+ subdirs='$(sorted-subdirs)'
touch $(objpfx)testroot.pristine/install.stamp
tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))

View File

@ -0,0 +1,50 @@
commit d50f09181eca10a91fd9035bb90711b265770dc9
Author: Alexandra Hájková <ahajkova@redhat.com>
Date: Mon May 13 19:31:53 2019 +0200
support: Add support_install_rootsbindir
Reviewed by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/support/Makefile b/support/Makefile
index fe416cd..18d39f5 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -180,7 +180,8 @@ CFLAGS-support_paths.c = \
-DOBJDIR_ELF_LDSO_PATH=\"`cd $(objpfx)/..; pwd`/elf/$(rtld-installed-name)\" \
-DINSTDIR_PATH=\"$(prefix)\" \
-DLIBDIR_PATH=\"$(libdir)\" \
- -DBINDIR_PATH=\"$(bindir)\"
+ -DBINDIR_PATH=\"$(bindir)\" \
+ -DROOTSBINDIR_PATH=\"$(rootsbindir)\"
ifeq (,$(CXX))
LINKS_DSO_PROGRAM = links-dso-program-c
diff --git a/support/support.h b/support/support.h
index b162491..13076b7 100644
--- a/support/support.h
+++ b/support/support.h
@@ -107,6 +107,8 @@ extern const char support_install_prefix[];
extern const char support_libdir_prefix[];
/* Corresponds to the install's bin/ directory. */
extern const char support_bindir_prefix[];
+/* Corresponds to the install's sbin/ directory. */
+extern const char support_install_rootsbindir[];
extern ssize_t support_copy_file_range (int, off64_t *, int, off64_t *,
size_t, unsigned int);
diff --git a/support/support_paths.c b/support/support_paths.c
index 75634aa..1fe3283 100644
--- a/support/support_paths.c
+++ b/support/support_paths.c
@@ -64,3 +64,10 @@ const char support_bindir_prefix[] = BINDIR_PATH;
#else
# error please -DBINDIR_PATH=something in the Makefile
#endif
+
+#ifdef ROOTSBINDIR_PATH
+/* Corresponds to the install's sbin/ directory. */
+const char support_install_rootsbindir[] = ROOTSBINDIR_PATH;
+#else
+# error please -DROOTSBINDIR_PATH=something in the Makefile
+#endif

View File

@ -0,0 +1,86 @@
From 304c61a24f909168c16793ccf7c686237e53d003 Mon Sep 17 00:00:00 2001
From: DJ Delorie <dj@redhat.com>
Date: Wed, 5 Dec 2018 12:39:47 -0500
Subject: test-container: move postclean outside of namespace changes
During postclean.req testing it was found that the fork in the
parent process (after the unshare syscall) would fail with ENOMEM
(see recursive_remove() in test-container.c). While failing with
ENOMEM is certainly unexpected, it is simply easier to refactor
the design and have the parent remain outside of the namespace.
This change moves the postclean.req processing to a distinct
process (the parent) that then forks the test process (which will
have to fork once more to complete uid/gid transitions). When the
test process exists the cleanup process will ensure all files are
deleted when a post clean is requested.
Signed-off-by: DJ Delorie <dj@redhat.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
[BZ #23948]
* support/test-container.c: Move postclean step to before we
change namespaces.
diff --git a/support/test-container.c b/support/test-container.c
index df450adfdb..1d1aebeaf3 100644
--- a/support/test-container.c
+++ b/support/test-container.c
@@ -921,6 +921,43 @@ main (int argc, char **argv)
}
}
+ if (do_postclean)
+ {
+ pid_t pc_pid = fork ();
+
+ if (pc_pid < 0)
+ {
+ FAIL_EXIT1 ("Can't fork for post-clean");
+ }
+ else if (pc_pid > 0)
+ {
+ /* Parent. */
+ int status;
+ waitpid (pc_pid, &status, 0);
+
+ /* Child has exited, we can post-clean the test root. */
+ printf("running post-clean rsync\n");
+ rsync (pristine_root_path, new_root_path, 1);
+
+ if (WIFEXITED (status))
+ exit (WEXITSTATUS (status));
+
+ if (WIFSIGNALED (status))
+ {
+ printf ("%%SIGNALLED%%\n");
+ exit (77);
+ }
+
+ printf ("%%EXITERROR%%\n");
+ exit (78);
+ }
+
+ /* Child continues. */
+ }
+
+ /* This is the last point in the program where we're still in the
+ "normal" namespace. */
+
#ifdef CLONE_NEWNS
/* The unshare here gives us our own spaces and capabilities. */
if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
@@ -974,14 +1011,6 @@ main (int argc, char **argv)
int status;
waitpid (child, &status, 0);
- /* There's a bit of magic here, since the buildroot is mounted
- in our space, the paths are still valid, and since the mounts
- aren't recursive, it sees *only* the built root, not anything
- we would normally se if we rsync'd to "/" like mounted /dev
- files. */
- if (do_postclean)
- rsync (pristine_root_path, new_root_path, 1);
-
if (WIFEXITED (status))
exit (WEXITSTATUS (status));

View File

@ -0,0 +1,268 @@
commit 99135114ba23c3110b7e4e650fabdc5e639746b7
Author: DJ Delorie <dj@redhat.com>
Date: Fri Jun 28 18:30:00 2019 -0500
nss_db: fix endent wrt NULL mappings [BZ #24695] [BZ #24696]
nss_db allows for getpwent et al to be called without a set*ent,
but it only works once. After the last get*ent a set*ent is
required to restart, because the end*ent did not properly reset
the module. Resetting it to NULL allows for a proper restart.
If the database doesn't exist, however, end*ent erroniously called
munmap which set errno.
The test case runs "makedb" inside the testroot, so needs selinux
DSOs installed.
diff -rupN a/nss/Makefile b/nss/Makefile
--- a/nss/Makefile 2019-11-04 15:14:16.721221038 -0500
+++ b/nss/Makefile 2019-11-04 15:15:46.447544678 -0500
@@ -60,6 +60,10 @@ tests = test-netdb test-digits-dots ts
tst-nss-test5
xtests = bug-erange
+tests-container = \
+ tst-nss-db-endpwent \
+ tst-nss-db-endgrent
+
# Tests which need libdl
ifeq (yes,$(build-shared))
tests += tst-nss-files-hosts-erange
diff -rupN a/nss/nss_db/db-open.c b/nss/nss_db/db-open.c
--- a/nss/nss_db/db-open.c 2018-08-01 01:10:47.000000000 -0400
+++ b/nss/nss_db/db-open.c 2019-11-04 15:15:10.520213846 -0500
@@ -63,5 +63,9 @@ internal_setent (const char *file, struc
void
internal_endent (struct nss_db_map *mapping)
{
- munmap (mapping->header, mapping->len);
+ if (mapping->header != NULL)
+ {
+ munmap (mapping->header, mapping->len);
+ mapping->header = NULL;
+ }
}
diff -rupN a/nss/tst-nss-db-endgrent.c b/nss/tst-nss-db-endgrent.c
--- a/nss/tst-nss-db-endgrent.c 1969-12-31 19:00:00.000000000 -0500
+++ b/nss/tst-nss-db-endgrent.c 2019-11-04 15:15:10.526214069 -0500
@@ -0,0 +1,54 @@
+/* Test for endgrent changing errno for BZ #24696
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <support/check.h>
+#include <support/support.h>
+
+/* The following test verifies that if the db NSS Service is initialized
+ with no database (getgrent), that a subsequent closure (endgrent) does
+ not set errno. In the case of the db service it is not an error to close
+ the service and so it should not set errno. */
+
+static int
+do_test (void)
+{
+ /* Just make sure it's not there, although usually it won't be. */
+ unlink ("/var/db/group.db");
+
+ /* This, in conjunction with the testroot's nsswitch.conf, causes
+ the nss_db module to be "connected" and initialized - but the
+ testroot has no group.db, so no mapping will be created. */
+ getgrent ();
+
+ errno = 0;
+
+ /* Before the fix, this would call munmap (NULL) and set errno. */
+ endgrent ();
+
+ if (errno != 0)
+ FAIL_EXIT1 ("endgrent set errno to %d\n", errno);
+
+ return 0;
+}
+#include <support/test-driver.c>
diff -rupN a/nss/tst-nss-db-endgrent.root/etc/nsswitch.conf b/nss/tst-nss-db-endgrent.root/etc/nsswitch.conf
--- a/nss/tst-nss-db-endgrent.root/etc/nsswitch.conf 1969-12-31 19:00:00.000000000 -0500
+++ b/nss/tst-nss-db-endgrent.root/etc/nsswitch.conf 2019-11-04 15:15:10.539214550 -0500
@@ -0,0 +1 @@
+group : db files
diff -rupN a/nss/tst-nss-db-endpwent.c b/nss/tst-nss-db-endpwent.c
--- a/nss/tst-nss-db-endpwent.c 1969-12-31 19:00:00.000000000 -0500
+++ b/nss/tst-nss-db-endpwent.c 2019-11-04 15:15:10.545214772 -0500
@@ -0,0 +1,66 @@
+/* Test for endpwent->getpwent crash for BZ #24695
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <support/support.h>
+#include <support/check.h>
+
+/* It is entirely allowed to start with a getpwent call without
+ resetting the state of the service via a call to setpwent.
+ You can also call getpwent more times than you have entries in
+ the service, and it should not fail. This test iteratates the
+ database once, gets to the end, and then attempts a second
+ iteration to look for crashes. */
+
+static void
+try_it (void)
+{
+ struct passwd *pw;
+
+ /* setpwent is intentionally omitted here. The first call to
+ getpwent detects that it's first and initializes. The second
+ time try_it is called, this "first call" was not detected before
+ the fix, and getpwent would crash. */
+
+ while ((pw = getpwent ()) != NULL)
+ ;
+
+ /* We only care if this segfaults or not. */
+ endpwent ();
+}
+
+static int
+do_test (void)
+{
+ char *cmd;
+
+ cmd = xasprintf ("%s/makedb -o /var/db/passwd.db /var/db/passwd.in",
+ support_bindir_prefix);
+ system (cmd);
+ free (cmd);
+
+ try_it ();
+ try_it ();
+
+ return 0;
+}
+#include <support/test-driver.c>
diff -rupN a/nss/tst-nss-db-endpwent.root/etc/nsswitch.conf b/nss/tst-nss-db-endpwent.root/etc/nsswitch.conf
--- a/nss/tst-nss-db-endpwent.root/etc/nsswitch.conf 1969-12-31 19:00:00.000000000 -0500
+++ b/nss/tst-nss-db-endpwent.root/etc/nsswitch.conf 2019-11-04 15:15:10.556215180 -0500
@@ -0,0 +1 @@
+passwd: db
diff -rupN a/nss/tst-nss-db-endpwent.root/var/db/passwd.in b/nss/tst-nss-db-endpwent.root/var/db/passwd.in
--- a/nss/tst-nss-db-endpwent.root/var/db/passwd.in 1969-12-31 19:00:00.000000000 -0500
+++ b/nss/tst-nss-db-endpwent.root/var/db/passwd.in 2019-11-04 15:15:10.567215588 -0500
@@ -0,0 +1,4 @@
+.root root:x:0:0:root:/root:/bin/bash
+=0 root:x:0:0:root:/root:/bin/bash
+.bin bin:x:1:1:bin:/bin:/sbin/nologin
+=1 bin:x:1:1:bin:/bin:/sbin/nologin
diff -rupN a/support/Makefile b/support/Makefile
--- a/support/Makefile 2019-11-04 15:14:20.416357911 -0500
+++ b/support/Makefile 2019-11-04 15:15:10.574215847 -0500
@@ -180,6 +180,11 @@ LINKS_DSO_PROGRAM = links-dso-program
LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)
endif
+ifeq (yes,$(have-selinux))
+LDLIBS-$(LINKS_DSO_PROGRAM) += -lselinux
+endif
+
+
LDLIBS-test-container = $(libsupport)
others += test-container
diff -rupN a/support/links-dso-program-c.c b/support/links-dso-program-c.c
--- a/support/links-dso-program-c.c 2019-11-04 15:14:17.073234077 -0500
+++ b/support/links-dso-program-c.c 2019-11-04 15:15:10.580216069 -0500
@@ -1,9 +1,26 @@
#include <stdio.h>
+/* makedb needs selinux dso's. */
+#ifdef HAVE_SELINUX
+# include <selinux/selinux.h>
+#endif
+
+/* The purpose of this file is to indicate to the build system which
+ shared objects need to be copied into the testroot, such as gcc or
+ selinux support libraries. This program is never executed, only
+ scanned for dependencies on shared objects, so the code below may
+ seem weird - it's written to survive gcc optimization and force
+ such dependencies.
+*/
+
int
main (int argc, char **argv)
{
/* Complexity to keep gcc from optimizing this away. */
printf ("This is a test %s.\n", argc > 1 ? argv[1] : "null");
+#ifdef HAVE_SELINUX
+ /* This exists to force libselinux.so to be required. */
+ printf ("selinux %d\n", is_selinux_enabled ());
+#endif
return 0;
}
diff -rupN a/support/links-dso-program.cc b/support/links-dso-program.cc
--- a/support/links-dso-program.cc 2019-11-04 15:14:17.079234300 -0500
+++ b/support/links-dso-program.cc 2019-11-04 15:15:10.587216328 -0500
@@ -1,11 +1,28 @@
#include <iostream>
+/* makedb needs selinux dso's. */
+#ifdef HAVE_SELINUX
+# include <selinux/selinux.h>
+#endif
+
using namespace std;
+/* The purpose of this file is to indicate to the build system which
+ shared objects need to be copied into the testroot, such as gcc or
+ selinux support libraries. This program is never executed, only
+ scanned for dependencies on shared objects, so the code below may
+ seem weird - it's written to survive gcc optimization and force
+ such dependencies.
+*/
+
int
main (int argc, char **argv)
{
/* Complexity to keep gcc from optimizing this away. */
cout << (argc > 1 ? argv[1] : "null");
+#ifdef HAVE_SELINUX
+ /* This exists to force libselinux.so to be required. */
+ cout << "selinux " << is_selinux_enabled ();
+#endif
return 0;
}

View File

@ -0,0 +1,196 @@
commit 4b7c74179c8928d971d370e1137d202f891a4cf5
Author: Carlos O'Donell <carlos@redhat.com>
Date: Wed Mar 20 12:40:18 2019 -0400
nss: Make nsswitch.conf more distribution friendly.
The current default nsswitch.conf file provided by glibc is not very
distribution friendly. The file contains some minimal directives that no
real distribution uses. This update aims to provide a rich set of
comments which are useful for all distributions, and a broader set of
service defines which should work for all distributions.
Tested defaults on x86_64 and they work. The nsswitch.conf file more
closely matches what we have in Fedora now, and I'll adjust Fedora to
use this version with minor changes to enable Fedora-specific service
providers.
v2
- Add missing databases to manual.
- Add link to manual from default nsswitch.conf.
- Sort nsswitch.conf according to most used database first.
v3
- Only mention implemented services in 'NSS Basics.'
- Mention 'automount' in 'Services in the NSS configuration.'
- Sort services in alphabetical order.
v4
- Project name is 'Samba'.
v5
- Fix typo in manual/nss.texi.
v6
- Fix another typo in manual/nss.texi. Ran spell checker this time.
diff --git a/manual/nss.texi b/manual/nss.texi
index 164ae33246..821469a78a 100644
--- a/manual/nss.texi
+++ b/manual/nss.texi
@@ -56,13 +56,17 @@ functions to access the databases.
@noindent
The databases available in the NSS are
+@cindex aliases
@cindex ethers
@cindex group
+@cindex gshadow
@cindex hosts
+@cindex initgroups
@cindex netgroup
@cindex networks
-@cindex protocols
@cindex passwd
+@cindex protocols
+@cindex publickey
@cindex rpc
@cindex services
@cindex shadow
@@ -75,16 +79,22 @@ Ethernet numbers,
@comment @pxref{Ethernet Numbers}.
@item group
Groups of users, @pxref{Group Database}.
+@item gshadow
+Group passphrase hashes and related information.
@item hosts
Host names and numbers, @pxref{Host Names}.
+@item initgroups
+Supplementary group access list.
@item netgroup
Network wide list of host and users, @pxref{Netgroup Database}.
@item networks
Network names and numbers, @pxref{Networks Database}.
-@item protocols
-Network protocols, @pxref{Protocols Database}.
@item passwd
User identities, @pxref{User Database}.
+@item protocols
+Network protocols, @pxref{Protocols Database}.
+@item publickey
+Public keys for Secure RPC.
@item rpc
Remote procedure call names and numbers.
@comment @pxref{RPC Database}.
@@ -96,8 +106,8 @@ User passphrase hashes and related information.
@end table
@noindent
-There will be some more added later (@code{automount}, @code{bootparams},
-@code{netmasks}, and @code{publickey}).
+@c We currently don't implement automount, netmasks, or bootparams.
+More databases may be added later.
@node NSS Configuration File, NSS Module Internals, NSS Basics, Name Service Switch
@section The NSS Configuration File
@@ -159,6 +169,10 @@ these files since they should be placed in a directory where they are
found automatically. Only the names of all available services are
important.
+Lastly, some system software may make use of the NSS configuration file
+to store their own configuration for similar purposes. Examples of this
+include the @code{automount} service which is used by @code{autofs}.
+
@node Actions in the NSS configuration, Notes on NSS Configuration File, Services in the NSS configuration, NSS Configuration File
@subsection Actions in the NSS configuration
diff --git a/nss/nsswitch.conf b/nss/nsswitch.conf
index 39ca88bf51..f553588114 100644
--- a/nss/nsswitch.conf
+++ b/nss/nsswitch.conf
@@ -1,20 +1,69 @@
+#
# /etc/nsswitch.conf
#
-# Example configuration of GNU Name Service Switch functionality.
+# An example Name Service Switch config file. This file should be
+# sorted with the most-used services at the beginning.
#
+# Valid databases are: aliases, ethers, group, gshadow, hosts,
+# initgroups, netgroup, networks, passwd, protocols, publickey,
+# rpc, services, and shadow.
+#
+# Valid service provider entries include (in alphabetical order):
+#
+# compat Use /etc files plus *_compat pseudo-db
+# db Use the pre-processed /var/db files
+# dns Use DNS (Domain Name Service)
+# files Use the local files in /etc
+# hesiod Use Hesiod (DNS) for user lookups
+# nis Use NIS (NIS version 2), also called YP
+# nisplus Use NIS+ (NIS version 3)
+#
+# See `info libc 'NSS Basics'` for more information.
+#
+# Commonly used alternative service providers (may need installation):
+#
+# ldap Use LDAP directory server
+# myhostname Use systemd host names
+# mymachines Use systemd machine names
+# mdns*, mdns*_minimal Use Avahi mDNS/DNS-SD
+# resolve Use systemd resolved resolver
+# sss Use System Security Services Daemon (sssd)
+# systemd Use systemd for dynamic user option
+# winbind Use Samba winbind support
+# wins Use Samba wins support
+# wrapper Use wrapper module for testing
+#
+# Notes:
+#
+# 'sssd' performs its own 'files'-based caching, so it should generally
+# come before 'files'.
+#
+# WARNING: Running nscd with a secondary caching service like sssd may
+# lead to unexpected behaviour, especially with how long
+# entries are cached.
+#
+# Installation instructions:
+#
+# To use 'db', install the appropriate package(s) (provide 'makedb' and
+# libnss_db.so.*), and place the 'db' in front of 'files' for entries
+# you want to be looked up first in the databases, like this:
+#
+# passwd: db files
+# shadow: db files
+# group: db files
-passwd: db files
-group: db files
-initgroups: db [SUCCESS=continue] files
-shadow: db files
-gshadow: files
-
-hosts: files dns
-networks: files dns
-
-protocols: db files
-services: db files
-ethers: db files
-rpc: db files
-
-netgroup: db files
+# In alphabetical order. Re-order as required to optimize peformance.
+aliases: files
+ethers: files
+group: files
+gshadow: files
+hosts: files dns
+initgroups: files
+netgroup: files
+networks: files dns
+passwd: files
+protocols: files
+publickey: files
+rpc: files
+shadow: files
+services: files

View File

@ -0,0 +1,38 @@
commit d34d4c80226b3f5a1b51a8e5b005a52fba07d7ba
Author: Carlos O'Donell <carlos@redhat.com>
Date: Wed Mar 20 22:11:32 2019 -0400
nscd: Improve nscd.conf comments.
This change adds a warning to nscd.conf about running multiple caching
services together and that it may lead to unexpected behaviours. Also we
add a note that enabling the 'shared' option will cause cache hit rates
to be misreported (a side effect of the implementation).
v2
- Rewrite comment to avoid implementation details.
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
index 39b875912d..487ffe461d 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -3,6 +3,9 @@
#
# An example Name Service Cache config file. This file is needed by nscd.
#
+# WARNING: Running nscd with a secondary caching service like sssd may lead to
+# unexpected behaviour, especially with how long entries are cached.
+#
# Legal entries are:
#
# logfile <file>
@@ -23,6 +26,9 @@
# check-files <service> <yes|no>
# persistent <service> <yes|no>
# shared <service> <yes|no>
+# NOTE: Setting 'shared' to a value of 'yes' will accelerate the lookup,
+# but those lookups will not be counted as cache hits
+# i.e. 'nscd -g' may show '0%'.
# max-db-size <service> <number bytes>
# auto-propagate <service> <yes|no>
#

View File

@ -0,0 +1,40 @@
diff -Nrup a/nss/nsswitch.conf b/nss/nsswitch.conf
--- a/nss/nsswitch.conf 2019-10-25 12:14:09.255834866 -0400
+++ b/nss/nsswitch.conf 2019-10-25 12:50:08.425769248 -0400
@@ -1,7 +1,7 @@
#
# /etc/nsswitch.conf
#
-# An example Name Service Switch config file. This file should be
+# Name Service Switch config file. This file should be
# sorted with the most-used services at the beginning.
#
# Valid databases are: aliases, ethers, group, gshadow, hosts,
@@ -52,18 +52,20 @@
# shadow: db files
# group: db files
-# In alphabetical order. Re-order as required to optimize peformance.
+# In order of likelihood of use to accelerate lookup.
+passwd: sss files
+shadow: files sss
+group: sss files
+hosts: files dns myhostname
+services: files sss
+netgroup: sss
+automount: files sss
+
aliases: files
ethers: files
-group: files
gshadow: files
-hosts: files dns
initgroups: files
-netgroup: files
networks: files dns
-passwd: files
protocols: files
publickey: files
rpc: files
-shadow: files
-services: files

View File

@ -0,0 +1,27 @@
commit eed1f6fcdb0526498223ebfe95f91ef5dec2172a
Author: Carlos O'Donell <carlos@redhat.com>
Date: Tue Oct 29 11:58:03 2019 -0400
Comment out initgroups from example nsswitch.conf (Bug 25146)
In commit 4b7c74179c8928d971d370e1137d202f891a4cf5 the nsswitch.conf
file was harmonized with downstream distributions, but this change
included adding "initgroups: files". We should not add initgroups by
default, we can have it, but it should be commented out to allow it
to inherit the settings for group. The problem is principally that
downstream authconfig won't update initgroups and it will get out of
sync with the setting for group.
diff -Nrup a/nss/nsswitch.conf b/nss/nsswitch.conf
--- a/nss/nsswitch.conf 2019-10-29 14:13:15.883199544 -0400
+++ b/nss/nsswitch.conf 2019-10-29 14:15:44.860978858 -0400
@@ -64,7 +64,8 @@ automount: files sss
aliases: files
ethers: files
gshadow: files
-initgroups: files
+# Allow initgroups to default to the setting for group.
+# initgroups: files
networks: files dns
protocols: files
publickey: files

View File

@ -0,0 +1,512 @@
commit 1a7fe2ebe52b3c8bf465d1756e69452d05c1c103
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Aug 5 15:54:10 2019 +0200
login: Remove utmp backend jump tables [BZ #23518]
There is just one file-based implementation, so this dispatch
mechanism is unnecessary. Instead of the vtable pointer
__libc_utmp_jump_table, use a non-negative file_fd as the indicator
that the backend is initialized.
diff --git a/login/getutent_r.c b/login/getutent_r.c
index 6a244ba6e0b86da7..44239ecb81bacea4 100644
--- a/login/getutent_r.c
+++ b/login/getutent_r.c
@@ -23,115 +23,16 @@
#include "utmp-private.h"
-
-/* Functions defined here. */
-static int setutent_unknown (void);
-static int getutent_r_unknown (struct utmp *buffer, struct utmp **result);
-static int getutid_r_unknown (const struct utmp *line, struct utmp *buffer,
- struct utmp **result);
-static int getutline_r_unknown (const struct utmp *id, struct utmp *buffer,
- struct utmp **result);
-static struct utmp *pututline_unknown (const struct utmp *data);
-static void endutent_unknown (void);
-
-/* Initial Jump table. */
-const struct utfuncs __libc_utmp_unknown_functions =
-{
- setutent_unknown,
- getutent_r_unknown,
- getutid_r_unknown,
- getutline_r_unknown,
- pututline_unknown,
- endutent_unknown,
- NULL
-};
-
-/* Currently selected backend. */
-const struct utfuncs *__libc_utmp_jump_table = &__libc_utmp_unknown_functions;
-
/* We need to protect the opening of the file. */
__libc_lock_define_initialized (, __libc_utmp_lock attribute_hidden)
-static int
-setutent_unknown (void)
-{
- int result;
-
- result = (*__libc_utmp_file_functions.setutent) ();
- if (result)
- __libc_utmp_jump_table = &__libc_utmp_file_functions;
-
- return result;
-}
-
-
-static int
-getutent_r_unknown (struct utmp *buffer, struct utmp **result)
-{
- /* The backend was not yet initialized. */
- if (setutent_unknown ())
- return (*__libc_utmp_jump_table->getutent_r) (buffer, result);
-
- /* Not available. */
- *result = NULL;
- return -1;
-}
-
-
-static int
-getutid_r_unknown (const struct utmp *id, struct utmp *buffer,
- struct utmp **result)
-{
- /* The backend was not yet initialized. */
- if (setutent_unknown ())
- return (*__libc_utmp_jump_table->getutid_r) (id, buffer, result);
-
- /* Not available. */
- *result = NULL;
- return -1;
-}
-
-
-static int
-getutline_r_unknown (const struct utmp *line, struct utmp *buffer,
- struct utmp **result)
-{
- /* The backend was not yet initialized. */
- if (setutent_unknown ())
- return (*__libc_utmp_jump_table->getutline_r) (line, buffer, result);
-
- /* Not available. */
- *result = NULL;
- return -1;
-}
-
-
-static struct utmp *
-pututline_unknown (const struct utmp *data)
-{
- /* The backend was not yet initialized. */
- if (setutent_unknown ())
- return (*__libc_utmp_jump_table->pututline) (data);
-
- /* Not available. */
- return NULL;
-}
-
-
-static void
-endutent_unknown (void)
-{
- /* Nothing to do. */
-}
-
-
void
__setutent (void)
{
__libc_lock_lock (__libc_utmp_lock);
- (*__libc_utmp_jump_table->setutent) ();
+ __libc_setutent ();
__libc_lock_unlock (__libc_utmp_lock);
}
@@ -145,7 +46,7 @@ __getutent_r (struct utmp *buffer, struct utmp **result)
__libc_lock_lock (__libc_utmp_lock);
- retval = (*__libc_utmp_jump_table->getutent_r) (buffer, result);
+ retval = __libc_getutent_r (buffer, result);
__libc_lock_unlock (__libc_utmp_lock);
@@ -162,7 +63,7 @@ __pututline (const struct utmp *data)
__libc_lock_lock (__libc_utmp_lock);
- buffer = (*__libc_utmp_jump_table->pututline) (data);
+ buffer = __libc_pututline (data);
__libc_lock_unlock (__libc_utmp_lock);
@@ -177,8 +78,7 @@ __endutent (void)
{
__libc_lock_lock (__libc_utmp_lock);
- (*__libc_utmp_jump_table->endutent) ();
- __libc_utmp_jump_table = &__libc_utmp_unknown_functions;
+ __libc_endutent ();
__libc_lock_unlock (__libc_utmp_lock);
}
diff --git a/login/getutid_r.c b/login/getutid_r.c
index b7d3dbac75774b0a..8cb6b16d735e8265 100644
--- a/login/getutid_r.c
+++ b/login/getutid_r.c
@@ -49,7 +49,7 @@ __getutid_r (const struct utmp *id, struct utmp *buffer, struct utmp **result)
__libc_lock_lock (__libc_utmp_lock);
- retval = (*__libc_utmp_jump_table->getutid_r) (id, buffer, result);
+ retval = __libc_getutid_r (id, buffer, result);
__libc_lock_unlock (__libc_utmp_lock);
diff --git a/login/getutline_r.c b/login/getutline_r.c
index 6996887f76b28816..5607c19ed2e1ca66 100644
--- a/login/getutline_r.c
+++ b/login/getutline_r.c
@@ -36,7 +36,7 @@ __getutline_r (const struct utmp *line, struct utmp *buffer,
__libc_lock_lock (__libc_utmp_lock);
- retval = (*__libc_utmp_jump_table->getutline_r) (line, buffer, result);
+ retval = __libc_getutline_r (line, buffer, result);
__libc_lock_unlock (__libc_utmp_lock);
diff --git a/login/updwtmp.c b/login/updwtmp.c
index 56fb41916a776c0a..7ae96224ca789b6d 100644
--- a/login/updwtmp.c
+++ b/login/updwtmp.c
@@ -29,7 +29,7 @@ __updwtmp (const char *wtmp_file, const struct utmp *utmp)
{
const char *file_name = TRANSFORM_UTMP_FILE_NAME (wtmp_file);
- (*__libc_utmp_file_functions.updwtmp) (file_name, utmp);
+ __libc_updwtmp (file_name, utmp);
}
libc_hidden_def (__updwtmp)
weak_alias (__updwtmp, updwtmp)
diff --git a/login/utmp-private.h b/login/utmp-private.h
index bd8773984cfc56de..5c2048ee52dc3cee 100644
--- a/login/utmp-private.h
+++ b/login/utmp-private.h
@@ -24,24 +24,17 @@
#include <utmp.h>
#include <libc-lock.h>
-/* The structure describing the functions in a backend. */
-struct utfuncs
-{
- int (*setutent) (void);
- int (*getutent_r) (struct utmp *, struct utmp **);
- int (*getutid_r) (const struct utmp *, struct utmp *, struct utmp **);
- int (*getutline_r) (const struct utmp *, struct utmp *, struct utmp **);
- struct utmp *(*pututline) (const struct utmp *);
- void (*endutent) (void);
- int (*updwtmp) (const char *, const struct utmp *);
-};
-
-/* The tables from the services. */
-extern const struct utfuncs __libc_utmp_file_functions attribute_hidden;
-extern const struct utfuncs __libc_utmp_unknown_functions attribute_hidden;
-
-/* Currently selected backend. */
-extern const struct utfuncs *__libc_utmp_jump_table attribute_hidden;
+/* These functions check for initialization, but not perform any
+ locking. */
+int __libc_setutent (void) attribute_hidden;
+int __libc_getutent_r (struct utmp *, struct utmp **) attribute_hidden;
+int __libc_getutid_r (const struct utmp *, struct utmp *, struct utmp **)
+ attribute_hidden;
+int __libc_getutline_r (const struct utmp *, struct utmp *, struct utmp **)
+ attribute_hidden;
+struct utmp *__libc_pututline (const struct utmp *) attribute_hidden;
+void __libc_endutent (void) attribute_hidden;
+int __libc_updwtmp (const char *, const struct utmp *) attribute_hidden;
/* Current file name. */
extern const char *__libc_utmp_file_name attribute_hidden;
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 040a5057116bb69d..069e6d0452e333ad 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -105,37 +105,12 @@ static void timeout_handler (int signum) {};
alarm (old_timeout); \
} while (0)
-
-/* Functions defined here. */
-static int setutent_file (void);
-static int getutent_r_file (struct utmp *buffer, struct utmp **result);
-static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
- struct utmp **result);
-static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
- struct utmp **result);
-static struct utmp *pututline_file (const struct utmp *data);
-static void endutent_file (void);
-static int updwtmp_file (const char *file, const struct utmp *utmp);
-
-/* Jump table for file functions. */
-const struct utfuncs __libc_utmp_file_functions =
-{
- setutent_file,
- getutent_r_file,
- getutid_r_file,
- getutline_r_file,
- pututline_file,
- endutent_file,
- updwtmp_file
-};
-
-
#ifndef TRANSFORM_UTMP_FILE_NAME
# define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
#endif
-static int
-setutent_file (void)
+int
+__libc_setutent (void)
{
if (file_fd < 0)
{
@@ -166,15 +141,19 @@ setutent_file (void)
return 1;
}
+/* Preform initialization if necessary. */
+static bool
+maybe_setutent (void)
+{
+ return file_fd >= 0 || __libc_setutent ();
+}
-static int
-getutent_r_file (struct utmp *buffer, struct utmp **result)
+int
+__libc_getutent_r (struct utmp *buffer, struct utmp **result)
{
ssize_t nbytes;
- assert (file_fd >= 0);
-
- if (file_offset == -1l)
+ if (!maybe_setutent () || file_offset == -1l)
{
/* Not available. */
*result = NULL;
@@ -279,13 +258,11 @@ unlock_return:
/* For implementing this function we don't use the getutent_r function
because we can avoid the reposition on every new entry this way. */
-static int
-getutid_r_file (const struct utmp *id, struct utmp *buffer,
- struct utmp **result)
+int
+__libc_getutid_r (const struct utmp *id, struct utmp *buffer,
+ struct utmp **result)
{
- assert (file_fd >= 0);
-
- if (file_offset == -1l)
+ if (!maybe_setutent () || file_offset == -1l)
{
*result = NULL;
return -1;
@@ -309,13 +286,11 @@ getutid_r_file (const struct utmp *id, struct utmp *buffer,
/* For implementing this function we don't use the getutent_r function
because we can avoid the reposition on every new entry this way. */
-static int
-getutline_r_file (const struct utmp *line, struct utmp *buffer,
- struct utmp **result)
+int
+__libc_getutline_r (const struct utmp *line, struct utmp *buffer,
+ struct utmp **result)
{
- assert (file_fd >= 0);
-
- if (file_offset == -1l)
+ if (!maybe_setutent () || file_offset == -1l)
{
*result = NULL;
return -1;
@@ -361,15 +336,16 @@ unlock_return:
}
-static struct utmp *
-pututline_file (const struct utmp *data)
+struct utmp *
+__libc_pututline (const struct utmp *data)
{
+ if (!maybe_setutent ())
+ return NULL;
+
struct utmp buffer;
struct utmp *pbuf;
int found;
- assert (file_fd >= 0);
-
if (! file_writable)
{
/* We must make the file descriptor writable before going on. */
@@ -467,18 +443,19 @@ pututline_file (const struct utmp *data)
}
-static void
-endutent_file (void)
+void
+__libc_endutent (void)
{
- assert (file_fd >= 0);
-
- __close_nocancel_nostatus (file_fd);
- file_fd = -1;
+ if (file_fd >= 0)
+ {
+ __close_nocancel_nostatus (file_fd);
+ file_fd = -1;
+ }
}
-static int
-updwtmp_file (const char *file, const struct utmp *utmp)
+int
+__libc_updwtmp (const char *file, const struct utmp *utmp)
{
int result = -1;
off64_t offset;
diff --git a/login/utmpname.c b/login/utmpname.c
index 21cb890a1a2fdc92..73b19c33ceab4dd7 100644
--- a/login/utmpname.c
+++ b/login/utmpname.c
@@ -42,8 +42,7 @@ __utmpname (const char *file)
__libc_lock_lock (__libc_utmp_lock);
/* Close the old file. */
- (*__libc_utmp_jump_table->endutent) ();
- __libc_utmp_jump_table = &__libc_utmp_unknown_functions;
+ __libc_endutent ();
if (strcmp (file, __libc_utmp_file_name) != 0)
{
diff --git a/manual/users.texi b/manual/users.texi
index 4ed79ba26fc8e9d0..a006bb58acfd0568 100644
--- a/manual/users.texi
+++ b/manual/users.texi
@@ -894,9 +894,9 @@ The @code{getlogin} function is declared in @file{unistd.h}, while
@c ttyname_r dup @ascuheap @acsmem @acsfd
@c strncpy dup ok
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->setutent dup @mtasurace:utent @acsfd
-@c *libc_utmp_jump_table->getutline_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer
-@c *libc_utmp_jump_table->endutent dup @mtasurace:utent @asulock @aculock
+@c __libc_setutent dup @mtasurace:utent @acsfd
+@c __libc_getutline_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer
+@c __libc_endutent dup @mtasurace:utent @asulock @aculock
@c libc_lock_unlock dup ok
@c strlen dup ok
@c memcpy dup ok
@@ -1111,7 +1111,7 @@ compatibility only, @file{utmp.h} defines @code{ut_time} as an alias for
@c setutent @mtasurace:utent @asulock @aculock @acsfd
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->setutent @mtasurace:utent @acsfd
+@c __libc_setutent @mtasurace:utent @acsfd
@c setutent_unknown @mtasurace:utent @acsfd
@c *libc_utmp_file_functions.setutent = setutent_file @mtasurace:utent @acsfd
@c open_not_cancel_2 dup @acsfd
@@ -1152,7 +1152,7 @@ A null pointer is returned in case no further entry is available.
@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
@c endutent @mtasurace:utent @asulock @aculock @acsfd
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->endutent @mtasurace:utent @acsfd
+@c __libc_endutent @mtasurace:utent @acsfd
@c endutent_unknown ok
@c endutent_file @mtasurace:utent @acsfd
@c close_not_cancel_no_status dup @acsfd
@@ -1230,7 +1230,7 @@ over again.
@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
@c pututline @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->pututline @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c __libc_pututline @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
@c pututline_unknown @mtasurace:utent @acsfd
@c setutent_unknown dup @mtasurace:utent @acsfd
@c pututline_file @mtascusig:ALRM @mtascutimer @acsfd
@@ -1282,7 +1282,7 @@ user-provided buffer.
@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
@c getutent_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->getutent_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c __libc_getutent_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
@c getutent_r_unknown @mtasurace:utent @acsfd
@c setutent_unknown dup @mtasurace:utent @acsfd
@c getutent_r_file @mtasurace:utent @mtascusig:ALRM @mtascutimer
@@ -1319,7 +1319,7 @@ This function is a GNU extension.
@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
@c getutid_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->getutid_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c __libc_getutid_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
@c getutid_r_unknown @mtasurace:utent @acsfd
@c setutent_unknown dup @mtasurace:utent @acsfd
@c getutid_r_file @mtascusig:ALRM @mtascutimer
@@ -1349,7 +1349,7 @@ This function is a GNU extension.
@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
@c getutline_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->getutline_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c __libc_getutline_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
@c getutline_r_unknown @mtasurace:utent @acsfd
@c setutent_unknown dup @mtasurace:utent @acsfd
@c getutline_r_file @mtasurace:utent @mtascusig:ALRM @mtascutimer
@@ -1393,7 +1393,7 @@ be used.
@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{}}}
@c utmpname @mtasurace:utent @asulock @ascuheap @aculock @acsmem
@c libc_lock_lock dup @asulock @aculock
-@c *libc_utmp_jump_table->endutent dup @mtasurace:utent
+@c __libc_endutent dup @mtasurace:utent
@c strcmp dup ok
@c free dup @ascuheap @acsmem
@c strdup dup @ascuheap @acsmem
diff --git a/sysdeps/unix/getlogin_r.c b/sysdeps/unix/getlogin_r.c
index 444df7e4d3210cf6..180c0bbca13d0f87 100644
--- a/sysdeps/unix/getlogin_r.c
+++ b/sysdeps/unix/getlogin_r.c
@@ -64,8 +64,8 @@ __getlogin_r (char *name, size_t name_len)
held so that our search is thread-safe. */
__libc_lock_lock (__libc_utmp_lock);
- (*__libc_utmp_jump_table->setutent) ();
- result = (*__libc_utmp_jump_table->getutline_r) (&line, &buffer, &ut);
+ __libc_setutent ();
+ result = __libc_getutline_r (&line, &buffer, &ut);
if (result < 0)
{
if (errno == ESRCH)
@@ -74,8 +74,7 @@ __getlogin_r (char *name, size_t name_len)
else
result = errno;
}
- (*__libc_utmp_jump_table->endutent) ();
- __libc_utmp_jump_table = &__libc_utmp_unknown_functions;
+ __libc_endutent ();
__libc_lock_unlock (__libc_utmp_lock);
if (result == 0)

View File

@ -0,0 +1,389 @@
commit be6b16d975683e6cca57852cd4cfe715b2a9d8b1
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Nov 7 18:15:18 2019 +0100
login: Acquire write lock early in pututline [BZ #24882]
It has been reported that due to lack of fairness in POSIX file
locking, the current reader-to-writer lock upgrade can result in
lack of forward progress. Acquiring the write lock directly
hopefully avoids this issue if there are only writers.
This also fixes bug 24882 due to the cache revalidation in
__libc_pututline.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Change-Id: I57e31ae30719e609a53505a0924dda101d46372e
diff --git a/login/Makefile b/login/Makefile
index 82132c83fd799357..030cf489b2e037d4 100644
--- a/login/Makefile
+++ b/login/Makefile
@@ -44,7 +44,7 @@ subdir-dirs = programs
vpath %.c programs
tests := tst-utmp tst-utmpx tst-grantpt tst-ptsname tst-getlogin tst-updwtmpx \
- tst-pututxline-lockfail
+ tst-pututxline-lockfail tst-pututxline-cache
# Build the -lutil library with these extra functions.
extra-libs := libutil
@@ -74,3 +74,4 @@ $(inst_libexecdir)/pt_chown: $(objpfx)pt_chown $(+force)
-$(INSTALL_PROGRAM) -m 4755 -o root $< $@
$(objpfx)tst-pututxline-lockfail: $(shared-thread-library)
+$(objpfx)tst-pututxline-cache: $(shared-thread-library)
diff --git a/login/tst-pututxline-cache.c b/login/tst-pututxline-cache.c
new file mode 100644
index 0000000000000000..3f30dd1776711769
--- /dev/null
+++ b/login/tst-pututxline-cache.c
@@ -0,0 +1,193 @@
+/* Test case for cache invalidation after concurrent write (bug 24882).
+ Copyright (C) 2019 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; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+/* This test writes an entry to the utmpx file, reads it (so that it
+ is cached) in process1, and overwrites the same entry in process2
+ with something that does not match the search criteria. At this
+ point, the cache of the first process is stale, and when process1
+ attempts to write a new record which would have gone to the same
+ place (as indicated by the cache), it needs to realize that it has
+ to pick a different slot because the old slot is now used for
+ something else. */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <utmp.h>
+#include <utmpx.h>
+
+/* Set to the path of the utmp file. */
+static char *utmp_file;
+
+/* Used to synchronize the subprocesses. The barrier itself is
+ allocated in shared memory. */
+static pthread_barrier_t *barrier;
+
+/* setutxent with error checking. */
+static void
+xsetutxent (void)
+{
+ errno = 0;
+ setutxent ();
+ TEST_COMPARE (errno, 0);
+}
+
+/* getutxent with error checking. */
+static struct utmpx *
+xgetutxent (void)
+{
+ errno = 0;
+ struct utmpx *result = getutxent ();
+ if (result == NULL)
+ FAIL_EXIT1 ("getutxent: %m");
+ return result;
+}
+
+static void
+put_entry (const char *id, pid_t pid, const char *user, const char *line)
+{
+ struct utmpx ut =
+ {
+ .ut_type = LOGIN_PROCESS,
+ .ut_pid = pid,
+ .ut_host = "localhost",
+ };
+ strcpy (ut.ut_id, id);
+ strncpy (ut.ut_user, user, sizeof (ut.ut_user));
+ strncpy (ut.ut_line, line, sizeof (ut.ut_line));
+ TEST_VERIFY (pututxline (&ut) != NULL);
+}
+
+/* Use two cooperating subprocesses to avoid issues related to
+ unlock-on-close semantics of POSIX advisory locks. */
+
+static __attribute__ ((noreturn)) void
+process1 (void)
+{
+ TEST_COMPARE (utmpname (utmp_file), 0);
+
+ /* Create an entry. */
+ xsetutxent ();
+ put_entry ("1", 101, "root", "process1");
+
+ /* Retrieve the entry. This will fill the internal cache. */
+ {
+ errno = 0;
+ setutxent ();
+ TEST_COMPARE (errno, 0);
+ struct utmpx ut =
+ {
+ .ut_type = LOGIN_PROCESS,
+ .ut_line = "process1",
+ };
+ struct utmpx *result = getutxline (&ut);
+ if (result == NULL)
+ FAIL_EXIT1 ("getutxline (\"process1\"): %m");
+ TEST_COMPARE (result->ut_pid, 101);
+ }
+
+ /* Signal the other process to overwrite the entry. */
+ xpthread_barrier_wait (barrier);
+
+ /* Wait for the other process to complete the write operation. */
+ xpthread_barrier_wait (barrier);
+
+ /* Add another entry. Note: This time, there is no setutxent call. */
+ put_entry ("1", 103, "root", "process1");
+
+ _exit (0);
+}
+
+static void
+process2 (void *closure)
+{
+ /* Wait for the first process to write its entry. */
+ xpthread_barrier_wait (barrier);
+
+ /* Truncate the file. The glibc interface does not support
+ re-purposing records, but an external expiration mechanism may
+ trigger this. */
+ TEST_COMPARE (truncate64 (utmp_file, 0), 0);
+
+ /* Write the replacement entry. */
+ TEST_COMPARE (utmpname (utmp_file), 0);
+ xsetutxent ();
+ put_entry ("2", 102, "user", "process2");
+
+ /* Signal the other process that the entry has been replaced. */
+ xpthread_barrier_wait (barrier);
+}
+
+static int
+do_test (void)
+{
+ xclose (create_temp_file ("tst-tumpx-cache-write-", &utmp_file));
+ {
+ pthread_barrierattr_t attr;
+ xpthread_barrierattr_init (&attr);
+ xpthread_barrierattr_setpshared (&attr, PTHREAD_SCOPE_PROCESS);
+ barrier = support_shared_allocate (sizeof (*barrier));
+ xpthread_barrier_init (barrier, &attr, 2);
+ }
+
+ /* Run both subprocesses in parallel. */
+ {
+ pid_t pid1 = xfork ();
+ if (pid1 == 0)
+ process1 ();
+ support_isolate_in_subprocess (process2, NULL);
+ int status;
+ xwaitpid (pid1, &status, 0);
+ TEST_COMPARE (status, 0);
+ }
+
+ /* Check that the utmpx database contains the expected records. */
+ {
+ TEST_COMPARE (utmpname (utmp_file), 0);
+ xsetutxent ();
+
+ struct utmpx *ut = xgetutxent ();
+ TEST_COMPARE_STRING (ut->ut_id, "2");
+ TEST_COMPARE (ut->ut_pid, 102);
+ TEST_COMPARE_STRING (ut->ut_user, "user");
+ TEST_COMPARE_STRING (ut->ut_line, "process2");
+
+ ut = xgetutxent ();
+ TEST_COMPARE_STRING (ut->ut_id, "1");
+ TEST_COMPARE (ut->ut_pid, 103);
+ TEST_COMPARE_STRING (ut->ut_user, "root");
+ TEST_COMPARE_STRING (ut->ut_line, "process1");
+
+ if (getutxent () != NULL)
+ FAIL_EXIT1 ("additional utmpx entry");
+ }
+
+ xpthread_barrier_destroy (barrier);
+ support_shared_free (barrier);
+ free (utmp_file);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 9ad80364682bae92..6bba120db9cc574e 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -186,19 +186,11 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
/* Search for *ID, updating last_entry and file_offset. Return 0 on
- success and -1 on failure. If the locking operation failed, write
- true to *LOCK_FAILED. */
+ success and -1 on failure. Does not perform locking; for that see
+ internal_getut_r below. */
static int
-internal_getut_r (const struct utmp *id, bool *lock_failed)
+internal_getut_nolock (const struct utmp *id)
{
- int result = -1;
-
- if (try_file_lock (file_fd, F_RDLCK))
- {
- *lock_failed = true;
- return -1;
- }
-
if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
|| id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
{
@@ -213,7 +205,7 @@ internal_getut_r (const struct utmp *id, bool *lock_failed)
{
__set_errno (ESRCH);
file_offset = -1l;
- goto unlock_return;
+ return -1;
}
file_offset += sizeof (struct utmp);
@@ -234,7 +226,7 @@ internal_getut_r (const struct utmp *id, bool *lock_failed)
{
__set_errno (ESRCH);
file_offset = -1l;
- goto unlock_return;
+ return -1;
}
file_offset += sizeof (struct utmp);
@@ -243,15 +235,26 @@ internal_getut_r (const struct utmp *id, bool *lock_failed)
}
}
- result = 0;
+ return 0;
+}
-unlock_return:
- file_unlock (file_fd);
+/* Search for *ID, updating last_entry and file_offset. Return 0 on
+ success and -1 on failure. If the locking operation failed, write
+ true to *LOCK_FAILED. */
+static int
+internal_getut_r (const struct utmp *id, bool *lock_failed)
+{
+ if (try_file_lock (file_fd, F_RDLCK))
+ {
+ *lock_failed = true;
+ return -1;
+ }
+ int result = internal_getut_nolock (id);
+ file_unlock (file_fd);
return result;
}
-
/* For implementing this function we don't use the getutent_r function
because we can avoid the reposition on every new entry this way. */
int
@@ -279,7 +282,6 @@ __libc_getutid_r (const struct utmp *id, struct utmp *buffer,
return 0;
}
-
/* For implementing this function we don't use the getutent_r function
because we can avoid the reposition on every new entry this way. */
int
@@ -336,7 +338,6 @@ __libc_pututline (const struct utmp *data)
return NULL;
struct utmp *pbuf;
- int found;
if (! file_writable)
{
@@ -358,7 +359,12 @@ __libc_pututline (const struct utmp *data)
file_writable = true;
}
+ /* Exclude other writers before validating the cache. */
+ if (try_file_lock (file_fd, F_WRLCK))
+ return NULL;
+
/* Find the correct place to insert the data. */
+ bool found = false;
if (file_offset > 0
&& ((last_entry.ut_type == data->ut_type
&& (last_entry.ut_type == RUN_LVL
@@ -366,23 +372,30 @@ __libc_pututline (const struct utmp *data)
|| last_entry.ut_type == OLD_TIME
|| last_entry.ut_type == NEW_TIME))
|| __utmp_equal (&last_entry, data)))
- found = 1;
- else
{
- bool lock_failed = false;
- found = internal_getut_r (data, &lock_failed);
-
- if (__builtin_expect (lock_failed, false))
+ if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
{
- __set_errno (EAGAIN);
+ file_unlock (file_fd);
return NULL;
}
+ if (__read_nocancel (file_fd, &last_entry, sizeof (last_entry))
+ != sizeof (last_entry))
+ {
+ if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
+ {
+ file_unlock (file_fd);
+ return NULL;
+ }
+ found = false;
+ }
+ else
+ found = __utmp_equal (&last_entry, data);
}
- if (try_file_lock (file_fd, F_WRLCK))
- return NULL;
+ if (!found)
+ found = internal_getut_nolock (data) >= 0;
- if (found < 0)
+ if (!found)
{
/* We append the next entry. */
file_offset = __lseek64 (file_fd, 0, SEEK_END);
@@ -411,7 +424,7 @@ __libc_pututline (const struct utmp *data)
{
/* If we appended a new record this is only partially written.
Remove it. */
- if (found < 0)
+ if (!found)
(void) __ftruncate64 (file_fd, file_offset);
pbuf = NULL;
}

View File

@ -0,0 +1,138 @@
commit 76a7c103eb9060f9e3ba01d073ae4621a17d8b46
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Nov 12 12:02:57 2019 +0100
login: Introduce matches_last_entry to utmp processing
This simplifies internal_getut_nolock and fixes a regression,
introduced in commit be6b16d975683e6cca57852cd4cfe715b2a9d8b1
("login: Acquire write lock early in pututline [BZ #24882]")
in pututxline because __utmp_equal can only compare process-related
utmp entries.
Fixes: be6b16d975683e6cca57852cd4cfe715b2a9d8b1
Change-Id: Ib8a85002f7f87ee41590846d16d7e52bdb82f5a5
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 6bba120db9cc574e..e653d14967c4fb7a 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -43,6 +43,25 @@ static off64_t file_offset;
/* Cache for the last read entry. */
static struct utmp last_entry;
+/* Returns true if *ENTRY matches last_entry, based on
+ data->ut_type. */
+static bool
+matches_last_entry (const struct utmp *data)
+{
+ if (file_offset <= 0)
+ /* Nothing has been read. last_entry is stale and cannot match. */
+ return false;
+
+ if (data->ut_type == RUN_LVL
+ || data->ut_type == BOOT_TIME
+ || data->ut_type == OLD_TIME
+ || data->ut_type == NEW_TIME)
+ /* For some entry types, only a type match is required. */
+ return data->ut_type == last_entry.ut_type;
+ else
+ /* For the process-related entries, a full match is needed. */
+ return __utmp_equal (&last_entry, data);
+}
/* Locking timeout. */
#ifndef TIMEOUT
@@ -133,9 +152,6 @@ __libc_setutent (void)
__lseek64 (file_fd, 0, SEEK_SET);
file_offset = 0;
- /* Make sure the entry won't match. */
- last_entry.ut_type = -1;
-
return 1;
}
@@ -191,48 +207,20 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
static int
internal_getut_nolock (const struct utmp *id)
{
- if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
- || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
+ while (1)
{
- /* Search for next entry with type RUN_LVL, BOOT_TIME,
- OLD_TIME, or NEW_TIME. */
-
- while (1)
+ /* Read the next entry. */
+ if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
+ != sizeof (struct utmp))
{
- /* Read the next entry. */
- if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
- != sizeof (struct utmp))
- {
- __set_errno (ESRCH);
- file_offset = -1l;
- return -1;
- }
- file_offset += sizeof (struct utmp);
-
- if (id->ut_type == last_entry.ut_type)
- break;
+ __set_errno (ESRCH);
+ file_offset = -1l;
+ return -1;
}
- }
- else
- {
- /* Search for the next entry with the specified ID and with type
- INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS. */
-
- while (1)
- {
- /* Read the next entry. */
- if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
- != sizeof (struct utmp))
- {
- __set_errno (ESRCH);
- file_offset = -1l;
- return -1;
- }
- file_offset += sizeof (struct utmp);
+ file_offset += sizeof (struct utmp);
- if (__utmp_equal (&last_entry, id))
- break;
- }
+ if (matches_last_entry (id))
+ break;
}
return 0;
@@ -365,13 +353,7 @@ __libc_pututline (const struct utmp *data)
/* Find the correct place to insert the data. */
bool found = false;
- if (file_offset > 0
- && ((last_entry.ut_type == data->ut_type
- && (last_entry.ut_type == RUN_LVL
- || last_entry.ut_type == BOOT_TIME
- || last_entry.ut_type == OLD_TIME
- || last_entry.ut_type == NEW_TIME))
- || __utmp_equal (&last_entry, data)))
+ if (matches_last_entry (data))
{
if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
{
@@ -389,7 +371,7 @@ __libc_pututline (const struct utmp *data)
found = false;
}
else
- found = __utmp_equal (&last_entry, data);
+ found = matches_last_entry (data);
}
if (!found)

View File

@ -0,0 +1,114 @@
commit fed33b0fb03d1942a6713286176d42869c0f1580
Author: Leandro Pereira <leandro.pereira@microsoft.com>
Date: Wed Oct 2 12:42:28 2019 -0400
Add nocancel version of pread64()
This is in preparation for changes in the dynamic linker so that
pread() is used instead of lseek()+read().
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
sysdeps/unix/sysv/linux/Makefile
(Textual conflict in routines list.)
diff --git a/sysdeps/generic/not-cancel.h b/sysdeps/generic/not-cancel.h
index d9f8a75dbda85ed5..260e6e4081f5fe16 100644
--- a/sysdeps/generic/not-cancel.h
+++ b/sysdeps/generic/not-cancel.h
@@ -41,6 +41,8 @@
(void) __close (fd)
#define __read_nocancel(fd, buf, n) \
__read (fd, buf, n)
+#define __pread64_nocancel(fd, buf, count, offset) \
+ __pread64 (fd, buf, count, offset)
#define __write_nocancel(fd, buf, n) \
__write (fd, buf, n)
#define __writev_nocancel_nostatus(fd, iov, n) \
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 773aaea0e980bdd6..fb4ccd63ddec7eca 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -174,7 +174,8 @@ sysdep_routines += xstatconv internal_statvfs internal_statvfs64 \
close_nocancel fcntl_nocancel nanosleep_nocancel \
open_nocancel open64_nocancel \
openat_nocancel openat64_nocancel \
- pause_nocancel read_nocancel waitpid_nocancel write_nocancel
+ pause_nocancel read_nocancel pread64_nocancel \
+ waitpid_nocancel write_nocancel
sysdep_headers += bits/fcntl-linux.h
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index 336c13b57dba727a..95759bead1b0b3ca 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -176,6 +176,7 @@ libc {
__syscall_rt_sigqueueinfo;
__open_nocancel;
__read_nocancel;
+ __pread64_nocancel;
__close_nocancel;
__sigtimedwait;
# functions used by nscd
diff --git a/sysdeps/unix/sysv/linux/not-cancel.h b/sysdeps/unix/sysv/linux/not-cancel.h
index 09de92dee9437d98..e58db475682a8c6a 100644
--- a/sysdeps/unix/sysv/linux/not-cancel.h
+++ b/sysdeps/unix/sysv/linux/not-cancel.h
@@ -43,6 +43,9 @@ __typeof (openat64) __openat64_nocancel;
/* Non cancellable read syscall. */
__typeof (__read) __read_nocancel;
+/* Non cancellable pread syscall (LFS version). */
+__typeof (__pread64) __pread64_nocancel;
+
/* Uncancelable write. */
__typeof (__write) __write_nocancel;
@@ -84,6 +87,7 @@ hidden_proto (__open64_nocancel)
hidden_proto (__openat_nocancel)
hidden_proto (__openat64_nocancel)
hidden_proto (__read_nocancel)
+hidden_proto (__pread64_nocancel)
hidden_proto (__write_nocancel)
hidden_proto (__close_nocancel)
hidden_proto (__waitpid_nocancel)
diff --git a/sysdeps/unix/sysv/linux/pread64_nocancel.c b/sysdeps/unix/sysv/linux/pread64_nocancel.c
new file mode 100644
index 0000000000000000..dab61260e5db43b5
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/pread64_nocancel.c
@@ -0,0 +1,32 @@
+/* Linux pread64() syscall implementation -- non-cancellable.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <unistd.h>
+#include <sysdep-cancel.h>
+#include <not-cancel.h>
+
+#ifndef __NR_pread64
+# define __NR_pread64 __NR_pread
+#endif
+
+ssize_t
+__pread64_nocancel (int fd, void *buf, size_t count, off64_t offset)
+{
+ return INLINE_SYSCALL_CALL (pread64, fd, buf, count, SYSCALL_LL64_PRW (offset));
+}
+hidden_def (__pread64_nocancel)

View File

@ -0,0 +1,307 @@
commit d4625a19fe64f664119a541b317fb83de01bb273
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Nov 12 12:25:49 2019 +0100
login: Use pread64 in utmp implementation
This reduces the possible error scenarios considerably because
no longer can file seek fail, leaving the file descriptor in an
inconsistent state and out of sync with the cache.
As a result, it is possible to avoid setting file_offset to -1
to make an error persistent. Instead, subsequent calls will retry
the operation and report any errors returned by the kernel.
This change also avoids reading the file from the start if pututline
is called multiple times, to work around lock acquisition failures
due to timeouts.
Change-Id: If21ea0c162c38830a89331ea93cddec14c0974de
diff --git a/login/utmp_file.c b/login/utmp_file.c
index e653d14967c4fb7a..c828a28ac54c150e 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -162,12 +162,35 @@ maybe_setutent (void)
return file_fd >= 0 || __libc_setutent ();
}
+/* Reads the entry at file_offset, storing it in last_entry and
+ updating file_offset on success. Returns -1 for a read error, 0
+ for EOF, and 1 for a successful read. last_entry and file_offset
+ are only updated on a successful and complete read. */
+static ssize_t
+read_last_entry (void)
+{
+ struct utmp buffer;
+ ssize_t nbytes = __pread64_nocancel (file_fd, &buffer, sizeof (buffer),
+ file_offset);
+ if (nbytes < 0)
+ return -1;
+ else if (nbytes != sizeof (buffer))
+ /* Assume EOF. */
+ return 0;
+ else
+ {
+ last_entry = buffer;
+ file_offset += sizeof (buffer);
+ return 1;
+ }
+}
+
int
__libc_getutent_r (struct utmp *buffer, struct utmp **result)
{
- ssize_t nbytes;
+ int saved_errno = errno;
- if (!maybe_setutent () || file_offset == -1l)
+ if (!maybe_setutent ())
{
/* Not available. */
*result = NULL;
@@ -175,25 +198,22 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
}
if (try_file_lock (file_fd, F_RDLCK))
- nbytes = 0;
- else
- {
- /* Read the next entry. */
- nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
- file_unlock (file_fd);
- }
+ return -1;
- if (nbytes != sizeof (struct utmp))
+ ssize_t nbytes = read_last_entry ();
+ file_unlock (file_fd);
+
+ if (nbytes <= 0) /* Read error or EOF. */
{
- if (nbytes != 0)
- file_offset = -1l;
+ if (nbytes == 0)
+ /* errno should be unchanged to indicate success. A premature
+ EOF is treated like an EOF (missing complete record at the
+ end). */
+ __set_errno (saved_errno);
*result = NULL;
return -1;
}
- /* Update position pointer. */
- file_offset += sizeof (struct utmp);
-
memcpy (buffer, &last_entry, sizeof (struct utmp));
*result = buffer;
@@ -209,15 +229,15 @@ internal_getut_nolock (const struct utmp *id)
{
while (1)
{
- /* Read the next entry. */
- if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
- != sizeof (struct utmp))
+ ssize_t nbytes = read_last_entry ();
+ if (nbytes < 0)
+ return -1;
+ if (nbytes == 0)
{
+ /* End of file reached. */
__set_errno (ESRCH);
- file_offset = -1l;
return -1;
}
- file_offset += sizeof (struct utmp);
if (matches_last_entry (id))
break;
@@ -249,7 +269,7 @@ int
__libc_getutid_r (const struct utmp *id, struct utmp *buffer,
struct utmp **result)
{
- if (!maybe_setutent () || file_offset == -1l)
+ if (!maybe_setutent ())
{
*result = NULL;
return -1;
@@ -276,7 +296,7 @@ int
__libc_getutline_r (const struct utmp *line, struct utmp *buffer,
struct utmp **result)
{
- if (!maybe_setutent () || file_offset == -1l)
+ if (!maybe_setutent ())
{
*result = NULL;
return -1;
@@ -290,16 +310,21 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
while (1)
{
- /* Read the next entry. */
- if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
- != sizeof (struct utmp))
+ ssize_t nbytes = read_last_entry ();
+ if (nbytes < 0)
{
+ file_unlock (file_fd);
+ *result = NULL;
+ return -1;
+ }
+ if (nbytes == 0)
+ {
+ /* End of file reached. */
+ file_unlock (file_fd);
__set_errno (ESRCH);
- file_offset = -1l;
*result = NULL;
- goto unlock_return;
+ return -1;
}
- file_offset += sizeof (struct utmp);
/* Stop if we found a user or login entry. */
if ((last_entry.ut_type == USER_PROCESS
@@ -309,20 +334,18 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
break;
}
+ file_unlock (file_fd);
memcpy (buffer, &last_entry, sizeof (struct utmp));
*result = buffer;
-unlock_return:
- file_unlock (file_fd);
-
- return ((*result == NULL) ? -1 : 0);
+ return 0;
}
struct utmp *
__libc_pututline (const struct utmp *data)
{
- if (!maybe_setutent () || file_offset == -1l)
+ if (!maybe_setutent ())
return NULL;
struct utmp *pbuf;
@@ -337,8 +360,7 @@ __libc_pututline (const struct utmp *data)
if (new_fd == -1)
return NULL;
- if (__lseek64 (new_fd, __lseek64 (file_fd, 0, SEEK_CUR), SEEK_SET) == -1
- || __dup2 (new_fd, file_fd) < 0)
+ if (__dup2 (new_fd, file_fd) < 0)
{
__close_nocancel_nostatus (new_fd);
return NULL;
@@ -355,69 +377,70 @@ __libc_pututline (const struct utmp *data)
bool found = false;
if (matches_last_entry (data))
{
- if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
+ /* Read back the entry under the write lock. */
+ file_offset -= sizeof (last_entry);
+ ssize_t nbytes = read_last_entry ();
+ if (nbytes < 0)
{
file_unlock (file_fd);
return NULL;
}
- if (__read_nocancel (file_fd, &last_entry, sizeof (last_entry))
- != sizeof (last_entry))
- {
- if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
- {
- file_unlock (file_fd);
- return NULL;
- }
- found = false;
- }
+
+ if (nbytes == 0)
+ /* End of file reached. */
+ found = false;
else
found = matches_last_entry (data);
}
if (!found)
+ /* Search forward for the entry. */
found = internal_getut_nolock (data) >= 0;
+ off64_t write_offset;
if (!found)
{
/* We append the next entry. */
- file_offset = __lseek64 (file_fd, 0, SEEK_END);
- if (file_offset % sizeof (struct utmp) != 0)
- {
- file_offset -= file_offset % sizeof (struct utmp);
- __ftruncate64 (file_fd, file_offset);
-
- if (__lseek64 (file_fd, 0, SEEK_END) < 0)
- {
- pbuf = NULL;
- goto unlock_return;
- }
- }
+ write_offset = __lseek64 (file_fd, 0, SEEK_END);
+
+ /* Round down to the next multiple of the entry size. This
+ ensures any partially-written record is overwritten by the
+ new record. */
+ write_offset = (write_offset / sizeof (struct utmp)
+ * sizeof (struct utmp));
}
else
+ /* Overwrite last_entry. */
+ write_offset = file_offset - sizeof (struct utmp);
+
+ /* Write the new data. */
+ ssize_t nbytes;
+ if (__lseek64 (file_fd, write_offset, SEEK_SET) < 0
+ || (nbytes = __write_nocancel (file_fd, data, sizeof (struct utmp))) < 0)
{
- /* We replace the just read entry. */
- file_offset -= sizeof (struct utmp);
- __lseek64 (file_fd, file_offset, SEEK_SET);
+ /* There is no need to recover the file position because all
+ reads use pread64, and any future write is preceded by
+ another seek. */
+ file_unlock (file_fd);
+ return NULL;
}
- /* Write the new data. */
- if (__write_nocancel (file_fd, data, sizeof (struct utmp))
- != sizeof (struct utmp))
+ if (nbytes != sizeof (struct utmp))
{
/* If we appended a new record this is only partially written.
Remove it. */
if (!found)
- (void) __ftruncate64 (file_fd, file_offset);
- pbuf = NULL;
- }
- else
- {
- file_offset += sizeof (struct utmp);
- pbuf = (struct utmp *) data;
+ (void) __ftruncate64 (file_fd, write_offset);
+ file_unlock (file_fd);
+ /* Assume that the write failure was due to missing disk
+ space. */
+ __set_errno (ENOSPC);
+ return NULL;
}
- unlock_return:
file_unlock (file_fd);
+ file_offset = write_offset + sizeof (struct utmp);
+ pbuf = (struct utmp *) data;
return pbuf;
}

View File

@ -0,0 +1,676 @@
commit a33b817f13170b5c24263b92e7e09880fe797d7e
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 13 12:09:32 2019 +0200
login: Assume that _HAVE_UT_* constants are true
Make the GNU version of bits/utmp.h the generic version because
all remaining ports use it (with a sysdeps override for
Linux s390/s390x).
Conflicts:
bits/utmp.h
sysdeps/gnu/bits/utmp.h
(Upstream copyright year change.)
diff --git a/bits/utmp.h b/bits/utmp.h
index 6e8695fbf072e5f1..3c02dd4f3fe4e99b 100644
--- a/bits/utmp.h
+++ b/bits/utmp.h
@@ -1,5 +1,5 @@
-/* The `struct utmp' type, describing entries in the utmp file. Generic/BSDish
- Copyright (C) 1993-2018 Free Software Foundation, Inc.
+/* The `struct utmp' type, describing entries in the utmp file.
+ Copyright (C) 1993-2019 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
@@ -21,29 +21,106 @@
#endif
#include <paths.h>
-#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <bits/wordsize.h>
-#define UT_NAMESIZE 8
-#define UT_LINESIZE 8
-#define UT_HOSTSIZE 16
+#define UT_LINESIZE 32
+#define UT_NAMESIZE 32
+#define UT_HOSTSIZE 256
+/* The structure describing an entry in the database of
+ previous logins. */
struct lastlog
{
- time_t ll_time;
+#if __WORDSIZE_TIME64_COMPAT32
+ int32_t ll_time;
+#else
+ __time_t ll_time;
+#endif
char ll_line[UT_LINESIZE];
char ll_host[UT_HOSTSIZE];
};
-struct utmp
+
+/* The structure describing the status of a terminated process. This
+ type is used in `struct utmp' below. */
+struct exit_status
{
- char ut_line[UT_LINESIZE];
- char ut_user[UT_NAMESIZE];
-#define ut_name ut_user
- char ut_host[UT_HOSTSIZE];
- long int ut_time;
+ short int e_termination; /* Process termination status. */
+ short int e_exit; /* Process exit status. */
};
-#define _HAVE_UT_HOST 1 /* We have the ut_host field. */
+/* The structure describing an entry in the user accounting database. */
+struct utmp
+{
+ short int ut_type; /* Type of login. */
+ pid_t ut_pid; /* Process ID of login process. */
+ char ut_line[UT_LINESIZE]
+ __attribute_nonstring__; /* Devicename. */
+ char ut_id[4]; /* Inittab ID. */
+ char ut_user[UT_NAMESIZE]
+ __attribute_nonstring__; /* Username. */
+ char ut_host[UT_HOSTSIZE]
+ __attribute_nonstring__; /* Hostname for remote login. */
+ struct exit_status ut_exit; /* Exit status of a process marked
+ as DEAD_PROCESS. */
+/* The ut_session and ut_tv fields must be the same size when compiled
+ 32- and 64-bit. This allows data files and shared memory to be
+ shared between 32- and 64-bit applications. */
+#if __WORDSIZE_TIME64_COMPAT32
+ int32_t ut_session; /* Session ID, used for windowing. */
+ struct
+ {
+ int32_t tv_sec; /* Seconds. */
+ int32_t tv_usec; /* Microseconds. */
+ } ut_tv; /* Time entry was made. */
+#else
+ long int ut_session; /* Session ID, used for windowing. */
+ struct timeval ut_tv; /* Time entry was made. */
+#endif
+
+ int32_t ut_addr_v6[4]; /* Internet address of remote host. */
+ char __glibc_reserved[20]; /* Reserved for future use. */
+};
+
+/* Backwards compatibility hacks. */
+#define ut_name ut_user
+#ifndef _NO_UT_TIME
+/* We have a problem here: `ut_time' is also used otherwise. Define
+ _NO_UT_TIME if the compiler complains. */
+# define ut_time ut_tv.tv_sec
+#endif
+#define ut_xtime ut_tv.tv_sec
+#define ut_addr ut_addr_v6[0]
+
+
+/* Values for the `ut_type' field of a `struct utmp'. */
+#define EMPTY 0 /* No valid user accounting information. */
+
+#define RUN_LVL 1 /* The system's runlevel. */
+#define BOOT_TIME 2 /* Time of system boot. */
+#define NEW_TIME 3 /* Time after system clock changed. */
+#define OLD_TIME 4 /* Time when system clock changed. */
+
+#define INIT_PROCESS 5 /* Process spawned by the init process. */
+#define LOGIN_PROCESS 6 /* Session leader of a logged in user. */
+#define USER_PROCESS 7 /* Normal process. */
+#define DEAD_PROCESS 8 /* Terminated process. */
+
+#define ACCOUNTING 9
+
+/* Old Linux name for the EMPTY type. */
+#define UT_UNKNOWN EMPTY
+
+
+/* Tell the user that we have a modern system with UT_HOST, UT_PID,
+ UT_TYPE, UT_ID and UT_TV fields. */
+#define _HAVE_UT_TYPE 1
+#define _HAVE_UT_PID 1
+#define _HAVE_UT_ID 1
+#define _HAVE_UT_TV 1
+#define _HAVE_UT_HOST 1
diff --git a/login/getutid_r.c b/login/getutid_r.c
index 8cb6b16d735e8265..11b288e99be6ee50 100644
--- a/login/getutid_r.c
+++ b/login/getutid_r.c
@@ -32,7 +32,6 @@ __libc_lock_define (extern, __libc_utmp_lock attribute_hidden)
int
__getutid_r (const struct utmp *id, struct utmp *buffer, struct utmp **result)
{
-#if (_HAVE_UT_ID - 0) && (_HAVE_UT_TYPE - 0)
int retval;
/* Test whether ID has any of the legal types. */
@@ -54,10 +53,6 @@ __getutid_r (const struct utmp *id, struct utmp *buffer, struct utmp **result)
__libc_lock_unlock (__libc_utmp_lock);
return retval;
-#else /* !_HAVE_UT_ID && !_HAVE_UT_TYPE */
- __set_errno (ENOSYS);
- return -1;
-#endif
}
libc_hidden_def (__getutid_r)
weak_alias (__getutid_r, getutid_r)
diff --git a/login/getutmp.c b/login/getutmp.c
index 481150d5ef5a0bf0..32468ecae699fbf7 100644
--- a/login/getutmp.c
+++ b/login/getutmp.c
@@ -23,23 +23,11 @@
void
getutmp (const struct utmpx *utmpx, struct utmp *utmp)
{
-#if _HAVE_UT_TYPE - 0
utmp->ut_type = utmpx->ut_type;
-#endif
-#if _HAVE_UT_PID - 0
utmp->ut_pid = utmpx->ut_pid;
-#endif
memcpy (utmp->ut_line, utmpx->ut_line, sizeof (utmp->ut_line));
memcpy (utmp->ut_user, utmpx->ut_user, sizeof (utmp->ut_user));
-#if _HAVE_UT_ID - 0
memcpy (utmp->ut_id, utmpx->ut_id, sizeof (utmp->ut_id));
-#endif
-#if _HAVE_UT_HOST - 0
memcpy (utmp->ut_host, utmpx->ut_host, sizeof (utmp->ut_host));
-#endif
-#if _HAVE_UT_TV - 0
utmp->ut_tv = utmpx->ut_tv;
-#else
- utmp->ut_time = utmpx->ut_time;
-#endif
}
diff --git a/login/getutmpx.c b/login/getutmpx.c
index 34145fe8db71faf0..92a182698e2be438 100644
--- a/login/getutmpx.c
+++ b/login/getutmpx.c
@@ -24,24 +24,11 @@ void
getutmpx (const struct utmp *utmp, struct utmpx *utmpx)
{
memset (utmpx, 0, sizeof (struct utmpx));
-
-#if _HAVE_UT_TYPE - 0
utmpx->ut_type = utmp->ut_type;
-#endif
-#if _HAVE_UT_PID - 0
utmpx->ut_pid = utmp->ut_pid;
-#endif
memcpy (utmpx->ut_line, utmp->ut_line, sizeof (utmp->ut_line));
memcpy (utmpx->ut_user, utmp->ut_user, sizeof (utmp->ut_user));
-#if _HAVE_UT_ID - 0
memcpy (utmpx->ut_id, utmp->ut_id, sizeof (utmp->ut_id));
-#endif
-#if _HAVE_UT_HOST - 0
memcpy (utmpx->ut_host, utmp->ut_host, sizeof (utmp->ut_host));
-#endif
-#if _HAVE_UT_TV - 0
utmpx->ut_tv = utmp->ut_tv;
-#else
- utmpx->ut_time = utmp->ut_time;
-#endif
}
diff --git a/login/login.c b/login/login.c
index 5d48cd487f237ca0..1729fc070fcc1e4b 100644
--- a/login/login.c
+++ b/login/login.c
@@ -91,12 +91,8 @@ login (const struct utmp *ut)
struct utmp copy = *ut;
/* Fill in those fields we supply. */
-#if _HAVE_UT_TYPE - 0
copy.ut_type = USER_PROCESS;
-#endif
-#if _HAVE_UT_PID - 0
copy.ut_pid = getpid ();
-#endif
/* Seek tty. */
found_tty = tty_name (STDIN_FILENO, &tty, sizeof (_tty));
diff --git a/login/logout.c b/login/logout.c
index d49bc4ecac9a8379..4d76ecf1b40d306a 100644
--- a/login/logout.c
+++ b/login/logout.c
@@ -36,9 +36,7 @@ logout (const char *line)
setutent ();
/* Fill in search information. */
-#if _HAVE_UT_TYPE - 0
tmp.ut_type = USER_PROCESS;
-#endif
strncpy (tmp.ut_line, line, sizeof tmp.ut_line);
/* Read the record. */
@@ -46,20 +44,12 @@ logout (const char *line)
{
/* Clear information about who & from where. */
memset (ut->ut_name, '\0', sizeof ut->ut_name);
-#if _HAVE_UT_HOST - 0
memset (ut->ut_host, '\0', sizeof ut->ut_host);
-#endif
-#if _HAVE_UT_TV - 0
struct timeval tv;
__gettimeofday (&tv, NULL);
ut->ut_tv.tv_sec = tv.tv_sec;
ut->ut_tv.tv_usec = tv.tv_usec;
-#else
- ut->ut_time = time (NULL);
-#endif
-#if _HAVE_UT_TYPE - 0
ut->ut_type = DEAD_PROCESS;
-#endif
if (pututline (ut) != NULL)
result = 1;
diff --git a/login/logwtmp.c b/login/logwtmp.c
index a19da4ab5ef7a624..e0b52b23e3603b7c 100644
--- a/login/logwtmp.c
+++ b/login/logwtmp.c
@@ -30,26 +30,16 @@ logwtmp (const char *line, const char *name, const char *host)
/* Set information in new entry. */
memset (&ut, 0, sizeof (ut));
-#if _HAVE_UT_PID - 0
ut.ut_pid = getpid ();
-#endif
-#if _HAVE_UT_TYPE - 0
ut.ut_type = name[0] ? USER_PROCESS : DEAD_PROCESS;
-#endif
strncpy (ut.ut_line, line, sizeof ut.ut_line);
strncpy (ut.ut_name, name, sizeof ut.ut_name);
-#if _HAVE_UT_HOST - 0
strncpy (ut.ut_host, host, sizeof ut.ut_host);
-#endif
-#if _HAVE_UT_TV - 0
struct timeval tv;
__gettimeofday (&tv, NULL);
ut.ut_tv.tv_sec = tv.tv_sec;
ut.ut_tv.tv_usec = tv.tv_usec;
-#else
- ut.ut_time = time (NULL);
-#endif
updwtmp (_PATH_WTMP, &ut);
}
diff --git a/login/programs/utmpdump.c b/login/programs/utmpdump.c
index dccdb669f5fb9c74..1763e55af2f03d8d 100644
--- a/login/programs/utmpdump.c
+++ b/login/programs/utmpdump.c
@@ -37,47 +37,11 @@ print_entry (struct utmp *up)
temp_tv.tv_sec = up->ut_tv.tv_sec;
temp_tv.tv_usec = up->ut_tv.tv_usec;
- (printf) (
- /* The format string. */
-#if _HAVE_UT_TYPE
- "[%d] "
-#endif
-#if _HAVE_UT_PID
- "[%05d] "
-#endif
-#if _HAVE_UT_ID
- "[%-4.4s] "
-#endif
- "[%-8.8s] [%-12.12s]"
-#if _HAVE_UT_HOST
- " [%-16.16s]"
-#endif
- " [%-15.15s]"
-#if _HAVE_UT_TV
- " [%ld]"
-#endif
- "\n"
- /* The arguments. */
-#if _HAVE_UT_TYPE
- , up->ut_type
-#endif
-#if _HAVE_UT_PID
- , up->ut_pid
-#endif
-#if _HAVE_UT_ID
- , up->ut_id
-#endif
- , up->ut_user, up->ut_line
-#if _HAVE_UT_HOST
- , up->ut_host
-#endif
-#if _HAVE_UT_TV
- , 4 + ctime (&temp_tv.tv_sec)
- , (long int) temp_tv.tv_usec
-#else
- , 4 + ctime (&up->ut_time)
-#endif
- );
+ printf ("[%d] [%05d] [%-4.4s] [%-8.8s] [%-12.12s] [%-16.16s] [%-15.15s]"
+ " [%ld]\n",
+ up->ut_type, up->ut_pid, up->ut_id, up->ut_user, up->ut_line,
+ up->ut_host, 4 + ctime (&temp_tv.tv_sec),
+ (long int) temp_tv.tv_usec);
}
int
diff --git a/login/tst-utmp.c b/login/tst-utmp.c
index 8cc7aafa89c0ea8c..49b0cbda2a719643 100644
--- a/login/tst-utmp.c
+++ b/login/tst-utmp.c
@@ -39,8 +39,6 @@
#endif
-#if defined UTMPX || _HAVE_UT_TYPE
-
/* Prototype for our test function. */
static int do_test (int argc, char *argv[]);
@@ -75,11 +73,7 @@ do_prepare (int argc, char *argv[])
struct utmp entry[] =
{
-#if defined UTMPX || _HAVE_UT_TV
#define UT(a) .ut_tv = { .tv_sec = (a)}
-#else
-#define UT(a) .ut_time = (a)
-#endif
{ .ut_type = BOOT_TIME, .ut_pid = 1, UT(1000) },
{ .ut_type = RUN_LVL, .ut_pid = 1, UT(2000) },
@@ -167,11 +161,7 @@ simulate_login (const char *line, const char *user)
entry[n].ut_pid = (entry_pid += 27);
entry[n].ut_type = USER_PROCESS;
strncpy (entry[n].ut_user, user, sizeof (entry[n].ut_user));
-#if defined UTMPX || _HAVE_UT_TV - 0
entry[n].ut_tv.tv_sec = (entry_time += 1000);
-#else
- entry[n].ut_time = (entry_time += 1000);
-#endif
setutent ();
if (pututline (&entry[n]) == NULL)
@@ -201,11 +191,7 @@ simulate_logout (const char *line)
{
entry[n].ut_type = DEAD_PROCESS;
strncpy (entry[n].ut_user, "", sizeof (entry[n].ut_user));
-#if defined UTMPX || _HAVE_UT_TV - 0
entry[n].ut_tv.tv_sec = (entry_time += 1000);
-#else
- entry[n].ut_time = (entry_time += 1000);
-#endif
setutent ();
if (pututline (&entry[n]) == NULL)
@@ -390,14 +376,3 @@ do_test (int argc, char *argv[])
return result;
}
-
-#else
-
-/* No field 'ut_type' in struct utmp. */
-int
-main (void)
-{
- return 0;
-}
-
-#endif
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 069e6d0452e333ad..da1baa6948d0eb39 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -129,14 +129,7 @@ __libc_setutent (void)
file_offset = 0;
/* Make sure the entry won't match. */
-#if _HAVE_UT_TYPE - 0
last_entry.ut_type = -1;
-#else
- last_entry.ut_line[0] = '\177';
-# if _HAVE_UT_ID - 0
- last_entry.ut_id[0] = '\0';
-# endif
-#endif
return 1;
}
@@ -201,7 +194,6 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
LOCKING_FAILED ();
}
-#if _HAVE_UT_TYPE - 0
if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
|| id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
{
@@ -225,7 +217,6 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
}
}
else
-#endif /* _HAVE_UT_TYPE */
{
/* Search for the next entry with the specified ID and with type
INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS. */
@@ -316,13 +307,10 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
file_offset += sizeof (struct utmp);
/* Stop if we found a user or login entry. */
- if (
-#if _HAVE_UT_TYPE - 0
- (last_entry.ut_type == USER_PROCESS
+ if ((last_entry.ut_type == USER_PROCESS
|| last_entry.ut_type == LOGIN_PROCESS)
- &&
-#endif
- !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
+ && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
+ == 0))
break;
}
@@ -368,16 +356,12 @@ __libc_pututline (const struct utmp *data)
/* Find the correct place to insert the data. */
if (file_offset > 0
- && (
-#if _HAVE_UT_TYPE - 0
- (last_entry.ut_type == data->ut_type
+ && ((last_entry.ut_type == data->ut_type
&& (last_entry.ut_type == RUN_LVL
|| last_entry.ut_type == BOOT_TIME
|| last_entry.ut_type == OLD_TIME
|| last_entry.ut_type == NEW_TIME))
- ||
-#endif
- __utmp_equal (&last_entry, data)))
+ || __utmp_equal (&last_entry, data)))
found = 1;
else
{
diff --git a/sysdeps/generic/utmp-equal.h b/sysdeps/generic/utmp-equal.h
index 8b5c2e2cd2c4cf95..39993af192ab66ce 100644
--- a/sysdeps/generic/utmp-equal.h
+++ b/sysdeps/generic/utmp-equal.h
@@ -27,26 +27,16 @@
static int
__utmp_equal (const struct utmp *entry, const struct utmp *match)
{
- return
- (
-#if _HAVE_UT_TYPE - 0
- (entry->ut_type == INIT_PROCESS
- || entry->ut_type == LOGIN_PROCESS
- || entry->ut_type == USER_PROCESS
- || entry->ut_type == DEAD_PROCESS)
- &&
- (match->ut_type == INIT_PROCESS
- || match->ut_type == LOGIN_PROCESS
- || match->ut_type == USER_PROCESS
- || match->ut_type == DEAD_PROCESS)
- &&
-#endif
-#if _HAVE_UT_ID - 0
- (entry->ut_id[0] && match->ut_id[0]
- ? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
- : strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0)
-#else
- strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
-#endif
- );
+ return (entry->ut_type == INIT_PROCESS
+ || entry->ut_type == LOGIN_PROCESS
+ || entry->ut_type == USER_PROCESS
+ || entry->ut_type == DEAD_PROCESS)
+ && (match->ut_type == INIT_PROCESS
+ || match->ut_type == LOGIN_PROCESS
+ || match->ut_type == USER_PROCESS
+ || match->ut_type == DEAD_PROCESS)
+ && (entry->ut_id[0] && match->ut_id[0]
+ ? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
+ : (strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line)
+ == 0));
}
diff --git a/sysdeps/gnu/bits/utmp.h b/sysdeps/gnu/bits/utmp.h
deleted file mode 100644
index 47a6082eacc56b4d..0000000000000000
--- a/sysdeps/gnu/bits/utmp.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/* The `struct utmp' type, describing entries in the utmp file. GNU version.
- Copyright (C) 1993-2018 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
- <http://www.gnu.org/licenses/>. */
-
-#ifndef _UTMP_H
-# error "Never include <bits/utmp.h> directly; use <utmp.h> instead."
-#endif
-
-#include <paths.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <bits/wordsize.h>
-
-
-#define UT_LINESIZE 32
-#define UT_NAMESIZE 32
-#define UT_HOSTSIZE 256
-
-
-/* The structure describing an entry in the database of
- previous logins. */
-struct lastlog
- {
-#if __WORDSIZE_TIME64_COMPAT32
- int32_t ll_time;
-#else
- __time_t ll_time;
-#endif
- char ll_line[UT_LINESIZE];
- char ll_host[UT_HOSTSIZE];
- };
-
-
-/* The structure describing the status of a terminated process. This
- type is used in `struct utmp' below. */
-struct exit_status
- {
- short int e_termination; /* Process termination status. */
- short int e_exit; /* Process exit status. */
- };
-
-
-/* The structure describing an entry in the user accounting database. */
-struct utmp
-{
- short int ut_type; /* Type of login. */
- pid_t ut_pid; /* Process ID of login process. */
- char ut_line[UT_LINESIZE]
- __attribute_nonstring__; /* Devicename. */
- char ut_id[4]; /* Inittab ID. */
- char ut_user[UT_NAMESIZE]
- __attribute_nonstring__; /* Username. */
- char ut_host[UT_HOSTSIZE]
- __attribute_nonstring__; /* Hostname for remote login. */
- struct exit_status ut_exit; /* Exit status of a process marked
- as DEAD_PROCESS. */
-/* The ut_session and ut_tv fields must be the same size when compiled
- 32- and 64-bit. This allows data files and shared memory to be
- shared between 32- and 64-bit applications. */
-#if __WORDSIZE_TIME64_COMPAT32
- int32_t ut_session; /* Session ID, used for windowing. */
- struct
- {
- int32_t tv_sec; /* Seconds. */
- int32_t tv_usec; /* Microseconds. */
- } ut_tv; /* Time entry was made. */
-#else
- long int ut_session; /* Session ID, used for windowing. */
- struct timeval ut_tv; /* Time entry was made. */
-#endif
-
- int32_t ut_addr_v6[4]; /* Internet address of remote host. */
- char __glibc_reserved[20]; /* Reserved for future use. */
-};
-
-/* Backwards compatibility hacks. */
-#define ut_name ut_user
-#ifndef _NO_UT_TIME
-/* We have a problem here: `ut_time' is also used otherwise. Define
- _NO_UT_TIME if the compiler complains. */
-# define ut_time ut_tv.tv_sec
-#endif
-#define ut_xtime ut_tv.tv_sec
-#define ut_addr ut_addr_v6[0]
-
-
-/* Values for the `ut_type' field of a `struct utmp'. */
-#define EMPTY 0 /* No valid user accounting information. */
-
-#define RUN_LVL 1 /* The system's runlevel. */
-#define BOOT_TIME 2 /* Time of system boot. */
-#define NEW_TIME 3 /* Time after system clock changed. */
-#define OLD_TIME 4 /* Time when system clock changed. */
-
-#define INIT_PROCESS 5 /* Process spawned by the init process. */
-#define LOGIN_PROCESS 6 /* Session leader of a logged in user. */
-#define USER_PROCESS 7 /* Normal process. */
-#define DEAD_PROCESS 8 /* Terminated process. */
-
-#define ACCOUNTING 9
-
-/* Old Linux name for the EMPTY type. */
-#define UT_UNKNOWN EMPTY
-
-
-/* Tell the user that we have a modern system with UT_HOST, UT_PID,
- UT_TYPE, UT_ID and UT_TV fields. */
-#define _HAVE_UT_TYPE 1
-#define _HAVE_UT_PID 1
-#define _HAVE_UT_ID 1
-#define _HAVE_UT_TV 1
-#define _HAVE_UT_HOST 1

View File

@ -0,0 +1,260 @@
commit 5a3afa9738f3dbbaf8c0a35665318c1af782111b
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 13 15:53:19 2019 +0200
login: Replace macro-based control flow with function calls in utmp
diff --git a/login/utmp_file.c b/login/utmp_file.c
index da1baa6948d0eb39..812de8fd3d099ce9 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -52,58 +52,71 @@ static struct utmp last_entry;
/* Do-nothing handler for locking timeout. */
static void timeout_handler (int signum) {};
-/* LOCK_FILE(fd, type) failure_statement
- attempts to get a lock on the utmp file referenced by FD. If it fails,
- the failure_statement is executed, otherwise it is skipped.
- LOCKING_FAILED()
- jumps into the UNLOCK_FILE macro and ensures cleanup of LOCK_FILE.
- UNLOCK_FILE(fd)
- unlocks the utmp file referenced by FD and performs the cleanup of
- LOCK_FILE.
- */
-#define LOCK_FILE(fd, type) \
-{ \
- struct flock fl; \
- struct sigaction action, old_action; \
- unsigned int old_timeout; \
- \
- /* Cancel any existing alarm. */ \
- old_timeout = alarm (0); \
- \
- /* Establish signal handler. */ \
- action.sa_handler = timeout_handler; \
- __sigemptyset (&action.sa_mask); \
- action.sa_flags = 0; \
- __sigaction (SIGALRM, &action, &old_action); \
- \
- alarm (TIMEOUT); \
- \
- /* Try to get the lock. */ \
- memset (&fl, '\0', sizeof (struct flock)); \
- fl.l_type = (type); \
- fl.l_whence = SEEK_SET; \
- if (__fcntl64_nocancel ((fd), F_SETLKW, &fl) < 0)
-
-#define LOCKING_FAILED() \
- goto unalarm_return
-
-#define UNLOCK_FILE(fd) \
- /* Unlock the file. */ \
- fl.l_type = F_UNLCK; \
- __fcntl64_nocancel ((fd), F_SETLKW, &fl); \
- \
- unalarm_return: \
- /* Reset the signal handler and alarm. We must reset the alarm \
- before resetting the handler so our alarm does not generate a \
- spurious SIGALRM seen by the user. However, we cannot just set \
- the user's old alarm before restoring the handler, because then \
- it's possible our handler could catch the user alarm's SIGARLM \
- and then the user would never see the signal he expected. */ \
- alarm (0); \
- __sigaction (SIGALRM, &old_action, NULL); \
- if (old_timeout != 0) \
- alarm (old_timeout); \
-} while (0)
+
+/* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
+ operation failed and recovery needs to be performed.
+ (file_lock_restore (LOCKING) still needs to be called.)
+
+ file_unlock (FD) removes the lock (which must have been
+ acquired).
+
+ file_lock_restore (LOCKING) is needed to clean up in both
+ cases. */
+
+struct file_locking
+{
+ struct sigaction old_action;
+ unsigned int old_timeout;
+};
+
+static bool
+try_file_lock (struct file_locking *locking, int fd, int type)
+{
+ /* Cancel any existing alarm. */
+ locking->old_timeout = alarm (0);
+
+ /* Establish signal handler. */
+ struct sigaction action;
+ action.sa_handler = timeout_handler;
+ __sigemptyset (&action.sa_mask);
+ action.sa_flags = 0;
+ __sigaction (SIGALRM, &action, &locking->old_action);
+
+ alarm (TIMEOUT);
+
+ /* Try to get the lock. */
+ struct flock fl =
+ {
+ .l_type = type,
+ fl.l_whence = SEEK_SET,
+ };
+ return __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
+}
+
+static void
+file_unlock (int fd)
+{
+ struct flock fl =
+ {
+ .l_type = F_UNLCK,
+ };
+ __fcntl64_nocancel (fd, F_SETLKW, &fl);
+}
+
+static void
+file_lock_restore (struct file_locking *locking)
+{
+ /* Reset the signal handler and alarm. We must reset the alarm
+ before resetting the handler so our alarm does not generate a
+ spurious SIGALRM seen by the user. However, we cannot just set
+ the user's old alarm before restoring the handler, because then
+ it's possible our handler could catch the user alarm's SIGARLM
+ and then the user would never see the signal he expected. */
+ alarm (0);
+ __sigaction (SIGALRM, &locking->old_action, NULL);
+ if (locking->old_timeout != 0)
+ alarm (locking->old_timeout);
+}
#ifndef TRANSFORM_UTMP_FILE_NAME
# define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
@@ -153,16 +166,16 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
return -1;
}
- LOCK_FILE (file_fd, F_RDLCK)
+ struct file_locking fl;
+ if (try_file_lock (&fl, file_fd, F_RDLCK))
+ nbytes = 0;
+ else
{
- nbytes = 0;
- LOCKING_FAILED ();
+ /* Read the next entry. */
+ nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
+ file_unlock (file_fd);
}
-
- /* Read the next entry. */
- nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
-
- UNLOCK_FILE (file_fd);
+ file_lock_restore (&fl);
if (nbytes != sizeof (struct utmp))
{
@@ -188,10 +201,12 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
{
int result = -1;
- LOCK_FILE (file_fd, F_RDLCK)
+ struct file_locking fl;
+ if (try_file_lock (&fl, file_fd, F_RDLCK))
{
*lock_failed = true;
- LOCKING_FAILED ();
+ file_lock_restore (&fl);
+ return -1;
}
if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
@@ -241,7 +256,8 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
result = 0;
unlock_return:
- UNLOCK_FILE (file_fd);
+ file_unlock (file_fd);
+ file_lock_restore (&fl);
return result;
}
@@ -287,10 +303,12 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
return -1;
}
- LOCK_FILE (file_fd, F_RDLCK)
+ struct file_locking fl;
+ if (try_file_lock (&fl, file_fd, F_RDLCK))
{
*result = NULL;
- LOCKING_FAILED ();
+ file_lock_restore (&fl);
+ return -1;
}
while (1)
@@ -318,7 +336,8 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
*result = buffer;
unlock_return:
- UNLOCK_FILE (file_fd);
+ file_unlock (file_fd);
+ file_lock_restore (&fl);
return ((*result == NULL) ? -1 : 0);
}
@@ -375,10 +394,11 @@ __libc_pututline (const struct utmp *data)
}
}
- LOCK_FILE (file_fd, F_WRLCK)
+ struct file_locking fl;
+ if (try_file_lock (&fl, file_fd, F_WRLCK))
{
- pbuf = NULL;
- LOCKING_FAILED ();
+ file_lock_restore (&fl);
+ return NULL;
}
if (found < 0)
@@ -421,7 +441,8 @@ __libc_pututline (const struct utmp *data)
}
unlock_return:
- UNLOCK_FILE (file_fd);
+ file_unlock (file_fd);
+ file_lock_restore (&fl);
return pbuf;
}
@@ -450,8 +471,13 @@ __libc_updwtmp (const char *file, const struct utmp *utmp)
if (fd < 0)
return -1;
- LOCK_FILE (fd, F_WRLCK)
- LOCKING_FAILED ();
+ struct file_locking fl;
+ if (try_file_lock (&fl, fd, F_WRLCK))
+ {
+ file_lock_restore (&fl);
+ __close_nocancel_nostatus (fd);
+ return -1;
+ }
/* Remember original size of log file. */
offset = __lseek64 (fd, 0, SEEK_END);
@@ -477,7 +503,8 @@ __libc_updwtmp (const char *file, const struct utmp *utmp)
result = 0;
unlock_return:
- UNLOCK_FILE (fd);
+ file_unlock (file_fd);
+ file_lock_restore (&fl);
/* Close WTMP file. */
__close_nocancel_nostatus (fd);

View File

@ -0,0 +1,155 @@
commit 341da5b4b6253de9a7581a066f33f89cacb44dec
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 15 10:30:23 2019 +0200
login: Fix updwtmp, updwtmx unlocking
Commit 5a3afa9738f3dbbaf8c0a35665318c1af782111b (login: Replace
macro-based control flow with function calls in utmp) introduced
a regression because after it, __libc_updwtmp attempts to unlock
the wrong file descriptor.
diff --git a/login/Makefile b/login/Makefile
index 8b31991be835fa8e..81986ab6bd8560ea 100644
--- a/login/Makefile
+++ b/login/Makefile
@@ -43,7 +43,7 @@ endif
subdir-dirs = programs
vpath %.c programs
-tests := tst-utmp tst-utmpx tst-grantpt tst-ptsname tst-getlogin
+tests := tst-utmp tst-utmpx tst-grantpt tst-ptsname tst-getlogin tst-updwtmpx
# Build the -lutil library with these extra functions.
extra-libs := libutil
diff --git a/login/tst-updwtmpx.c b/login/tst-updwtmpx.c
new file mode 100644
index 0000000000000000..0a4a27daeb0440fd
--- /dev/null
+++ b/login/tst-updwtmpx.c
@@ -0,0 +1,112 @@
+/* Basic test coverage for updwtmpx.
+ Copyright (C) 2019 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; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+/* This program runs a series of tests. Each one calls updwtmpx
+ twice, to write two records, optionally with misalignment in the
+ file, and reads back the results. */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+#include <utmpx.h>
+
+static int
+do_test (void)
+{
+ /* Two entries filled with an arbitrary bit pattern. */
+ struct utmpx entries[2];
+ unsigned char pad;
+ {
+ unsigned char *p = (unsigned char *) &entries[0];
+ for (size_t i = 0; i < sizeof (entries); ++i)
+ {
+ p[i] = i;
+ }
+ /* Make sure that the first and second entry and the padding are
+ different. */
+ p[sizeof (struct utmpx)] = p[0] + 1;
+ pad = p[0] + 2;
+ }
+
+ char *path;
+ int fd = create_temp_file ("tst-updwtmpx-", &path);
+
+ /* Used to check that updwtmpx does not leave an open file
+ descriptor around. */
+ struct support_descriptors *descriptors = support_descriptors_list ();
+
+ /* updwtmpx is expected to remove misalignment. Optionally insert
+ one byte of misalignment at the start and in the middle (after
+ the first entry). */
+ for (int misaligned_start = 0; misaligned_start < 2; ++misaligned_start)
+ for (int misaligned_middle = 0; misaligned_middle < 2; ++misaligned_middle)
+ {
+ if (test_verbose > 0)
+ printf ("info: misaligned_start=%d misaligned_middle=%d\n",
+ misaligned_start, misaligned_middle);
+
+ xftruncate (fd, 0);
+ TEST_COMPARE (pwrite64 (fd, &pad, misaligned_start, 0),
+ misaligned_start);
+
+ /* Write first entry and check it. */
+ errno = 0;
+ updwtmpx (path, &entries[0]);
+ TEST_COMPARE (errno, 0);
+ support_descriptors_check (descriptors);
+ TEST_COMPARE (xlseek (fd, 0, SEEK_END), sizeof (struct utmpx));
+ struct utmpx buffer;
+ TEST_COMPARE (pread64 (fd, &buffer, sizeof (buffer), 0),
+ sizeof (buffer));
+ TEST_COMPARE_BLOB (&entries[0], sizeof (entries[0]),
+ &buffer, sizeof (buffer));
+
+ /* Middle mis-alignmet. */
+ TEST_COMPARE (pwrite64 (fd, &pad, misaligned_middle,
+ sizeof (struct utmpx)), misaligned_middle);
+
+ /* Write second entry and check both entries. */
+ errno = 0;
+ updwtmpx (path, &entries[1]);
+ TEST_COMPARE (errno, 0);
+ support_descriptors_check (descriptors);
+ TEST_COMPARE (xlseek (fd, 0, SEEK_END), 2 * sizeof (struct utmpx));
+ TEST_COMPARE (pread64 (fd, &buffer, sizeof (buffer), 0),
+ sizeof (buffer));
+ TEST_COMPARE_BLOB (&entries[0], sizeof (entries[0]),
+ &buffer, sizeof (buffer));
+ TEST_COMPARE (pread64 (fd, &buffer, sizeof (buffer), sizeof (buffer)),
+ sizeof (buffer));
+ TEST_COMPARE_BLOB (&entries[1], sizeof (entries[1]),
+ &buffer, sizeof (buffer));
+ }
+
+ support_descriptors_free (descriptors);
+ free (path);
+ xclose (fd);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 812de8fd3d099ce9..54f424fd6165bae7 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -503,7 +503,7 @@ __libc_updwtmp (const char *file, const struct utmp *utmp)
result = 0;
unlock_return:
- file_unlock (file_fd);
+ file_unlock (fd);
file_lock_restore (&fl);
/* Close WTMP file. */

View File

@ -0,0 +1,33 @@
commit 0d5b2917530ccaf8ad312dfbb7bce69d569c23ad
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 15 16:09:20 2019 +0200
login: Use struct flock64 in utmp [BZ #24880]
Commit 06ab719d30b01da401150068054d3b8ea93dd12f ("Fix Linux fcntl OFD
locks for non-LFS architectures (BZ#20251)") introduced the use of
fcntl64 into the utmp implementation. However, the lock file
structure was not updated to struct flock64 at that point.
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 54f424fd6165bae7..8b6fee96b623fa90 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -85,7 +85,7 @@ try_file_lock (struct file_locking *locking, int fd, int type)
alarm (TIMEOUT);
/* Try to get the lock. */
- struct flock fl =
+ struct flock64 fl =
{
.l_type = type,
fl.l_whence = SEEK_SET,
@@ -96,7 +96,7 @@ try_file_lock (struct file_locking *locking, int fd, int type)
static void
file_unlock (int fd)
{
- struct flock fl =
+ struct flock64 fl =
{
.l_type = F_UNLCK,
};

View File

@ -0,0 +1,204 @@
commit 628598be7e1bfaa04f34df71ef6678f2c5103dfd
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 15 16:09:05 2019 +0200
login: Disarm timer after utmp lock acquisition [BZ #24879]
If the file processing takes a long time for some reason, SIGALRM can
arrive while the file is still being processed. At that point, file
access will fail with EINTR. Disarming the timer after lock
acquisition avoids that. (If there was a previous alarm, it is the
responsibility of the caller to deal with the EINTR error.)
diff --git a/login/utmp_file.c b/login/utmp_file.c
index 8b6fee96b623fa90..a736d3d25e005920 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -55,32 +55,23 @@ static void timeout_handler (int signum) {};
/* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
operation failed and recovery needs to be performed.
- (file_lock_restore (LOCKING) still needs to be called.)
file_unlock (FD) removes the lock (which must have been
- acquired).
-
- file_lock_restore (LOCKING) is needed to clean up in both
- cases. */
-
-struct file_locking
-{
- struct sigaction old_action;
- unsigned int old_timeout;
-};
+ successfully acquired). */
static bool
-try_file_lock (struct file_locking *locking, int fd, int type)
+try_file_lock (int fd, int type)
{
/* Cancel any existing alarm. */
- locking->old_timeout = alarm (0);
+ int old_timeout = alarm (0);
/* Establish signal handler. */
+ struct sigaction old_action;
struct sigaction action;
action.sa_handler = timeout_handler;
__sigemptyset (&action.sa_mask);
action.sa_flags = 0;
- __sigaction (SIGALRM, &action, &locking->old_action);
+ __sigaction (SIGALRM, &action, &old_action);
alarm (TIMEOUT);
@@ -90,7 +81,23 @@ try_file_lock (struct file_locking *locking, int fd, int type)
.l_type = type,
fl.l_whence = SEEK_SET,
};
- return __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
+
+ bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
+ int saved_errno = errno;
+
+ /* Reset the signal handler and alarm. We must reset the alarm
+ before resetting the handler so our alarm does not generate a
+ spurious SIGALRM seen by the user. However, we cannot just set
+ the user's old alarm before restoring the handler, because then
+ it's possible our handler could catch the user alarm's SIGARLM and
+ then the user would never see the signal he expected. */
+ alarm (0);
+ __sigaction (SIGALRM, &old_action, NULL);
+ if (old_timeout != 0)
+ alarm (old_timeout);
+
+ __set_errno (saved_errno);
+ return status;
}
static void
@@ -103,21 +110,6 @@ file_unlock (int fd)
__fcntl64_nocancel (fd, F_SETLKW, &fl);
}
-static void
-file_lock_restore (struct file_locking *locking)
-{
- /* Reset the signal handler and alarm. We must reset the alarm
- before resetting the handler so our alarm does not generate a
- spurious SIGALRM seen by the user. However, we cannot just set
- the user's old alarm before restoring the handler, because then
- it's possible our handler could catch the user alarm's SIGARLM
- and then the user would never see the signal he expected. */
- alarm (0);
- __sigaction (SIGALRM, &locking->old_action, NULL);
- if (locking->old_timeout != 0)
- alarm (locking->old_timeout);
-}
-
#ifndef TRANSFORM_UTMP_FILE_NAME
# define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
#endif
@@ -166,8 +158,7 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
return -1;
}
- struct file_locking fl;
- if (try_file_lock (&fl, file_fd, F_RDLCK))
+ if (try_file_lock (file_fd, F_RDLCK))
nbytes = 0;
else
{
@@ -175,7 +166,6 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
file_unlock (file_fd);
}
- file_lock_restore (&fl);
if (nbytes != sizeof (struct utmp))
{
@@ -201,11 +191,9 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
{
int result = -1;
- struct file_locking fl;
- if (try_file_lock (&fl, file_fd, F_RDLCK))
+ if (try_file_lock (file_fd, F_RDLCK))
{
*lock_failed = true;
- file_lock_restore (&fl);
return -1;
}
@@ -257,7 +245,6 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
unlock_return:
file_unlock (file_fd);
- file_lock_restore (&fl);
return result;
}
@@ -303,11 +290,9 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
return -1;
}
- struct file_locking fl;
- if (try_file_lock (&fl, file_fd, F_RDLCK))
+ if (try_file_lock (file_fd, F_RDLCK))
{
*result = NULL;
- file_lock_restore (&fl);
return -1;
}
@@ -337,7 +322,6 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
unlock_return:
file_unlock (file_fd);
- file_lock_restore (&fl);
return ((*result == NULL) ? -1 : 0);
}
@@ -394,12 +378,8 @@ __libc_pututline (const struct utmp *data)
}
}
- struct file_locking fl;
- if (try_file_lock (&fl, file_fd, F_WRLCK))
- {
- file_lock_restore (&fl);
- return NULL;
- }
+ if (try_file_lock (file_fd, F_WRLCK))
+ return NULL;
if (found < 0)
{
@@ -442,7 +422,6 @@ __libc_pututline (const struct utmp *data)
unlock_return:
file_unlock (file_fd);
- file_lock_restore (&fl);
return pbuf;
}
@@ -471,10 +450,8 @@ __libc_updwtmp (const char *file, const struct utmp *utmp)
if (fd < 0)
return -1;
- struct file_locking fl;
- if (try_file_lock (&fl, fd, F_WRLCK))
+ if (try_file_lock (fd, F_WRLCK))
{
- file_lock_restore (&fl);
__close_nocancel_nostatus (fd);
return -1;
}
@@ -504,7 +481,6 @@ __libc_updwtmp (const char *file, const struct utmp *utmp)
unlock_return:
file_unlock (fd);
- file_lock_restore (&fl);
/* Close WTMP file. */
__close_nocancel_nostatus (fd);

View File

@ -0,0 +1,309 @@
commit 61d3db428176d9d0822e4e680305fe34285edff2
Author: Florian Weimer <fweimer@redhat.com>
Date: Wed Aug 28 11:59:45 2019 +0200
login: pututxline could fail to overwrite existing entries [BZ #24902]
The internal_getut_r function updates the file_offset variable and
therefore must always update last_entry as well.
Previously, if pututxline could not upgrade the read lock to a
write lock, internal_getut_r would update file_offset only,
without updating last_entry, and a subsequent call would not
overwrite the existing utmpx entry at file_offset, instead
creating a new entry. This has been observed to cause unbounded
file growth in high-load situations.
This commit removes the buffer argument to internal_getut_r and
updates the last_entry variable directly, along with file_offset.
Initially reported and fixed by Ondřej Lysoněk.
Reviewed-by: Gabriel F. T. Gomes <gabrielftg@linux.ibm.com>
diff --git a/login/Makefile b/login/Makefile
index 81986ab6bd8560ea..82132c83fd799357 100644
--- a/login/Makefile
+++ b/login/Makefile
@@ -43,7 +43,8 @@ endif
subdir-dirs = programs
vpath %.c programs
-tests := tst-utmp tst-utmpx tst-grantpt tst-ptsname tst-getlogin tst-updwtmpx
+tests := tst-utmp tst-utmpx tst-grantpt tst-ptsname tst-getlogin tst-updwtmpx \
+ tst-pututxline-lockfail
# Build the -lutil library with these extra functions.
extra-libs := libutil
@@ -71,3 +72,5 @@ endif
$(inst_libexecdir)/pt_chown: $(objpfx)pt_chown $(+force)
$(make-target-directory)
-$(INSTALL_PROGRAM) -m 4755 -o root $< $@
+
+$(objpfx)tst-pututxline-lockfail: $(shared-thread-library)
diff --git a/login/tst-pututxline-lockfail.c b/login/tst-pututxline-lockfail.c
new file mode 100644
index 0000000000000000..47c25dc0658d3c60
--- /dev/null
+++ b/login/tst-pututxline-lockfail.c
@@ -0,0 +1,176 @@
+/* Test the lock upgrade path in tst-pututxline.
+ Copyright (C) 2019 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; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+/* pututxline upgrades the read lock on the file to a write lock.
+ This test verifies that if the lock upgrade fails, the utmp
+ subsystem remains in a consistent state, so that pututxline can be
+ called again. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <utmpx.h>
+
+/* Path to the temporary utmp file. */
+static char *path;
+
+/* Used to synchronize the subprocesses. The barrier itself is
+ allocated in shared memory. */
+static pthread_barrier_t *barrier;
+
+/* Use pututxline to write an entry for PID. */
+static struct utmpx *
+write_entry (pid_t pid)
+{
+ struct utmpx ut =
+ {
+ .ut_type = LOGIN_PROCESS,
+ .ut_id = "1",
+ .ut_user = "root",
+ .ut_pid = pid,
+ .ut_line = "entry",
+ .ut_host = "localhost",
+ };
+ return pututxline (&ut);
+}
+
+/* Create the initial entry in a subprocess, so that the utmp
+ subsystem in the original process is not disturbed. */
+static void
+subprocess_create_entry (void *closure)
+{
+ TEST_COMPARE (utmpname (path), 0);
+ TEST_VERIFY (write_entry (101) != NULL);
+}
+
+/* Acquire an advisory read lock on PATH. */
+__attribute__ ((noreturn)) static void
+subprocess_lock_file (void)
+{
+ int fd = xopen (path, O_RDONLY, 0);
+
+ struct flock64 fl =
+ {
+ .l_type = F_RDLCK,
+ fl.l_whence = SEEK_SET,
+ };
+ TEST_COMPARE (fcntl64 (fd, F_SETLKW, &fl), 0);
+
+ /* Signal to the main process that the lock has been acquired. */
+ xpthread_barrier_wait (barrier);
+
+ /* Wait for the unlock request from the main process. */
+ xpthread_barrier_wait (barrier);
+
+ /* Implicitly unlock the file. */
+ xclose (fd);
+
+ /* Overwrite the existing entry. */
+ TEST_COMPARE (utmpname (path), 0);
+ errno = 0;
+ setutxent ();
+ TEST_COMPARE (errno, 0);
+ TEST_VERIFY (write_entry (102) != NULL);
+ errno = 0;
+ endutxent ();
+ TEST_COMPARE (errno, 0);
+
+ _exit (0);
+}
+
+static int
+do_test (void)
+{
+ xclose (create_temp_file ("tst-pututxline-lockfail-", &path));
+
+ {
+ pthread_barrierattr_t attr;
+ xpthread_barrierattr_init (&attr);
+ xpthread_barrierattr_setpshared (&attr, PTHREAD_SCOPE_PROCESS);
+ barrier = support_shared_allocate (sizeof (*barrier));
+ xpthread_barrier_init (barrier, &attr, 2);
+ xpthread_barrierattr_destroy (&attr);
+ }
+
+ /* Write the initial entry. */
+ support_isolate_in_subprocess (subprocess_create_entry, NULL);
+
+ pid_t locker_pid = xfork ();
+ if (locker_pid == 0)
+ subprocess_lock_file ();
+
+ /* Wait for the file locking to complete. */
+ xpthread_barrier_wait (barrier);
+
+ /* Try to add another entry. This attempt will fail, with EINTR or
+ EAGAIN. */
+ TEST_COMPARE (utmpname (path), 0);
+ TEST_VERIFY (write_entry (102) == NULL);
+ if (errno != EINTR)
+ TEST_COMPARE (errno, EAGAIN);
+
+ /* Signal the subprocess to overwrite the entry. */
+ xpthread_barrier_wait (barrier);
+
+ /* Wait for write and unlock to complete. */
+ {
+ int status;
+ xwaitpid (locker_pid, &status, 0);
+ TEST_COMPARE (status, 0);
+ }
+
+ /* The file is no longer locked, so this operation will succeed. */
+ TEST_VERIFY (write_entry (103) != NULL);
+ errno = 0;
+ endutxent ();
+ TEST_COMPARE (errno, 0);
+
+ /* Check that there is just one entry with the expected contents.
+ If pututxline becomes desynchronized internally, the entry is not
+ overwritten (bug 24902). */
+ errno = 0;
+ setutxent ();
+ TEST_COMPARE (errno, 0);
+ struct utmpx *ut = getutxent ();
+ TEST_VERIFY_EXIT (ut != NULL);
+ TEST_COMPARE (ut->ut_type, LOGIN_PROCESS);
+ TEST_COMPARE_STRING (ut->ut_id, "1");
+ TEST_COMPARE_STRING (ut->ut_user, "root");
+ TEST_COMPARE (ut->ut_pid, 103);
+ TEST_COMPARE_STRING (ut->ut_line, "entry");
+ TEST_COMPARE_STRING (ut->ut_host, "localhost");
+ TEST_VERIFY (getutxent () == NULL);
+ errno = 0;
+ endutxent ();
+ TEST_COMPARE (errno, 0);
+
+ xpthread_barrier_destroy (barrier);
+ support_shared_free (barrier);
+ free (path);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/login/utmp_file.c b/login/utmp_file.c
index a736d3d25e005920..cbc53d06de280af9 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -185,9 +185,11 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
}
+/* Search for *ID, updating last_entry and file_offset. Return 0 on
+ success and -1 on failure. If the locking operation failed, write
+ true to *LOCK_FAILED. */
static int
-internal_getut_r (const struct utmp *id, struct utmp *buffer,
- bool *lock_failed)
+internal_getut_r (const struct utmp *id, bool *lock_failed)
{
int result = -1;
@@ -206,7 +208,7 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
while (1)
{
/* Read the next entry. */
- if (__read_nocancel (file_fd, buffer, sizeof (struct utmp))
+ if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
!= sizeof (struct utmp))
{
__set_errno (ESRCH);
@@ -215,7 +217,7 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
}
file_offset += sizeof (struct utmp);
- if (id->ut_type == buffer->ut_type)
+ if (id->ut_type == last_entry.ut_type)
break;
}
}
@@ -227,7 +229,7 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
while (1)
{
/* Read the next entry. */
- if (__read_nocancel (file_fd, buffer, sizeof (struct utmp))
+ if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
!= sizeof (struct utmp))
{
__set_errno (ESRCH);
@@ -236,7 +238,7 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer,
}
file_offset += sizeof (struct utmp);
- if (__utmp_equal (buffer, id))
+ if (__utmp_equal (&last_entry, id))
break;
}
}
@@ -265,7 +267,7 @@ __libc_getutid_r (const struct utmp *id, struct utmp *buffer,
/* We don't have to distinguish whether we can lock the file or
whether there is no entry. */
bool lock_failed = false;
- if (internal_getut_r (id, &last_entry, &lock_failed) < 0)
+ if (internal_getut_r (id, &lock_failed) < 0)
{
*result = NULL;
return -1;
@@ -330,10 +332,9 @@ unlock_return:
struct utmp *
__libc_pututline (const struct utmp *data)
{
- if (!maybe_setutent ())
+ if (!maybe_setutent () || file_offset == -1l)
return NULL;
- struct utmp buffer;
struct utmp *pbuf;
int found;
@@ -369,7 +370,7 @@ __libc_pututline (const struct utmp *data)
else
{
bool lock_failed = false;
- found = internal_getut_r (data, &buffer, &lock_failed);
+ found = internal_getut_r (data, &lock_failed);
if (__builtin_expect (lock_failed, false))
{

View File

@ -0,0 +1,86 @@
commit c2adefbafcdd2519ff43eca6891c77cd7b29ab62
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 15 16:09:43 2019 +0200
login: Add nonstring attributes to struct utmp, struct utmpx [BZ #24899]
Commit 7532837d7b03b3ca5b9a63d77a5bd81dd23f3d9c ("The
-Wstringop-truncation option new in GCC 8 detects common misuses")
added __attribute_nonstring__ to bits/utmp.h, but it did not update
the parallel bits/utmpx.h header. In struct utmp, the nonstring
attribute for ut_id was missing.
diff --git a/bits/utmp.h b/bits/utmp.h
index 3c02dd4f3fe4e99b..854b342164b785e0 100644
--- a/bits/utmp.h
+++ b/bits/utmp.h
@@ -61,7 +61,8 @@ struct utmp
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]
__attribute_nonstring__; /* Devicename. */
- char ut_id[4]; /* Inittab ID. */
+ char ut_id[4]
+ __attribute_nonstring__; /* Inittab ID. */
char ut_user[UT_NAMESIZE]
__attribute_nonstring__; /* Username. */
char ut_host[UT_HOSTSIZE]
diff --git a/sysdeps/gnu/bits/utmpx.h b/sysdeps/gnu/bits/utmpx.h
index 2a77efc607ae2ac0..71c743ebfcd41194 100644
--- a/sysdeps/gnu/bits/utmpx.h
+++ b/sysdeps/gnu/bits/utmpx.h
@@ -56,10 +56,14 @@ struct utmpx
{
short int ut_type; /* Type of login. */
__pid_t ut_pid; /* Process ID of login process. */
- char ut_line[__UT_LINESIZE]; /* Devicename. */
- char ut_id[4]; /* Inittab ID. */
- char ut_user[__UT_NAMESIZE]; /* Username. */
- char ut_host[__UT_HOSTSIZE]; /* Hostname for remote login. */
+ char ut_line[__UT_LINESIZE]
+ __attribute_nonstring__; /* Devicename. */
+ char ut_id[4]
+ __attribute_nonstring__; /* Inittab ID. */
+ char ut_user[__UT_NAMESIZE]
+ __attribute_nonstring__; /* Username. */
+ char ut_host[__UT_HOSTSIZE]
+ __attribute_nonstring__; /* Hostname for remote login. */
struct __exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */
diff --git a/sysdeps/unix/sysv/linux/s390/bits/utmp.h b/sysdeps/unix/sysv/linux/s390/bits/utmp.h
index b3fa362f478ae6fe..82e8d17e2e8cc031 100644
--- a/sysdeps/unix/sysv/linux/s390/bits/utmp.h
+++ b/sysdeps/unix/sysv/linux/s390/bits/utmp.h
@@ -61,7 +61,8 @@ struct utmp
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]
__attribute_nonstring__; /* Devicename. */
- char ut_id[4]; /* Inittab ID. */
+ char ut_id[4]
+ __attribute_nonstring__; /* Inittab ID. */
char ut_user[UT_NAMESIZE]
__attribute_nonstring__; /* Username. */
char ut_host[UT_HOSTSIZE]
diff --git a/sysdeps/unix/sysv/linux/s390/bits/utmpx.h b/sysdeps/unix/sysv/linux/s390/bits/utmpx.h
index 3d3036c3b91e6f57..3818ed3aa4df1e65 100644
--- a/sysdeps/unix/sysv/linux/s390/bits/utmpx.h
+++ b/sysdeps/unix/sysv/linux/s390/bits/utmpx.h
@@ -56,10 +56,14 @@ struct utmpx
{
short int ut_type; /* Type of login. */
__pid_t ut_pid; /* Process ID of login process. */
- char ut_line[__UT_LINESIZE]; /* Devicename. */
- char ut_id[4]; /* Inittab ID. */
- char ut_user[__UT_NAMESIZE]; /* Username. */
- char ut_host[__UT_HOSTSIZE]; /* Hostname for remote login. */
+ char ut_line[__UT_LINESIZE]
+ __attribute_nonstring__; /* Devicename. */
+ char ut_id[4]
+ __attribute_nonstring__; /* Inittab ID. */
+ char ut_user[__UT_NAMESIZE]
+ __attribute_nonstring__; /* Username. */
+ char ut_host[__UT_HOSTSIZE]
+ __attribute_nonstring__; /* Hostname for remote login. */
struct __exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */

View File

@ -0,0 +1,26 @@
commit b0a83ae71b2588bd2a9e6b40f95191602940e01e
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Nov 7 09:53:41 2019 +0100
login: Remove double-assignment of fl.l_whence in try_file_lock
Since l_whence is the second member of struct flock, it is written
twice. The double-assignment is technically undefined behavior due to
the lack of a sequence point.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Change-Id: I2baf9e70690e723c61051b25ccbd510aec15976c
diff --git a/login/utmp_file.c b/login/utmp_file.c
index cbc53d06de280af9..9ad80364682bae92 100644
--- a/login/utmp_file.c
+++ b/login/utmp_file.c
@@ -79,7 +79,7 @@ try_file_lock (int fd, int type)
struct flock64 fl =
{
.l_type = type,
- fl.l_whence = SEEK_SET,
+ .l_whence = SEEK_SET,
};
bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;

View File

@ -0,0 +1,305 @@
commit bc79db3fd487daea36e7c130f943cfb9826a41b4
Author: Stefan Liebler <stli@linux.ibm.com>
Date: Wed Feb 6 09:06:34 2019 +0100
Fix alignment of TLS variables for tls variant TLS_TCB_AT_TP [BZ #23403]
The alignment of TLS variables is wrong if accessed from within a thread
for architectures with tls variant TLS_TCB_AT_TP.
For the main thread the static tls data is properly aligned.
For other threads the alignment depends on the alignment of the thread
pointer as the static tls data is located relative to this pointer.
This patch adds this alignment for TLS_TCB_AT_TP variants in the same way
as it is already done for TLS_DTV_AT_TP. The thread pointer is also already
properly aligned if the user provides its own stack for the new thread.
This patch extends the testcase nptl/tst-tls1.c in order to check the
alignment of the tls variables and it adds a pthread_create invocation
with a user provided stack.
The test itself is migrated from test-skeleton.c to test-driver.c
and the missing support functions xpthread_attr_setstack and xposix_memalign
are added.
ChangeLog:
[BZ #23403]
* nptl/allocatestack.c (allocate_stack): Align pointer pd for
TLS_TCB_AT_TP tls variant.
* nptl/tst-tls1.c: Migrate to support/test-driver.c.
Add alignment checks.
* support/Makefile (libsupport-routines): Add xposix_memalign and
xpthread_setstack.
* support/support.h: Add xposix_memalign.
* support/xthread.h: Add xpthread_attr_setstack.
* support/xposix_memalign.c: New File.
* support/xpthread_attr_setstack.c: Likewise.
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index 670cb8ffe6..590350647b 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -572,7 +572,9 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
/* Place the thread descriptor at the end of the stack. */
#if TLS_TCB_AT_TP
- pd = (struct pthread *) ((char *) mem + size) - 1;
+ pd = (struct pthread *) ((((uintptr_t) mem + size)
+ - TLS_TCB_SIZE)
+ & ~__static_tls_align_m1);
#elif TLS_DTV_AT_TP
pd = (struct pthread *) ((((uintptr_t) mem + size
- __static_tls_size)
diff --git a/nptl/tst-tls1.c b/nptl/tst-tls1.c
index 00489e23e9..1a915224a7 100644
--- a/nptl/tst-tls1.c
+++ b/nptl/tst-tls1.c
@@ -19,12 +19,16 @@
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
-
+#include <stdint.h>
+#include <inttypes.h>
+#include <support/support.h>
+#include <support/check.h>
+#include <support/xthread.h>
struct test_s
{
- int a;
- int b;
+ __attribute__ ((aligned(0x20))) int a;
+ __attribute__ ((aligned(0x200))) int b;
};
#define INIT_A 1
@@ -36,15 +40,34 @@ __thread struct test_s s __attribute__ ((tls_model ("initial-exec"))) =
.b = INIT_B
};
+/* Use noinline in combination with not static to ensure that the
+ alignment check is really done. Otherwise it was optimized out! */
+__attribute__ ((noinline)) void
+check_alignment (const char *thr_name, const char *ptr_name,
+ int *ptr, int alignment)
+{
+ uintptr_t offset_aligment = ((uintptr_t) ptr) & (alignment - 1);
+ if (offset_aligment)
+ {
+ FAIL_EXIT1 ("%s (%p) is not 0x%x-byte aligned in %s thread\n",
+ ptr_name, ptr, alignment, thr_name);
+ }
+}
+
+static void
+check_s (const char *thr_name)
+{
+ if (s.a != INIT_A || s.b != INIT_B)
+ FAIL_EXIT1 ("initial value of s in %s thread wrong\n", thr_name);
+
+ check_alignment (thr_name, "s.a", &s.a, 0x20);
+ check_alignment (thr_name, "s.b", &s.b, 0x200);
+}
static void *
tf (void *arg)
{
- if (s.a != INIT_A || s.b != INIT_B)
- {
- puts ("initial value of s in child thread wrong");
- exit (1);
- }
+ check_s ("child");
++s.a;
@@ -55,25 +78,14 @@ tf (void *arg)
int
do_test (void)
{
- if (s.a != INIT_A || s.b != INIT_B)
- {
- puts ("initial value of s in main thread wrong");
- exit (1);
- }
+ check_s ("main");
pthread_attr_t a;
- if (pthread_attr_init (&a) != 0)
- {
- puts ("attr_init failed");
- exit (1);
- }
+ xpthread_attr_init (&a);
- if (pthread_attr_setstacksize (&a, 1 * 1024 * 1024) != 0)
- {
- puts ("attr_setstacksize failed");
- return 1;
- }
+#define STACK_SIZE (1 * 1024 * 1024)
+ xpthread_attr_setstacksize (&a, STACK_SIZE);
#define N 10
int i;
@@ -83,29 +95,25 @@ do_test (void)
pthread_t th[M];
int j;
for (j = 0; j < M; ++j, ++s.a)
- if (pthread_create (&th[j], &a, tf, NULL) != 0)
- {
- puts ("pthread_create failed");
- exit (1);
- }
+ th[j] = xpthread_create (&a, tf, NULL);
for (j = 0; j < M; ++j)
- if (pthread_join (th[j], NULL) != 0)
- {
- puts ("pthread_join failed");
- exit (1);
- }
+ xpthread_join (th[j]);
}
- if (pthread_attr_destroy (&a) != 0)
- {
- puts ("attr_destroy failed");
- exit (1);
- }
+ /* Also check the alignment of the tls variables if a misaligned stack is
+ specified. */
+ pthread_t th;
+ void *thr_stack = NULL;
+ thr_stack = xposix_memalign (0x200, STACK_SIZE + 1);
+ xpthread_attr_setstack (&a, thr_stack + 1, STACK_SIZE);
+ th = xpthread_create (&a, tf, NULL);
+ xpthread_join (th);
+ free (thr_stack);
+
+ xpthread_attr_destroy (&a);
return 0;
}
-
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
diff --git a/support/Makefile b/support/Makefile
index c15b93647c..9ff0ec3fff 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -99,10 +99,12 @@ libsupport-routines = \
xopen \
xpipe \
xpoll \
+ xposix_memalign \
xpthread_attr_destroy \
xpthread_attr_init \
xpthread_attr_setdetachstate \
xpthread_attr_setguardsize \
+ xpthread_attr_setstack \
xpthread_attr_setstacksize \
xpthread_barrier_destroy \
xpthread_barrier_init \
diff --git a/support/support.h b/support/support.h
index 119495e5a9..97fef2cd23 100644
--- a/support/support.h
+++ b/support/support.h
@@ -86,6 +86,7 @@ int support_descriptor_supports_holes (int fd);
void *xmalloc (size_t) __attribute__ ((malloc));
void *xcalloc (size_t n, size_t s) __attribute__ ((malloc));
void *xrealloc (void *p, size_t n);
+void *xposix_memalign (size_t alignment, size_t n);
char *xasprintf (const char *format, ...)
__attribute__ ((format (printf, 1, 2), malloc));
char *xstrdup (const char *);
diff --git a/support/xposix_memalign.c b/support/xposix_memalign.c
new file mode 100644
index 0000000000..5501a0846a
--- /dev/null
+++ b/support/xposix_memalign.c
@@ -0,0 +1,35 @@
+/* Error-checking wrapper for posix_memalign.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/support.h>
+#include <stdlib.h>
+#include <errno.h>
+
+void *
+xposix_memalign (size_t alignment, size_t n)
+{
+ void *p = NULL;
+
+ int ret = posix_memalign (&p, alignment, n);
+ if (ret)
+ {
+ errno = ret;
+ oom_error ("posix_memalign", n);
+ }
+ return p;
+}
diff --git a/support/xpthread_attr_setstack.c b/support/xpthread_attr_setstack.c
new file mode 100644
index 0000000000..c3772e240b
--- /dev/null
+++ b/support/xpthread_attr_setstack.c
@@ -0,0 +1,26 @@
+/* pthread_attr_setstack with error checking.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/xthread.h>
+
+void
+xpthread_attr_setstack (pthread_attr_t *attr, void *stackaddr, size_t stacksize)
+{
+ xpthread_check_return ("pthread_attr_setstack",
+ pthread_attr_setstack (attr, stackaddr, stacksize));
+}
diff --git a/support/xthread.h b/support/xthread.h
index 9fe1f68b3b..5204f78ed2 100644
--- a/support/xthread.h
+++ b/support/xthread.h
@@ -68,6 +68,8 @@ void xpthread_attr_destroy (pthread_attr_t *attr);
void xpthread_attr_init (pthread_attr_t *attr);
void xpthread_attr_setdetachstate (pthread_attr_t *attr,
int detachstate);
+void xpthread_attr_setstack (pthread_attr_t *attr, void *stackaddr,
+ size_t stacksize);
void xpthread_attr_setstacksize (pthread_attr_t *attr,
size_t stacksize);
void xpthread_attr_setguardsize (pthread_attr_t *attr,

View File

@ -0,0 +1,192 @@
commit cb89ba9c72f66327f5d66034681eb1d46eedf96f
Author: DJ Delorie <dj@redhat.com>
Date: Thu Aug 8 19:09:43 2019 -0400
Add glibc.malloc.mxfast tunable
* elf/dl-tunables.list: Add glibc.malloc.mxfast.
* manual/tunables.texi: Document it.
* malloc/malloc.c (do_set_mxfast): New.
(__libc_mallopt): Call it.
* malloc/arena.c: Add mxfast tunable.
* malloc/tst-mxfast.c: New.
* malloc/Makefile: Add it.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit c48d92b430c480de06762f80c104922239416826)
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 1f8ecb8437a0460f..1ff6fcb6f24f93a8 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -85,6 +85,11 @@ glibc {
tcache_unsorted_limit {
type: SIZE_T
}
+ mxfast {
+ type: SIZE_T
+ minval: 0
+ security_level: SXID_IGNORE
+ }
}
tune {
hwcap_mask {
diff --git a/malloc/Makefile b/malloc/Makefile
index 228a1279a5960d8c..bf9a53cb7c5ebacb 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -39,6 +39,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tst-malloc-too-large \
tst-malloc-stats-cancellation \
tst-tcfree1 tst-tcfree2 tst-tcfree3 \
+ tst-mxfast \
tests-static := \
tst-interpose-static-nothread \
@@ -196,6 +197,8 @@ tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV)
tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3
tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV)
+tst-mxfast-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=0:glibc.malloc.mxfast=0
+
ifeq ($(experimental-malloc),yes)
CPPFLAGS-malloc.c += -DUSE_TCACHE=1
else
diff --git a/malloc/arena.c b/malloc/arena.c
index ff8fd5d2a7e51ac8..f5c7ad4570ad6186 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -237,6 +237,7 @@ TUNABLE_CALLBACK_FNDECL (set_tcache_max, size_t)
TUNABLE_CALLBACK_FNDECL (set_tcache_count, size_t)
TUNABLE_CALLBACK_FNDECL (set_tcache_unsorted_limit, size_t)
#endif
+TUNABLE_CALLBACK_FNDECL (set_mxfast, size_t)
#else
/* Initialization routine. */
#include <string.h>
@@ -324,6 +325,7 @@ ptmalloc_init (void)
TUNABLE_GET (tcache_unsorted_limit, size_t,
TUNABLE_CALLBACK (set_tcache_unsorted_limit));
# endif
+ TUNABLE_GET (mxfast, size_t, TUNABLE_CALLBACK (set_mxfast));
#else
const char *s = NULL;
if (__glibc_likely (_environ != NULL))
diff --git a/malloc/malloc.c b/malloc/malloc.c
index fcf480acdaea1b86..9756ed0a0d28c5f6 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -5142,6 +5142,19 @@ do_set_tcache_unsorted_limit (size_t value)
}
#endif
+static inline int
+__always_inline
+do_set_mxfast (size_t value)
+{
+ if (value >= 0 && value <= MAX_FAST_SIZE)
+ {
+ LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
+ set_max_fast (value);
+ return 1;
+ }
+ return 0;
+}
+
int
__libc_mallopt (int param_number, int value)
{
@@ -5161,13 +5174,7 @@ __libc_mallopt (int param_number, int value)
switch (param_number)
{
case M_MXFAST:
- if (value >= 0 && value <= MAX_FAST_SIZE)
- {
- LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
- set_max_fast (value);
- }
- else
- res = 0;
+ do_set_mxfast (value);
break;
case M_TRIM_THRESHOLD:
diff --git a/malloc/tst-mxfast.c b/malloc/tst-mxfast.c
new file mode 100644
index 0000000000000000..7a371d2f9d2f0005
--- /dev/null
+++ b/malloc/tst-mxfast.c
@@ -0,0 +1,50 @@
+/* Test that glibc.malloc.mxfast tunable works.
+ Copyright (C) 2018, 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* This test verifies that setting the glibc.malloc.mxfast tunable to
+ zero results in free'd blocks being returned to the small bins, not
+ the fast bins. */
+
+#include <malloc.h>
+#include <assert.h>
+
+int
+do_test(void)
+{
+ struct mallinfo m;
+ char * volatile p1;
+ char * volatile p2;
+
+ /* Arbitrary value; must be in default fastbin range. */
+ p1 = malloc (3);
+ /* Something large so that p1 isn't a "top block" */
+ p2 = malloc (512);
+ free (p1);
+
+ m = mallinfo();
+
+ /* This will fail if there are any blocks in the fastbins. */
+ assert (m.smblks == 0);
+
+ /* To keep gcc happy. */
+ free (p2);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/manual/tunables.texi b/manual/tunables.texi
index f6c49250e3889ddd..3dc6f9a44592c030 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -213,6 +213,18 @@ pre-fill the per-thread cache with. The default, or when set to zero,
is no limit.
@end deftp
+@deftp Tunable glibc.malloc.mxfast
+One of the optimizations malloc uses is to maintain a series of ``fast
+bins'' that hold chunks up to a specific size. The default and
+maximum size which may be held this way is 80 bytes on 32-bit systems
+or 160 bytes on 64-bit systems. Applications which value size over
+speed may choose to reduce the size of requests which are serviced
+from fast bins with this tunable. Note that the value specified
+includes malloc's internal overhead, which is normally the size of one
+pointer, so add 4 on 32-bit systems or 8 on 64-bit systems to the size
+passed to @code{malloc} for the largest bin size to enable.
+@end deftp
+
@node Elision Tunables
@section Elision Tunables
@cindex elision tunables

View File

@ -0,0 +1,72 @@
commit 5dab5eafb3dc2f72aaab911084d127d1af45a08c
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 15 11:37:18 2019 +0200
malloc: Various cleanups for malloc/tst-mxfast
(cherry picked from commit f9769a239784772453d595bc2f4bed8739810e06)
diff --git a/malloc/Makefile b/malloc/Makefile
index bf9a53cb7c5ebacb..19c2a846ed8ce049 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -39,7 +39,6 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tst-malloc-too-large \
tst-malloc-stats-cancellation \
tst-tcfree1 tst-tcfree2 tst-tcfree3 \
- tst-mxfast \
tests-static := \
tst-interpose-static-nothread \
@@ -55,7 +54,7 @@ tests-internal += \
tst-dynarray-at-fail \
ifneq (no,$(have-tunables))
-tests += tst-malloc-usable-tunables
+tests += tst-malloc-usable-tunables tst-mxfast
tests-static += tst-malloc-usable-static-tunables
endif
diff --git a/malloc/tst-mxfast.c b/malloc/tst-mxfast.c
index 7a371d2f9d2f0005..7a7750bc71024bfb 100644
--- a/malloc/tst-mxfast.c
+++ b/malloc/tst-mxfast.c
@@ -1,5 +1,5 @@
/* Test that glibc.malloc.mxfast tunable works.
- Copyright (C) 2018, 2019 Free Software Foundation, Inc.
+ Copyright (C) 2019 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
@@ -21,14 +21,14 @@
the fast bins. */
#include <malloc.h>
-#include <assert.h>
+#include <support/check.h>
int
-do_test(void)
+do_test (void)
{
struct mallinfo m;
- char * volatile p1;
- char * volatile p2;
+ char *volatile p1;
+ char *volatile p2;
/* Arbitrary value; must be in default fastbin range. */
p1 = malloc (3);
@@ -36,10 +36,10 @@ do_test(void)
p2 = malloc (512);
free (p1);
- m = mallinfo();
+ m = mallinfo ();
/* This will fail if there are any blocks in the fastbins. */
- assert (m.smblks == 0);
+ TEST_COMPARE (m.smblks, 0);
/* To keep gcc happy. */
free (p2);

View File

@ -0,0 +1,31 @@
commit f144981490bd2ab13189d85902ca74beecb307e4
Author: DJ Delorie <dj@redhat.com>
Date: Wed Oct 30 18:03:14 2019 -0400
Base max_fast on alignment, not width, of bins (Bug 24903)
set_max_fast sets the "impossibly small" value based on,
eventually, MALLOC_ALIGNMENT. The comparisons for the smallest
chunk used is, eventually, MIN_CHUNK_SIZE. Note that i386
is the only platform where these are the same, so a smallest
chunk *would* be put in a no-fastbins fastbin.
This change calculates the "impossibly small" value
based on MIN_CHUNK_SIZE instead, so that we can know it will
always be impossibly small.
(cherry picked from commit ff12e0fb91b9072800f031cb21fb2651ee7b6251)
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 9756ed0a0d28c5f6..90825b2aaed53761 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1635,7 +1635,7 @@ static INTERNAL_SIZE_T global_max_fast;
#define set_max_fast(s) \
global_max_fast = (((s) == 0) \
- ? SMALLBIN_WIDTH : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
+ ? MIN_CHUNK_SIZE / 2 : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
static inline INTERNAL_SIZE_T
get_max_fast (void)

View File

@ -0,0 +1,142 @@
commit 2c75b545de6fe3c44138799c68217a94bc669a88
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Jun 18 16:42:10 2019 +0200
elf: Refuse to dlopen PIE objects [BZ #24323]
Another executable has already been mapped, so the dynamic linker
cannot perform relocations correctly for the second executable.
diff --git a/elf/Makefile b/elf/Makefile
index 08e2f99..27a2fa8 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -310,7 +310,7 @@ test-xfail-tst-protected1b = yes
endif
ifeq (yesyes,$(have-fpie)$(build-shared))
modules-names += tst-piemod1
-tests += tst-pie1 tst-pie2
+tests += tst-pie1 tst-pie2 tst-dlopen-pie
tests-pie += tst-pie1 tst-pie2
ifeq (yes,$(have-protected-data))
tests += vismain
@@ -1084,6 +1084,8 @@ CFLAGS-tst-pie2.c += $(pie-ccflag)
$(objpfx)tst-piemod1.so: $(libsupport)
$(objpfx)tst-pie1: $(objpfx)tst-piemod1.so
+$(objpfx)tst-dlopen-pie: $(libdl)
+$(objpfx)tst-dlopen-pie.out: $(objpfx)tst-pie1
ifeq (yes,$(build-shared))
# NB: Please keep cet-built-dso in sysdeps/x86/Makefile in sync with
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 2bbef81..5abeb86 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1158,6 +1158,10 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
goto call_lose;
}
+ /* dlopen of an executable is not valid because it is not possible
+ to perform proper relocations, handle static TLS, or run the
+ ELF constructors. For PIE, the check needs the dynamic
+ section, so there is another check below. */
if (__glibc_unlikely (type != ET_DYN)
&& __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0))
{
@@ -1194,9 +1198,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
elf_get_dynamic_info (l, NULL);
/* Make sure we are not dlopen'ing an object that has the
- DF_1_NOOPEN flag set. */
- if (__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN)
- && (mode & __RTLD_DLOPEN))
+ DF_1_NOOPEN flag set, or a PIE object. */
+ if ((__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN)
+ && (mode & __RTLD_DLOPEN))
+ || (__glibc_unlikely (l->l_flags_1 & DF_1_PIE)
+ && __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0)))
{
/* We are not supposed to load this object. Free all resources. */
_dl_unmap_segments (l);
@@ -1207,7 +1213,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (l->l_phdr_allocated)
free ((void *) l->l_phdr);
- errstring = N_("shared object cannot be dlopen()ed");
+ if (l->l_flags_1 & DF_1_PIE)
+ errstring
+ = N_("cannot dynamically load position-independent executable");
+ else
+ errstring = N_("shared object cannot be dlopen()ed");
goto call_lose;
}
diff --git a/elf/tst-dlopen-pie.c b/elf/tst-dlopen-pie.c
new file mode 100644
index 0000000..6a41c73
--- /dev/null
+++ b/elf/tst-dlopen-pie.c
@@ -0,0 +1,49 @@
+/* dlopen test for PIE objects.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* This test attempts to open the (otherwise unrelated) PIE test
+ program elf/tst-pie1 and expects the attempt to fail. */
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static void
+test_mode (int mode)
+{
+ char *pie_path = xasprintf ("%s/elf/tst-pie1", support_objdir_root);
+ if (dlopen (pie_path, mode) != NULL)
+ FAIL_EXIT1 ("dlopen succeeded unexpectedly (%d)", mode);
+ const char *message = dlerror ();
+ const char *expected
+ = "cannot dynamically load position-independent executable";
+ if (strstr (message, expected) == NULL)
+ FAIL_EXIT1 ("unexpected error message (mode %d): %s", mode, message);
+}
+
+static int
+do_test (void)
+{
+ test_mode (RTLD_LAZY);
+ test_mode (RTLD_NOW);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/include/elf.h b/include/elf.h
index ab76aaf..14ed67f 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -23,7 +23,7 @@
# endif
# define DT_1_SUPPORTED_MASK \
(DF_1_NOW | DF_1_NODELETE | DF_1_INITFIRST | DF_1_NOOPEN \
- | DF_1_ORIGIN | DF_1_NODEFLIB)
+ | DF_1_ORIGIN | DF_1_NODEFLIB | DF_1_PIE)
#endif /* !_ISOMAC */
#endif /* elf.h */

View File

@ -0,0 +1,82 @@
commit 6c29942cbf059aca47fd4bbd852ea42c9d46b71f
Author: Stefan Liebler <stli@linux.ibm.com>
Date: Mon Feb 18 16:12:01 2019 +0100
misc/tst-clone3: Fix waiting for exited thread.
From time to time the test misc/tst-clone3 fails with a timeout.
Then futex_wait is blocking. Usually ctid should be set to zero
due to CLONE_CHILD_CLEARTID and the futex should be waken up.
But the fail occures if the thread has already exited before
ctid is set to the return value of clone(). Then futex_wait() will
block as there will be nobody who wakes the futex up again.
This patch initializes ctid to a known value before calling clone
and the kernel is the only one who updates the value to zero after clone.
If futex_wait is called then it is either waked up due to the exited thread
or the futex syscall fails as *ctid_ptr is already zero instead of the
specified value 1.
ChangeLog:
* sysdeps/unix/sysv/linux/tst-clone3.c (do_test):
Initialize ctid with a known value and remove update of ctid
after clone.
(wait_tid): Adjust arguments and call futex_wait with ctid_val
as assumed current value of ctid_ptr.
diff --git a/sysdeps/unix/sysv/linux/tst-clone3.c b/sysdeps/unix/sysv/linux/tst-clone3.c
index 784ce18f5343ec72..9f1ed6355e7acffd 100644
--- a/sysdeps/unix/sysv/linux/tst-clone3.c
+++ b/sysdeps/unix/sysv/linux/tst-clone3.c
@@ -27,6 +27,7 @@
#include <stackinfo.h> /* For _STACK_GROWS_{UP,DOWN}. */
#include <support/check.h>
+#include <stdatomic.h>
/* Test if clone call with CLONE_THREAD does not call exit_group. The 'f'
function returns '1', which will be used by clone thread to call the
@@ -42,11 +43,14 @@ f (void *a)
/* Futex wait for TID argument, similar to pthread_join internal
implementation. */
-#define wait_tid(tid) \
- do { \
- __typeof (tid) __tid; \
- while ((__tid = (tid)) != 0) \
- futex_wait (&(tid), __tid); \
+#define wait_tid(ctid_ptr, ctid_val) \
+ do { \
+ __typeof (*(ctid_ptr)) __tid; \
+ /* We need acquire MO here so that we synchronize with the \
+ kernel's store to 0 when the clone terminates. */ \
+ while ((__tid = atomic_load_explicit (ctid_ptr, \
+ memory_order_acquire)) != 0) \
+ futex_wait (ctid_ptr, ctid_val); \
} while (0)
static inline int
@@ -64,7 +68,11 @@ do_test (void)
clone_flags |= CLONE_VM | CLONE_SIGHAND;
/* We will used ctid to call on futex to wait for thread exit. */
clone_flags |= CLONE_CHILD_CLEARTID;
- pid_t ctid, tid;
+ /* Initialize with a known value. ctid is set to zero by the kernel after the
+ cloned thread has exited. */
+#define CTID_INIT_VAL 1
+ pid_t ctid = CTID_INIT_VAL;
+ pid_t tid;
#ifdef __ia64__
extern int __clone2 (int (*__fn) (void *__arg), void *__child_stack_base,
@@ -86,8 +94,7 @@ do_test (void)
if (tid == -1)
FAIL_EXIT1 ("clone failed: %m");
- ctid = tid;
- wait_tid (ctid);
+ wait_tid (&ctid, CTID_INIT_VAL);
return 2;
}

View File

@ -0,0 +1,159 @@
commit 481c30cb9573a280649fbf27251e6a0f4af1b2b1
Author: Alexandra Hájková <ahajkova@redhat.com>
Date: Thu May 9 13:51:40 2019 +0200
elf: Add tst-ldconfig-bad-aux-cache test [BZ #18093]
This test corrupts /var/cache/ldconfig/aux-cache and executes ldconfig
to check it will not segfault using the corrupted aux_cache. The test
uses the test-in-container framework. Verified no regressions on
x86_64.
diff --git a/elf/Makefile b/elf/Makefile
index 139d072e136284e1..8e907e69eb35e089 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -156,6 +156,9 @@ tests-static-internal := tst-tls1-static tst-tls2-static \
CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
tst-tls1-static-non-pie-no-pie = yes
+tests-container = \
+ tst-ldconfig-bad-aux-cache
+
tests := tst-tls9 tst-leaks1 \
tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \
tst-auxv
diff --git a/elf/tst-ldconfig-bad-aux-cache.c b/elf/tst-ldconfig-bad-aux-cache.c
new file mode 100644
index 0000000000000000..68ce90a95648f6ab
--- /dev/null
+++ b/elf/tst-ldconfig-bad-aux-cache.c
@@ -0,0 +1,117 @@
+/* Test ldconfig does not segfault when aux-cache is corrupted (Bug 18093).
+ Copyright (C) 2019 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; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+/* This test does the following:
+ Run ldconfig to create the caches.
+ Corrupt the caches.
+ Run ldconfig again.
+ At each step we verify that ldconfig does not crash. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <ftw.h>
+#include <stdint.h>
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+
+#include <dirent.h>
+
+static int
+display_info (const char *fpath, const struct stat *sb,
+ int tflag, struct FTW *ftwbuf)
+{
+ printf ("info: %-3s %2d %7jd %-40s %d %s\n",
+ (tflag == FTW_D) ? "d" : (tflag == FTW_DNR) ? "dnr" :
+ (tflag == FTW_DP) ? "dp" : (tflag == FTW_F) ? "f" :
+ (tflag == FTW_NS) ? "ns" : (tflag == FTW_SL) ? "sl" :
+ (tflag == FTW_SLN) ? "sln" : "???",
+ ftwbuf->level, (intmax_t) sb->st_size,
+ fpath, ftwbuf->base, fpath + ftwbuf->base);
+ /* To tell nftw to continue. */
+ return 0;
+}
+
+/* Run ldconfig with a corrupt aux-cache, in particular we test for size
+ truncation that might happen if a previous ldconfig run failed or if
+ there were storage or power issues while we were writing the file.
+ We want ldconfig not to crash, and it should be able to do so by
+ computing the expected size of the file (bug 18093). */
+static int
+do_test (void)
+{
+ char *prog = xasprintf ("%s/ldconfig", support_install_rootsbindir);
+ char *const args[] = { prog, NULL };
+ const char *path = "/var/cache/ldconfig/aux-cache";
+ struct stat64 fs;
+ long int size, new_size, i;
+ int status;
+ pid_t pid;
+
+ /* Create the needed directories. */
+ xmkdirp ("/var/cache/ldconfig", 0777);
+
+ pid = xfork ();
+ /* Run ldconfig fist to generate the aux-cache. */
+ if (pid == 0)
+ {
+ execv (args[0], args);
+ _exit (1);
+ }
+ else
+ {
+ xwaitpid (pid, &status, 0);
+ TEST_COMPARE(status, 0);
+ xstat (path, &fs);
+
+ size = fs.st_size;
+ /* Run 3 tests, each truncating aux-cache shorter and shorter. */
+ for (i = 3; i > 0; i--)
+ {
+ new_size = size * i / 4;
+ if (truncate (path, new_size))
+ FAIL_EXIT1 ("truncation failed: %m");
+ if (nftw (path, display_info, 1000, 0) == -1)
+ FAIL_EXIT1 ("nftw failed.");
+
+ pid = xfork ();
+ /* Verify that ldconfig can run with a truncated
+ aux-cache and doesn't crash. */
+ if (pid == 0)
+ {
+ execv (args[0], args);
+ _exit (1);
+ }
+ else
+ {
+ xwaitpid (pid, &status, 0);
+ TEST_COMPARE(status, 0);
+ }
+ }
+ }
+
+ free (prog);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ldconfig-bad-aux-cache.root/etc/ld.so.conf b/elf/tst-ldconfig-bad-aux-cache.root/etc/ld.so.conf
new file mode 100644
index 0000000000000000..e1e74dbda2bf3dfa
--- /dev/null
+++ b/elf/tst-ldconfig-bad-aux-cache.root/etc/ld.so.conf
@@ -0,0 +1,2 @@
+# This file was created to suppress a warning from ldconfig:
+# /sbin/ldconfig: Warning: ignoring configuration file that cannot be opened: /etc/ld.so.conf: No such file or directory
diff --git a/elf/tst-ldconfig-bad-aux-cache.root/postclean.req b/elf/tst-ldconfig-bad-aux-cache.root/postclean.req
new file mode 100644
index 0000000000000000..e69de29bb2d1d643

View File

@ -0,0 +1,112 @@
commit a6c1ce778e5c05a2e6925883b410157ef47654fd
Author: Alexandra Hájková <ahajkova@redhat.com>
Date: Mon Aug 5 13:18:57 2019 +0200
elf: tst-ldconfig-bad-aux-cache: use support_capture_subprocess
diff --git a/elf/tst-ldconfig-bad-aux-cache.c b/elf/tst-ldconfig-bad-aux-cache.c
index 68ce90a95648f6ab..6e22ff815eaaa817 100644
--- a/elf/tst-ldconfig-bad-aux-cache.c
+++ b/elf/tst-ldconfig-bad-aux-cache.c
@@ -31,6 +31,7 @@
#include <ftw.h>
#include <stdint.h>
+#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xunistd.h>
@@ -52,6 +53,15 @@ display_info (const char *fpath, const struct stat *sb,
return 0;
}
+static void
+execv_wrapper (void *args)
+{
+ char **argv = args;
+
+ execv (argv[0], argv);
+ FAIL_EXIT1 ("execv: %m");
+}
+
/* Run ldconfig with a corrupt aux-cache, in particular we test for size
truncation that might happen if a previous ldconfig run failed or if
there were storage or power issues while we were writing the file.
@@ -61,53 +71,38 @@ static int
do_test (void)
{
char *prog = xasprintf ("%s/ldconfig", support_install_rootsbindir);
- char *const args[] = { prog, NULL };
+ char *args[] = { prog, NULL };
const char *path = "/var/cache/ldconfig/aux-cache";
struct stat64 fs;
long int size, new_size, i;
- int status;
- pid_t pid;
/* Create the needed directories. */
xmkdirp ("/var/cache/ldconfig", 0777);
- pid = xfork ();
- /* Run ldconfig fist to generate the aux-cache. */
- if (pid == 0)
- {
- execv (args[0], args);
- _exit (1);
- }
- else
+ /* Run ldconfig first to generate the aux-cache. */
+ struct support_capture_subprocess result;
+ result = support_capture_subprocess (execv_wrapper, args);
+ support_capture_subprocess_check (&result, "execv", 0, sc_allow_none);
+ support_capture_subprocess_free (&result);
+
+ xstat (path, &fs);
+
+ size = fs.st_size;
+ /* Run 3 tests, each truncating aux-cache shorter and shorter. */
+ for (i = 3; i > 0; i--)
{
- xwaitpid (pid, &status, 0);
- TEST_COMPARE(status, 0);
- xstat (path, &fs);
-
- size = fs.st_size;
- /* Run 3 tests, each truncating aux-cache shorter and shorter. */
- for (i = 3; i > 0; i--)
- {
- new_size = size * i / 4;
- if (truncate (path, new_size))
- FAIL_EXIT1 ("truncation failed: %m");
- if (nftw (path, display_info, 1000, 0) == -1)
- FAIL_EXIT1 ("nftw failed.");
-
- pid = xfork ();
- /* Verify that ldconfig can run with a truncated
- aux-cache and doesn't crash. */
- if (pid == 0)
- {
- execv (args[0], args);
- _exit (1);
- }
- else
- {
- xwaitpid (pid, &status, 0);
- TEST_COMPARE(status, 0);
- }
- }
+ new_size = size * i / 4;
+ if (truncate (path, new_size))
+ FAIL_EXIT1 ("truncation failed: %m");
+ if (nftw (path, display_info, 1000, 0) == -1)
+ FAIL_EXIT1 ("nftw failed.");
+
+ /* Verify that ldconfig can run with a truncated
+ aux-cache and doesn't crash. */
+ struct support_capture_subprocess result;
+ result = support_capture_subprocess (execv_wrapper, args);
+ support_capture_subprocess_check (&result, "execv", 0, sc_allow_none);
+ support_capture_subprocess_free (&result);
}
free (prog);

View File

@ -0,0 +1,49 @@
commit 17432d7150bdab3bce2ea66c70ad6c920f54077a
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Jun 28 10:15:30 2019 +0200
support: Add xdlvsym function
diff --git a/support/xdlfcn.c b/support/xdlfcn.c
index f34bb059c00f27f7..b4a6b85649d181c8 100644
--- a/support/xdlfcn.c
+++ b/support/xdlfcn.c
@@ -48,6 +48,26 @@ xdlsym (void *handle, const char *symbol)
return sym;
}
+void *
+xdlvsym (void *handle, const char *symbol, const char *version)
+{
+ /* Clear any pending errors. */
+ dlerror ();
+
+ void *sym = dlvsym (handle, symbol, version);
+
+ if (sym == NULL)
+ {
+ const char *error = dlerror ();
+ if (error != NULL)
+ FAIL_EXIT1 ("error: dlvsym: %s\n", error);
+ /* If there was no error, we found a NULL symbol. Return the
+ NULL value in this case. */
+ }
+
+ return sym;
+}
+
void
xdlclose (void *handle)
{
diff --git a/support/xdlfcn.h b/support/xdlfcn.h
index 5ab7494e70924f52..ab1cbb3cb9bb1cc7 100644
--- a/support/xdlfcn.h
+++ b/support/xdlfcn.h
@@ -26,6 +26,7 @@ __BEGIN_DECLS
/* Each of these terminates process on failure with relevant error message. */
void *xdlopen (const char *filename, int flags);
void *xdlsym (void *handle, const char *symbol);
+void *xdlvsym (void *handle, const char *symbol, const char *version);
void xdlclose (void *handle);

View File

@ -0,0 +1,342 @@
commit f0b2132b35248c1f4a80f62a2c38cddcc802aa8c
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Jun 28 10:12:50 2019 +0200
ld.so: Support moving versioned symbols between sonames [BZ #24741]
This change should be fully backwards-compatible because the old
code aborted the load if a soname mismatch was encountered
(instead of searching further for a matching symbol). This means
that no different symbols are found.
The soname check was explicitly disabled for the skip_map != NULL
case. However, this only happens with dl(v)sym and RTLD_NEXT,
and those lookups do not come with a verneed entry that could be used
for the check.
The error check was already explicitly disabled for the skip_map !=
NULL case, that is, when dl(v)sym was called with RTLD_NEXT. But
_dl_vsym always sets filename in the struct r_found_version argument
to NULL, so the check was not active anyway. This means that
symbol lookup results for the skip_map != NULL case do not change,
either.
Conflicts:
elf/Makefile
(usual missing backports)
diff --git a/elf/Makefile b/elf/Makefile
index 29aa3a96738e4176..73f9e25ea5efd63a 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -187,7 +187,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \
tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
- tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note
+ tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
+ tst-sonamemove-link tst-sonamemove-dlopen
# reldep9
tests-internal += loadtest unload unload2 circleload1 \
neededtest neededtest2 neededtest3 neededtest4 \
@@ -275,7 +276,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-latepthreadmod $(tst-tls-many-dynamic-modules) \
tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
- tst-absolute-zero-lib tst-big-note-lib
+ tst-absolute-zero-lib tst-big-note-lib \
+ tst-sonamemove-linkmod1 \
+ tst-sonamemove-runmod1 tst-sonamemove-runmod2
ifeq (yes,$(have-mtls-dialect-gnu2))
tests += tst-gnu2-tls1
@@ -1374,6 +1377,28 @@ tst-audit12-ENV = LD_AUDIT=$(objpfx)tst-auditmod12.so
$(objpfx)tst-audit12mod1.so: $(objpfx)tst-audit12mod2.so
LDFLAGS-tst-audit12mod2.so = -Wl,--version-script=tst-audit12mod2.map
+# tst-sonamemove links against an older implementation of the library.
+LDFLAGS-tst-sonamemove-linkmod1.so = \
+ -Wl,--version-script=tst-sonamemove-linkmod1.map \
+ -Wl,-soname,tst-sonamemove-runmod1.so
+LDFLAGS-tst-sonamemove-runmod1.so = -Wl,--no-as-needed \
+ -Wl,--version-script=tst-sonamemove-runmod1.map \
+ -Wl,-soname,tst-sonamemove-runmod1.so
+LDFLAGS-tst-sonamemove-runmod2.so = \
+ -Wl,--version-script=tst-sonamemove-runmod2.map \
+ -Wl,-soname,tst-sonamemove-runmod2.so
+$(objpfx)tst-sonamemove-runmod1.so: $(objpfx)tst-sonamemove-runmod2.so
+# Link against the link module, but depend on the run-time modules
+# for execution.
+$(objpfx)tst-sonamemove-link: $(objpfx)tst-sonamemove-linkmod1.so
+$(objpfx)tst-sonamemove-link.out: \
+ $(objpfx)tst-sonamemove-runmod1.so \
+ $(objpfx)tst-sonamemove-runmod2.so
+$(objpfx)tst-sonamemove-dlopen: $(libdl)
+$(objpfx)tst-sonamemove-dlopen.out: \
+ $(objpfx)tst-sonamemove-runmod1.so \
+ $(objpfx)tst-sonamemove-runmod2.so
+
# Override -z defs, so that we can reference an undefined symbol.
# Force lazy binding for the same reason.
LDFLAGS-tst-latepthreadmod.so = \
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 68ecc6179f608547..1d046caf017b582b 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -536,11 +536,7 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
}
skip:
- /* If this current map is the one mentioned in the verneed entry
- and we have not found a weak entry, it is a bug. */
- if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
- && __glibc_unlikely (_dl_name_match_p (version->filename, map)))
- return -1;
+ ;
}
while (++i < n);
@@ -810,34 +806,10 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
/* Search the relevant loaded objects for a definition. */
for (size_t start = i; *scope != NULL; start = 0, ++scope)
- {
- int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref,
- &current_value, *scope, start, version, flags,
- skip_map, type_class, undef_map);
- if (res > 0)
- break;
-
- if (__glibc_unlikely (res < 0) && skip_map == NULL)
- {
- /* Oh, oh. The file named in the relocation entry does not
- contain the needed symbol. This code is never reached
- for unversioned lookups. */
- assert (version != NULL);
- const char *reference_name = undef_map ? undef_map->l_name : "";
- struct dl_exception exception;
- /* XXX We cannot translate the message. */
- _dl_exception_create_format
- (&exception, DSO_FILENAME (reference_name),
- "symbol %s version %s not defined in file %s"
- " with link time reference%s",
- undef_name, version->name, version->filename,
- res == -2 ? " (no version symbols)" : "");
- _dl_signal_cexception (0, &exception, N_("relocation error"));
- _dl_exception_free (&exception);
- *ref = NULL;
- return 0;
- }
- }
+ if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
+ &current_value, *scope, start, version, flags,
+ skip_map, type_class, undef_map) != 0)
+ break;
if (__glibc_unlikely (current_value.s == NULL))
{
diff --git a/elf/tst-sonamemove-dlopen.c b/elf/tst-sonamemove-dlopen.c
new file mode 100644
index 0000000000000000..c496705044cdd53c
--- /dev/null
+++ b/elf/tst-sonamemove-dlopen.c
@@ -0,0 +1,35 @@
+/* Check that a moved versioned symbol can be found using dlsym, dlvsym.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <stddef.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static int
+do_test (void)
+{
+ /* tst-sonamemove-runmod1.so does not define moved_function, but it
+ depends on tst-sonamemove-runmod2.so, which does. */
+ void *handle = xdlopen ("tst-sonamemove-runmod1.so", RTLD_NOW);
+ TEST_VERIFY (xdlsym (handle, "moved_function") != NULL);
+ TEST_VERIFY (xdlvsym (handle, "moved_function", "SONAME_MOVE") != NULL);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-sonamemove-link.c b/elf/tst-sonamemove-link.c
new file mode 100644
index 0000000000000000..4bc3bf32f88f97a9
--- /dev/null
+++ b/elf/tst-sonamemove-link.c
@@ -0,0 +1,41 @@
+/* Check that a versioned symbol can move from one library to another.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* At link time, moved_function is bound to the symbol version
+ SONAME_MOVE in tst-sonamemove-runmod1.so, using the
+ tst-sonamemove-linkmod1.so stub object.
+
+ At run time, the process loads the real tst-sonamemove-runmod1.so,
+ which depends on tst-sonamemove-runmod2.so.
+ tst-sonamemove-runmod1.so does not define moved_function, but
+ tst-sonamemove-runmod2.so does.
+
+ The net effect is that the versioned symbol
+ moved_function@SONAME_MOVE moved from the soname
+ tst-sonamemove-linkmod1.so at link time to the soname
+ tst-sonamemove-linkmod2.so at run time. */
+void moved_function (void);
+
+static int
+do_test (void)
+{
+ moved_function ();
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-sonamemove-linkmod1.c b/elf/tst-sonamemove-linkmod1.c
new file mode 100644
index 0000000000000000..b8a354e5e394f566
--- /dev/null
+++ b/elf/tst-sonamemove-linkmod1.c
@@ -0,0 +1,25 @@
+/* Link interface for (lack of) soname matching in versioned symbol refs.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* This function moved from tst-sonamemove-runmod1.so. This module is
+ intended for linking only, to simulate an old application which was
+ linked against an older version of the library. */
+void
+moved_function (void)
+{
+}
diff --git a/elf/tst-sonamemove-linkmod1.map b/elf/tst-sonamemove-linkmod1.map
new file mode 100644
index 0000000000000000..8fe5904018972009
--- /dev/null
+++ b/elf/tst-sonamemove-linkmod1.map
@@ -0,0 +1,3 @@
+SONAME_MOVE {
+ global: moved_function;
+};
diff --git a/elf/tst-sonamemove-runmod1.c b/elf/tst-sonamemove-runmod1.c
new file mode 100644
index 0000000000000000..5c409e22898bc836
--- /dev/null
+++ b/elf/tst-sonamemove-runmod1.c
@@ -0,0 +1,23 @@
+/* Run-time module whose moved_function moved to a library dependency.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* Dummy function to add the required symbol version. */
+void
+other_function (void)
+{
+}
diff --git a/elf/tst-sonamemove-runmod1.map b/elf/tst-sonamemove-runmod1.map
new file mode 100644
index 0000000000000000..2ea81c6e6ffae2be
--- /dev/null
+++ b/elf/tst-sonamemove-runmod1.map
@@ -0,0 +1,3 @@
+SONAME_MOVE {
+ global: other_function;
+};
diff --git a/elf/tst-sonamemove-runmod2.c b/elf/tst-sonamemove-runmod2.c
new file mode 100644
index 0000000000000000..b5e482eff57d7d83
--- /dev/null
+++ b/elf/tst-sonamemove-runmod2.c
@@ -0,0 +1,24 @@
+/* Run-time module with the actual implementation of moved_function.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* In the test scenario, this function was originally in
+ tst-sonamemove-runmod1.so. */
+void
+moved_function (void)
+{
+}
diff --git a/elf/tst-sonamemove-runmod2.map b/elf/tst-sonamemove-runmod2.map
new file mode 100644
index 0000000000000000..8fe5904018972009
--- /dev/null
+++ b/elf/tst-sonamemove-runmod2.map
@@ -0,0 +1,3 @@
+SONAME_MOVE {
+ global: moved_function;
+};

View File

@ -0,0 +1,40 @@
commit 47ad5e1a2a3ab8eeda491454cbef3b1c5239dc02
Author: Joseph Myers <joseph@codesourcery.com>
Date: Tue Jan 1 02:01:02 2019 +0000
Update syscall-names.list for Linux 4.20.
This patch updates sysdeps/unix/sysv/linux/syscall-names.list for
Linux 4.20. Although there are no new syscalls, the
riscv_flush_icache syscall has moved to asm/unistd.h (previously in
asm/syscalls.h) and so now needs to be added to the list.
Tested with build-many-glibcs.py.
* sysdeps/unix/sysv/linux/syscall-names.list: Update kernel
version to 4.20.
(riscv_flush_icache): New syscall.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index 698069a52d..b650dc07cc 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -22,8 +22,8 @@
# names are only used if the installed kernel headers also provide
# them.
-# The list of system calls is current as of Linux 4.19.
-kernel 4.19
+# The list of system calls is current as of Linux 4.20.
+kernel 4.20
FAST_atomic_update
FAST_cmpxchg
@@ -431,6 +431,7 @@ renameat
renameat2
request_key
restart_syscall
+riscv_flush_icache
rmdir
rseq
rt_sigaction

View File

@ -0,0 +1,43 @@
commit 477e739b324349df854209117047779ac3142130
Author: Joseph Myers <joseph@codesourcery.com>
Date: Fri Mar 15 18:18:40 2019 +0000
Update syscall-names.list for Linux 5.0.
This patch updates sysdeps/unix/sysv/linux/syscall-names.list for
Linux 5.0. Based on testing with build-many-glibcs.py, the only new
entry needed is for old_getpagesize (a newly added __NR_* name for an
old syscall on ia64). (Because 5.0 changes how syscall tables are
handled in the kernel, checking diffs wasn't a useful way of looking
for new syscalls in 5.0 as most of the syscall tables were moved to
the new representation without actually adding any syscalls to them.)
Tested with build-many-glibcs.py.
* sysdeps/unix/sysv/linux/syscall-names.list: Update kernel
version to 5.0.
(old_getpagesize): New syscall.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index b650dc07cc..0227e52a5f 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -22,8 +22,8 @@
# names are only used if the installed kernel headers also provide
# them.
-# The list of system calls is current as of Linux 4.20.
-kernel 4.20
+# The list of system calls is current as of Linux 5.0.
+kernel 5.0
FAST_atomic_update
FAST_cmpxchg
@@ -261,6 +261,7 @@ nfsservctl
ni_syscall
nice
old_adjtimex
+old_getpagesize
oldfstat
oldlstat
oldolduname

View File

@ -0,0 +1,181 @@
commit 7621676f7a5130c030f7fff1cab72dbf2993b837
Author: Joseph Myers <joseph@codesourcery.com>
Date: Tue May 7 23:57:26 2019 +0000
Update syscall-names.list for Linux 5.1.
This patch updates syscall-names.list for Linux 5.1 (which has many
new syscalls, mainly but not entirely ones for 64-bit time).
Tested with build-many-glibcs.py (before the revert of the move to
Linux 5.1 there; verified there were no tst-syscall-list failures).
* sysdeps/unix/sysv/linux/syscall-names.list: Update kernel
version to 5.1.
(clock_adjtime64) New syscall.
(clock_getres_time64) Likewise.
(clock_gettime64) Likewise.
(clock_nanosleep_time64) Likewise.
(clock_settime64) Likewise.
(futex_time64) Likewise.
(io_pgetevents_time64) Likewise.
(io_uring_enter) Likewise.
(io_uring_register) Likewise.
(io_uring_setup) Likewise.
(mq_timedreceive_time64) Likewise.
(mq_timedsend_time64) Likewise.
(pidfd_send_signal) Likewise.
(ppoll_time64) Likewise.
(pselect6_time64) Likewise.
(recvmmsg_time64) Likewise.
(rt_sigtimedwait_time64) Likewise.
(sched_rr_get_interval_time64) Likewise.
(semtimedop_time64) Likewise.
(timer_gettime64) Likewise.
(timer_settime64) Likewise.
(timerfd_gettime64) Likewise.
(timerfd_settime64) Likewise.
(utimensat_time64) Likewise.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index 0227e52a5f..2d0354b8b3 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -22,8 +22,8 @@
# names are only used if the installed kernel headers also provide
# them.
-# The list of system calls is current as of Linux 5.0.
-kernel 5.0
+# The list of system calls is current as of Linux 5.1.
+kernel 5.1
FAST_atomic_update
FAST_cmpxchg
@@ -63,10 +63,15 @@ chown
chown32
chroot
clock_adjtime
+clock_adjtime64
clock_getres
+clock_getres_time64
clock_gettime
+clock_gettime64
clock_nanosleep
+clock_nanosleep_time64
clock_settime
+clock_settime64
clone
clone2
close
@@ -128,6 +133,7 @@ ftime
ftruncate
ftruncate64
futex
+futex_time64
futimesat
get_kernel_syms
get_mempolicy
@@ -187,8 +193,12 @@ io_cancel
io_destroy
io_getevents
io_pgetevents
+io_pgetevents_time64
io_setup
io_submit
+io_uring_enter
+io_uring_register
+io_uring_setup
ioctl
ioperm
iopl
@@ -242,7 +252,9 @@ mq_getsetattr
mq_notify
mq_open
mq_timedreceive
+mq_timedreceive_time64
mq_timedsend
+mq_timedsend_time64
mq_unlink
mremap
msgctl
@@ -389,6 +401,7 @@ perf_event_open
perfctr
perfmonctl
personality
+pidfd_send_signal
pipe
pipe2
pivot_root
@@ -397,6 +410,7 @@ pkey_free
pkey_mprotect
poll
ppoll
+ppoll_time64
prctl
pread64
preadv
@@ -407,6 +421,7 @@ process_vm_writev
prof
profil
pselect6
+pselect6_time64
ptrace
putpmsg
pwrite64
@@ -424,6 +439,7 @@ reboot
recv
recvfrom
recvmmsg
+recvmmsg_time64
recvmsg
remap_file_pages
removexattr
@@ -442,6 +458,7 @@ rt_sigqueueinfo
rt_sigreturn
rt_sigsuspend
rt_sigtimedwait
+rt_sigtimedwait_time64
rt_tgsigqueueinfo
rtas
s390_guarded_storage
@@ -457,6 +474,7 @@ sched_getattr
sched_getparam
sched_getscheduler
sched_rr_get_interval
+sched_rr_get_interval_time64
sched_set_affinity
sched_setaffinity
sched_setattr
@@ -470,6 +488,7 @@ semctl
semget
semop
semtimedop
+semtimedop_time64
send
sendfile
sendfile64
@@ -567,11 +586,15 @@ timer_create
timer_delete
timer_getoverrun
timer_gettime
+timer_gettime64
timer_settime
+timer_settime64
timerfd
timerfd_create
timerfd_gettime
+timerfd_gettime64
timerfd_settime
+timerfd_settime64
times
tkill
truncate
@@ -591,6 +614,7 @@ userfaultfd
ustat
utime
utimensat
+utimensat_time64
utimes
utrap_install
vfork

View File

@ -0,0 +1,58 @@
commit 0bb8f8c791862a4ff38a584af23bbb5bf3f90acd
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri May 31 13:52:16 2019 +0200
Linux: Add oddly-named arm syscalls to syscall-names.list
<asm/unistd.h> on arm defines the following macros:
#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
#define __ARM_NR_get_tls (__ARM_NR_BASE+6)
These do not follow the regular __NR_* naming convention and
have so far been ignored by the syscall-names.list consistency
checks. This commit adds these names to the file, preparing
for the availability of these names in the regular __NR_*
namespace.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index 2d0354b8b3..ae8adabb70 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -52,6 +52,7 @@ bdflush
bind
bpf
break
+breakpoint
brk
cachectl
cacheflush
@@ -139,6 +140,7 @@ get_kernel_syms
get_mempolicy
get_robust_list
get_thread_area
+get_tls
getcpu
getcwd
getdents
@@ -499,6 +501,7 @@ set_mempolicy
set_robust_list
set_thread_area
set_tid_address
+set_tls
setdomainname
setfsgid
setfsgid32
@@ -611,6 +614,8 @@ unlinkat
unshare
uselib
userfaultfd
+usr26
+usr32
ustat
utime
utimensat

View File

@ -0,0 +1,30 @@
commit a63b96fbddbf97feaa068a9efed3b5623a1a1e78
Author: Vincent Chen <vincentc@andestech.com>
Date: Wed Jun 26 17:30:11 2019 +0800
Linux: Add nds32 specific syscalls to syscall-names.list
The nds32 creates two specific syscalls, udftrap and fp_udfiex_crtl, in
kernel v5.0 and v5.2, respectively. Add these two syscalls to
syscall-names.list.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index ae8adabb70..95aa3ec7a5 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -121,6 +121,7 @@ finit_module
flistxattr
flock
fork
+fp_udfiex_crtl
free_hugepages
fremovexattr
fsetxattr
@@ -603,6 +604,7 @@ tkill
truncate
truncate64
tuxcall
+udftrap
ugetrlimit
ulimit
umask

View File

@ -0,0 +1,52 @@
commit 1f7097d09ce628878107ed30341cfc1eb3649a81
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Jul 19 08:53:04 2019 +0200
Linux: Update syscall-names.list to Linux 5.2
This adds the system call names fsconfig, fsmount, fsopen, fspick,
move_mount, open_tree.
Tested with build-many-glibcs.py.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index 95aa3ec7a5..21bf37c627 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -23,7 +23,7 @@
# them.
# The list of system calls is current as of Linux 5.1.
-kernel 5.1
+kernel 5.2
FAST_atomic_update
FAST_cmpxchg
@@ -124,7 +124,11 @@ fork
fp_udfiex_crtl
free_hugepages
fremovexattr
+fsconfig
fsetxattr
+fsmount
+fsopen
+fspick
fstat
fstat64
fstatat64
@@ -248,6 +252,7 @@ mmap
mmap2
modify_ldt
mount
+move_mount
move_pages
mprotect
mpx
@@ -285,6 +290,7 @@ oldumount
olduname
open
open_by_handle_at
+open_tree
openat
osf_adjtime
osf_afs_syscall

View File

@ -0,0 +1,24 @@
commit 9c37bde5a2067e5b4dc878bac0291d6b207b8add
Author: Joseph Myers <joseph@codesourcery.com>
Date: Fri Aug 2 15:08:02 2019 +0000
Update kernel version in comment in syscall-names.list.
This patch updates the Linux kernel version in a comment in
syscall-names.list to agree with the following "kernel" line.
* sysdeps/unix/sysv/linux/syscall-names.list: Update comment.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index 21bf37c627..9dcdd293d3 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -22,7 +22,7 @@
# names are only used if the installed kernel headers also provide
# them.
-# The list of system calls is current as of Linux 5.1.
+# The list of system calls is current as of Linux 5.2.
kernel 5.2
FAST_atomic_update

View File

@ -0,0 +1,47 @@
commit 0f02b6cfc44af73d4d4363c46b3cbb18b8ff9171
Author: Joseph Myers <joseph@codesourcery.com>
Date: Wed Sep 18 22:57:46 2019 +0000
Update syscall-names.list for Linux 5.3.
This patch updates syscall-names.list for Linux 5.3, adding two new
syscalls.
Tested with build-many-glibcs.py.
* sysdeps/unix/sysv/linux/syscall-names.list: Update kernel
version to 5.3.
(clone3): New syscall.
(pidfd_open): Likewise.
diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
index e2382d3414..b55ffbc2a0 100644
--- a/sysdeps/unix/sysv/linux/syscall-names.list
+++ b/sysdeps/unix/sysv/linux/syscall-names.list
@@ -22,8 +22,8 @@
# names are only used if the installed kernel headers also provide
# them.
-# The list of system calls is current as of Linux 5.2.
-kernel 5.2
+# The list of system calls is current as of Linux 5.3.
+kernel 5.3
FAST_atomic_update
FAST_cmpxchg
@@ -75,6 +75,7 @@ clock_settime
clock_settime64
clone
clone2
+clone3
close
cmpxchg_badaddr
connect
@@ -410,6 +411,7 @@ perf_event_open
perfctr
perfmonctl
personality
+pidfd_open
pidfd_send_signal
pipe
pipe2

View File

@ -0,0 +1,165 @@
commit e621246ec6393ea08ae50310f9d5e72500f8c9bc
Author: Carlos O'Donell <carlos@redhat.com>
Date: Mon Apr 8 17:35:05 2019 -0400
malloc: Set and reset all hooks for tracing (Bug 16573)
If an error occurs during the tracing operation, particularly during a
call to lock_and_info() which calls _dl_addr, we may end up calling back
into the malloc-subsystem and relock the loader lock and deadlock. For
all intents and purposes the call to _dl_addr can call any of the malloc
family API functions and so we should disable all tracing before calling
such loader functions. This is similar to the strategy that the new
malloc tracer takes when calling the real malloc, namely that all
tracing ceases at the boundary to the real function and any faults at
that point are the purvue of the library (though the new tracer does
this on a per-thread basis in an MT-safe fashion). Since the new tracer
and the hook deprecation are not yet complete we must fix these issues
where we can.
Tested on x86_64 with no regressions.
Co-authored-by: Kwok Cheung Yeung <kcy@codesourcery.com>
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/malloc/mtrace.c b/malloc/mtrace.c
index 9064f209ec3b24c6..546d37a26018bf41 100644
--- a/malloc/mtrace.c
+++ b/malloc/mtrace.c
@@ -121,6 +121,41 @@ lock_and_info (const void *caller, Dl_info *mem)
return res;
}
+static void tr_freehook (void *, const void *);
+static void * tr_mallochook (size_t, const void *);
+static void * tr_reallochook (void *, size_t, const void *);
+static void * tr_memalignhook (size_t, size_t, const void *);
+
+/* Set all the default non-trace hooks. */
+static __always_inline void
+set_default_hooks (void)
+{
+ __free_hook = tr_old_free_hook;
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+ __memalign_hook = tr_old_memalign_hook;
+}
+
+/* Set all of the tracing hooks used for mtrace. */
+static __always_inline void
+set_trace_hooks (void)
+{
+ __free_hook = tr_freehook;
+ __malloc_hook = tr_mallochook;
+ __realloc_hook = tr_reallochook;
+ __memalign_hook = tr_memalignhook;
+}
+
+/* Save the current set of hooks as the default hooks. */
+static __always_inline void
+save_default_hooks (void)
+{
+ tr_old_free_hook = __free_hook;
+ tr_old_malloc_hook = __malloc_hook;
+ tr_old_realloc_hook = __realloc_hook;
+ tr_old_memalign_hook = __memalign_hook;
+}
+
static void
tr_freehook (void *ptr, const void *caller)
{
@@ -138,12 +173,12 @@ tr_freehook (void *ptr, const void *caller)
tr_break ();
__libc_lock_lock (lock);
}
- __free_hook = tr_old_free_hook;
+ set_default_hooks ();
if (tr_old_free_hook != NULL)
(*tr_old_free_hook)(ptr, caller);
else
free (ptr);
- __free_hook = tr_freehook;
+ set_trace_hooks ();
__libc_lock_unlock (lock);
}
@@ -155,12 +190,12 @@ tr_mallochook (size_t size, const void *caller)
Dl_info mem;
Dl_info *info = lock_and_info (caller, &mem);
- __malloc_hook = tr_old_malloc_hook;
+ set_default_hooks ();
if (tr_old_malloc_hook != NULL)
hdr = (void *) (*tr_old_malloc_hook)(size, caller);
else
hdr = (void *) malloc (size);
- __malloc_hook = tr_mallochook;
+ set_trace_hooks ();
tr_where (caller, info);
/* We could be printing a NULL here; that's OK. */
@@ -185,16 +220,12 @@ tr_reallochook (void *ptr, size_t size, const void *caller)
Dl_info mem;
Dl_info *info = lock_and_info (caller, &mem);
- __free_hook = tr_old_free_hook;
- __malloc_hook = tr_old_malloc_hook;
- __realloc_hook = tr_old_realloc_hook;
+ set_default_hooks ();
if (tr_old_realloc_hook != NULL)
hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
else
hdr = (void *) realloc (ptr, size);
- __free_hook = tr_freehook;
- __malloc_hook = tr_mallochook;
- __realloc_hook = tr_reallochook;
+ set_trace_hooks ();
tr_where (caller, info);
if (hdr == NULL)
@@ -230,14 +261,12 @@ tr_memalignhook (size_t alignment, size_t size, const void *caller)
Dl_info mem;
Dl_info *info = lock_and_info (caller, &mem);
- __memalign_hook = tr_old_memalign_hook;
- __malloc_hook = tr_old_malloc_hook;
+ set_default_hooks ();
if (tr_old_memalign_hook != NULL)
hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
else
hdr = (void *) memalign (alignment, size);
- __memalign_hook = tr_memalignhook;
- __malloc_hook = tr_mallochook;
+ set_trace_hooks ();
tr_where (caller, info);
/* We could be printing a NULL here; that's OK. */
@@ -305,14 +334,8 @@ mtrace (void)
malloc_trace_buffer = mtb;
setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
fprintf (mallstream, "= Start\n");
- tr_old_free_hook = __free_hook;
- __free_hook = tr_freehook;
- tr_old_malloc_hook = __malloc_hook;
- __malloc_hook = tr_mallochook;
- tr_old_realloc_hook = __realloc_hook;
- __realloc_hook = tr_reallochook;
- tr_old_memalign_hook = __memalign_hook;
- __memalign_hook = tr_memalignhook;
+ save_default_hooks ();
+ set_trace_hooks ();
#ifdef _LIBC
if (!added_atexit_handler)
{
@@ -338,10 +361,7 @@ muntrace (void)
file. */
FILE *f = mallstream;
mallstream = NULL;
- __free_hook = tr_old_free_hook;
- __malloc_hook = tr_old_malloc_hook;
- __realloc_hook = tr_old_realloc_hook;
- __memalign_hook = tr_old_memalign_hook;
+ set_default_hooks ();
fprintf (f, "= End\n");
fclose (f);

View File

@ -0,0 +1,92 @@
commit dc0afac3252d0c53716ccaf0b424f7769a66d695
Author: marxin <mliska@suse.cz>
Date: Wed Feb 20 14:54:35 2019 +0100
Add new Fortran vector math header file.
diff --git a/bits/math-vector-fortran.h b/bits/math-vector-fortran.h
new file mode 100644
index 0000000000000000..7c1e095094e24571
--- /dev/null
+++ b/bits/math-vector-fortran.h
@@ -0,0 +1,19 @@
+! Platform-specific declarations of SIMD math functions for Fortran. -*- f90 -*-
+! Copyright (C) 2019 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
+! <http://www.gnu.org/licenses/>.
+
+! No SIMD math functions are available for this platform.
diff --git a/math/Makefile b/math/Makefile
index 90b3b68916e12d85..16e68754fc863ea2 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -26,6 +26,7 @@ headers := math.h bits/mathcalls.h bits/mathinline.h \
fpu_control.h complex.h bits/cmathcalls.h fenv.h \
bits/fenv.h bits/fenvinline.h bits/mathdef.h tgmath.h \
bits/math-finite.h bits/math-vector.h \
+ bits/math-vector-fortran.h \
bits/libm-simd-decl-stubs.h bits/iscanonical.h \
bits/flt-eval-method.h bits/fp-fast.h bits/fp-logb.h \
bits/long-double.h bits/mathcalls-helper-functions.h \
diff --git a/sysdeps/x86/fpu/bits/math-vector-fortran.h b/sysdeps/x86/fpu/bits/math-vector-fortran.h
new file mode 100644
index 0000000000000000..36051cc73ea03602
--- /dev/null
+++ b/sysdeps/x86/fpu/bits/math-vector-fortran.h
@@ -0,0 +1,43 @@
+! Platform-specific declarations of SIMD math functions for Fortran. -*- f90 -*-
+! Copyright (C) 2019 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
+! <http://www.gnu.org/licenses/>.
+
+!GCC$ builtin (cos) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (cosf) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (sin) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (sinf) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (sincos) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (sincosf) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (log) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (logf) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (exp) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (expf) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (pow) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (powf) attributes simd (notinbranch) if('x86_64')
+
+!GCC$ builtin (cos) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (cosf) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (sin) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (sinf) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (sincos) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (sincosf) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (log) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (logf) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (exp) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (expf) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (pow) attributes simd (notinbranch) if('x32')
+!GCC$ builtin (powf) attributes simd (notinbranch) if('x32')

View File

@ -0,0 +1,56 @@
commit ae514971341dcc08ec7f8622493a65e7eb1ef9d2
Author: marxin <mliska@suse.cz>
Date: Thu Mar 7 09:39:55 2019 +0100
Fix location where math-vector-fortran.h is installed.
2019-03-07 Martin Liska <mliska@suse.cz>
* math/Makefile: Change location where math-vector-fortran.h is
installed.
* math/finclude/math-vector-fortran.h: Move from bits/math-vector-fortran.h.
* sysdeps/x86/fpu/finclude/math-vector-fortran.h: Move
from sysdeps/x86/fpu/bits/math-vector-fortran.h.
* scripts/check-installed-headers.sh: Skip Fortran header files.
* scripts/check-wrapper-headers.py: Likewise.
Conflicts:
scripts/check-wrapper-headers.py
(Script does not exist downstream, change dropped.)
diff --git a/math/Makefile b/math/Makefile
index 16e68754fc863ea2..df73d70840b61cd7 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -26,7 +26,7 @@ headers := math.h bits/mathcalls.h bits/mathinline.h \
fpu_control.h complex.h bits/cmathcalls.h fenv.h \
bits/fenv.h bits/fenvinline.h bits/mathdef.h tgmath.h \
bits/math-finite.h bits/math-vector.h \
- bits/math-vector-fortran.h \
+ finclude/math-vector-fortran.h \
bits/libm-simd-decl-stubs.h bits/iscanonical.h \
bits/flt-eval-method.h bits/fp-fast.h bits/fp-logb.h \
bits/long-double.h bits/mathcalls-helper-functions.h \
diff --git a/bits/math-vector-fortran.h b/math/finclude/math-vector-fortran.h
similarity index 100%
rename from bits/math-vector-fortran.h
rename to math/finclude/math-vector-fortran.h
diff --git a/scripts/check-installed-headers.sh b/scripts/check-installed-headers.sh
index 4a062e9cdaa57978..7a1969b43a144ebb 100644
--- a/scripts/check-installed-headers.sh
+++ b/scripts/check-installed-headers.sh
@@ -84,6 +84,10 @@ for header in "$@"; do
(sys/elf.h)
continue;;
+ # Skip Fortran headers.
+ (finclude/*)
+ continue;;
+
# sys/sysctl.h is unsupported for x32.
(sys/sysctl.h)
case "$is_x32" in
diff --git a/sysdeps/x86/fpu/bits/math-vector-fortran.h b/sysdeps/x86/fpu/finclude/math-vector-fortran.h
similarity index 100%
rename from sysdeps/x86/fpu/bits/math-vector-fortran.h
rename to sysdeps/x86/fpu/finclude/math-vector-fortran.h

View File

@ -0,0 +1,544 @@
commit 09e1b0e3f6facc1af2dbcfef204f0aaa8718772b
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon May 20 21:54:57 2019 +0200
libio: Remove codecvt vtable [BZ #24588]
The codecvt vtable is not a real vtable because it also contains the
conversion state data. Furthermore, wide stream support was added to
GCC 3.0, after a C++ ABI bump, so there is no compatibility
requirement with libstdc++.
This change removes several unmangled function pointers which could
be used with a corrupted FILE object to redirect execution. (libio
vtable verification did not cover the codecvt vtable.)
Reviewed-by: Yann Droneaud <ydroneaud@opteya.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/libio/fileops.c b/libio/fileops.c
index d2070a856e..daa5a05877 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -331,9 +331,6 @@ _IO_new_file_fopen (FILE *fp, const char *filename, const char *mode,
cc = fp->_codecvt = &fp->_wide_data->_codecvt;
- /* The functions are always the same. */
- *cc = __libio_codecvt;
-
cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps;
cc->__cd_in.__cd.__steps = fcts.towc;
diff --git a/libio/iofgetpos.c b/libio/iofgetpos.c
index 8032192440..388c4a0708 100644
--- a/libio/iofgetpos.c
+++ b/libio/iofgetpos.c
@@ -70,8 +70,7 @@ _IO_new_fgetpos (FILE *fp, __fpos_t *posp)
else
{
posp->__pos = pos;
- if (fp->_mode > 0
- && (*fp->_codecvt->__codecvt_do_encoding) (fp->_codecvt) < 0)
+ if (fp->_mode > 0 && __libio_codecvt_encoding (fp->_codecvt) < 0)
/* This is a stateful encoding, safe the state. */
posp->__state = fp->_wide_data->_IO_state;
}
diff --git a/libio/iofgetpos64.c b/libio/iofgetpos64.c
index 54de6a8205..6a0ba50d29 100644
--- a/libio/iofgetpos64.c
+++ b/libio/iofgetpos64.c
@@ -54,8 +54,7 @@ _IO_new_fgetpos64 (FILE *fp, __fpos64_t *posp)
else
{
posp->__pos = pos;
- if (fp->_mode > 0
- && (*fp->_codecvt->__codecvt_do_encoding) (fp->_codecvt) < 0)
+ if (fp->_mode > 0 && __libio_codecvt_encoding (fp->_codecvt) < 0)
/* This is a stateful encoding, safe the state. */
posp->__state = fp->_wide_data->_IO_state;
}
diff --git a/libio/iofsetpos.c b/libio/iofsetpos.c
index d7b1abbc61..4df1aae082 100644
--- a/libio/iofsetpos.c
+++ b/libio/iofsetpos.c
@@ -58,8 +58,7 @@ _IO_new_fsetpos (FILE *fp, const __fpos_t *posp)
else
{
result = 0;
- if (fp->_mode > 0
- && (*fp->_codecvt->__codecvt_do_encoding) (fp->_codecvt) < 0)
+ if (fp->_mode > 0 && __libio_codecvt_encoding (fp->_codecvt) < 0)
/* This is a stateful encoding, restore the state. */
fp->_wide_data->_IO_state = posp->__state;
}
diff --git a/libio/iofsetpos64.c b/libio/iofsetpos64.c
index d1865b728e..f382ba0dc1 100644
--- a/libio/iofsetpos64.c
+++ b/libio/iofsetpos64.c
@@ -48,8 +48,7 @@ _IO_new_fsetpos64 (FILE *fp, const fpos64_t *posp)
else
{
result = 0;
- if (fp->_mode > 0
- && (*fp->_codecvt->__codecvt_do_encoding) (fp->_codecvt) < 0)
+ if (fp->_mode > 0 && __libio_codecvt_encoding (fp->_codecvt) < 0)
/* This is a stateful encoding, safe the state. */
fp->_wide_data->_IO_state = posp->__state;
}
diff --git a/libio/iofwide.c b/libio/iofwide.c
index 247cfde3d0..80cb2d5074 100644
--- a/libio/iofwide.c
+++ b/libio/iofwide.c
@@ -39,44 +39,6 @@
#include <sysdep.h>
-/* Prototypes of libio's codecvt functions. */
-static enum __codecvt_result do_out (struct _IO_codecvt *codecvt,
- __mbstate_t *statep,
- const wchar_t *from_start,
- const wchar_t *from_end,
- const wchar_t **from_stop, char *to_start,
- char *to_end, char **to_stop);
-static enum __codecvt_result do_unshift (struct _IO_codecvt *codecvt,
- __mbstate_t *statep, char *to_start,
- char *to_end, char **to_stop);
-static enum __codecvt_result do_in (struct _IO_codecvt *codecvt,
- __mbstate_t *statep,
- const char *from_start,
- const char *from_end,
- const char **from_stop, wchar_t *to_start,
- wchar_t *to_end, wchar_t **to_stop);
-static int do_encoding (struct _IO_codecvt *codecvt);
-static int do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
- const char *from_start,
- const char *from_end, size_t max);
-static int do_max_length (struct _IO_codecvt *codecvt);
-static int do_always_noconv (struct _IO_codecvt *codecvt);
-
-
-/* The functions used in `codecvt' for libio are always the same. */
-const struct _IO_codecvt __libio_codecvt =
-{
- .__codecvt_destr = NULL, /* Destructor, never used. */
- .__codecvt_do_out = do_out,
- .__codecvt_do_unshift = do_unshift,
- .__codecvt_do_in = do_in,
- .__codecvt_do_encoding = do_encoding,
- .__codecvt_do_always_noconv = do_always_noconv,
- .__codecvt_do_length = do_length,
- .__codecvt_do_max_length = do_max_length
-};
-
-
/* Return orientation of stream. If mode is nonzero try to change
the orientation first. */
#undef _IO_fwide
@@ -118,9 +80,6 @@ _IO_fwide (FILE *fp, int mode)
assert (fcts.towc_nsteps == 1);
assert (fcts.tomb_nsteps == 1);
- /* The functions are always the same. */
- *cc = __libio_codecvt;
-
cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps;
cc->__cd_in.__cd.__steps = fcts.towc;
@@ -150,11 +109,11 @@ _IO_fwide (FILE *fp, int mode)
}
-static enum __codecvt_result
-do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
- const wchar_t *from_start, const wchar_t *from_end,
- const wchar_t **from_stop, char *to_start, char *to_end,
- char **to_stop)
+enum __codecvt_result
+__libio_codecvt_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
+ const wchar_t *from_start, const wchar_t *from_end,
+ const wchar_t **from_stop, char *to_start, char *to_end,
+ char **to_stop)
{
enum __codecvt_result result;
@@ -202,57 +161,11 @@ do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
}
-static enum __codecvt_result
-do_unshift (struct _IO_codecvt *codecvt, __mbstate_t *statep,
- char *to_start, char *to_end, char **to_stop)
-{
- enum __codecvt_result result;
-
- struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
- int status;
- size_t dummy;
-
- codecvt->__cd_out.__cd.__data[0].__outbuf = (unsigned char *) to_start;
- codecvt->__cd_out.__cd.__data[0].__outbufend = (unsigned char *) to_end;
- codecvt->__cd_out.__cd.__data[0].__statep = statep;
-
- __gconv_fct fct = gs->__fct;
-#ifdef PTR_DEMANGLE
- if (gs->__shlib_handle != NULL)
- PTR_DEMANGLE (fct);
-#endif
-
- status = DL_CALL_FCT (fct,
- (gs, codecvt->__cd_out.__cd.__data, NULL, NULL,
- NULL, &dummy, 1, 0));
-
- *to_stop = (char *) codecvt->__cd_out.__cd.__data[0].__outbuf;
-
- switch (status)
- {
- case __GCONV_OK:
- case __GCONV_EMPTY_INPUT:
- result = __codecvt_ok;
- break;
-
- case __GCONV_FULL_OUTPUT:
- case __GCONV_INCOMPLETE_INPUT:
- result = __codecvt_partial;
- break;
-
- default:
- result = __codecvt_error;
- break;
- }
-
- return result;
-}
-
-
-static enum __codecvt_result
-do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
- const char *from_start, const char *from_end, const char **from_stop,
- wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
+enum __codecvt_result
+__libio_codecvt_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
+ const char *from_start, const char *from_end,
+ const char **from_stop,
+ wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
{
enum __codecvt_result result;
@@ -300,8 +213,8 @@ do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
}
-static int
-do_encoding (struct _IO_codecvt *codecvt)
+int
+__libio_codecvt_encoding (struct _IO_codecvt *codecvt)
{
/* See whether the encoding is stateful. */
if (codecvt->__cd_in.__cd.__steps[0].__stateful)
@@ -317,16 +230,10 @@ do_encoding (struct _IO_codecvt *codecvt)
}
-static int
-do_always_noconv (struct _IO_codecvt *codecvt)
-{
- return 0;
-}
-
-
-static int
-do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
- const char *from_start, const char *from_end, size_t max)
+int
+__libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
+ const char *from_start, const char *from_end,
+ size_t max)
{
int result;
const unsigned char *cp = (const unsigned char *) from_start;
@@ -353,10 +260,3 @@ do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
return result;
}
-
-
-static int
-do_max_length (struct _IO_codecvt *codecvt)
-{
- return codecvt->__cd_in.__cd.__steps[0].__max_needed_from;
-}
diff --git a/libio/libio.h b/libio/libio.h
index c38095ff77..b985c386a2 100644
--- a/libio/libio.h
+++ b/libio/libio.h
@@ -116,40 +116,8 @@ struct _IO_marker {
int _pos;
};
-/* This is the structure from the libstdc++ codecvt class. */
-enum __codecvt_result
-{
- __codecvt_ok,
- __codecvt_partial,
- __codecvt_error,
- __codecvt_noconv
-};
-
-/* The order of the elements in the following struct must match the order
- of the virtual functions in the libstdc++ codecvt class. */
struct _IO_codecvt
{
- void (*__codecvt_destr) (struct _IO_codecvt *);
- enum __codecvt_result (*__codecvt_do_out) (struct _IO_codecvt *,
- __mbstate_t *,
- const wchar_t *,
- const wchar_t *,
- const wchar_t **, char *,
- char *, char **);
- enum __codecvt_result (*__codecvt_do_unshift) (struct _IO_codecvt *,
- __mbstate_t *, char *,
- char *, char **);
- enum __codecvt_result (*__codecvt_do_in) (struct _IO_codecvt *,
- __mbstate_t *,
- const char *, const char *,
- const char **, wchar_t *,
- wchar_t *, wchar_t **);
- int (*__codecvt_do_encoding) (struct _IO_codecvt *);
- int (*__codecvt_do_always_noconv) (struct _IO_codecvt *);
- int (*__codecvt_do_length) (struct _IO_codecvt *, __mbstate_t *,
- const char *, const char *, size_t);
- int (*__codecvt_do_max_length) (struct _IO_codecvt *);
-
_IO_iconv_t __cd_in;
_IO_iconv_t __cd_out;
};
diff --git a/libio/libioP.h b/libio/libioP.h
index 7bdec86a62..66afaa8968 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -476,7 +476,6 @@ extern const struct _IO_jump_t _IO_streambuf_jumps;
extern const struct _IO_jump_t _IO_old_proc_jumps attribute_hidden;
extern const struct _IO_jump_t _IO_str_jumps attribute_hidden;
extern const struct _IO_jump_t _IO_wstr_jumps attribute_hidden;
-extern const struct _IO_codecvt __libio_codecvt attribute_hidden;
extern int _IO_do_write (FILE *, const char *, size_t);
libc_hidden_proto (_IO_do_write)
extern int _IO_new_do_write (FILE *, const char *, size_t);
@@ -932,4 +931,32 @@ IO_validate_vtable (const struct _IO_jump_t *vtable)
return vtable;
}
+/* Character set conversion. */
+
+enum __codecvt_result
+{
+ __codecvt_ok,
+ __codecvt_partial,
+ __codecvt_error,
+ __codecvt_noconv
+};
+
+enum __codecvt_result __libio_codecvt_out (struct _IO_codecvt *,
+ __mbstate_t *,
+ const wchar_t *,
+ const wchar_t *,
+ const wchar_t **, char *,
+ char *, char **)
+ attribute_hidden;
+enum __codecvt_result __libio_codecvt_in (struct _IO_codecvt *,
+ __mbstate_t *,
+ const char *, const char *,
+ const char **, wchar_t *,
+ wchar_t *, wchar_t **)
+ attribute_hidden;
+int __libio_codecvt_encoding (struct _IO_codecvt *) attribute_hidden;
+int __libio_codecvt_length (struct _IO_codecvt *, __mbstate_t *,
+ const char *, const char *, size_t)
+ attribute_hidden;
+
#endif /* libioP.h. */
diff --git a/libio/wfileops.c b/libio/wfileops.c
index 69fbb62a02..f1863db638 100644
--- a/libio/wfileops.c
+++ b/libio/wfileops.c
@@ -72,11 +72,11 @@ _IO_wdo_write (FILE *fp, const wchar_t *data, size_t to_do)
}
/* Now convert from the internal format into the external buffer. */
- result = (*cc->__codecvt_do_out) (cc, &fp->_wide_data->_IO_state,
- data, data + to_do, &new_data,
- write_ptr,
- buf_end,
- &write_ptr);
+ result = __libio_codecvt_out (cc, &fp->_wide_data->_IO_state,
+ data, data + to_do, &new_data,
+ write_ptr,
+ buf_end,
+ &write_ptr);
/* Write out what we produced so far. */
if (_IO_new_do_write (fp, write_base, write_ptr - write_base) == EOF)
@@ -140,12 +140,12 @@ _IO_wfile_underflow (FILE *fp)
fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
fp->_wide_data->_IO_buf_base;
- status = (*cd->__codecvt_do_in) (cd, &fp->_wide_data->_IO_state,
- fp->_IO_read_ptr, fp->_IO_read_end,
- &read_stop,
- fp->_wide_data->_IO_read_ptr,
- fp->_wide_data->_IO_buf_end,
- &fp->_wide_data->_IO_read_end);
+ status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
+ fp->_IO_read_ptr, fp->_IO_read_end,
+ &read_stop,
+ fp->_wide_data->_IO_read_ptr,
+ fp->_wide_data->_IO_buf_end,
+ &fp->_wide_data->_IO_read_end);
fp->_IO_read_base = fp->_IO_read_ptr;
fp->_IO_read_ptr = (char *) read_stop;
@@ -266,11 +266,11 @@ _IO_wfile_underflow (FILE *fp)
naccbuf += to_copy;
from = accbuf;
}
- status = (*cd->__codecvt_do_in) (cd, &fp->_wide_data->_IO_state,
- from, to, &read_ptr_copy,
- fp->_wide_data->_IO_read_end,
- fp->_wide_data->_IO_buf_end,
- &fp->_wide_data->_IO_read_end);
+ status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
+ from, to, &read_ptr_copy,
+ fp->_wide_data->_IO_read_end,
+ fp->_wide_data->_IO_buf_end,
+ &fp->_wide_data->_IO_read_end);
if (__glibc_unlikely (naccbuf != 0))
fp->_IO_read_ptr += MAX (0, read_ptr_copy - &accbuf[naccbuf - to_copy]);
@@ -372,12 +372,12 @@ _IO_wfile_underflow_mmap (FILE *fp)
fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
fp->_wide_data->_IO_buf_base;
- (*cd->__codecvt_do_in) (cd, &fp->_wide_data->_IO_state,
- fp->_IO_read_ptr, fp->_IO_read_end,
- &read_stop,
- fp->_wide_data->_IO_read_ptr,
- fp->_wide_data->_IO_buf_end,
- &fp->_wide_data->_IO_read_end);
+ __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
+ fp->_IO_read_ptr, fp->_IO_read_end,
+ &read_stop,
+ fp->_wide_data->_IO_read_ptr,
+ fp->_wide_data->_IO_buf_end,
+ &fp->_wide_data->_IO_read_end);
fp->_IO_read_ptr = (char *) read_stop;
@@ -495,7 +495,7 @@ _IO_wfile_sync (FILE *fp)
struct _IO_codecvt *cv = fp->_codecvt;
off64_t new_pos;
- int clen = (*cv->__codecvt_do_encoding) (cv);
+ int clen = __libio_codecvt_encoding (cv);
if (clen > 0)
/* It is easy, a fixed number of input bytes are used for each
@@ -511,9 +511,9 @@ _IO_wfile_sync (FILE *fp)
size_t wnread = (fp->_wide_data->_IO_read_ptr
- fp->_wide_data->_IO_read_base);
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
- nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
- fp->_IO_read_base,
- fp->_IO_read_end, wnread);
+ nread = __libio_codecvt_length (cv, &fp->_wide_data->_IO_state,
+ fp->_IO_read_base,
+ fp->_IO_read_end, wnread);
fp->_IO_read_ptr = fp->_IO_read_base + nread;
delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
}
@@ -548,7 +548,7 @@ adjust_wide_data (FILE *fp, bool do_convert)
{
struct _IO_codecvt *cv = fp->_codecvt;
- int clen = (*cv->__codecvt_do_encoding) (cv);
+ int clen = __libio_codecvt_encoding (cv);
/* Take the easy way out for constant length encodings if we don't need to
convert. */
@@ -565,12 +565,12 @@ adjust_wide_data (FILE *fp, bool do_convert)
{
fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
- status = (*cv->__codecvt_do_in) (cv, &fp->_wide_data->_IO_state,
- fp->_IO_read_base, fp->_IO_read_ptr,
- &read_stop,
- fp->_wide_data->_IO_read_base,
- fp->_wide_data->_IO_buf_end,
- &fp->_wide_data->_IO_read_end);
+ status = __libio_codecvt_in (cv, &fp->_wide_data->_IO_state,
+ fp->_IO_read_base, fp->_IO_read_ptr,
+ &read_stop,
+ fp->_wide_data->_IO_read_base,
+ fp->_wide_data->_IO_buf_end,
+ &fp->_wide_data->_IO_read_end);
/* Should we return EILSEQ? */
if (__glibc_unlikely (status == __codecvt_error))
@@ -648,7 +648,7 @@ do_ftell_wide (FILE *fp)
}
struct _IO_codecvt *cv = fp->_codecvt;
- int clen = (*cv->__codecvt_do_encoding) (cv);
+ int clen = __libio_codecvt_encoding (cv);
if (!unflushed_writes)
{
@@ -663,9 +663,9 @@ do_ftell_wide (FILE *fp)
size_t delta = wide_read_ptr - wide_read_base;
__mbstate_t state = fp->_wide_data->_IO_last_state;
- nread = (*cv->__codecvt_do_length) (cv, &state,
- fp->_IO_read_base,
- fp->_IO_read_end, delta);
+ nread = __libio_codecvt_length (cv, &state,
+ fp->_IO_read_base,
+ fp->_IO_read_end, delta);
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
}
}
@@ -688,9 +688,8 @@ do_ftell_wide (FILE *fp)
enum __codecvt_result status;
__mbstate_t state = fp->_wide_data->_IO_last_state;
- status = (*cv->__codecvt_do_out) (cv, &state,
- in, in + delta, &in,
- out, out + outsize, &outstop);
+ status = __libio_codecvt_out (cv, &state, in, in + delta, &in,
+ out, out + outsize, &outstop);
/* We don't check for __codecvt_partial because it can be
returned on one of two conditions: either the output
@@ -801,7 +800,7 @@ _IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
find out which position in the external buffer corresponds to
the current position in the internal buffer. */
cv = fp->_codecvt;
- clen = (*cv->__codecvt_do_encoding) (cv);
+ clen = __libio_codecvt_encoding (cv);
if (mode != 0 || !was_writing)
{
@@ -819,10 +818,10 @@ _IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
delta = (fp->_wide_data->_IO_read_ptr
- fp->_wide_data->_IO_read_base);
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
- nread = (*cv->__codecvt_do_length) (cv,
- &fp->_wide_data->_IO_state,
- fp->_IO_read_base,
- fp->_IO_read_end, delta);
+ nread = __libio_codecvt_length (cv,
+ &fp->_wide_data->_IO_state,
+ fp->_IO_read_base,
+ fp->_IO_read_end, delta);
fp->_IO_read_ptr = fp->_IO_read_base + nread;
fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;

View File

@ -0,0 +1,81 @@
commit 4997e8f31e7415652c3dedec672c0e9bf8caa9ca
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Fri Feb 1 10:39:57 2019 -0200
math: Enable some math builtins for clang
This patch enable the builtin usage for clang for the C99 functions
fpclassify, isfinite, isnormal, isnan, isinf, and sigbit. This allows
clang optimize the calls on frontend instead of call the appropriate
glibc symbols.
Checked on aarch64-linux-gnu and x86_64-linux-gnu. I checked the supported
version for each builtin based on released version from clang/llvm.
* math/math.h (fpclassify, isfinite, isnormal, isnan): Use builtin for
clang 2.8.
(signbit): Use builtin for clang 3.3.
(isinf): Use builtin for clang 3.7.
diff --git a/math/math.h b/math/math.h
index ddee4e408389722f..b3b414f3678e91f7 100644
--- a/math/math.h
+++ b/math/math.h
@@ -874,7 +874,8 @@ enum
the __SUPPORT_SNAN__ check may be skipped for those versions. */
/* Return number of classification appropriate for X. */
-# if __GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__ \
+# if ((__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
+ || __glibc_clang_prereq (2,8)) \
&& (!defined __OPTIMIZE_SIZE__ || defined __cplusplus)
/* The check for __cplusplus allows the use of the builtin, even
when optimization for size is on. This is provided for
@@ -889,7 +890,7 @@ enum
# endif
/* Return nonzero value if sign of X is negative. */
-# if __GNUC_PREREQ (6,0)
+# if __GNUC_PREREQ (6,0) || __glibc_clang_prereq (3,3)
# define signbit(x) __builtin_signbit (x)
# elif defined __cplusplus
/* In C++ mode, __MATH_TG cannot be used, because it relies on
@@ -907,14 +908,16 @@ enum
# endif
/* Return nonzero value if X is not +-Inf or NaN. */
-# if __GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__
+# if (__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
+ || __glibc_clang_prereq (2,8)
# define isfinite(x) __builtin_isfinite (x)
# else
# define isfinite(x) __MATH_TG ((x), __finite, (x))
# endif
/* Return nonzero value if X is neither zero, subnormal, Inf, nor NaN. */
-# if __GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__
+# if (__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
+ || __glibc_clang_prereq (2,8)
# define isnormal(x) __builtin_isnormal (x)
# else
# define isnormal(x) (fpclassify (x) == FP_NORMAL)
@@ -922,7 +925,8 @@ enum
/* Return nonzero value if X is a NaN. We could use `fpclassify' but
we already have this functions `__isnan' and it is faster. */
-# if __GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__
+# if (__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
+ || __glibc_clang_prereq (2,8)
# define isnan(x) __builtin_isnan (x)
# else
# define isnan(x) __MATH_TG ((x), __isnan, (x))
@@ -939,7 +943,8 @@ enum
# define isinf(x) \
(__builtin_types_compatible_p (__typeof (x), _Float128) \
? __isinff128 (x) : __builtin_isinf_sign (x))
-# elif __GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__
+# elif (__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
+ || __glibc_clang_prereq (3,7)
# define isinf(x) __builtin_isinf_sign (x)
# else
# define isinf(x) __MATH_TG ((x), __isinf, (x))

View File

@ -0,0 +1,740 @@
commit 711a322a235d4c8177713f11aa59156603b94aeb
Author: Zack Weinberg <zackw@panix.com>
Date: Mon Mar 11 10:59:27 2019 -0400
Use a proper C tokenizer to implement the obsolete typedefs test.
The test for obsolete typedefs in installed headers was implemented
using grep, and could therefore get false positives on e.g. “ulong”
in a comment. It was also scanning all of the headers included by
our headers, and therefore testing headers we dont control, e.g.
Linux kernel headers.
This patch splits the obsolete-typedef test from
scripts/check-installed-headers.sh to a separate program,
scripts/check-obsolete-constructs.py. Being implemented in Python,
it is feasible to make it tokenize C accurately enough to avoid false
positives on the contents of comments and strings. It also only
examines $(headers) in each subdirectory--all the headers we install,
but not any external dependencies of those headers. Headers whose
installed name starts with finclude/ are ignored, on the assumption
that they contain Fortran.
It is also feasible to make the new test understand the difference
between _defining_ the obsolete typedefs and _using_ the obsolete
typedefs, which means posix/{bits,sys}/types.h no longer need to be
exempted. This uncovered an actual bug in bits/types.h: __quad_t and
__u_quad_t were being used to define __S64_TYPE, __U64_TYPE,
__SQUAD_TYPE and __UQUAD_TYPE. These are changed to __int64_t and
__uint64_t respectively. This is a safe change, despite the comments
in bits/types.h claiming a difference between __quad_t and __int64_t,
because those comments are incorrect. In all current ABIs, both
__quad_t and __int64_t are long when long is a 64-bit type, and
long long when long is a 32-bit type, and similarly for __u_quad_t
and __uint64_t. (Changing the types to be what the comments say they
are would be an ABI break, as it affects C++ name mangling.) This
patch includes a minimal change to make the comments not completely
wrong.
sys/types.h was defining the legacy BSD u_intN_t typedefs using a
construct that was not necessarily consistent with how the C99 uintN_t
typedefs are defined, and is also too complicated for the new script to
understand (it lexes C relatively accurately, but it does not attempt
to expand preprocessor macros, nor does it do any actual parsing).
This patch cuts all of that out and uses bits/types.h's __uintN_t typedefs
to define u_intN_t instead. This is verified to not change the ABI on
any supported architecture, via the c++-types test, which means u_intN_t
and uintN_t were, in fact, consistent on all supported architectures.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
* scripts/check-obsolete-constructs.py: New test script.
* scripts/check-installed-headers.sh: Remove tests for
obsolete typedefs, superseded by check-obsolete-constructs.py.
* Rules: Run scripts/check-obsolete-constructs.py over $(headers)
as a special test. Update commentary.
* posix/bits/types.h (__SQUAD_TYPE, __S64_TYPE): Define as __int64_t.
(__UQUAD_TYPE, __U64_TYPE): Define as __uint64_t.
Update commentary.
* posix/sys/types.h (__u_intN_t): Remove.
(u_int8_t): Typedef using __uint8_t.
(u_int16_t): Typedef using __uint16_t.
(u_int32_t): Typedef using __uint32_t.
(u_int64_t): Typedef using __uint64_t.
Conflicts:
Rules
(textual conflicts due to lack of check-wrapper-headers test.)
diff --git a/Rules b/Rules
index 5abb7270aa8e24aa..a07dbb8d978b5769 100644
--- a/Rules
+++ b/Rules
@@ -82,7 +82,8 @@ $(common-objpfx)dummy.c:
common-generated += dummy.o dummy.c
ifneq "$(headers)" ""
-# Special test of all the installed headers in this directory.
+# Test that all of the headers installed by this directory can be compiled
+# in isolation.
tests-special += $(objpfx)check-installed-headers-c.out
libof-check-installed-headers-c := testsuite
$(objpfx)check-installed-headers-c.out: \
@@ -93,6 +94,8 @@ $(objpfx)check-installed-headers-c.out: \
$(evaluate-test)
ifneq "$(CXX)" ""
+# If a C++ compiler is available, also test that they can be compiled
+# in isolation as C++.
tests-special += $(objpfx)check-installed-headers-cxx.out
libof-check-installed-headers-cxx := testsuite
$(objpfx)check-installed-headers-cxx.out: \
@@ -101,8 +104,19 @@ $(objpfx)check-installed-headers-cxx.out: \
"$(CXX) $(filter-out -std=%,$(CXXFLAGS)) -D_ISOMAC $(+includes)" \
$(headers) > $@; \
$(evaluate-test)
-endif
-endif
+endif # $(CXX)
+
+# Test that none of the headers installed by this directory use certain
+# obsolete constructs (e.g. legacy BSD typedefs superseded by stdint.h).
+# This script does not need $(py-env).
+tests-special += $(objpfx)check-obsolete-constructs.out
+libof-check-obsolete-constructs := testsuite
+$(objpfx)check-obsolete-constructs.out: \
+ $(..)scripts/check-obsolete-constructs.py $(headers)
+ $(PYTHON) $^ > $@ 2>&1; \
+ $(evaluate-test)
+
+endif # $(headers)
# This makes all the auxiliary and test programs.
diff --git a/posix/bits/types.h b/posix/bits/types.h
index 5e22ce41bf4c29b3..64f344c6e7897491 100644
--- a/posix/bits/types.h
+++ b/posix/bits/types.h
@@ -86,7 +86,7 @@ __extension__ typedef unsigned long long int __uintmax_t;
32 -- "natural" 32-bit type (always int)
64 -- "natural" 64-bit type (long or long long)
LONG32 -- 32-bit type, traditionally long
- QUAD -- 64-bit type, always long long
+ QUAD -- 64-bit type, traditionally long long
WORD -- natural type of __WORDSIZE bits (int or long)
LONGWORD -- type of __WORDSIZE bits, traditionally long
@@ -112,14 +112,14 @@ __extension__ typedef unsigned long long int __uintmax_t;
#define __SLONGWORD_TYPE long int
#define __ULONGWORD_TYPE unsigned long int
#if __WORDSIZE == 32
-# define __SQUAD_TYPE __quad_t
-# define __UQUAD_TYPE __u_quad_t
+# define __SQUAD_TYPE __int64_t
+# define __UQUAD_TYPE __uint64_t
# define __SWORD_TYPE int
# define __UWORD_TYPE unsigned int
# define __SLONG32_TYPE long int
# define __ULONG32_TYPE unsigned long int
-# define __S64_TYPE __quad_t
-# define __U64_TYPE __u_quad_t
+# define __S64_TYPE __int64_t
+# define __U64_TYPE __uint64_t
/* We want __extension__ before typedef's that use nonstandard base types
such as `long long' in C89 mode. */
# define __STD_TYPE __extension__ typedef
diff --git a/posix/sys/types.h b/posix/sys/types.h
index db524d6cd13f0379..47eff1a7b1a91c81 100644
--- a/posix/sys/types.h
+++ b/posix/sys/types.h
@@ -154,37 +154,20 @@ typedef unsigned int uint;
#include <bits/stdint-intn.h>
-#if !__GNUC_PREREQ (2, 7)
-
/* These were defined by ISO C without the first `_'. */
-typedef unsigned char u_int8_t;
-typedef unsigned short int u_int16_t;
-typedef unsigned int u_int32_t;
-# if __WORDSIZE == 64
-typedef unsigned long int u_int64_t;
-# else
-__extension__ typedef unsigned long long int u_int64_t;
-# endif
-
-typedef int register_t;
-
-#else
-
-/* For GCC 2.7 and later, we can use specific type-size attributes. */
-# define __u_intN_t(N, MODE) \
- typedef unsigned int u_int##N##_t __attribute__ ((__mode__ (MODE)))
-
-__u_intN_t (8, __QI__);
-__u_intN_t (16, __HI__);
-__u_intN_t (32, __SI__);
-__u_intN_t (64, __DI__);
+typedef __uint8_t u_int8_t;
+typedef __uint16_t u_int16_t;
+typedef __uint32_t u_int32_t;
+typedef __uint64_t u_int64_t;
+#if __GNUC_PREREQ (2, 7)
typedef int register_t __attribute__ ((__mode__ (__word__)));
-
+#else
+typedef int register_t;
+#endif
/* Some code from BIND tests this macro to see if the types above are
defined. */
-#endif
#define __BIT_TYPES_DEFINED__ 1
diff --git a/scripts/check-installed-headers.sh b/scripts/check-installed-headers.sh
index 7a1969b43a144ebb..c2aeea5aabcc7ffd 100644
--- a/scripts/check-installed-headers.sh
+++ b/scripts/check-installed-headers.sh
@@ -16,11 +16,9 @@
# License along with the GNU C Library; if not, see
# <http://www.gnu.org/licenses/>.
-# Check installed headers for cleanliness. For each header, confirm
-# that it's possible to compile a file that includes that header and
-# does nothing else, in several different compilation modes. Also,
-# scan the header for a set of obsolete typedefs that should no longer
-# appear.
+# For each installed header, confirm that it's possible to compile a
+# file that includes that header and does nothing else, in several
+# different compilation modes.
# These compilation switches assume GCC or compatible, which is probably
# fine since we also assume that when _building_ glibc.
@@ -31,13 +29,6 @@ cxx_modes="-std=c++98 -std=gnu++98 -std=c++11 -std=gnu++11"
# These are probably the most commonly used three.
lib_modes="-D_DEFAULT_SOURCE=1 -D_GNU_SOURCE=1 -D_XOPEN_SOURCE=700"
-# sys/types.h+bits/types.h have to define the obsolete types.
-# rpc(svc)/* have the obsolete types too deeply embedded in their API
-# to remove.
-skip_obsolete_type_check='*/sys/types.h|*/bits/types.h|*/rpc/*|*/rpcsvc/*'
-obsolete_type_re=\
-'\<((__)?(quad_t|u(short|int|long|_(char|short|int([0-9]+_t)?|long|quad_t))))\>'
-
if [ $# -lt 3 ]; then
echo "usage: $0 c|c++ \"compile command\" header header header..." >&2
exit 2
@@ -46,14 +37,10 @@ case "$1" in
(c)
lang_modes="$c_modes"
cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.c)
- already="$skip_obsolete_type_check"
;;
(c++)
lang_modes="$cxx_modes"
cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.cc)
- # The obsolete-type check can be skipped for C++; it is
- # sufficient to do it for C.
- already="*"
;;
(*)
echo "usage: $0 c|c++ \"compile command\" header header header..." >&2
@@ -155,22 +142,8 @@ $expanded_lib_mode
int avoid_empty_translation_unit;
EOF
if $cc_cmd -fsyntax-only $lang_mode "$cih_test_c" 2>&1
- then
- includes=$($cc_cmd -fsyntax-only -H $lang_mode \
- "$cih_test_c" 2>&1 | sed -ne 's/^[.][.]* //p')
- for h in $includes; do
- # Don't repeat work.
- eval 'case "$h" in ('"$already"') continue;; esac'
-
- if grep -qE "$obsolete_type_re" "$h"; then
- echo "*** Obsolete types detected:"
- grep -HE "$obsolete_type_re" "$h"
- failed=1
- fi
- already="$already|$h"
- done
- else
- failed=1
+ then :
+ else failed=1
fi
done
done
diff --git a/scripts/check-obsolete-constructs.py b/scripts/check-obsolete-constructs.py
new file mode 100755
index 0000000000000000..ce5c72251f4d7cc0
--- /dev/null
+++ b/scripts/check-obsolete-constructs.py
@@ -0,0 +1,466 @@
+#! /usr/bin/python3
+# Copyright (C) 2019 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
+# <http://www.gnu.org/licenses/>.
+
+"""Verifies that installed headers do not use any obsolete constructs:
+ * legacy BSD typedefs superseded by <stdint.h>:
+ ushort uint ulong u_char u_short u_int u_long u_intNN_t quad_t u_quad_t
+ (sys/types.h is allowed to _define_ these types, but not to use them
+ to define anything else).
+"""
+
+import argparse
+import collections
+import re
+import sys
+
+# Simplified lexical analyzer for C preprocessing tokens.
+# Does not implement trigraphs.
+# Does not implement backslash-newline in the middle of any lexical
+# item other than a string literal.
+# Does not implement universal-character-names in identifiers.
+# Treats prefixed strings (e.g. L"...") as two tokens (L and "...")
+# Accepts non-ASCII characters only within comments and strings.
+
+# Caution: The order of the outermost alternation matters.
+# STRING must be before BAD_STRING, CHARCONST before BAD_CHARCONST,
+# BLOCK_COMMENT before BAD_BLOCK_COM before PUNCTUATOR, and OTHER must
+# be last.
+# Caution: There should be no capturing groups other than the named
+# captures in the outermost alternation.
+
+# For reference, these are all of the C punctuators as of C11:
+# [ ] ( ) { } , ; ? ~
+# ! != * *= / /= ^ ^= = ==
+# # ##
+# % %= %> %: %:%:
+# & &= &&
+# | |= ||
+# + += ++
+# - -= -- ->
+# . ...
+# : :>
+# < <% <: << <<= <=
+# > >= >> >>=
+
+# The BAD_* tokens are not part of the official definition of pp-tokens;
+# they match unclosed strings, character constants, and block comments,
+# so that the regex engine doesn't have to backtrack all the way to the
+# beginning of a broken construct and then emit dozens of junk tokens.
+
+PP_TOKEN_RE_ = re.compile(r"""
+ (?P<STRING> \"(?:[^\"\\\r\n]|\\(?:[\r\n -~]|\r\n))*\")
+ |(?P<BAD_STRING> \"(?:[^\"\\\r\n]|\\[ -~])*)
+ |(?P<CHARCONST> \'(?:[^\'\\\r\n]|\\(?:[\r\n -~]|\r\n))*\')
+ |(?P<BAD_CHARCONST> \'(?:[^\'\\\r\n]|\\[ -~])*)
+ |(?P<BLOCK_COMMENT> /\*(?:\*(?!/)|[^*])*\*/)
+ |(?P<BAD_BLOCK_COM> /\*(?:\*(?!/)|[^*])*\*?)
+ |(?P<LINE_COMMENT> //[^\r\n]*)
+ |(?P<IDENT> [_a-zA-Z][_a-zA-Z0-9]*)
+ |(?P<PP_NUMBER> \.?[0-9](?:[0-9a-df-oq-zA-DF-OQ-Z_.]|[eEpP][+-]?)*)
+ |(?P<PUNCTUATOR>
+ [,;?~(){}\[\]]
+ | [!*/^=]=?
+ | \#\#?
+ | %(?:[=>]|:(?:%:)?)?
+ | &[=&]?
+ |\|[=|]?
+ |\+[=+]?
+ | -[=->]?
+ |\.(?:\.\.)?
+ | :>?
+ | <(?:[%:]|<(?:=|<=?)?)?
+ | >(?:=|>=?)?)
+ |(?P<ESCNL> \\(?:\r|\n|\r\n))
+ |(?P<WHITESPACE> [ \t\n\r\v\f]+)
+ |(?P<OTHER> .)
+""", re.DOTALL | re.VERBOSE)
+
+HEADER_NAME_RE_ = re.compile(r"""
+ < [^>\r\n]+ >
+ | " [^"\r\n]+ "
+""", re.DOTALL | re.VERBOSE)
+
+ENDLINE_RE_ = re.compile(r"""\r|\n|\r\n""")
+
+# based on the sample code in the Python re documentation
+Token_ = collections.namedtuple("Token", (
+ "kind", "text", "line", "column", "context"))
+Token_.__doc__ = """
+ One C preprocessing token, comment, or chunk of whitespace.
+ 'kind' identifies the token type, which will be one of:
+ STRING, CHARCONST, BLOCK_COMMENT, LINE_COMMENT, IDENT,
+ PP_NUMBER, PUNCTUATOR, ESCNL, WHITESPACE, HEADER_NAME,
+ or OTHER. The BAD_* alternatives in PP_TOKEN_RE_ are
+ handled within tokenize_c, below.
+
+ 'text' is the sequence of source characters making up the token;
+ no decoding whatsoever is performed.
+
+ 'line' and 'column' give the position of the first character of the
+ token within the source file. They are both 1-based.
+
+ 'context' indicates whether or not this token occurred within a
+ preprocessing directive; it will be None for running text,
+ '<null>' for the leading '#' of a directive line (because '#'
+ all by itself on a line is a "null directive"), or the name of
+ the directive for tokens within a directive line, starting with
+ the IDENT for the name itself.
+"""
+
+def tokenize_c(file_contents, reporter):
+ """Yield a series of Token objects, one for each preprocessing
+ token, comment, or chunk of whitespace within FILE_CONTENTS.
+ The REPORTER object is expected to have one method,
+ reporter.error(token, message), which will be called to
+ indicate a lexical error at the position of TOKEN.
+ If MESSAGE contains the four-character sequence '{!r}', that
+ is expected to be replaced by repr(token.text).
+ """
+
+ Token = Token_
+ PP_TOKEN_RE = PP_TOKEN_RE_
+ ENDLINE_RE = ENDLINE_RE_
+ HEADER_NAME_RE = HEADER_NAME_RE_
+
+ line_num = 1
+ line_start = 0
+ pos = 0
+ limit = len(file_contents)
+ directive = None
+ at_bol = True
+ while pos < limit:
+ if directive == "include":
+ mo = HEADER_NAME_RE.match(file_contents, pos)
+ if mo:
+ kind = "HEADER_NAME"
+ directive = "after_include"
+ else:
+ mo = PP_TOKEN_RE.match(file_contents, pos)
+ kind = mo.lastgroup
+ if kind != "WHITESPACE":
+ directive = "after_include"
+ else:
+ mo = PP_TOKEN_RE.match(file_contents, pos)
+ kind = mo.lastgroup
+
+ text = mo.group()
+ line = line_num
+ column = mo.start() - line_start
+ adj_line_start = 0
+ # only these kinds can contain a newline
+ if kind in ("WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT",
+ "STRING", "CHARCONST", "BAD_BLOCK_COM", "ESCNL"):
+ for tmo in ENDLINE_RE.finditer(text):
+ line_num += 1
+ adj_line_start = tmo.end()
+ if adj_line_start:
+ line_start = mo.start() + adj_line_start
+
+ # Track whether or not we are scanning a preprocessing directive.
+ if kind == "LINE_COMMENT" or (kind == "WHITESPACE" and adj_line_start):
+ at_bol = True
+ directive = None
+ else:
+ if kind == "PUNCTUATOR" and text == "#" and at_bol:
+ directive = "<null>"
+ elif kind == "IDENT" and directive == "<null>":
+ directive = text
+ at_bol = False
+
+ # Report ill-formed tokens and rewrite them as their well-formed
+ # equivalents, so downstream processing doesn't have to know about them.
+ # (Rewriting instead of discarding provides better error recovery.)
+ if kind == "BAD_BLOCK_COM":
+ reporter.error(Token("BAD_BLOCK_COM", "", line, column+1, ""),
+ "unclosed block comment")
+ text += "*/"
+ kind = "BLOCK_COMMENT"
+ elif kind == "BAD_STRING":
+ reporter.error(Token("BAD_STRING", "", line, column+1, ""),
+ "unclosed string")
+ text += "\""
+ kind = "STRING"
+ elif kind == "BAD_CHARCONST":
+ reporter.error(Token("BAD_CHARCONST", "", line, column+1, ""),
+ "unclosed char constant")
+ text += "'"
+ kind = "CHARCONST"
+
+ tok = Token(kind, text, line, column+1,
+ "include" if directive == "after_include" else directive)
+ # Do not complain about OTHER tokens inside macro definitions.
+ # $ and @ appear in macros defined by headers intended to be
+ # included from assembly language, e.g. sysdeps/mips/sys/asm.h.
+ if kind == "OTHER" and directive != "define":
+ self.error(tok, "stray {!r} in program")
+
+ yield tok
+ pos = mo.end()
+
+#
+# Base and generic classes for individual checks.
+#
+
+class ConstructChecker:
+ """Scan a stream of C preprocessing tokens and possibly report
+ problems with them. The REPORTER object passed to __init__ has
+ one method, reporter.error(token, message), which should be
+ called to indicate a problem detected at the position of TOKEN.
+ If MESSAGE contains the four-character sequence '{!r}' then that
+ will be replaced with a textual representation of TOKEN.
+ """
+ def __init__(self, reporter):
+ self.reporter = reporter
+
+ def examine(self, tok):
+ """Called once for each token in a header file.
+ Call self.reporter.error if a problem is detected.
+ """
+ raise NotImplementedError
+
+ def eof(self):
+ """Called once at the end of the stream. Subclasses need only
+ override this if it might have something to do."""
+ pass
+
+class NoCheck(ConstructChecker):
+ """Generic checker class which doesn't do anything. Substitute this
+ class for a real checker when a particular check should be skipped
+ for some file."""
+
+ def examine(self, tok):
+ pass
+
+#
+# Check for obsolete type names.
+#
+
+# The obsolete type names we're looking for:
+OBSOLETE_TYPE_RE_ = re.compile(r"""\A
+ (__)?
+ ( quad_t
+ | u(?: short | int | long
+ | _(?: char | short | int(?:[0-9]+_t)? | long | quad_t )))
+\Z""", re.VERBOSE)
+
+class ObsoleteNotAllowed(ConstructChecker):
+ """Don't allow any use of the obsolete typedefs."""
+ def examine(self, tok):
+ if OBSOLETE_TYPE_RE_.match(tok.text):
+ self.reporter.error(tok, "use of {!r}")
+
+class ObsoletePrivateDefinitionsAllowed(ConstructChecker):
+ """Allow definitions of the private versions of the
+ obsolete typedefs; that is, 'typedef [anything] __obsolete;'
+ """
+ def __init__(self, reporter):
+ super().__init__(reporter)
+ self.in_typedef = False
+ self.prev_token = None
+
+ def examine(self, tok):
+ # bits/types.h hides 'typedef' in a macro sometimes.
+ if (tok.kind == "IDENT"
+ and tok.text in ("typedef", "__STD_TYPE")
+ and tok.context is None):
+ self.in_typedef = True
+ elif tok.kind == "PUNCTUATOR" and tok.text == ";" and self.in_typedef:
+ self.in_typedef = False
+ if self.prev_token.kind == "IDENT":
+ m = OBSOLETE_TYPE_RE_.match(self.prev_token.text)
+ if m and m.group(1) != "__":
+ self.reporter.error(self.prev_token, "use of {!r}")
+ self.prev_token = None
+ else:
+ self._check_prev()
+
+ self.prev_token = tok
+
+ def eof(self):
+ self._check_prev()
+
+ def _check_prev(self):
+ if (self.prev_token is not None
+ and self.prev_token.kind == "IDENT"
+ and OBSOLETE_TYPE_RE_.match(self.prev_token.text)):
+ self.reporter.error(self.prev_token, "use of {!r}")
+
+class ObsoletePublicDefinitionsAllowed(ConstructChecker):
+ """Allow definitions of the public versions of the obsolete
+ typedefs. Only specific forms of definition are allowed:
+
+ typedef __obsolete obsolete; // identifiers must agree
+ typedef __uintN_t u_intN_t; // N must agree
+ typedef unsigned long int ulong;
+ typedef unsigned short int ushort;
+ typedef unsigned int uint;
+ """
+ def __init__(self, reporter):
+ super().__init__(reporter)
+ self.typedef_tokens = []
+
+ def examine(self, tok):
+ if tok.kind in ("WHITESPACE", "BLOCK_COMMENT",
+ "LINE_COMMENT", "NL", "ESCNL"):
+ pass
+
+ elif (tok.kind == "IDENT" and tok.text == "typedef"
+ and tok.context is None):
+ if self.typedef_tokens:
+ self.reporter.error(tok, "typedef inside typedef")
+ self._reset()
+ self.typedef_tokens.append(tok)
+
+ elif tok.kind == "PUNCTUATOR" and tok.text == ";":
+ self._finish()
+
+ elif self.typedef_tokens:
+ self.typedef_tokens.append(tok)
+
+ def eof(self):
+ self._reset()
+
+ def _reset(self):
+ while self.typedef_tokens:
+ tok = self.typedef_tokens.pop(0)
+ if tok.kind == "IDENT" and OBSOLETE_TYPE_RE_.match(tok.text):
+ self.reporter.error(tok, "use of {!r}")
+
+ def _finish(self):
+ if not self.typedef_tokens: return
+ if self.typedef_tokens[-1].kind == "IDENT":
+ m = OBSOLETE_TYPE_RE_.match(self.typedef_tokens[-1].text)
+ if m:
+ if self._permissible_public_definition(m):
+ self.typedef_tokens.clear()
+ self._reset()
+
+ def _permissible_public_definition(self, m):
+ if m.group(1) == "__": return False
+ name = m.group(2)
+ toks = self.typedef_tokens
+ ntok = len(toks)
+ if ntok == 3 and toks[1].kind == "IDENT":
+ defn = toks[1].text
+ n = OBSOLETE_TYPE_RE_.match(defn)
+ if n and n.group(1) == "__" and n.group(2) == name:
+ return True
+
+ if (name[:5] == "u_int" and name[-2:] == "_t"
+ and defn[:6] == "__uint" and defn[-2:] == "_t"
+ and name[5:-2] == defn[6:-2]):
+ return True
+
+ return False
+
+ if (name == "ulong" and ntok == 5
+ and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
+ and toks[2].kind == "IDENT" and toks[2].text == "long"
+ and toks[3].kind == "IDENT" and toks[3].text == "int"):
+ return True
+
+ if (name == "ushort" and ntok == 5
+ and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
+ and toks[2].kind == "IDENT" and toks[2].text == "short"
+ and toks[3].kind == "IDENT" and toks[3].text == "int"):
+ return True
+
+ if (name == "uint" and ntok == 4
+ and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
+ and toks[2].kind == "IDENT" and toks[2].text == "int"):
+ return True
+
+ return False
+
+def ObsoleteTypedefChecker(reporter, fname):
+ """Factory: produce an instance of the appropriate
+ obsolete-typedef checker for FNAME."""
+
+ # The obsolete rpc/ and rpcsvc/ headers are allowed to use the
+ # obsolete types, because it would be more trouble than it's
+ # worth to remove them from headers that we intend to stop
+ # installing eventually anyway.
+ if (fname.startswith("rpc/")
+ or fname.startswith("rpcsvc/")
+ or "/rpc/" in fname
+ or "/rpcsvc/" in fname):
+ return NoCheck(reporter)
+
+ # bits/types.h is allowed to define the __-versions of the
+ # obsolete types.
+ if (fname == "bits/types.h"
+ or fname.endswith("/bits/types.h")):
+ return ObsoletePrivateDefinitionsAllowed(reporter)
+
+ # sys/types.h is allowed to use the __-versions of the
+ # obsolete types, but only to define the unprefixed versions.
+ if (fname == "sys/types.h"
+ or fname.endswith("/sys/types.h")):
+ return ObsoletePublicDefinitionsAllowed(reporter)
+
+ return ObsoleteNotAllowed(reporter)
+
+#
+# Master control
+#
+
+class HeaderChecker:
+ """Perform all of the checks on each header. This is also the
+ "reporter" object expected by tokenize_c and ConstructChecker.
+ """
+ def __init__(self):
+ self.fname = None
+ self.status = 0
+
+ def error(self, tok, message):
+ self.status = 1
+ if '{!r}' in message:
+ message = message.format(tok.text)
+ sys.stderr.write("{}:{}:{}: error: {}\n".format(
+ self.fname, tok.line, tok.column, message))
+
+ def check(self, fname):
+ self.fname = fname
+ try:
+ with open(fname, "rt") as fp:
+ contents = fp.read()
+ except OSError as e:
+ sys.stderr.write("{}: {}\n".format(fname, e.strerror))
+ self.status = 1
+ return
+
+ typedef_checker = ObsoleteTypedefChecker(self, self.fname)
+
+ for tok in tokenize_c(contents, self):
+ typedef_checker.examine(tok)
+
+def main():
+ ap = argparse.ArgumentParser(description=__doc__)
+ ap.add_argument("headers", metavar="header", nargs="+",
+ help="one or more headers to scan for obsolete constructs")
+ args = ap.parse_args()
+
+ checker = HeaderChecker()
+ for fname in args.headers:
+ # Headers whose installed name begins with "finclude/" contain
+ # Fortran, not C, and this program should completely ignore them.
+ if not (fname.startswith("finclude/") or "/finclude/" in fname):
+ checker.check(fname)
+ sys.exit(checker.status)
+
+main()

View File

@ -0,0 +1,24 @@
commit d5dfad4326fc683c813df1e37bbf5cf920591c8e
Author: Marcin Kościelnicki <mwk@0x04.net>
Date: Thu Nov 21 00:20:15 2019 +0100
rtld: Check __libc_enable_secure before honoring LD_PREFER_MAP_32BIT_EXEC (CVE-2019-19126) [BZ #25204]
The problem was introduced in glibc 2.23, in commit
b9eb92ab05204df772eb4929eccd018637c9f3e9
("Add Prefer_MAP_32BIT_EXEC to map executable pages with MAP_32BIT").
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/dl-librecon.h b/sysdeps/unix/sysv/linux/x86_64/64/dl-librecon.h
index 194369174df08946..ac694c032e7baf87 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/dl-librecon.h
+++ b/sysdeps/unix/sysv/linux/x86_64/64/dl-librecon.h
@@ -31,7 +31,8 @@
environment variable, LD_PREFER_MAP_32BIT_EXEC. */
#define EXTRA_LD_ENVVARS \
case 21: \
- if (memcmp (envline, "PREFER_MAP_32BIT_EXEC", 21) == 0) \
+ if (!__libc_enable_secure \
+ && memcmp (envline, "PREFER_MAP_32BIT_EXEC", 21) == 0) \
GLRO(dl_x86_cpu_features).feature[index_arch_Prefer_MAP_32BIT_EXEC] \
|= bit_arch_Prefer_MAP_32BIT_EXEC; \
break;

View File

@ -0,0 +1,70 @@
commit bfa864e1645e140da2e1aae3cf0d0ba0674f6eb5
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date: Tue Nov 12 19:18:32 2019 +0100
Don't use a custom wrapper macro around __has_include (bug 25189).
This causes issues when using clang with -frewrite-includes to e.g.,
submit the translation unit to a distributed compiler.
In my case, I was building Firefox using sccache.
See [1] for a reduced test-case since I initially thought this was a
clang bug, and [2] for more context.
Apparently doing this is invalid C++ per [cpp.cond], which mentions [3]:
> The #ifdef and #ifndef directives, and the defined conditional
> inclusion operator, shall treat __has_include and __has_cpp_attribute
> as if they were the names of defined macros. The identifiers
> __has_include and __has_cpp_attribute shall not appear in any context
> not mentioned in this subclause.
[1]: https://bugs.llvm.org/show_bug.cgi?id=43982
[2]: https://bugs.llvm.org/show_bug.cgi?id=37990
[3]: http://eel.is/c++draft/cpp.cond#7.sentence-2
Change-Id: Id4b8ee19176a9e4624b533087ba870c418f27e60
diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 9e840e602f815d86..3f6fe3cc8563b493 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -412,14 +412,6 @@
# define __glibc_has_attribute(attr) 0
#endif
-#ifdef __has_include
-/* Do not use a function-like macro, so that __has_include can inhibit
- macro expansion. */
-# define __glibc_has_include __has_include
-#else
-# define __glibc_has_include(header) 0
-#endif
-
#if (!defined _Noreturn \
&& (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 201112 \
&& !__GNUC_PREREQ (4,7))
diff --git a/sysdeps/unix/sysv/linux/bits/statx.h b/sysdeps/unix/sysv/linux/bits/statx.h
index 206878723fd37881..aaccfdc2dc03a1dc 100644
--- a/sysdeps/unix/sysv/linux/bits/statx.h
+++ b/sysdeps/unix/sysv/linux/bits/statx.h
@@ -26,11 +26,13 @@
/* Use "" to work around incorrect macro expansion of the
__has_include argument (GCC PR 80005). */
-#if __glibc_has_include ("linux/stat.h")
-# include "linux/stat.h"
-# ifdef STATX_TYPE
-# define __statx_timestamp_defined 1
-# define __statx_defined 1
+#ifdef __has_include
+# if __has_include ("linux/stat.h")
+# include "linux/stat.h"
+# ifdef STATX_TYPE
+# define __statx_timestamp_defined 1
+# define __statx_defined 1
+# endif
# endif
#endif

View File

@ -1,56 +0,0 @@
#
# /etc/nsswitch.conf
#
# An example Name Service Switch config file. This file should be
# sorted with the most-used services at the beginning.
#
# The entry '[NOTFOUND=return]' means that the search for an
# entry should stop if the search in the previous entry turned
# up nothing. Note that if the search failed due to some other reason
# (like no NIS server responding) then the search continues with the
# next entry.
#
# Valid entries include:
#
# nisplus Use NIS+ (NIS version 3)
# nis Use NIS (NIS version 2), also called YP
# dns Use DNS (Domain Name Service)
# files Use the local files in /etc
# db Use the pre-processed /var/db files
# compat Use /etc files plus *_compat pseudo-databases
# hesiod Use Hesiod (DNS) for user lookups
# sss Use sssd (System Security Services Daemon)
# [NOTFOUND=return] Stop searching if not found so far
#
# 'sssd' performs its own 'files'-based caching, so it should
# generally come before 'files'.
# To use 'db', install the nss_db package, and put the 'db' in front
# of 'files' for entries you want to be looked up first in the
# databases, like this:
#
# passwd: db files
# shadow: db files
# group: db files
passwd: sss files
shadow: files sss
group: sss files
hosts: files dns myhostname
bootparams: files
ethers: files
netmasks: files
networks: files
protocols: files
rpc: files
services: files sss
netgroup: sss
publickey: files
automount: files sss
aliases: files

View File

@ -1,6 +1,6 @@
%define glibcsrcdir glibc-2.28
%define glibcversion 2.28
%define glibcrelease 72%{?dist}.1
%define glibcrelease 101%{?dist}
# Pre-release tarballs are pulled in from git using a command that is
# effectively:
#
@ -121,7 +121,6 @@ URL: http://www.gnu.org/software/glibc/
Source0: %{?glibc_release_url}%{glibcsrcdir}.tar.xz
Source1: build-locale-archive.c
Source4: nscd.conf
Source7: nsswitch.conf
Source8: power6emul.c
Source9: bench.mk
Source10: glibc-bench-compare
@ -315,7 +314,88 @@ Patch180: glibc-rh1717438.patch
Patch181: glibc-rh1727152.patch
Patch182: glibc-rh1724975.patch
Patch183: glibc-rh1722215.patch
Patch184: glibc-rh1777797.patch
Patch184: glibc-rh1764234-1.patch
Patch185: glibc-rh1764234-2.patch
Patch186: glibc-rh1764234-3.patch
Patch187: glibc-rh1764234-4.patch
Patch188: glibc-rh1764234-5.patch
Patch189: glibc-rh1764234-6.patch
Patch190: glibc-rh1764234-7.patch
Patch191: glibc-rh1764234-8.patch
Patch192: glibc-rh1747505-1.patch
Patch193: glibc-rh1747505-2.patch
Patch194: glibc-rh1747505-3.patch
Patch195: glibc-rh1747505-4.patch
Patch196: glibc-rh1747453.patch
Patch197: glibc-rh1764241.patch
Patch198: glibc-rh1746933-1.patch
Patch199: glibc-rh1746933-2.patch
Patch200: glibc-rh1746933-3.patch
Patch201: glibc-rh1735747-1.patch
Patch202: glibc-rh1735747-2.patch
Patch203: glibc-rh1764226-1.patch
Patch204: glibc-rh1764226-2.patch
Patch205: glibc-rh1764226-3.patch
Patch206: glibc-rh1764218-1.patch
Patch207: glibc-rh1764218-2.patch
Patch208: glibc-rh1764218-3.patch
Patch209: glibc-rh1682954.patch
Patch210: glibc-rh1746928.patch
Patch211: glibc-rh1747502.patch
Patch212: glibc-rh1747502-1.patch
Patch213: glibc-rh1747502-2.patch
Patch214: glibc-rh1747502-3.patch
Patch215: glibc-rh1747502-4.patch
Patch216: glibc-rh1747502-5.patch
Patch217: glibc-rh1747502-6.patch
Patch218: glibc-rh1747502-7.patch
Patch219: glibc-rh1747502-8.patch
Patch220: glibc-rh1747502-9.patch
Patch221: glibc-rh1726638-1.patch
Patch222: glibc-rh1726638-2.patch
Patch223: glibc-rh1726638-3.patch
Patch224: glibc-rh1764238-1.patch
Patch225: glibc-rh1764238-2.patch
Patch226: glibc-rh1764242.patch
Patch227: glibc-rh1769304.patch
Patch228: glibc-rh1749439-1.patch
Patch229: glibc-rh1749439-2.patch
Patch230: glibc-rh1749439-3.patch
Patch231: glibc-rh1749439-4.patch
Patch232: glibc-rh1749439-5.patch
Patch233: glibc-rh1749439-6.patch
Patch234: glibc-rh1749439-7.patch
Patch235: glibc-rh1749439-8.patch
Patch236: glibc-rh1749439-9.patch
Patch237: glibc-rh1749439-10.patch
Patch238: glibc-rh1749439-11.patch
Patch239: glibc-rh1749439-12.patch
Patch240: glibc-rh1749439-13.patch
Patch241: glibc-rh1764231-1.patch
Patch242: glibc-rh1764231-2.patch
Patch243: glibc-rh1764235.patch
Patch244: glibc-rh1361965.patch
Patch245: glibc-rh1764223.patch
Patch246: glibc-rh1764214.patch
Patch247: glibc-rh1774021.patch
Patch248: glibc-rh1775294.patch
Patch249: glibc-rh1777241.patch
Patch250: glibc-rh1410154-1.patch
Patch251: glibc-rh1410154-2.patch
Patch252: glibc-rh1410154-3.patch
Patch253: glibc-rh1410154-4.patch
Patch254: glibc-rh1410154-5.patch
Patch255: glibc-rh1410154-6.patch
Patch256: glibc-rh1410154-7.patch
Patch257: glibc-rh1410154-8.patch
Patch258: glibc-rh1410154-9.patch
Patch259: glibc-rh1410154-10.patch
Patch260: glibc-rh1410154-11.patch
Patch261: glibc-rh1410154-12.patch
Patch262: glibc-rh1410154-13.patch
Patch263: glibc-rh1410154-14.patch
Patch264: glibc-rh1410154-15.patch
Patch265: glibc-rh1410154-16.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -1238,7 +1318,7 @@ mv %{glibc_sysroot}%{_prefix}/lib/locale/*.filelist .
# Install configuration files for services
##############################################################################
install -p -m 644 %{SOURCE7} %{glibc_sysroot}/etc/nsswitch.conf
install -p -m 644 nss/nsswitch.conf %{glibc_sysroot}/etc/nsswitch.conf
%ifnarch %{auxarches}
# This is for ncsd - in glibc 2.2
@ -2202,8 +2282,95 @@ fi
%files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
%changelog
* Thu Nov 28 2019 Florian Weimer <fweimer@redhat.com> - 2.28-72.1
- s390x: Fix z15 strstr for patterns crossing pages (#1777797)
* Thu Jan 16 2020 Florian Weimer <fweimer@redhat.com> - 2.28-101
- ld.so: Reset GL (dl_initfirst) pointer on dlopen failure (#1410154)
* Fri Dec 13 2019 Florian Weimer <fweimer@redhat.com> - 2.28-100
- Roll back dynamic linker state on dlopen failure (#1410154)
* Wed Nov 27 2019 Florian Weimer <fweimer@redhat.com> - 2.28-99
- s390x: Fix z15 strstr for patterns crossing pages (#1777241)
* Wed Nov 27 2019 Florian Weimer <fweimer@redhat.com> - 2.28-98
- Rebuild with new rpm (#1654901)
* Fri Nov 22 2019 Florian Weimer <fweimer@redhat.com> - 2.28-97
- Avoid invalid __has_include in <sys/stat.h> (#1775294)
* Fri Nov 22 2019 Florian Weimer <fweimer@redhat.com> - 2.28-96
- x86-64: Ignore LD_PREFER_MAP_32BIT_EXEC in SUID binaries (#1774021)
* Thu Nov 14 2019 DJ Delorie <dj@redhat.com> - 2.28-95
- Fix alignment of TLS variables for tls variant TLS_TCB_AT_TP (#1764214)
* Thu Nov 14 2019 DJ Delorie <dj@redhat.com> - 2.28-94
- Refuse to dlopen PIE objects (#1764223)
* Thu Nov 14 2019 Carlos O'Donell <carlos@redhat.com> - 2.28-93
- Fix C.UTF-8 locale source ellipsis expressions (#1361965)
* Thu Nov 14 2019 Carlos O'Donell <carlos@redhat.com> - 2.28-92
- Fix hangs during malloc tracing (#1764235)
* Thu Nov 14 2019 Carlos O'Donell <carlos@redhat.com> - 2.28-91
- Support moving versioned symbols between sonames (#1764231)
* Wed Nov 13 2019 Florian Weimer <fweimer@redhat.com> - 2.28-90
- Avoid creating stale utmp entries for repeated pututxline (#1749439)
* Wed Nov 6 2019 Florian Weimer <fweimer@redhat.com> - 2.28-89
- Backport more precise tokenizer for installed headers test (#1769304)
* Wed Nov 6 2019 Florian Weimer <fweimer@redhat.com> - 2.28-88
- math: Enable some math builtins for clang in LLVM Toolset (#1764242)
* Wed Nov 6 2019 Florian Weimer <fweimer@redhat.com> - 2.28-87
- Support Fortran vectorized math functions with GCC Toolset 9 (#1764238)
* Wed Nov 6 2019 Florian Weimer <fweimer@redhat.com> - 2.28-86
- aarch64: Support STO_AARCH64_VARIANT_PCS, DT_AARCH64_VARIANT_PCS (#1726638)
* Mon Nov 4 2019 DJ Delorie <dj@redhat.com> - 2.28-85
- Add more test-in-container support (#1747502)
* Fri Nov 1 2019 DJ Delorie <dj@redhat.com> - 2.28-84
- Fix calling getpwent after endpwent (#1747502)
* Fri Nov 1 2019 DJ Delorie <dj@redhat.com> - 2.28-83
- nptl: Avoid fork handler lock for async-signal-safe fork (#1746928)
* Thu Oct 31 2019 DJ Delorie <dj@redhat.com> - 2.28-82
- Call _dl_open_check after relocation (#1682954)
* Thu Oct 31 2019 Arjun Shankar <arjun@redhat.com> - 2.28-81
- Add malloc fastbin tunable (#1764218)
* Thu Oct 31 2019 Arjun Shankar <arjun@redhat.com> - 2.28-80
- Fix race condition in tst-clone3 and add a new ldconfig test,
tst-ldconfig-bad-aux-cache (#1764226)
* Thu Oct 31 2019 Arjun Shankar <arjun@redhat.com> - 2.28-79
- Remove unwanted whitespace from size lines and account for top chunk in
malloc_info output (#1735747)
* Wed Oct 30 2019 Arjun Shankar <arjun@redhat.com> - 2.28-78
- Enhance malloc tcache (#1746933)
* Tue Oct 29 2019 Patsy Griffin <patsy@redhat.com> - 2.28-77
- Don't define initgroups in nsswitch.conf (#1747505)
* Mon Oct 28 2019 Patsy Griffin <patsy@redhat.com> - 2.28-76
- libio: Remove codecvt vtable. (#1764241)
* Mon Oct 28 2019 Patsy Griffin <patsy@redhat.com> - 2.28-75
- Implement --preload option for the dynamic linker.(#1747453)
* Mon Oct 28 2019 Patsy Griffin <patsy@redhat.com> - 2.28-74
- Make nsswitch.conf more distribution friendly.
Improve nscd.conf comments. (#1747505)
* Fri Oct 25 2019 Patsy Griffin <patsy@redhat.com> - 2.28-73
- Update system call names list to Linux 5.3 (#1764234)
* Mon Jul 22 2019 Carlos O'Donell <carlos@redhat.com> - 2.28-72
- Skip wide buffer handling for legacy stdio handles (#1722215)