import glibc-2.28-225.el8

This commit is contained in:
CentOS Sources 2023-05-16 06:23:16 +00:00 committed by root
parent 362627e54e
commit b73861e187
65 changed files with 13556 additions and 2 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,79 @@
commit dbb75513f5cf9285c77c9e55777c5c35b653f890
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Sep 6 07:38:10 2022 +0200
elf: Rename _dl_sort_maps parameter from skip to force_first
The new implementation will not be able to skip an arbitrary number
of objects.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index 99354dc08a010dd3..7a586749adc3fa7d 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -27,12 +27,12 @@
If FOR_FINI is true, this is called for finishing an object. */
static void
_dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
- unsigned int skip, bool for_fini)
+ bool force_first, bool for_fini)
{
/* Allows caller to do the common optimization of skipping the first map,
usually the main binary. */
- maps += skip;
- nmaps -= skip;
+ maps += force_first;
+ nmaps -= force_first;
/* A list of one element need not be sorted. */
if (nmaps <= 1)
@@ -182,7 +182,7 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map,
static void
_dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
- unsigned int skip __attribute__ ((unused)), bool for_fini)
+ bool force_first __attribute__ ((unused)), bool for_fini)
{
for (int i = nmaps - 1; i >= 0; i--)
maps[i]->l_visited = 0;
@@ -286,7 +286,7 @@ _dl_sort_maps_init (void)
void
_dl_sort_maps (struct link_map **maps, unsigned int nmaps,
- unsigned int skip, bool for_fini)
+ bool force_first, bool for_fini)
{
/* It can be tempting to use a static function pointer to store and call
the current selected sorting algorithm routine, but experimentation
@@ -296,9 +296,9 @@ _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
input cases. A simple if-case with direct function calls appears to
be the fastest. */
if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original))
- _dl_sort_maps_original (maps, nmaps, skip, for_fini);
+ _dl_sort_maps_original (maps, nmaps, force_first, for_fini);
else
- _dl_sort_maps_dfs (maps, nmaps, skip, for_fini);
+ _dl_sort_maps_dfs (maps, nmaps, force_first, for_fini);
}
#endif /* HAVE_TUNABLES. */
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 9f09a4a280396659..2c1b4c47c6a6c643 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1056,9 +1056,11 @@ extern void _dl_init (struct link_map *main_map, int argc, char **argv,
initializer functions have completed. */
extern void _dl_fini (void) attribute_hidden;
-/* Sort array MAPS according to dependencies of the contained objects. */
+/* Sort array MAPS according to dependencies of the contained objects.
+ If FORCE_FIRST, MAPS[0] keeps its place even if the dependencies
+ say otherwise. */
extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
- unsigned int skip, bool for_fini) attribute_hidden;
+ bool force_first, bool for_fini) attribute_hidden;
/* The dynamic linker calls this function before and having changing
any shared object mappings. The `r_state' member of `struct r_debug'

View File

@ -0,0 +1,90 @@
commit 1df71d32fe5f5905ffd5d100e5e9ca8ad6210891
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Sep 20 11:00:42 2022 +0200
elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937)
The implementation in _dl_close_worker requires that the first
element of l_initfini is always this very map (“We are always the
zeroth entry, and since we don't include ourselves in the
dependency analysis start at 1.”). Rather than fixing that
assumption, this commit adds an implementation of the force_first
argument to the new dependency sorting algorithm. This also means
that the directly dlopen'ed shared object is always initialized last,
which is the least surprising behavior in the presence of cycles.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index 7a586749adc3fa7d..6f5c17b47b98fbc7 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -182,8 +182,9 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map,
static void
_dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
- bool force_first __attribute__ ((unused)), bool for_fini)
+ bool force_first, bool for_fini)
{
+ struct link_map *first_map = maps[0];
for (int i = nmaps - 1; i >= 0; i--)
maps[i]->l_visited = 0;
@@ -208,14 +209,6 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
Adjusting the order so that maps[0] is last traversed naturally avoids
this problem.
- Further, the old "optimization" of skipping the main object at maps[0]
- from the call-site (i.e. _dl_sort_maps(maps+1,nmaps-1)) is in general
- no longer valid, since traversing along object dependency-links
- may "find" the main object even when it is not included in the initial
- order (e.g. a dlopen()'ed shared object can have circular dependencies
- linked back to itself). In such a case, traversing N-1 objects will
- create a N-object result, and raise problems.
-
To summarize, just passing in the full list, and iterating from back
to front makes things much more straightforward. */
@@ -274,6 +267,27 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
}
memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
+
+ /* Skipping the first object at maps[0] is not valid in general,
+ since traversing along object dependency-links may "find" that
+ first object even when it is not included in the initial order
+ (e.g., a dlopen'ed shared object can have circular dependencies
+ linked back to itself). In such a case, traversing N-1 objects
+ will create a N-object result, and raise problems. Instead,
+ force the object back into first place after sorting. This naive
+ approach may introduce further dependency ordering violations
+ compared to rotating the cycle until the first map is again in
+ the first position, but as there is a cycle, at least one
+ violation is already present. */
+ if (force_first && maps[0] != first_map)
+ {
+ int i;
+ for (i = 0; maps[i] != first_map; ++i)
+ ;
+ assert (i < nmaps);
+ memmove (&maps[1], maps, i * sizeof (maps[0]));
+ maps[0] = first_map;
+ }
}
void
diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
index 5f7f18ef270bc12d..4bf9052db16fb352 100644
--- a/elf/dso-sort-tests-1.def
+++ b/elf/dso-sort-tests-1.def
@@ -64,3 +64,10 @@ output: b>a>{}<a<b
tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
+
+# Test that even in the presence of dependency loops involving dlopen'ed
+# object, that object is initialized last (and not unloaded prematurely).
+# Final destructor order is indeterminate due to the cycle.
+tst-bz28937: {+a;+b;-b;+c;%c};a->a1;a->a2;a2->a;b->b1;c->a1;c=>a1
+output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a<a2<c<a1
+output(glibc.rtld.dynamic_sort=2): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a2<a<c<a1

View File

@ -0,0 +1,35 @@
Downstream-specific patch to link DSO sorting tests with -ldl
if needed. Upstream does not need this because <dlfcn.h> interfaces
are part of libc.
diff --git a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py
index 43b5ec4d920ad6a3..ae85e0f4a6ae5b3e 100644
--- a/scripts/dso-ordering-test.py
+++ b/scripts/dso-ordering-test.py
@@ -657,6 +657,8 @@ def process_testcase(t):
% (test_name + "-" + dep + ".FAKE.so",
("$(objpfx)" + test_subdir + "/"
+ test_name + "-" + dep + ".so")))
+ makefile.write(
+ "LDLIBS-%s += -Wl,--as-needed -ldl -Wl,--no-as-needed\n" % dso)
rule = ("$(objpfx)" + test_subdir + "/"
+ test_name + "-" + dep + ".FAKE.os: "
"$(objpfx)" + test_srcdir
@@ -685,6 +687,8 @@ def process_testcase(t):
+ test_descr.soname_map[o] + ".so")
ldflags += (" -Wl,-soname=" + soname)
makefile.write("LDFLAGS-%s = %s\n" % (dso, ldflags))
+ makefile.write(
+ "LDLIBS-%s += -Wl,--as-needed -ldl -Wl,--no-as-needed\n" % dso)
if o in test_descr.callrefs:
makefile.write("%s-no-z-defs = yes\n" % (dso))
@@ -702,6 +706,8 @@ def process_testcase(t):
+ test_descr.soname_map['#'] + ".so")
ldflags += (" -Wl,-soname=" + soname)
makefile.write("LDFLAGS-%s = %s\n" % (test_name, ldflags))
+ makefile.write(
+ "LDLIBS-%s += -Wl,--as-needed -ldl -Wl,--no-as-needed\n" % test_name)
rule = ("$(objpfx)" + test_subdir + "/" + test_name + ".o: "
"$(objpfx)" + test_srcdir + test_name + ".c\n"
"\t$(compile.c) $(OUTPUT_OPTION)\n")

View File

@ -0,0 +1,189 @@
commit b4bbedb1e75737a80bcc3d53d6eef1fbe0b5f4d5
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Sat Nov 6 14:13:27 2021 -0700
dso-ordering-test.py: Put all sources in one directory [BZ #28550]
Put all sources for DSO sorting tests in the dso-sort-tests-src directory
and compile test relocatable objects with
$(objpfx)tst-dso-ordering1-dir/tst-dso-ordering1-a.os: $(objpfx)dso-sort-tests-src/tst-dso-ordering1-a.c
$(compile.c) $(OUTPUT_OPTION)
to avoid random $< values from $(before-compile) when compiling test
relocatable objects with
$(objpfx)%$o: $(objpfx)%.c $(before-compile); $$(compile-command.c)
compile-command.c = $(compile.c) $(OUTPUT_OPTION) $(compile-mkdep-flags)
compile.c = $(CC) $< -c $(CFLAGS) $(CPPFLAGS)
for 3 "make -j 28" parallel builds on a machine with 112 cores at the
same time.
This partially fixes BZ #28550.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py
index 944ee740527d60fd..bde0406be9da14fc 100644
--- a/scripts/dso-ordering-test.py
+++ b/scripts/dso-ordering-test.py
@@ -526,9 +526,13 @@ def process_testcase(t):
base_test_name = t.test_name
test_subdir = base_test_name + "-dir"
testpfx = objpfx + test_subdir + "/"
+ test_srcdir = "dso-sort-tests-src/"
+ testpfx_src = objpfx + test_srcdir
if not os.path.exists(testpfx):
os.mkdir(testpfx)
+ if not os.path.exists(testpfx_src):
+ os.mkdir(testpfx_src)
def find_objs_not_depended_on(t):
objs_not_depended_on = []
@@ -595,6 +599,11 @@ def process_testcase(t):
# Print out needed Makefile fragments for use in glibc/elf/Makefile.
module_names = ""
for o in test_descr.objs:
+ rule = ("$(objpfx)" + test_subdir + "/" + test_name
+ + "-" + o + ".os: $(objpfx)" + test_srcdir
+ + test_name + "-" + o + ".c\n"
+ "\t$(compile.c) $(OUTPUT_OPTION)\n")
+ makefile.write (rule)
module_names += " " + test_subdir + "/" + test_name + "-" + o
makefile.write("modules-names +=%s\n" % (module_names))
@@ -637,7 +646,7 @@ def process_testcase(t):
# object. This only needs to be done at most once for
# an object name.
if not dep in fake_created:
- f = open(testpfx + test_name + "-" + dep
+ f = open(testpfx_src + test_name + "-" + dep
+ ".FAKE.c", "w")
f.write(" \n")
f.close()
@@ -648,6 +657,12 @@ def process_testcase(t):
% (test_name + "-" + dep + ".FAKE.so",
("$(objpfx)" + test_subdir + "/"
+ test_name + "-" + dep + ".so")))
+ rule = ("$(objpfx)" + test_subdir + "/"
+ + test_name + "-" + dep + ".FAKE.os: "
+ "$(objpfx)" + test_srcdir
+ + test_name + "-" + dep + ".FAKE.c\n"
+ "\t$(compile.c) $(OUTPUT_OPTION)\n")
+ makefile.write (rule)
makefile.write \
("modules-names += %s\n"
% (test_subdir + "/"
@@ -687,6 +702,10 @@ def process_testcase(t):
+ test_descr.soname_map['#'] + ".so")
ldflags += (" -Wl,-soname=" + soname)
makefile.write("LDFLAGS-%s = %s\n" % (test_name, ldflags))
+ rule = ("$(objpfx)" + test_subdir + "/" + test_name + ".o: "
+ "$(objpfx)" + test_srcdir + test_name + ".c\n"
+ "\t$(compile.c) $(OUTPUT_OPTION)\n")
+ makefile.write (rule)
not_depended_objs = find_objs_not_depended_on(test_descr)
if not_depended_objs:
@@ -745,7 +764,7 @@ def process_testcase(t):
" something_failed=true\n"
"else\n"
" diff -wu ${common_objpfx}elf/%s/%s%s.output \\\n"
- " ${common_objpfx}elf/%s/%s%s.exp\n"
+ " ${common_objpfx}elf/%s%s%s.exp\n"
" if [ $? -ne 0 ]; then\n"
" echo '%sFAIL: %s%s expected output comparison'\n"
" something_failed=true\n"
@@ -753,14 +772,14 @@ def process_testcase(t):
"fi\n"
% (("X" if xfail else ""), test_name, tunable_descr,
test_subdir, test_name, tunable_sfx,
- test_subdir, base_test_name, exp_tunable_sfx,
+ test_srcdir, base_test_name, exp_tunable_sfx,
("X" if xfail else ""), test_name, tunable_descr))
# Generate C files according to dependency and calling relations from
# description string.
for obj in test_descr.objs:
src_name = test_name + "-" + obj + ".c"
- f = open(testpfx + src_name, "w")
+ f = open(testpfx_src + src_name, "w")
if obj in test_descr.callrefs:
called_objs = test_descr.callrefs[obj]
for callee in called_objs:
@@ -804,7 +823,7 @@ def process_testcase(t):
f.close()
# Open C file for writing main program
- f = open(testpfx + test_name + ".c", "w")
+ f = open(testpfx_src + test_name + ".c", "w")
# if there are some operations in main(), it means we need -ldl
f.write("#include <stdio.h>\n")
@@ -885,7 +904,7 @@ def process_testcase(t):
for obj in test_descr.objs:
src_name = test_name + "-" + obj + ".c"
obj_name = test_name + "-" + obj + ".os"
- run_cmd([build_gcc, "-c", "-fPIC", testpfx + src_name,
+ run_cmd([build_gcc, "-c", "-fPIC", testpfx_src + src_name,
"-o", testpfx + obj_name])
obj_processed = {}
@@ -903,10 +922,12 @@ def process_testcase(t):
deps.append(dep + ".FAKE")
if not dep in fake_created:
base_name = testpfx + test_name + "-" + dep
+ src_base_name = (testpfx_src + test_name
+ + "-" + dep)
cmd = [build_gcc, "-Wl,--no-as-needed",
("-Wl,-soname=" + base_name + ".so"),
"-shared", base_name + ".FAKE.c",
- "-o", base_name + ".FAKE.so"]
+ "-o", src_base_name + ".FAKE.so"]
run_cmd(cmd)
fake_created[dep] = True
dso_deps = map(lambda d: testpfx + test_name + "-" + d + ".so",
@@ -932,7 +953,7 @@ def process_testcase(t):
main_deps = map(lambda d: testpfx + test_name + "-" + d + ".so",
deps)
cmd = [build_gcc, "-Wl,--no-as-needed", "-o", testpfx + test_name,
- testpfx + test_name + ".c", "-L%s" % (os.getcwd()),
+ testpfx_src + test_name + ".c", "-L%s" % (os.getcwd()),
"-Wl,-rpath-link=%s" % (os.getcwd())]
if '#' in test_descr.soname_map:
soname = ("-Wl,-soname=" + testpfx + test_name + "-"
@@ -987,14 +1008,14 @@ def process_testcase(t):
sfx = ""
if r[0] != "":
sfx = "-" + r[0].replace("=","_")
- f = open(testpfx + t.test_name + sfx + ".exp", "w")
+ f = open(testpfx_src + t.test_name + sfx + ".exp", "w")
(output, xfail) = r[1]
f.write('%s' % output)
f.close()
# Create header part of top-level testcase shell script, to wrap execution
# and output comparison together.
- t.sh = open(testpfx + t.test_name + ".sh", "w")
+ t.sh = open(testpfx_src + t.test_name + ".sh", "w")
t.sh.write("#!/bin/sh\n")
t.sh.write("# Test driver for %s, generated by "
"dso-ordering-test.py\n" % (t.test_name))
@@ -1022,12 +1043,12 @@ def process_testcase(t):
sfx = ""
if r[0] != "":
sfx = "-" + r[0].replace("=","_")
- expected_output_files += " $(objpfx)%s/%s%s.exp" % (test_subdir,
+ expected_output_files += " $(objpfx)%s%s%s.exp" % (test_srcdir,
t.test_name, sfx)
makefile.write \
- ("$(objpfx)%s.out: $(objpfx)%s/%s.sh%s "
+ ("$(objpfx)%s.out: $(objpfx)%s%s.sh%s "
"$(common-objpfx)support/test-run-command\n"
- % (t.test_name, test_subdir, t.test_name,
+ % (t.test_name, test_srcdir, t.test_name,
expected_output_files))
makefile.write("\t$(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' "
"'$(run-program-env)' > $@; $(evaluate-test)\n")

View File

@ -0,0 +1,589 @@
commit 15a0c5730d1d5aeb95f50c9ec7470640084feae8
Author: Chung-Lin Tang <cltang@codesourcery.com>
Date: Thu Oct 21 21:41:22 2021 +0800
elf: Fix slow DSO sorting behavior in dynamic loader (BZ #17645)
This second patch contains the actual implementation of a new sorting algorithm
for shared objects in the dynamic loader, which solves the slow behavior that
the current "old" algorithm falls into when the DSO set contains circular
dependencies.
The new algorithm implemented here is simply depth-first search (DFS) to obtain
the Reverse-Post Order (RPO) sequence, a topological sort. A new l_visited:1
bitfield is added to struct link_map to more elegantly facilitate such a search.
The DFS algorithm is applied to the input maps[nmap-1] backwards towards
maps[0]. This has the effect of a more "shallow" recursion depth in general
since the input is in BFS. Also, when combined with the natural order of
processing l_initfini[] at each node, this creates a resulting output sorting
closer to the intuitive "left-to-right" order in most cases.
Another notable implementation adjustment related to this _dl_sort_maps change
is the removing of two char arrays 'used' and 'done' in _dl_close_worker to
represent two per-map attributes. This has been changed to simply use two new
bit-fields l_map_used:1, l_map_done:1 added to struct link_map. This also allows
discarding the clunky 'used' array sorting that _dl_sort_maps had to sometimes
do along the way.
Tunable support for switching between different sorting algorithms at runtime is
also added. A new tunable 'glibc.rtld.dynamic_sort' with current valid values 1
(old algorithm) and 2 (new DFS algorithm) has been added. At time of commit
of this patch, the default setting is 1 (old algorithm).
Signed-off-by: Chung-Lin Tang <cltang@codesourcery.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Conflicts:
elf/dl-tunables.list
(No mem.tagging tunable downstream.)
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 74ca9a85dd309780..22225efb3226c3e1 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -167,8 +167,6 @@ _dl_close_worker (struct link_map *map, bool force)
bool any_tls = false;
const unsigned int nloaded = ns->_ns_nloaded;
- char used[nloaded];
- char done[nloaded];
struct link_map *maps[nloaded];
/* Run over the list and assign indexes to the link maps and enter
@@ -176,24 +174,21 @@ _dl_close_worker (struct link_map *map, bool force)
int idx = 0;
for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next)
{
+ l->l_map_used = 0;
+ l->l_map_done = 0;
l->l_idx = idx;
maps[idx] = l;
++idx;
-
}
assert (idx == nloaded);
- /* Prepare the bitmaps. */
- memset (used, '\0', sizeof (used));
- memset (done, '\0', sizeof (done));
-
/* Keep track of the lowest index link map we have covered already. */
int done_index = -1;
while (++done_index < nloaded)
{
struct link_map *l = maps[done_index];
- if (done[done_index])
+ if (l->l_map_done)
/* Already handled. */
continue;
@@ -204,12 +199,12 @@ _dl_close_worker (struct link_map *map, bool force)
/* 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
- && !used[done_index])
+ && !l->l_map_used)
continue;
/* We need this object and we handle it now. */
- done[done_index] = 1;
- used[done_index] = 1;
+ l->l_map_used = 1;
+ l->l_map_done = 1;
/* Signal the object is still needed. */
l->l_idx = IDX_STILL_USED;
@@ -225,9 +220,9 @@ _dl_close_worker (struct link_map *map, bool force)
{
assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded);
- if (!used[(*lp)->l_idx])
+ if (!(*lp)->l_map_used)
{
- used[(*lp)->l_idx] = 1;
+ (*lp)->l_map_used = 1;
/* If we marked a new object as used, and we've
already processed it, then we need to go back
and process again from that point forward to
@@ -250,9 +245,9 @@ _dl_close_worker (struct link_map *map, bool force)
{
assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded);
- if (!used[jmap->l_idx])
+ if (!jmap->l_map_used)
{
- used[jmap->l_idx] = 1;
+ jmap->l_map_used = 1;
if (jmap->l_idx - 1 < done_index)
done_index = jmap->l_idx - 1;
}
@@ -262,8 +257,7 @@ _dl_close_worker (struct link_map *map, bool force)
/* Sort the entries. We can skip looking for the binary itself which is
at the front of the search list for the main namespace. */
- _dl_sort_maps (maps + (nsid == LM_ID_BASE), nloaded - (nsid == LM_ID_BASE),
- used + (nsid == LM_ID_BASE), true);
+ _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE), true);
/* Call all termination functions at once. */
bool unload_any = false;
@@ -277,7 +271,7 @@ _dl_close_worker (struct link_map *map, bool force)
/* All elements must be in the same namespace. */
assert (imap->l_ns == nsid);
- if (!used[i])
+ if (!imap->l_map_used)
{
assert (imap->l_type == lt_loaded && !imap->l_nodelete_active);
@@ -315,7 +309,7 @@ _dl_close_worker (struct link_map *map, bool force)
if (i < first_loaded)
first_loaded = i;
}
- /* Else used[i]. */
+ /* Else imap->l_map_used. */
else if (imap->l_type == lt_loaded)
{
struct r_scope_elem *new_list = NULL;
@@ -524,7 +518,7 @@ _dl_close_worker (struct link_map *map, bool force)
for (unsigned int i = first_loaded; i < nloaded; ++i)
{
struct link_map *imap = maps[i];
- if (!used[i])
+ if (!imap->l_map_used)
{
assert (imap->l_type == lt_loaded);
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 007069f670eced95..9365d54c8e03e5f4 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -612,10 +612,9 @@ Filters not supported with LD_TRACE_PRELINKING"));
/* If libc.so.6 is the main map, it participates in the sort, so
that the relocation order is correct regarding libc.so.6. */
- if (l_initfini[0] == GL (dl_ns)[l_initfini[0]->l_ns].libc_map)
- _dl_sort_maps (l_initfini, nlist, NULL, false);
- else
- _dl_sort_maps (&l_initfini[1], nlist - 1, NULL, false);
+ _dl_sort_maps (l_initfini, nlist,
+ (l_initfini[0] != GL (dl_ns)[l_initfini[0]->l_ns].libc_map),
+ false);
/* Terminate the list of dependencies. */
l_initfini[nlist] = NULL;
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index eea9d8aad736a99e..e14259a3c8806e0d 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -95,8 +95,7 @@ _dl_fini (void)
/* Now we have to do the sorting. We can skip looking for the
binary itself which is at the front of the search list for
the main namespace. */
- _dl_sort_maps (maps + (ns == LM_ID_BASE), nmaps - (ns == LM_ID_BASE),
- NULL, true);
+ _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true);
/* We do not rely on the linked list of loaded object anymore
from this point on. We have our own list here (maps). The
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index b2a01ede627be1e9..398a08f28c4d9ff1 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -16,16 +16,24 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <assert.h>
#include <ldsodefs.h>
+#include <elf/dl-tunables.h>
+/* Note: this is the older, "original" sorting algorithm, being used as
+ default up to 2.35.
-/* Sort array MAPS according to dependencies of the contained objects.
- Array USED, if non-NULL, is permutated along MAPS. If FOR_FINI this is
- called for finishing an object. */
-void
-_dl_sort_maps (struct link_map **maps, unsigned int nmaps, char *used,
- bool for_fini)
+ Sort array MAPS according to dependencies of the contained objects.
+ If FOR_FINI is true, this is called for finishing an object. */
+static void
+_dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
+ unsigned int skip, bool for_fini)
{
+ /* Allows caller to do the common optimization of skipping the first map,
+ usually the main binary. */
+ maps += skip;
+ nmaps -= skip;
+
/* A list of one element need not be sorted. */
if (nmaps <= 1)
return;
@@ -66,14 +74,6 @@ _dl_sort_maps (struct link_map **maps, unsigned int nmaps, char *used,
(k - i) * sizeof (maps[0]));
maps[k] = thisp;
- if (used != NULL)
- {
- char here_used = used[i];
- memmove (&used[i], &used[i + 1],
- (k - i) * sizeof (used[0]));
- used[k] = here_used;
- }
-
if (seen[i + 1] > nmaps - i)
{
++i;
@@ -120,3 +120,183 @@ _dl_sort_maps (struct link_map **maps, unsigned int nmaps, char *used,
next:;
}
}
+
+#if !HAVE_TUNABLES
+/* In this case, just default to the original algorithm. */
+strong_alias (_dl_sort_maps_original, _dl_sort_maps);
+#else
+
+/* We use a recursive function due to its better clarity and ease of
+ implementation, as well as faster execution speed. We already use
+ alloca() for list allocation during the breadth-first search of
+ dependencies in _dl_map_object_deps(), and this should be on the
+ same order of worst-case stack usage.
+
+ Note: the '*rpo' parameter is supposed to point to one past the
+ last element of the array where we save the sort results, and is
+ decremented before storing the current map at each level. */
+
+static void
+dfs_traversal (struct link_map ***rpo, struct link_map *map,
+ bool *do_reldeps)
+{
+ if (map->l_visited)
+ return;
+
+ map->l_visited = 1;
+
+ if (map->l_initfini)
+ {
+ for (int i = 0; map->l_initfini[i] != NULL; i++)
+ {
+ struct link_map *dep = map->l_initfini[i];
+ if (dep->l_visited == 0
+ && dep->l_main_map == 0)
+ dfs_traversal (rpo, dep, do_reldeps);
+ }
+ }
+
+ if (__glibc_unlikely (do_reldeps != NULL && map->l_reldeps != NULL))
+ {
+ /* Indicate that we encountered relocation dependencies during
+ traversal. */
+ *do_reldeps = true;
+
+ for (int m = map->l_reldeps->act - 1; m >= 0; m--)
+ {
+ struct link_map *dep = map->l_reldeps->list[m];
+ if (dep->l_visited == 0
+ && dep->l_main_map == 0)
+ dfs_traversal (rpo, dep, do_reldeps);
+ }
+ }
+
+ *rpo -= 1;
+ **rpo = map;
+}
+
+/* Topologically sort array MAPS according to dependencies of the contained
+ objects. */
+
+static void
+_dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
+ unsigned int skip __attribute__ ((unused)), bool for_fini)
+{
+ for (int i = nmaps - 1; i >= 0; i--)
+ maps[i]->l_visited = 0;
+
+ /* We apply DFS traversal for each of maps[i] until the whole total order
+ is found and we're at the start of the Reverse-Postorder (RPO) sequence,
+ which is a topological sort.
+
+ We go from maps[nmaps - 1] backwards towards maps[0] at this level.
+ Due to the breadth-first search (BFS) ordering we receive, going
+ backwards usually gives a more shallow depth-first recursion depth,
+ adding more stack usage safety. Also, combined with the natural
+ processing order of l_initfini[] at each node during DFS, this maintains
+ an ordering closer to the original link ordering in the sorting results
+ under most simpler cases.
+
+ Another reason we order the top level backwards, it that maps[0] is
+ usually exactly the main object of which we're in the midst of
+ _dl_map_object_deps() processing, and maps[0]->l_initfini[] is still
+ blank. If we start the traversal from maps[0], since having no
+ dependencies yet filled in, maps[0] will always be immediately
+ incorrectly placed at the last place in the order (first in reverse).
+ Adjusting the order so that maps[0] is last traversed naturally avoids
+ this problem.
+
+ Further, the old "optimization" of skipping the main object at maps[0]
+ from the call-site (i.e. _dl_sort_maps(maps+1,nmaps-1)) is in general
+ no longer valid, since traversing along object dependency-links
+ may "find" the main object even when it is not included in the initial
+ order (e.g. a dlopen()'ed shared object can have circular dependencies
+ linked back to itself). In such a case, traversing N-1 objects will
+ create a N-object result, and raise problems.
+
+ To summarize, just passing in the full list, and iterating from back
+ to front makes things much more straightforward. */
+
+ /* Array to hold RPO sorting results, before we copy back to maps[]. */
+ struct link_map *rpo[nmaps];
+
+ /* The 'head' position during each DFS iteration. Note that we start at
+ one past the last element due to first-decrement-then-store (see the
+ bottom of above dfs_traversal() routine). */
+ struct link_map **rpo_head = &rpo[nmaps];
+
+ bool do_reldeps = false;
+ bool *do_reldeps_ref = (for_fini ? &do_reldeps : NULL);
+
+ for (int i = nmaps - 1; i >= 0; i--)
+ {
+ dfs_traversal (&rpo_head, maps[i], do_reldeps_ref);
+
+ /* We can break early if all objects are already placed. */
+ if (rpo_head == rpo)
+ goto end;
+ }
+ assert (rpo_head == rpo);
+
+ end:
+ /* Here we may do a second pass of sorting, using only l_initfini[]
+ static dependency links. This is avoided if !FOR_FINI or if we didn't
+ find any reldeps in the first DFS traversal.
+
+ The reason we do this is: while it is unspecified how circular
+ dependencies should be handled, the presumed reasonable behavior is to
+ have destructors to respect static dependency links as much as possible,
+ overriding reldeps if needed. And the first sorting pass, which takes
+ l_initfini/l_reldeps links equally, may not preserve this priority.
+
+ Hence we do a 2nd sorting pass, taking only DT_NEEDED links into account
+ (see how the do_reldeps argument to dfs_traversal() is NULL below). */
+ if (do_reldeps)
+ {
+ for (int i = nmaps - 1; i >= 0; i--)
+ rpo[i]->l_visited = 0;
+
+ struct link_map **maps_head = &maps[nmaps];
+ for (int i = nmaps - 1; i >= 0; i--)
+ {
+ dfs_traversal (&maps_head, rpo[i], NULL);
+
+ /* We can break early if all objects are already placed.
+ The below memcpy is not needed in the do_reldeps case here,
+ since we wrote back to maps[] during DFS traversal. */
+ if (maps_head == maps)
+ return;
+ }
+ assert (maps_head == maps);
+ return;
+ }
+
+ memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
+}
+
+void
+_dl_sort_maps_init (void)
+{
+ int32_t algorithm = TUNABLE_GET (glibc, rtld, dynamic_sort, int32_t, NULL);
+ GLRO(dl_dso_sort_algo) = algorithm == 1 ? dso_sort_algorithm_original
+ : dso_sort_algorithm_dfs;
+}
+
+void
+_dl_sort_maps (struct link_map **maps, unsigned int nmaps,
+ unsigned int skip, bool for_fini)
+{
+ /* It can be tempting to use a static function pointer to store and call
+ the current selected sorting algorithm routine, but experimentation
+ shows that current processors still do not handle indirect branches
+ that efficiently, plus a static function pointer will involve
+ PTR_MANGLE/DEMANGLE, further impairing performance of small, common
+ input cases. A simple if-case with direct function calls appears to
+ be the fastest. */
+ if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original))
+ _dl_sort_maps_original (maps, nmaps, skip, for_fini);
+ else
+ _dl_sort_maps_dfs (maps, nmaps, skip, for_fini);
+}
+
+#endif /* HAVE_TUNABLES. */
diff --git a/elf/dl-support.c b/elf/dl-support.c
index e9943e889ef447ad..ae03aec9764e29d3 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -155,6 +155,8 @@ size_t _dl_phnum;
uint64_t _dl_hwcap __attribute__ ((nocommon));
uint64_t _dl_hwcap2 __attribute__ ((nocommon));
+enum dso_sort_algorithm _dl_dso_sort_algo;
+
/* The value of the FPU control word the kernel will preset in hardware. */
fpu_control_t _dl_fpu_control = _FPU_DEFAULT;
diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c
index 998c5d52bcab8193..4e8a986541fc4c09 100644
--- a/elf/dl-sysdep.c
+++ b/elf/dl-sysdep.c
@@ -223,6 +223,9 @@ _dl_sysdep_start (void **start_argptr,
__tunables_init (_environ);
+ /* Initialize DSO sorting algorithm after tunables. */
+ _dl_sort_maps_init ();
+
#ifdef DL_SYSDEP_INIT
DL_SYSDEP_INIT;
#endif
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 6408a8e5ae92d2c6..54ef2a921310b229 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -140,4 +140,13 @@ glibc {
default: 512
}
}
+
+ rtld {
+ dynamic_sort {
+ type: INT_32
+ minval: 1
+ maxval: 2
+ default: 1
+ }
+ }
}
diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
index 873ddf55d91155c6..5f7f18ef270bc12d 100644
--- a/elf/dso-sort-tests-1.def
+++ b/elf/dso-sort-tests-1.def
@@ -62,5 +62,5 @@ output: b>a>{}<a<b
# The below expected outputs are what the two algorithms currently produce
# respectively, for regression testing purposes.
tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
-xfail_output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
+output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
diff --git a/elf/rtld.c b/elf/rtld.c
index b47e84ca2fb6f03c..cd2cc4024a3581c2 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1453,6 +1453,9 @@ dl_main (const ElfW(Phdr) *phdr,
main_map->l_name = (char *) "";
*user_entry = main_map->l_entry;
+ /* Set bit indicating this is the main program map. */
+ main_map->l_main_map = 1;
+
#ifdef HAVE_AUX_VECTOR
/* Adjust the on-stack auxiliary vector so that it looks like the
binary was executed directly. */
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index 4f3f7ee4e30a2b42..118afc271057afd4 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -10,5 +10,6 @@ glibc.malloc.tcache_max: 0x0 (min: 0x0, max: 0x[f]+)
glibc.malloc.tcache_unsorted_limit: 0x0 (min: 0x0, max: 0x[f]+)
glibc.malloc.top_pad: 0x0 (min: 0x0, max: 0x[f]+)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0x[f]+)
+glibc.rtld.dynamic_sort: 1 (min: 1, max: 2)
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
diff --git a/include/link.h b/include/link.h
index dd491989beb41353..041ff5f753a9ee11 100644
--- a/include/link.h
+++ b/include/link.h
@@ -181,6 +181,11 @@ struct link_map
unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */
unsigned int l_reserved:2; /* Reserved for internal use. */
+ unsigned int l_main_map:1; /* Nonzero for the map of the main program. */
+ unsigned int l_visited:1; /* Used internally for map dependency
+ graph traversal. */
+ unsigned int l_map_used:1; /* These two bits are used during traversal */
+ unsigned int l_map_done:1; /* of maps in _dl_close_worker. */
unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed
to by `l_phdr' is allocated. */
unsigned int l_soname_added:1; /* Nonzero if the SONAME is for sure in
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 43272cf885d1e3e6..c3f96cdc85208926 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -303,6 +303,17 @@ changed once allocated at process startup. The default allocation of
optional static TLS is 512 bytes and is allocated in every thread.
@end deftp
+@deftp Tunable glibc.rtld.dynamic_sort
+Sets the algorithm to use for DSO sorting, valid values are @samp{1} and
+@samp{2}. For value of @samp{1}, an older O(n^3) algorithm is used, which is
+long time tested, but may have performance issues when dependencies between
+shared objects contain cycles due to circular dependencies. When set to the
+value of @samp{2}, a different algorithm is used, which implements a
+topological sort through depth-first search, and does not exhibit the
+performance issues of @samp{1}.
+
+The default value of this tunable is @samp{1}.
+@end deftp
@node Elision Tunables
@section Elision Tunables
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 5e56550a4d556fa7..9f09a4a280396659 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -240,6 +240,13 @@ enum allowmask
};
+/* DSO sort algorithm to use (check dl-sort-maps.c). */
+enum dso_sort_algorithm
+ {
+ dso_sort_algorithm_original,
+ dso_sort_algorithm_dfs
+ };
+
struct audit_ifaces
{
void (*activity) (uintptr_t *, unsigned int);
@@ -633,6 +640,8 @@ struct rtld_global_ro
platforms. */
EXTERN uint64_t _dl_hwcap2;
+ EXTERN enum dso_sort_algorithm _dl_dso_sort_algo;
+
#ifdef SHARED
/* We add a function table to _rtld_global which is then used to
call the function instead of going through the PLT. The result
@@ -1049,7 +1058,7 @@ extern void _dl_fini (void) attribute_hidden;
/* Sort array MAPS according to dependencies of the contained objects. */
extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
- char *used, bool for_fini) attribute_hidden;
+ unsigned int skip, bool for_fini) attribute_hidden;
/* The dynamic linker calls this function before and having changing
any shared object mappings. The `r_state' member of `struct r_debug'
@@ -1167,6 +1176,9 @@ extern struct link_map * _dl_get_dl_main_map (void)
# endif
#endif
+/* Initialize the DSO sort algorithm to use. */
+extern void _dl_sort_maps_init (void) attribute_hidden;
+
/* Initialization of libpthread for statically linked applications.
If libpthread is not linked in, this is an empty function. */
void __pthread_initialize_minimal (void) weak_function;

View File

@ -0,0 +1,25 @@
commit d3bf2f5927d51258a51ac7fde04f4805f8ee294a
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Wed Nov 3 09:19:30 2021 -0300
elf: Do not run DSO sorting if tunables is not enabled
Since the argorithm selection requires tunables.
Checked on x86_64-linux-gnu with --enable-tunables=no.
diff --git a/elf/Makefile b/elf/Makefile
index e92f62f279566684..3b5e1f59e6696a2b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -998,8 +998,10 @@ include $(objpfx)$(1).generated-makefile
endef
# Generate from each testcase description file
+ifeq (yes,$(have-tunables))
$(eval $(call include_dsosort_tests,dso-sort-tests-1.def))
$(eval $(call include_dsosort_tests,dso-sort-tests-2.def))
+endif
check-abi: $(objpfx)check-abi-ld.out
tests-special += $(objpfx)check-abi-ld.out

View File

@ -0,0 +1,45 @@
commit 1f67d8286b5da9266a138198ef1f15c27cbb0010
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Mon Nov 15 16:28:39 2021 -0800
elf: Use a temporary file to generate Makefile fragments [BZ #28550]
1. Use a temporary file to generate Makefile fragments for DSO sorting
tests and use -include on them.
2. Add Makefile fragments to postclean-generated so that a "make clean"
removes the autogenerated fragments and a subsequent "make" regenerates
them.
This partially fixes BZ #28550.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/elf/Makefile b/elf/Makefile
index 3b5e1f59e6696a2b..22a8060f7d3bb1a1 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -986,6 +986,7 @@ tests-special += \
# tests-special
endif
+ifndef avoid-generated
# DSO sorting tests:
# The dso-ordering-test.py script generates testcase source files in $(objpfx),
# creating a $(objpfx)<testcase-name>-dir for each testcase, and creates a
@@ -993,9 +994,14 @@ endif
define include_dsosort_tests
$(objpfx)$(1).generated-makefile: $(1)
$(PYTHON) $(..)scripts/dso-ordering-test.py \
- --description-file $$< --objpfx $(objpfx) --output-makefile $$@
-include $(objpfx)$(1).generated-makefile
+ --description-file $$< --objpfx $(objpfx) --output-makefile $$@T
+ mv $$@T $$@
+-include $(objpfx)$(1).generated-makefile
endef
+endif
+
+postclean-generated += $(objpfx)/dso-sort-tests-2.generated-makefile \
+ $(objpfx)/dso-sort-tests-2.generated-makefile
# Generate from each testcase description file
ifeq (yes,$(have-tunables))

View File

@ -0,0 +1,49 @@
commit 0884724a95b60452ad483dbe086d237d02ba624d
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Dec 14 12:37:44 2021 +0100
elf: Use new dependency sorting algorithm by default
The default has to change eventually, and there are no known failures
that require a delay.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 54ef2a921310b229..f11ca5b3e8b09b43 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -146,7 +146,7 @@ glibc {
type: INT_32
minval: 1
maxval: 2
- default: 1
+ default: 2
}
}
}
diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp
index 118afc271057afd4..478ee8ab091685eb 100644
--- a/elf/tst-rtld-list-tunables.exp
+++ b/elf/tst-rtld-list-tunables.exp
@@ -10,6 +10,6 @@ glibc.malloc.tcache_max: 0x0 (min: 0x0, max: 0x[f]+)
glibc.malloc.tcache_unsorted_limit: 0x0 (min: 0x0, max: 0x[f]+)
glibc.malloc.top_pad: 0x0 (min: 0x0, max: 0x[f]+)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0x[f]+)
-glibc.rtld.dynamic_sort: 1 (min: 1, max: 2)
+glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
diff --git a/manual/tunables.texi b/manual/tunables.texi
index c3f96cdc85208926..7b70e80391ee87f7 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -312,7 +312,7 @@ value of @samp{2}, a different algorithm is used, which implements a
topological sort through depth-first search, and does not exhibit the
performance issues of @samp{1}.
-The default value of this tunable is @samp{1}.
+The default value of this tunable is @samp{2}.
@end deftp
@node Elision Tunables

View File

@ -0,0 +1,357 @@
commit 3a0588ae48fb35384a6bd33f9b66403badfa1262
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Tue Feb 8 15:22:49 2022 -0300
elf: Fix DFS sorting algorithm for LD_TRACE_LOADED_OBJECTS with missing libraries (BZ #28868)
On _dl_map_object the underlying file is not opened in trace mode
(in other cases where the underlying file can't be opened,
_dl_map_object quits with an error). If there any missing libraries
being processed, they will not be considered on final nlist size
passed on _dl_sort_maps later in the function. And it is then used by
_dl_sort_maps_dfs on the stack allocated working maps:
222 /* Array to hold RPO sorting results, before we copy back to maps[]. */
223 struct link_map *rpo[nmaps];
224
225 /* The 'head' position during each DFS iteration. Note that we start at
226 one past the last element due to first-decrement-then-store (see the
227 bottom of above dfs_traversal() routine). */
228 struct link_map **rpo_head = &rpo[nmaps];
However while transversing the 'l_initfini' on dfs_traversal it will
still consider the l_faked maps and thus update rpo more times than the
allocated working 'rpo', overflowing the stack object.
As suggested in bugzilla, one option would be to avoid sorting the maps
for trace mode. However I think ignoring l_faked object does make
sense (there is one less constraint to call the sorting function), it
allows a slight less stack usage for trace, and it is slight simpler
solution.
The tests does trigger the stack overflow, however I tried to make
it more generic to check different scenarios or missing objects.
Checked on x86_64-linux-gnu.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Conflicts:
elf/Makefile
(differences in backported tests)
diff --git a/elf/Makefile b/elf/Makefile
index 22a8060f7d3bb1a1..634c3113227d64a6 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -584,6 +584,11 @@ modules-names = \
libmarkermod5-3 \
libmarkermod5-4 \
libmarkermod5-5 \
+ libtracemod1-1 \
+ libtracemod2-1 \
+ libtracemod3-1 \
+ libtracemod4-1 \
+ libtracemod5-1 \
ltglobmod1 \
ltglobmod2 \
neededobj1 \
@@ -983,6 +988,11 @@ tests-special += \
$(objpfx)tst-initorder2-cmp.out \
$(objpfx)tst-unused-dep-cmp.out \
$(objpfx)tst-unused-dep.out \
+ $(objpfx)tst-trace1.out \
+ $(objpfx)tst-trace2.out \
+ $(objpfx)tst-trace3.out \
+ $(objpfx)tst-trace4.out \
+ $(objpfx)tst-trace5.out \
# tests-special
endif
@@ -2619,6 +2629,51 @@ $(objpfx)tst-rtld-run-static.out: $(objpfx)/ldconfig
$(objpfx)tst-dlmopen-gethostbyname: $(libdl)
$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
+
+LDFLAGS-libtracemod1-1.so += -Wl,-soname,libtracemod1.so
+LDFLAGS-libtracemod2-1.so += -Wl,-soname,libtracemod2.so
+LDFLAGS-libtracemod3-1.so += -Wl,-soname,libtracemod3.so
+LDFLAGS-libtracemod4-1.so += -Wl,-soname,libtracemod4.so
+LDFLAGS-libtracemod5-1.so += -Wl,-soname,libtracemod5.so
+
+$(objpfx)libtracemod1-1.so: $(objpfx)libtracemod2-1.so \
+ $(objpfx)libtracemod3-1.so
+$(objpfx)libtracemod2-1.so: $(objpfx)libtracemod4-1.so \
+ $(objpfx)libtracemod5-1.so
+
+define libtracemod-x
+$(objpfx)libtracemod$(1)/libtracemod$(1).so: $(objpfx)libtracemod$(1)-1.so
+ $$(make-target-directory)
+ cp $$< $$@
+endef
+libtracemod-suffixes = 1 2 3 4 5
+$(foreach i,$(libtracemod-suffixes), $(eval $(call libtracemod-x,$(i))))
+
+define tst-trace-skeleton
+$(objpfx)tst-trace$(1).out: $(objpfx)libtracemod1/libtracemod1.so \
+ $(objpfx)libtracemod2/libtracemod2.so \
+ $(objpfx)libtracemod3/libtracemod3.so \
+ $(objpfx)libtracemod4/libtracemod4.so \
+ $(objpfx)libtracemod5/libtracemod5.so \
+ $(..)scripts/tst-ld-trace.py \
+ tst-trace$(1).exp
+ ${ $(PYTHON) $(..)scripts/tst-ld-trace.py \
+ "$(test-wrapper-env) $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(common-objpfx):$(strip $(2)) \
+ $(objpfx)libtracemod1/libtracemod1.so" tst-trace$(1).exp \
+ } > $$@; $$(evaluate-test)
+endef
+
+$(eval $(call tst-trace-skeleton,1,))
+$(eval $(call tst-trace-skeleton,2,\
+ $(objpfx)libtracemod2))
+$(eval $(call tst-trace-skeleton,3,\
+ $(objpfx)libtracemod2:$(objpfx)libtracemod3))
+$(eval $(call tst-trace-skeleton,4,\
+ $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4))
+$(eval $(call tst-trace-skeleton,5,\
+ $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4:$(objpfx)libtracemod5))
+
$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \
$(objpfx)tst-audit-tlsdesc-mod2.so \
$(shared-thread-library)
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 9365d54c8e03e5f4..9ff589c8562b2dd1 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -489,6 +489,8 @@ _dl_map_object_deps (struct link_map *map,
for (nlist = 0, runp = known; runp; runp = runp->next)
{
+ /* _dl_sort_maps ignores l_faked object, so it is safe to not consider
+ them for nlist. */
if (__builtin_expect (trace_mode, 0) && runp->map->l_faked)
/* This can happen when we trace the loading. */
--map->l_searchlist.r_nlist;
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index 398a08f28c4d9ff1..99354dc08a010dd3 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -140,7 +140,9 @@ static void
dfs_traversal (struct link_map ***rpo, struct link_map *map,
bool *do_reldeps)
{
- if (map->l_visited)
+ /* _dl_map_object_deps ignores l_faked objects when calculating the
+ number of maps before calling _dl_sort_maps, ignore them as well. */
+ if (map->l_visited || map->l_faked)
return;
map->l_visited = 1;
diff --git a/elf/libtracemod1-1.c b/elf/libtracemod1-1.c
new file mode 100644
index 0000000000000000..7c89c9a5a40b9668
--- /dev/null
+++ b/elf/libtracemod1-1.c
@@ -0,0 +1 @@
+/* Empty */
diff --git a/elf/libtracemod2-1.c b/elf/libtracemod2-1.c
new file mode 100644
index 0000000000000000..7c89c9a5a40b9668
--- /dev/null
+++ b/elf/libtracemod2-1.c
@@ -0,0 +1 @@
+/* Empty */
diff --git a/elf/libtracemod3-1.c b/elf/libtracemod3-1.c
new file mode 100644
index 0000000000000000..7c89c9a5a40b9668
--- /dev/null
+++ b/elf/libtracemod3-1.c
@@ -0,0 +1 @@
+/* Empty */
diff --git a/elf/libtracemod4-1.c b/elf/libtracemod4-1.c
new file mode 100644
index 0000000000000000..7c89c9a5a40b9668
--- /dev/null
+++ b/elf/libtracemod4-1.c
@@ -0,0 +1 @@
+/* Empty */
diff --git a/elf/libtracemod5-1.c b/elf/libtracemod5-1.c
new file mode 100644
index 0000000000000000..7c89c9a5a40b9668
--- /dev/null
+++ b/elf/libtracemod5-1.c
@@ -0,0 +1 @@
+/* Empty */
diff --git a/elf/tst-trace1.exp b/elf/tst-trace1.exp
new file mode 100644
index 0000000000000000..4a6f5211a68fe2c8
--- /dev/null
+++ b/elf/tst-trace1.exp
@@ -0,0 +1,4 @@
+ld 1
+libc 1
+libtracemod2.so 0
+libtracemod3.so 0
diff --git a/elf/tst-trace2.exp b/elf/tst-trace2.exp
new file mode 100644
index 0000000000000000..e13506e2eb9aeca2
--- /dev/null
+++ b/elf/tst-trace2.exp
@@ -0,0 +1,6 @@
+ld 1
+libc 1
+libtracemod2.so 1
+libtracemod3.so 0
+libtracemod4.so 0
+libtracemod5.so 0
diff --git a/elf/tst-trace3.exp b/elf/tst-trace3.exp
new file mode 100644
index 0000000000000000..e574549d12a53d72
--- /dev/null
+++ b/elf/tst-trace3.exp
@@ -0,0 +1,6 @@
+ld 1
+libc 1
+libtracemod2.so 1
+libtracemod3.so 1
+libtracemod4.so 0
+libtracemod5.so 0
diff --git a/elf/tst-trace4.exp b/elf/tst-trace4.exp
new file mode 100644
index 0000000000000000..31ca97b35bde0009
--- /dev/null
+++ b/elf/tst-trace4.exp
@@ -0,0 +1,6 @@
+ld 1
+libc 1
+libtracemod2.so 1
+libtracemod3.so 1
+libtracemod4.so 1
+libtracemod5.so 0
diff --git a/elf/tst-trace5.exp b/elf/tst-trace5.exp
new file mode 100644
index 0000000000000000..5d7d95372656396f
--- /dev/null
+++ b/elf/tst-trace5.exp
@@ -0,0 +1,6 @@
+ld 1
+libc 1
+libtracemod2.so 1
+libtracemod3.so 1
+libtracemod4.so 1
+libtracemod5.so 1
diff --git a/scripts/tst-ld-trace.py b/scripts/tst-ld-trace.py
new file mode 100755
index 0000000000000000..f5a402800377f44b
--- /dev/null
+++ b/scripts/tst-ld-trace.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python3
+# Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format.
+# Copyright (C) 2022 Free Software Foundation, Inc.
+# Copyright The GNU Toolchain Authors.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+import argparse
+import os
+import subprocess
+import sys
+
+try:
+ subprocess.run
+except:
+ class _CompletedProcess:
+ def __init__(self, args, returncode, stdout=None, stderr=None):
+ self.args = args
+ self.returncode = returncode
+ self.stdout = stdout
+ self.stderr = stderr
+
+ def _run(*popenargs, input=None, timeout=None, check=False, **kwargs):
+ assert(timeout is None)
+ with subprocess.Popen(*popenargs, **kwargs) as process:
+ try:
+ stdout, stderr = process.communicate(input)
+ except:
+ process.kill()
+ process.wait()
+ raise
+ returncode = process.poll()
+ if check and returncode:
+ raise subprocess.CalledProcessError(returncode, popenargs)
+ return _CompletedProcess(popenargs, returncode, stdout, stderr)
+
+ subprocess.run = _run
+
+def is_vdso(lib):
+ return lib.startswith('linux-gate') or lib.startswith('linux-vdso')
+
+
+def parse_trace(cmd, fref):
+ new_env = os.environ.copy()
+ new_env['LD_TRACE_LOADED_OBJECTS'] = '1'
+ trace_out = subprocess.run(cmd, stdout=subprocess.PIPE, check=True,
+ universal_newlines=True, env=new_env).stdout
+ trace = []
+ for line in trace_out.splitlines():
+ line = line.strip()
+ if is_vdso(line):
+ continue
+ fields = line.split('=>' if '=>' in line else ' ')
+ lib = os.path.basename(fields[0].strip())
+ if lib.startswith('ld'):
+ lib = 'ld'
+ elif lib.startswith('libc'):
+ lib = 'libc'
+ found = 1 if fields[1].strip() != 'not found' else 0
+ trace += ['{} {}'.format(lib, found)]
+ trace = sorted(trace)
+
+ reference = sorted(line.replace('\n','') for line in fref.readlines())
+
+ ret = 0 if trace == reference else 1
+ if ret != 0:
+ for i in reference:
+ if i not in trace:
+ print("Only in {}: {}".format(fref.name, i))
+ for i in trace:
+ if i not in reference:
+ print("Only in trace: {}".format(i))
+
+ sys.exit(ret)
+
+
+def get_parser():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('command',
+ help='comand to run')
+ parser.add_argument('reference',
+ help='reference file to compare')
+ return parser
+
+
+def main(argv):
+ parser = get_parser()
+ opts = parser.parse_args(argv)
+ with open(opts.reference, 'r') as fref:
+ # Remove the initial 'env' command.
+ parse_trace(opts.command.split()[1:], fref)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])

View File

@ -0,0 +1,36 @@
commit a2211c76c3b994099fd58a06d6072d7495d699cd
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Mar 18 18:18:35 2022 +0100
scripts/dso-ordering-test.py: Fix C&P error in * callrefs processing
The elf/dso-sort-tests-src subdirectory is not changed by this commit,
so it seems that the cut-and-paste error was not material.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py
index bde0406be9da14fc..ee476c810c76f1b0 100644
--- a/scripts/dso-ordering-test.py
+++ b/scripts/dso-ordering-test.py
@@ -551,17 +551,17 @@ def process_testcase(t):
if obj in t.deps:
deps = t.deps[obj]
if '*' in deps:
- t.deps[obj].remove('*')
+ deps.remove('*')
t.add_deps([obj], non_dep_tgt_objs)
if obj in t.callrefs:
deps = t.callrefs[obj]
if '*' in deps:
- t.deps[obj].remove('*')
+ deps.remove('*')
t.add_callrefs([obj], non_dep_tgt_objs)
if "#" in t.deps:
deps = t.deps["#"]
if '*' in deps:
- t.deps["#"].remove('*')
+ deps.remove('*')
t.add_deps(["#"], non_dep_tgt_objs)
# If no main program was specified in dependency description, make a

View File

@ -0,0 +1,37 @@
commit 183d99737298bb3200f0610fdcd1c7549c8ed560
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Sep 6 07:38:10 2022 +0200
scripts/dso-ordering-test.py: Generate program run-time dependencies
The main program needs to depend on all shared objects, even objects
that have link-time dependencies among shared objects. Filtering
out shared objects that already have an link-time dependencies is not
necessary here; make will do this automatically.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py
index ee476c810c76f1b0..43b5ec4d920ad6a3 100644
--- a/scripts/dso-ordering-test.py
+++ b/scripts/dso-ordering-test.py
@@ -707,13 +707,12 @@ def process_testcase(t):
"\t$(compile.c) $(OUTPUT_OPTION)\n")
makefile.write (rule)
- not_depended_objs = find_objs_not_depended_on(test_descr)
- if not_depended_objs:
- depstr = ""
- for dep in not_depended_objs:
- depstr += (" $(objpfx)" + test_subdir + "/"
- + test_name + "-" + dep + ".so")
- makefile.write("$(objpfx)%s.out:%s\n" % (base_test_name, depstr))
+ # Ensure that all shared objects are built before running the
+ # test, whether there link-time dependencies or not.
+ depobjs = ["$(objpfx){}/{}-{}.so".format(test_subdir, test_name, dep)
+ for dep in test_descr.objs]
+ makefile.write("$(objpfx){}.out: {}\n".format(
+ base_test_name, " ".join(depobjs)))
# Add main executable to test-srcs
makefile.write("test-srcs += %s/%s\n" % (test_subdir, test_name))

View File

@ -0,0 +1,245 @@
From a1a486d70ebcc47a686ff5846875eacad0940e41 Mon Sep 17 00:00:00 2001
From: Eyal Itkin <eyalit@checkpoint.com>
Date: Fri, 20 Mar 2020 21:19:17 +0200
Subject: Add Safe-Linking to fastbins and tcache
Safe-Linking is a security mechanism that protects single-linked
lists (such as the fastbin and tcache) from being tampered by attackers.
The mechanism makes use of randomness from ASLR (mmap_base), and when
combined with chunk alignment integrity checks, it protects the "next"
pointers from being hijacked by an attacker.
While Safe-Unlinking protects double-linked lists (such as the small
bins), there wasn't any similar protection for attacks against
single-linked lists. This solution protects against 3 common attacks:
* Partial pointer override: modifies the lower bytes (Little Endian)
* Full pointer override: hijacks the pointer to an attacker's location
* Unaligned chunks: pointing the list to an unaligned address
The design assumes an attacker doesn't know where the heap is located,
and uses the ASLR randomness to "sign" the single-linked pointers. We
mark the pointer as P and the location in which it is stored as L, and
the calculation will be:
* PROTECT(P) := (L >> PAGE_SHIFT) XOR (P)
* *L = PROTECT(P)
This way, the random bits from the address L (which start at the bit
in the PAGE_SHIFT position), will be merged with LSB of the stored
protected pointer. This protection layer prevents an attacker from
modifying the pointer into a controlled value.
An additional check that the chunks are MALLOC_ALIGNed adds an
important layer:
* Attackers can't point to illegal (unaligned) memory addresses
* Attackers must guess correctly the alignment bits
On standard 32 bit Linux machines, an attack will directly fail 7
out of 8 times, and on 64 bit machines it will fail 15 out of 16
times.
This proposed patch was benchmarked and it's effect on the overall
performance of the heap was negligible and couldn't be distinguished
from the default variance between tests on the vanilla version. A
similar protection was added to Chromium's version of TCMalloc
in 2012, and according to their documentation it had an overhead of
less than 2%.
Reviewed-by: DJ Delorie <dj@redhat.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zacnella <adhemerval.zanella@linaro.org>
diff --git a/malloc/malloc.c b/malloc/malloc.c
index f7cd29bc2f..1282863681 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -327,6 +327,18 @@ __malloc_assert (const char *assertion, const char *file, unsigned int line,
# define MAX_TCACHE_COUNT UINT16_MAX
#endif
+/* Safe-Linking:
+ Use randomness from ASLR (mmap_base) to protect single-linked lists
+ of Fast-Bins and TCache. That is, mask the "next" pointers of the
+ lists' chunks, and also perform allocation alignment checks on them.
+ This mechanism reduces the risk of pointer hijacking, as was done with
+ Safe-Unlinking in the double-linked lists of Small-Bins.
+ It assumes a minimum page size of 4096 bytes (12 bits). Systems with
+ larger pages provide less entropy, although the pointer mangling
+ still works. */
+#define PROTECT_PTR(pos, ptr) \
+ ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
+#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)
/*
REALLOC_ZERO_BYTES_FREES should be set if a call to
@@ -2157,12 +2169,15 @@ do_check_malloc_state (mstate av)
while (p != 0)
{
+ if (__glibc_unlikely (!aligned_OK (p)))
+ malloc_printerr ("do_check_malloc_state(): " \
+ "unaligned fastbin chunk detected");
/* each chunk claims to be inuse */
do_check_inuse_chunk (av, p);
total += chunksize (p);
/* chunk belongs in this bin */
assert (fastbin_index (chunksize (p)) == i);
- p = p->fd;
+ p = REVEAL_PTR (p->fd);
}
}
@@ -2923,7 +2938,7 @@ tcache_put (mchunkptr chunk, size_t tc_idx)
detect a double free. */
e->key = tcache;
- e->next = tcache->entries[tc_idx];
+ e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}
@@ -2934,9 +2949,11 @@ static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
- tcache->entries[tc_idx] = e->next;
+ tcache->entries[tc_idx] = REVEAL_PTR (e->next);
--(tcache->counts[tc_idx]);
e->key = NULL;
+ if (__glibc_unlikely (!aligned_OK (e)))
+ malloc_printerr ("malloc(): unaligned tcache chunk detected");
return (void *) e;
}
@@ -2960,7 +2977,10 @@ tcache_thread_shutdown (void)
while (tcache_tmp->entries[i])
{
tcache_entry *e = tcache_tmp->entries[i];
- tcache_tmp->entries[i] = e->next;
+ if (__glibc_unlikely (!aligned_OK (e)))
+ malloc_printerr ("tcache_thread_shutdown(): " \
+ "unaligned tcache chunk detected");
+ tcache_tmp->entries[i] = REVEAL_PTR (e->next);
__libc_free (e);
}
}
@@ -3570,8 +3590,11 @@ _int_malloc (mstate av, size_t bytes)
victim = pp; \
if (victim == NULL) \
break; \
+ pp = REVEAL_PTR (victim->fd); \
+ if (__glibc_unlikely (!aligned_OK (pp))) \
+ malloc_printerr ("malloc(): unaligned fastbin chunk detected"); \
} \
- while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) \
+ while ((pp = catomic_compare_and_exchange_val_acq (fb, pp, victim)) \
!= victim); \
if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
@@ -3583,8 +3606,11 @@ _int_malloc (mstate av, size_t bytes)
if (victim != NULL)
{
+ if (__glibc_unlikely (!aligned_OK (victim)))
+ malloc_printerr ("malloc(): unaligned fastbin chunk detected");
+
if (SINGLE_THREAD_P)
- *fb = victim->fd;
+ *fb = REVEAL_PTR (victim->fd);
else
REMOVE_FB (fb, pp, victim);
if (__glibc_likely (victim != NULL))
@@ -3605,8 +3631,10 @@ _int_malloc (mstate av, size_t bytes)
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = *fb) != NULL)
{
+ if (__glibc_unlikely (!aligned_OK (tc_victim)))
+ malloc_printerr ("malloc(): unaligned fastbin chunk detected");
if (SINGLE_THREAD_P)
- *fb = tc_victim->fd;
+ *fb = REVEAL_PTR (tc_victim->fd);
else
{
REMOVE_FB (fb, pp, tc_victim);
@@ -4196,11 +4224,15 @@ _int_free (mstate av, mchunkptr p, int have_lock)
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
- tmp = tmp->next)
+ tmp = REVEAL_PTR (tmp->next))
+ {
+ if (__glibc_unlikely (!aligned_OK (tmp)))
+ malloc_printerr ("free(): unaligned chunk detected in tcache 2");
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
+ }
}
if (tcache->counts[tc_idx] < mp_.tcache_count)
@@ -4264,7 +4296,7 @@ _int_free (mstate av, mchunkptr p, int have_lock)
add (i.e., double free). */
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
- p->fd = old;
+ p->fd = PROTECT_PTR (&p->fd, old);
*fb = p;
}
else
@@ -4274,7 +4306,8 @@ _int_free (mstate av, mchunkptr p, int have_lock)
add (i.e., double free). */
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
- p->fd = old2 = old;
+ old2 = old;
+ p->fd = PROTECT_PTR (&p->fd, old);
}
while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2))
!= old2);
@@ -4472,13 +4505,17 @@ static void malloc_consolidate(mstate av)
if (p != 0) {
do {
{
+ if (__glibc_unlikely (!aligned_OK (p)))
+ malloc_printerr ("malloc_consolidate(): " \
+ "unaligned fastbin chunk detected");
+
unsigned int idx = fastbin_index (chunksize (p));
if ((&fastbin (av, idx)) != fb)
malloc_printerr ("malloc_consolidate(): invalid chunk size");
}
check_inuse_chunk(av, p);
- nextp = p->fd;
+ nextp = REVEAL_PTR (p->fd);
/* Slightly streamlined version of consolidation code in free() */
size = chunksize (p);
@@ -4896,8 +4933,13 @@ int_mallinfo (mstate av, struct mallinfo *m)
for (i = 0; i < NFASTBINS; ++i)
{
- for (p = fastbin (av, i); p != 0; p = p->fd)
+ for (p = fastbin (av, i);
+ p != 0;
+ p = REVEAL_PTR (p->fd))
{
+ if (__glibc_unlikely (!aligned_OK (p)))
+ malloc_printerr ("int_mallinfo(): " \
+ "unaligned fastbin chunk detected");
++nfastblocks;
fastavail += chunksize (p);
}
@@ -5437,8 +5479,11 @@ __malloc_info (int options, FILE *fp)
while (p != NULL)
{
+ if (__glibc_unlikely (!aligned_OK (p)))
+ malloc_printerr ("__malloc_info(): " \
+ "unaligned fastbin chunk detected");
++nthissize;
- p = p->fd;
+ p = REVEAL_PTR (p->fd);
}
fastavail += nthissize * thissize;

View File

@ -0,0 +1,87 @@
From 768358b6a80742f6be68ecd9f952f4b60614df96 Mon Sep 17 00:00:00 2001
From: Eyal Itkin <eyalit@checkpoint.com>
Date: Tue, 31 Mar 2020 01:55:13 -0400
Subject: Typo fixes and CR cleanup in Safe-Linking
Removed unneeded '\' chars from end of lines and fixed some
indentation issues that were introduced in the original
Safe-Linking patch.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 1282863681..0e4acb22f6 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2170,7 +2170,7 @@ do_check_malloc_state (mstate av)
while (p != 0)
{
if (__glibc_unlikely (!aligned_OK (p)))
- malloc_printerr ("do_check_malloc_state(): " \
+ malloc_printerr ("do_check_malloc_state(): "
"unaligned fastbin chunk detected");
/* each chunk claims to be inuse */
do_check_inuse_chunk (av, p);
@@ -2977,9 +2977,9 @@ tcache_thread_shutdown (void)
while (tcache_tmp->entries[i])
{
tcache_entry *e = tcache_tmp->entries[i];
- if (__glibc_unlikely (!aligned_OK (e)))
- malloc_printerr ("tcache_thread_shutdown(): " \
- "unaligned tcache chunk detected");
+ if (__glibc_unlikely (!aligned_OK (e)))
+ malloc_printerr ("tcache_thread_shutdown(): "
+ "unaligned tcache chunk detected");
tcache_tmp->entries[i] = REVEAL_PTR (e->next);
__libc_free (e);
}
@@ -4225,14 +4225,14 @@ _int_free (mstate av, mchunkptr p, int have_lock)
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = REVEAL_PTR (tmp->next))
- {
- if (__glibc_unlikely (!aligned_OK (tmp)))
- malloc_printerr ("free(): unaligned chunk detected in tcache 2");
- if (tmp == e)
- malloc_printerr ("free(): double free detected in tcache 2");
- /* If we get here, it was a coincidence. We've wasted a
- few cycles, but don't abort. */
- }
+ {
+ if (__glibc_unlikely (!aligned_OK (tmp)))
+ malloc_printerr ("free(): unaligned chunk detected in tcache 2");
+ if (tmp == e)
+ malloc_printerr ("free(): double free detected in tcache 2");
+ /* If we get here, it was a coincidence. We've wasted a
+ few cycles, but don't abort. */
+ }
}
if (tcache->counts[tc_idx] < mp_.tcache_count)
@@ -4506,7 +4506,7 @@ static void malloc_consolidate(mstate av)
do {
{
if (__glibc_unlikely (!aligned_OK (p)))
- malloc_printerr ("malloc_consolidate(): " \
+ malloc_printerr ("malloc_consolidate(): "
"unaligned fastbin chunk detected");
unsigned int idx = fastbin_index (chunksize (p));
@@ -4938,7 +4938,7 @@ int_mallinfo (mstate av, struct mallinfo *m)
p = REVEAL_PTR (p->fd))
{
if (__glibc_unlikely (!aligned_OK (p)))
- malloc_printerr ("int_mallinfo(): " \
+ malloc_printerr ("int_mallinfo(): "
"unaligned fastbin chunk detected");
++nfastblocks;
fastavail += chunksize (p);
@@ -5480,7 +5480,7 @@ __malloc_info (int options, FILE *fp)
while (p != NULL)
{
if (__glibc_unlikely (!aligned_OK (p)))
- malloc_printerr ("__malloc_info(): " \
+ malloc_printerr ("__malloc_info(): "
"unaligned fastbin chunk detected");
++nthissize;
p = REVEAL_PTR (p->fd);

View File

@ -0,0 +1,100 @@
From 49c3c37651e2d2ec4ff8ce21252bbbc08a9d6639 Mon Sep 17 00:00:00 2001
From: Eyal Itkin <eyalit@checkpoint.com>
Date: Tue, 31 Mar 2020 02:00:14 -0400
Subject: Fix alignment bug in Safe-Linking
Alignment checks should be performed on the user's buffer and NOT
on the mchunkptr as was done before. This caused bugs in 32 bit
versions, because: 2*sizeof(t) != MALLOC_ALIGNMENT.
As the tcache works on users' buffers it uses the aligned_OK()
check, and the rest work on mchunkptr and therefore check using
misaligned_chunk().
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 0e4acb22f6..6acb5ad43a 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2169,7 +2169,7 @@ do_check_malloc_state (mstate av)
while (p != 0)
{
- if (__glibc_unlikely (!aligned_OK (p)))
+ if (__glibc_unlikely (misaligned_chunk (p)))
malloc_printerr ("do_check_malloc_state(): "
"unaligned fastbin chunk detected");
/* each chunk claims to be inuse */
@@ -2949,11 +2949,11 @@ static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
+ if (__glibc_unlikely (!aligned_OK (e)))
+ malloc_printerr ("malloc(): unaligned tcache chunk detected");
tcache->entries[tc_idx] = REVEAL_PTR (e->next);
--(tcache->counts[tc_idx]);
e->key = NULL;
- if (__glibc_unlikely (!aligned_OK (e)))
- malloc_printerr ("malloc(): unaligned tcache chunk detected");
return (void *) e;
}
@@ -3591,7 +3591,7 @@ _int_malloc (mstate av, size_t bytes)
if (victim == NULL) \
break; \
pp = REVEAL_PTR (victim->fd); \
- if (__glibc_unlikely (!aligned_OK (pp))) \
+ if (__glibc_unlikely (pp != NULL && misaligned_chunk (pp))) \
malloc_printerr ("malloc(): unaligned fastbin chunk detected"); \
} \
while ((pp = catomic_compare_and_exchange_val_acq (fb, pp, victim)) \
@@ -3606,8 +3606,8 @@ _int_malloc (mstate av, size_t bytes)
if (victim != NULL)
{
- if (__glibc_unlikely (!aligned_OK (victim)))
- malloc_printerr ("malloc(): unaligned fastbin chunk detected");
+ if (__glibc_unlikely (misaligned_chunk (victim)))
+ malloc_printerr ("malloc(): unaligned fastbin chunk detected 2");
if (SINGLE_THREAD_P)
*fb = REVEAL_PTR (victim->fd);
@@ -3631,8 +3631,8 @@ _int_malloc (mstate av, size_t bytes)
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = *fb) != NULL)
{
- if (__glibc_unlikely (!aligned_OK (tc_victim)))
- malloc_printerr ("malloc(): unaligned fastbin chunk detected");
+ if (__glibc_unlikely (misaligned_chunk (tc_victim)))
+ malloc_printerr ("malloc(): unaligned fastbin chunk detected 3");
if (SINGLE_THREAD_P)
*fb = REVEAL_PTR (tc_victim->fd);
else
@@ -4505,7 +4505,7 @@ static void malloc_consolidate(mstate av)
if (p != 0) {
do {
{
- if (__glibc_unlikely (!aligned_OK (p)))
+ if (__glibc_unlikely (misaligned_chunk (p)))
malloc_printerr ("malloc_consolidate(): "
"unaligned fastbin chunk detected");
@@ -4937,7 +4937,7 @@ int_mallinfo (mstate av, struct mallinfo *m)
p != 0;
p = REVEAL_PTR (p->fd))
{
- if (__glibc_unlikely (!aligned_OK (p)))
+ if (__glibc_unlikely (misaligned_chunk (p)))
malloc_printerr ("int_mallinfo(): "
"unaligned fastbin chunk detected");
++nfastblocks;
@@ -5479,7 +5479,7 @@ __malloc_info (int options, FILE *fp)
while (p != NULL)
{
- if (__glibc_unlikely (!aligned_OK (p)))
+ if (__glibc_unlikely (misaligned_chunk (p)))
malloc_printerr ("__malloc_info(): "
"unaligned fastbin chunk detected");
++nthissize;

View File

@ -0,0 +1,215 @@
From 6310d570bf20348135d09e1f9de84a9ae7d06f83 Mon Sep 17 00:00:00 2001
From: Eyal Itkin <eyalit@checkpoint.com>
Date: Thu, 2 Apr 2020 07:26:35 -0400
Subject: Add tests for Safe-Linking
Adding the test "tst-safe-linking" for testing that Safe-Linking works
as expected. The test checks these 3 main flows:
* tcache protection
* fastbin protection
* malloc_consolidate() correctness
As there is a random chance of 1/16 that of the alignment will remain
correct, the test checks each flow up to 10 times, using different random
values for the pointer corruption. As a result, the chance for a false
failure of a given tested flow is 2**(-40), thus highly unlikely.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/malloc/Makefile b/malloc/Makefile
index 984045b5b9..e22cbde22d 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-safe-linking \
tests-static := \
tst-interpose-static-nothread \
diff --git a/malloc/tst-safe-linking.c b/malloc/tst-safe-linking.c
new file mode 100644
index 0000000000..067b6c09cf
--- /dev/null
+++ b/malloc/tst-safe-linking.c
@@ -0,0 +1,179 @@
+/* Test reporting of Safe-Linking caught errors.
+ 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/>. */
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+#include <stdbool.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+
+/* Run CALLBACK and check that the data on standard error equals
+ EXPECTED. */
+static void
+check (const char *test, void (*callback) (void *),
+ const char *expected)
+{
+ int i, rand_mask;
+ bool success = false;
+ /* There is a chance of 1/16 that a corrupted pointer will be aligned.
+ Try multiple times so that statistical failure will be improbable. */
+ for (i = 0; i < 10 && !success; ++i)
+ {
+ rand_mask = rand () & 0xFF;
+ struct support_capture_subprocess result
+ = support_capture_subprocess (callback, &rand_mask);
+ /* Did not crash, could happen. Try again. */
+ if (strlen (result.err.buffer) == 0)
+ continue;
+ /* Crashed, must be the expected result. */
+ if (strcmp (result.err.buffer, expected) != 0)
+ {
+ support_record_failure ();
+ printf ("error: test %s unexpected standard error data\n"
+ " expected: %s\n"
+ " actual: %s\n",
+ test, expected, result.err.buffer);
+ }
+ TEST_VERIFY (WIFSIGNALED (result.status));
+ if (WIFSIGNALED (result.status))
+ TEST_VERIFY (WTERMSIG (result.status) == SIGABRT);
+ support_capture_subprocess_free (&result);
+ success = true;
+ }
+ TEST_VERIFY (success);
+}
+
+/* Implementation details must be kept in sync with malloc. */
+#define TCACHE_FILL_COUNT 7
+#define TCACHE_ALLOC_SIZE 0x20
+#define MALLOC_CONSOLIDATE_SIZE 256*1024
+
+/* Try corrupting the tcache list. */
+static void
+test_tcache (void *closure)
+{
+ int mask = ((int *)closure)[0];
+ size_t size = TCACHE_ALLOC_SIZE;
+
+ /* Populate the tcache list. */
+ void * volatile a = malloc (size);
+ void * volatile b = malloc (size);
+ void * volatile c = malloc (size);
+ free (a);
+ free (b);
+ free (c);
+
+ /* Corrupt the pointer with a random value, and avoid optimizations. */
+ printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+ memset (c, mask & 0xFF, size);
+ printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+
+ c = malloc (size);
+ /* This line will trigger the Safe-Linking check. */
+ b = malloc (size);
+ printf ("b=%p\n", b);
+}
+
+/* Try corrupting the fastbin list. */
+static void
+test_fastbin (void *closure)
+{
+ int i;
+ int mask = ((int *)closure)[0];
+ size_t size = TCACHE_ALLOC_SIZE;
+
+ /* Take the tcache out of the game. */
+ for (i = 0; i < TCACHE_FILL_COUNT; ++i)
+ {
+ void * volatile p = calloc (1, size);
+ free (p);
+ }
+
+ /* Populate the fastbin list. */
+ void * volatile a = calloc (1, size);
+ void * volatile b = calloc (1, size);
+ void * volatile c = calloc (1, size);
+ free (a);
+ free (b);
+ free (c);
+
+ /* Corrupt the pointer with a random value, and avoid optimizations. */
+ printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+ memset (c, mask & 0xFF, size);
+ printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+
+ c = calloc (1, size);
+ /* This line will trigger the Safe-Linking check. */
+ b = calloc (1, size);
+ printf ("b=%p\n", b);
+}
+
+/* Try corrupting the fastbin list and trigger a consolidate. */
+static void
+test_fastbin_consolidate (void *closure)
+{
+ int i;
+ int mask = ((int*)closure)[0];
+ size_t size = TCACHE_ALLOC_SIZE;
+
+ /* Take the tcache out of the game. */
+ for (i = 0; i < TCACHE_FILL_COUNT; ++i)
+ {
+ void * volatile p = calloc (1, size);
+ free (p);
+ }
+
+ /* Populate the fastbin list. */
+ void * volatile a = calloc (1, size);
+ void * volatile b = calloc (1, size);
+ void * volatile c = calloc (1, size);
+ free (a);
+ free (b);
+ free (c);
+
+ /* Corrupt the pointer with a random value, and avoid optimizations. */
+ printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+ memset (c, mask & 0xFF, size);
+ printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+
+ /* This line will trigger the Safe-Linking check. */
+ b = malloc (MALLOC_CONSOLIDATE_SIZE);
+ printf ("b=%p\n", b);
+}
+
+static int
+do_test (void)
+{
+ /* Seed the random for the test. */
+ srand (time (NULL));
+
+ check ("test_tcache", test_tcache,
+ "malloc(): unaligned tcache chunk detected\n");
+ check ("test_fastbin", test_fastbin,
+ "malloc(): unaligned fastbin chunk detected 2\n");
+ check ("test_fastbin_consolidate", test_fastbin_consolidate,
+ "malloc_consolidate(): unaligned fastbin chunk detected\n");
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,35 @@
From b9cde4e3aa1ff338da7064daf1386b2f4a7351ba Mon Sep 17 00:00:00 2001
From: DJ Delorie <dj@redhat.com>
Date: Sat, 4 Apr 2020 01:44:56 -0400
Subject: malloc: ensure set_max_fast never stores zero [BZ #25733]
The code for set_max_fast() stores an "impossibly small value"
instead of zero, when the parameter is zero. However, for
small values of the parameter (ex: 1 or 2) the computation
results in a zero being stored anyway.
This patch checks for the parameter being small enough for the
computation to result in zero instead, so that a zero is never
stored.
key values which result in zero being stored:
x86-64: 1..7 (or other 64-bit)
i686: 1..11
armhfp: 1..3 (or other 32-bit)
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 6acb5ad43a..ee87ddbbf9 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1632,7 +1632,7 @@ static INTERNAL_SIZE_T global_max_fast;
*/
#define set_max_fast(s) \
- global_max_fast = (((s) == 0) \
+ global_max_fast = (((size_t) (s) <= MALLOC_ALIGN_MASK - SIZE_SZ) \
? MIN_CHUNK_SIZE / 2 : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
static inline INTERNAL_SIZE_T

View File

@ -0,0 +1,35 @@
From 0e00b35704e67c499c3abfbd5b6224a13d38b012 Mon Sep 17 00:00:00 2001
From: "W. Hashimoto" <ssmallkirby@gmail.com>
Date: Fri, 11 Dec 2020 16:59:10 -0500
Subject: malloc: Detect infinite-loop in _int_free when freeing tcache
[BZ#27052]
If linked-list of tcache contains a loop, it invokes infinite
loop in _int_free when freeing tcache. The PoC which invokes
such infinite loop is on the Bugzilla(#27052). This loop
should terminate when the loop exceeds mp_.tcache_count and
the program should abort. The affected glibc version is
2.29 or later.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 5b87bdb081..ec2d934595 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -4224,11 +4224,14 @@ _int_free (mstate av, mchunkptr p, int have_lock)
if (__glibc_unlikely (e->key == tcache))
{
tcache_entry *tmp;
+ size_t cnt = 0;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
- tmp = REVEAL_PTR (tmp->next))
+ tmp = REVEAL_PTR (tmp->next), ++cnt)
{
+ if (cnt >= mp_.tcache_count)
+ malloc_printerr ("free(): too many chunks detected in tcache");
if (__glibc_unlikely (!aligned_OK (tmp)))
malloc_printerr ("free(): unaligned chunk detected in tcache 2");
if (tmp == e)

View File

@ -0,0 +1,133 @@
From fc859c304898a5ec72e0ba5269ed136ed0ea10e1 Mon Sep 17 00:00:00 2001
From: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Wed, 7 Jul 2021 23:02:46 +0530
Subject: Harden tcache double-free check
The tcache allocator layer uses the tcache pointer as a key to
identify a block that may be freed twice. Since this is in the
application data area, an attacker exploiting a use-after-free could
potentially get access to the entire tcache structure through this
key. A detailed write-up was provided by Awarau here:
https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
Replace this static pointer use for key checking with one that is
generated at malloc initialization. The first attempt is through
getrandom with a fallback to random_bits(), which is a simple
pseudo-random number generator based on the clock. The fallback ought
to be sufficient since the goal of the randomness is only to make the
key arbitrary enough that it is very unlikely to collide with user
data.
Co-authored-by: Eyal Itkin <eyalit@checkpoint.com>
[note: context for arena.c chunk #2 changed to accomodate missing
tagging support code - DJ]
diff -rup a/malloc/arena.c b/malloc/arena.c
--- a/malloc/arena.c 2022-09-16 01:09:02.003843024 -0400
+++ b/malloc/arena.c 2022-09-16 01:25:51.879994057 -0400
@@ -286,6 +286,10 @@ extern struct dl_open_hook *_dl_open_hoo
libc_hidden_proto (_dl_open_hook);
#endif
+#if USE_TCACHE
+static void tcache_key_initialize (void);
+#endif
+
static void
ptmalloc_init (void)
{
@@ -294,6 +298,10 @@ ptmalloc_init (void)
__malloc_initialized = 0;
+#if USE_TCACHE
+ tcache_key_initialize ();
+#endif
+
#ifdef SHARED
/* In case this libc copy is in a non-default namespace, never use brk.
Likewise if dlopened from statically linked program. */
diff -rup a/malloc/malloc.c b/malloc/malloc.c
--- a/malloc/malloc.c 2022-09-16 01:09:05.491977387 -0400
+++ b/malloc/malloc.c 2022-09-16 01:25:51.883994213 -0400
@@ -247,6 +247,10 @@
/* For SINGLE_THREAD_P. */
#include <sysdep-cancel.h>
+/* For tcache double-free check. */
+#include <random-bits.h>
+#include <sys/random.h>
+
/*
Debugging:
@@ -2924,7 +2928,7 @@ typedef struct tcache_entry
{
struct tcache_entry *next;
/* This field exists to detect double frees. */
- struct tcache_perthread_struct *key;
+ uintptr_t key;
} tcache_entry;
/* There is one of these for each thread, which contains the
@@ -2941,6 +2945,31 @@ typedef struct tcache_perthread_struct
static __thread bool tcache_shutting_down = false;
static __thread tcache_perthread_struct *tcache = NULL;
+/* Process-wide key to try and catch a double-free in the same thread. */
+static uintptr_t tcache_key;
+
+/* The value of tcache_key does not really have to be a cryptographically
+ secure random number. It only needs to be arbitrary enough so that it does
+ not collide with values present in applications. If a collision does happen
+ consistently enough, it could cause a degradation in performance since the
+ entire list is checked to check if the block indeed has been freed the
+ second time. The odds of this happening are exceedingly low though, about 1
+ in 2^wordsize. There is probably a higher chance of the performance
+ degradation being due to a double free where the first free happened in a
+ different thread; that's a case this check does not cover. */
+static void
+tcache_key_initialize (void)
+{
+ if (__getrandom (&tcache_key, sizeof(tcache_key), GRND_NONBLOCK)
+ != sizeof (tcache_key))
+ {
+ tcache_key = random_bits ();
+#if __WORDSIZE == 64
+ tcache_key = (tcache_key << 32) | random_bits ();
+#endif
+ }
+}
+
/* Caller must ensure that we know tc_idx is valid and there's room
for more chunks. */
static __always_inline void
@@ -2950,7 +2979,7 @@ tcache_put (mchunkptr chunk, size_t tc_i
/* Mark this chunk as "in the tcache" so the test in _int_free will
detect a double free. */
- e->key = tcache;
+ e->key = tcache_key;
e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
tcache->entries[tc_idx] = e;
@@ -2967,7 +2996,7 @@ tcache_get (size_t tc_idx)
malloc_printerr ("malloc(): unaligned tcache chunk detected");
tcache->entries[tc_idx] = REVEAL_PTR (e->next);
--(tcache->counts[tc_idx]);
- e->key = NULL;
+ e->key = 0;
return (void *) e;
}
@@ -4231,7 +4260,7 @@ _int_free (mstate av, mchunkptr p, int h
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
- if (__glibc_unlikely (e->key == tcache))
+ if (__glibc_unlikely (e->key == tcache_key))
{
tcache_entry *tmp;
size_t cnt = 0;

View File

@ -0,0 +1,27 @@
commit 97f8225d22ef727ae9935cc231643efdc430d530
Author: Zack Weinberg <zackw@panix.com>
Date: Thu Mar 14 09:44:22 2019 -0400
scripts/check-obsolete-constructs.py: Process all headers as UTF-8.
A few of our installed headers contain UTF-8 in comments.
check-obsolete-constructs opened files without explicitly specifying
their encoding, so it would barf on these headers if “make check” was
run in a non-UTF-8 locale.
* scripts/check-obsolete-constructs.py (HeaderChecker.check):
Specify encoding="utf-8" when opening headers to check.
diff --git a/scripts/check-obsolete-constructs.py b/scripts/check-obsolete-constructs.py
index ce5c72251f4d7cc0..89d21dea6e788783 100755
--- a/scripts/check-obsolete-constructs.py
+++ b/scripts/check-obsolete-constructs.py
@@ -437,7 +437,7 @@ class HeaderChecker:
def check(self, fname):
self.fname = fname
try:
- with open(fname, "rt") as fp:
+ with open(fname, "rt", encoding="utf-8") as fp:
contents = fp.read()
except OSError as e:
sys.stderr.write("{}: {}\n".format(fname, e.strerror))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,409 @@
commit 198abcbb94618730dae1b3f4393efaa49e0ec8c7
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Apr 11 11:30:31 2022 +0200
Default to --with-default-link=no (bug 25812)
This is necessary to place the libio vtables into the RELRO segment.
New tests elf/tst-relro-ldso and elf/tst-relro-libc are added to
verify that this is what actually happens.
The new tests fail on ia64 due to lack of (default) RELRO support
inbutils, so they are XFAILed there.
Conflicts:
elf/Makefile
(missing valgrind smoke test)
diff --git a/INSTALL b/INSTALL
index b3a4370f592c5047..b69672b283c0b774 100644
--- a/INSTALL
+++ b/INSTALL
@@ -90,6 +90,12 @@ if 'CFLAGS' is specified it must enable optimization. For example:
library will still be usable, but functionality may be lost--for
example, you can't build a shared libc with old binutils.
+'--with-default-link=FLAG'
+ With '--with-default-link=yes', the build system does not use a
+ custom linker script for linking shared objects. The default for
+ FLAG is the opposite, 'no', because the custom linker script is
+ needed for full RELRO protection.
+
'--with-nonshared-cflags=CFLAGS'
Use additional compiler flags CFLAGS to build the parts of the
library which are always statically linked into applications and
diff --git a/configure b/configure
index 8b3681d2e28310c8..c794cea4359b3da3 100755
--- a/configure
+++ b/configure
@@ -3339,7 +3339,7 @@ fi
if test "${with_default_link+set}" = set; then :
withval=$with_default_link; use_default_link=$withval
else
- use_default_link=default
+ use_default_link=no
fi
@@ -5965,69 +5965,6 @@ fi
$as_echo "$libc_cv_hashstyle" >&6; }
-# The linker's default -shared behavior is good enough if it
-# does these things that our custom linker scripts ensure that
-# all allocated NOTE sections come first.
-if test "$use_default_link" = default; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sufficient default -shared layout" >&5
-$as_echo_n "checking for sufficient default -shared layout... " >&6; }
-if ${libc_cv_use_default_link+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- libc_cv_use_default_link=no
- cat > conftest.s <<\EOF
- .section .note.a,"a",%note
- .balign 4
- .long 4,4,9
- .string "GNU"
- .string "foo"
- .section .note.b,"a",%note
- .balign 4
- .long 4,4,9
- .string "GNU"
- .string "bar"
-EOF
- if { ac_try=' ${CC-cc} $ASFLAGS -shared -o conftest.so conftest.s 1>&5'
- { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
- (eval $ac_try) 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; } &&
- ac_try=`$READELF -S conftest.so | sed -n \
- '${x;p;}
- s/^ *\[ *[1-9][0-9]*\] *\([^ ][^ ]*\) *\([^ ][^ ]*\) .*$/\2 \1/
- t a
- b
- : a
- H'`
- then
- libc_seen_a=no libc_seen_b=no
- set -- $ac_try
- while test $# -ge 2 -a "$1" = NOTE; do
- case "$2" in
- .note.a) libc_seen_a=yes ;;
- .note.b) libc_seen_b=yes ;;
- esac
- shift 2
- done
- case "$libc_seen_a$libc_seen_b" in
- yesyes)
- libc_cv_use_default_link=yes
- ;;
- *)
- echo >&5 "\
-$libc_seen_a$libc_seen_b from:
-$ac_try"
- ;;
- esac
- fi
- rm -f conftest*
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_use_default_link" >&5
-$as_echo "$libc_cv_use_default_link" >&6; }
- use_default_link=$libc_cv_use_default_link
-fi
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLOB_DAT reloc" >&5
$as_echo_n "checking for GLOB_DAT reloc... " >&6; }
if ${libc_cv_has_glob_dat+:} false; then :
diff --git a/configure.ac b/configure.ac
index 82d9ab2fb67145bb..52429d82344954b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -152,7 +152,7 @@ AC_ARG_WITH([default-link],
AC_HELP_STRING([--with-default-link],
[do not use explicit linker scripts]),
[use_default_link=$withval],
- [use_default_link=default])
+ [use_default_link=no])
dnl Additional build flags injection.
AC_ARG_WITH([nonshared-cflags],
@@ -1352,59 +1352,6 @@ fi
rm -f conftest*])
AC_SUBST(libc_cv_hashstyle)
-# The linker's default -shared behavior is good enough if it
-# does these things that our custom linker scripts ensure that
-# all allocated NOTE sections come first.
-if test "$use_default_link" = default; then
- AC_CACHE_CHECK([for sufficient default -shared layout],
- libc_cv_use_default_link, [dnl
- libc_cv_use_default_link=no
- cat > conftest.s <<\EOF
- .section .note.a,"a",%note
- .balign 4
- .long 4,4,9
- .string "GNU"
- .string "foo"
- .section .note.b,"a",%note
- .balign 4
- .long 4,4,9
- .string "GNU"
- .string "bar"
-EOF
- if AC_TRY_COMMAND([dnl
- ${CC-cc} $ASFLAGS -shared -o conftest.so conftest.s 1>&AS_MESSAGE_LOG_FD]) &&
- ac_try=`$READELF -S conftest.so | sed -n \
- ['${x;p;}
- s/^ *\[ *[1-9][0-9]*\] *\([^ ][^ ]*\) *\([^ ][^ ]*\) .*$/\2 \1/
- t a
- b
- : a
- H']`
- then
- libc_seen_a=no libc_seen_b=no
- set -- $ac_try
- while test $# -ge 2 -a "$1" = NOTE; do
- case "$2" in
- .note.a) libc_seen_a=yes ;;
- .note.b) libc_seen_b=yes ;;
- esac
- shift 2
- done
- case "$libc_seen_a$libc_seen_b" in
- yesyes)
- libc_cv_use_default_link=yes
- ;;
- *)
- echo >&AS_MESSAGE_LOG_FD "\
-$libc_seen_a$libc_seen_b from:
-$ac_try"
- ;;
- esac
- fi
- rm -f conftest*])
- use_default_link=$libc_cv_use_default_link
-fi
-
AC_CACHE_CHECK(for GLOB_DAT reloc,
libc_cv_has_glob_dat, [dnl
cat > conftest.c <<EOF
diff --git a/elf/Makefile b/elf/Makefile
index 89ce4f5196e5eb39..1fdf40cbd49e233e 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -477,6 +477,40 @@ tests-execstack-yes = \
# tests-execstack-yes
endif
endif
+
+tests-special += $(objpfx)tst-relro-ldso.out $(objpfx)tst-relro-libc.out
+$(objpfx)tst-relro-ldso.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
+ $(objpfx)ld.so
+ $(PYTHON) tst-relro-symbols.py $(objpfx)ld.so \
+ --required=_rtld_global_ro \
+ > $@ 2>&1; $(evaluate-test)
+# The optional symbols are present in libc only if the architecture has
+# the GLIBC_2.0 symbol set in libc.
+$(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
+ $(common-objpfx)libc.so
+ $(PYTHON) tst-relro-symbols.py $(common-objpfx)libc.so \
+ --required=_IO_cookie_jumps \
+ --required=_IO_file_jumps \
+ --required=_IO_file_jumps_maybe_mmap \
+ --required=_IO_file_jumps_mmap \
+ --required=_IO_helper_jumps \
+ --required=_IO_mem_jumps \
+ --required=_IO_obstack_jumps \
+ --required=_IO_proc_jumps \
+ --required=_IO_str_chk_jumps \
+ --required=_IO_str_jumps \
+ --required=_IO_strn_jumps \
+ --required=_IO_wfile_jumps \
+ --required=_IO_wfile_jumps_maybe_mmap \
+ --required=_IO_wfile_jumps_mmap \
+ --required=_IO_wmem_jumps \
+ --required=_IO_wstr_jumps \
+ --required=_IO_wstrn_jumps \
+ --optional=_IO_old_cookie_jumps \
+ --optional=_IO_old_file_jumps \
+ --optional=_IO_old_proc_jumps \
+ > $@ 2>&1; $(evaluate-test)
+
tests += $(tests-execstack-$(have-z-execstack))
ifeq ($(run-built-tests),yes)
tests-special += \
diff --git a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py
new file mode 100644
index 0000000000000000..368ea3349f86bd81
--- /dev/null
+++ b/elf/tst-relro-symbols.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python3
+# Verify that certain symbols are covered by RELRO.
+# Copyright (C) 2022 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+"""Analyze a (shared) object to verify that certain symbols are
+present and covered by the PT_GNU_RELRO segment.
+
+"""
+
+import argparse
+import os.path
+import sys
+
+# Make available glibc Python modules.
+sys.path.append(os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'scripts'))
+
+import glibcelf
+
+def find_relro(path: str, img: glibcelf.Image) -> (int, int):
+ """Discover the address range of the PT_GNU_RELRO segment."""
+ for phdr in img.phdrs():
+ if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO:
+ # The computation is not entirely accurate because
+ # _dl_protect_relro in elf/dl-reloc.c rounds both the
+ # start end and downwards using the run-time page size.
+ return phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz
+ sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path))
+ sys.exit(1)
+
+def check_in_relro(kind, relro_begin, relro_end, name, start, size, error):
+ """Check if a section or symbol falls within in the RELRO segment."""
+ end = start + size - 1
+ if not (relro_begin <= start < end < relro_end):
+ error(
+ '{} {!r} of size {} at 0x{:x} is not in RELRO range [0x{:x}, 0x{:x})'.format(
+ kind, name.decode('UTF-8'), start, size,
+ relro_begin, relro_end))
+
+def get_parser():
+ """Return an argument parser for this script."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('object', help='path to object file to check')
+ parser.add_argument('--required', metavar='NAME', default=(),
+ help='required symbol names', nargs='*')
+ parser.add_argument('--optional', metavar='NAME', default=(),
+ help='required symbol names', nargs='*')
+ return parser
+
+def main(argv):
+ """The main entry point."""
+ parser = get_parser()
+ opts = parser.parse_args(argv)
+ img = glibcelf.Image.readfile(opts.object)
+
+ required_symbols = frozenset([sym.encode('UTF-8')
+ for sym in opts.required])
+ optional_symbols = frozenset([sym.encode('UTF-8')
+ for sym in opts.optional])
+ check_symbols = required_symbols | optional_symbols
+
+ # Tracks the symbols in check_symbols that have been found.
+ symbols_found = set()
+
+ # Discover the extent of the RELRO segment.
+ relro_begin, relro_end = find_relro(opts.object, img)
+ symbol_table_found = False
+
+ errors = False
+ def error(msg: str) -> None:
+ """Record an error condition and write a message to standard output."""
+ nonlocal errors
+ errors = True
+ sys.stdout.write('{}: error: {}\n'.format(opts.object, msg))
+
+ # Iterate over section headers to find the symbol table.
+ for shdr in img.shdrs():
+ if shdr.sh_type == glibcelf.Sht.SHT_SYMTAB:
+ symbol_table_found = True
+ for sym in img.syms(shdr):
+ if sym.st_name in check_symbols:
+ symbols_found.add(sym.st_name)
+
+ # Validate symbol type, section, and size.
+ if sym.st_info.type != glibcelf.Stt.STT_OBJECT:
+ error('symbol {!r} has wrong type {}'.format(
+ sym.st_name.decode('UTF-8'), sym.st_info.type))
+ if sym.st_shndx in glibcelf.Shn:
+ error('symbol {!r} has reserved section {}'.format(
+ sym.st_name.decode('UTF-8'), sym.st_shndx))
+ continue
+ if sym.st_size == 0:
+ error('symbol {!r} has size zero'.format(
+ sym.st_name.decode('UTF-8')))
+ continue
+
+ check_in_relro('symbol', relro_begin, relro_end,
+ sym.st_name, sym.st_value, sym.st_size,
+ error)
+ continue # SHT_SYMTAB
+ if shdr.sh_name == b'.data.rel.ro' \
+ or shdr.sh_name.startswith(b'.data.rel.ro.'):
+ check_in_relro('section', relro_begin, relro_end,
+ shdr.sh_name, shdr.sh_addr, shdr.sh_size,
+ error)
+ continue
+
+ if required_symbols - symbols_found:
+ for sym in sorted(required_symbols - symbols_found):
+ error('symbol {!r} not found'.format(sym.decode('UTF-8')))
+
+ if errors:
+ sys.exit(1)
+
+ if not symbol_table_found:
+ sys.stdout.write(
+ '{}: warning: no symbol table found (stripped object)\n'.format(
+ opts.object))
+ sys.exit(77)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/manual/install.texi b/manual/install.texi
index c262fd56d0cef67b..a2c43bd692de7825 100644
--- a/manual/install.texi
+++ b/manual/install.texi
@@ -117,6 +117,12 @@ problem and suppress these constructs, so that the library will still be
usable, but functionality may be lost---for example, you can't build a
shared libc with old binutils.
+@item --with-default-link=@var{FLAG}
+With @code{--with-default-link=yes}, the build system does not use a
+custom linker script for linking shared objects. The default for
+@var{FLAG} is the opposite, @samp{no}, because the custom linker script
+is needed for full RELRO protection.
+
@item --with-nonshared-cflags=@var{cflags}
Use additional compiler flags @var{cflags} to build the parts of the
library which are always statically linked into applications and
diff --git a/sysdeps/unix/sysv/linux/ia64/Makefile b/sysdeps/unix/sysv/linux/ia64/Makefile
index 97fc7df0b122d6a0..b1ad1ab7b1efa34c 100644
--- a/sysdeps/unix/sysv/linux/ia64/Makefile
+++ b/sysdeps/unix/sysv/linux/ia64/Makefile
@@ -1,3 +1,9 @@
+ifeq ($(subdir),elf)
+# ia64 does not support PT_GNU_RELRO.
+test-xfail-tst-relro-ldso = yes
+test-xfail-tst-relro-libc = yes
+endif
+
ifeq ($(subdir),misc)
sysdep_headers += sys/rse.h
endif

View File

@ -0,0 +1,26 @@
commit b571f3adffdcbed23f35ea39b0ca43809dbb4f5b
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Apr 22 19:34:52 2022 +0200
scripts/glibcelf.py: Mark as UNSUPPORTED on Python 3.5 and earlier
enum.IntFlag and enum.EnumMeta._missing_ support are not part of
earlier Python versions.
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index 8f7d0ca184845714..da0d5380f33a195e 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -28,6 +28,12 @@ import collections
import enum
import struct
+if not hasattr(enum, 'IntFlag'):
+ import sys
+ sys.stdout.write(
+ 'warning: glibcelf.py needs Python 3.6 for enum support\n')
+ sys.exit(77)
+
class _OpenIntEnum(enum.IntEnum):
"""Integer enumeration that supports arbitrary int values."""
@classmethod

View File

@ -0,0 +1,30 @@
Partial backport of the scripts/glibcelf.py part of:
commit 4610b24f5e4e6d2c4b769594efa6d460943163bb
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Tue Mar 29 14:08:54 2022 -0700
elf: Define DT_RELR related macros and types
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index da0d5380f33a195e..f847b36c55c15b8a 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -304,6 +304,7 @@ class Sht(_OpenIntEnum):
SHT_PREINIT_ARRAY = 16
SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18
+ SHT_RELR = 19
SHT_GNU_ATTRIBUTES = 0x6ffffff5
SHT_GNU_HASH = 0x6ffffff6
SHT_GNU_LIBLIST = 0x6ffffff7
@@ -593,6 +594,9 @@ class Dt(_OpenIntEnum):
DT_PREINIT_ARRAY = 32
DT_PREINIT_ARRAYSZ = 33
DT_SYMTAB_SHNDX = 34
+ DT_RELRSZ = 35
+ DT_RELR = 36
+ DT_RELRENT = 37
DT_GNU_PRELINKED = 0x6ffffdf5
DT_GNU_CONFLICTSZ = 0x6ffffdf6
DT_GNU_LIBLISTSZ = 0x6ffffdf7

View File

@ -0,0 +1,50 @@
commit d055481ce39d03652ac60de5078889e15b6917ff
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon May 16 21:59:24 2022 +0200
scripts/glibcelf.py: Add *T_RISCV_* constants
SHT_RISCV_ATTRIBUTES, PT_RISCV_ATTRIBUTES, DT_RISCV_VARIANT_CC were
added in commit 0b6c6750732483b4d59c2fcb45484079cd84157d
("Update RISC-V specific ELF definitions"). This caused the
elf/tst-glibcelf consistency check to fail.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index f847b36c55c15b8a..07bef940433b4c99 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -385,6 +385,10 @@ class ShtPARISC(enum.Enum):
SHT_PARISC_UNWIND = 0x70000001
SHT_PARISC_DOC = 0x70000002
+class ShtRISCV(enum.Enum):
+ """Supplemental SHT_* constants for EM_RISCV."""
+ SHT_RISCV_ATTRIBUTES = 0x70000003
+
class Pf(enum.IntFlag):
"""Program header flags. Type of Phdr.p_flags values."""
PF_X = 1
@@ -558,6 +562,10 @@ class PtPARISC(enum.Enum):
PT_PARISC_ARCHEXT = 0x70000000
PT_PARISC_UNWIND = 0x70000001
+class PtRISCV(enum.Enum):
+ """Supplemental PT_* constants for EM_RISCV."""
+ PT_RISCV_ATTRIBUTES = 0x70000003
+
class Dt(_OpenIntEnum):
"""ELF dynamic segment tags. Type of Dyn.d_val."""
DT_NULL = 0
@@ -710,6 +718,10 @@ class DtPPC64(enum.Enum):
DT_PPC64_OPDSZ = 0x70000002
DT_PPC64_OPT = 0x70000003
+class DtRISCV(enum.Enum):
+ """Supplemental DT_* constants for EM_RISCV."""
+ DT_RISCV_VARIANT_CC = 0x70000001
+
class DtSPARC(enum.Enum):
"""Supplemental DT_* constants for EM_SPARC."""
DT_SPARC_REGISTER = 0x70000001

View File

@ -0,0 +1,26 @@
commit 8521001731d6539382fa875f1cac9864c466ef27
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Mon Jun 6 14:41:24 2022 -0300
scripts/glibcelf.py: Add PT_AARCH64_MEMTAG_MTE constant
It was added in commit 603e5c8ba7257483c162cabb06eb6f79096429b6.
This caused the elf/tst-glibcelf consistency check to fail.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index 07bef940433b4c99..47f95d07baefb4ae 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -523,6 +523,10 @@ class Pt(_OpenIntEnum):
PT_SUNWBSS = 0x6ffffffa
PT_SUNWSTACK = 0x6ffffffb
+class PtAARCH64(enum.Enum):
+ """Supplemental PT_* constants for EM_AARCH64."""
+ PT_AARCH64_MEMTAG_MTE = 0x70000002
+
class PtARM(enum.Enum):
"""Supplemental PT_* constants for EM_ARM."""
PT_ARM_EXIDX = 0x70000001

View File

@ -0,0 +1,22 @@
Partial backport of the scripts/glibcelf.py part of:
commit 2d83247d90c9f0bfee7f3f2505bc1b13b6f36c04
Author: caiyinyu <caiyinyu@loongson.cn>
Date: Tue Jul 19 09:20:45 2022 +0800
LoongArch: Add relocations and ELF flags to elf.h and scripts/glibcelf.py
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index 47f95d07baefb4ae..de0509130ed9ad47 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -252,7 +252,8 @@ class Machine(_OpenIntEnum):
EM_RISCV = 243
EM_BPF = 247
EM_CSKY = 252
- EM_NUM = 253
+ EM_LOONGARCH = 258
+ EM_NUM = 259
EM_ALPHA = 0x9026
class Et(_OpenIntEnum):

View File

@ -0,0 +1,78 @@
commit bd13cb19f5e15e9e9a92a536e755fd93a97a67f6
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Aug 19 11:16:32 2022 +0200
scripts/glibcelf.py: Add hashing support
ELF and GNU hashes can now be computed using the elf_hash and
gnu_hash functions.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/elf/tst-glibcelf.py b/elf/tst-glibcelf.py
index bf15a3bad4479e08..e5026e2289df206b 100644
--- a/elf/tst-glibcelf.py
+++ b/elf/tst-glibcelf.py
@@ -240,6 +240,24 @@ def check_constant_values(cc):
error('{}: glibcelf has {!r}, <elf.h> has {!r}'.format(
name, glibcelf_value, elf_h_value))
+def check_hashes():
+ for name, expected_elf, expected_gnu in (
+ ('', 0, 0x1505),
+ ('PPPPPPPPPPPP', 0, 0x9f105c45),
+ ('GLIBC_2.0', 0xd696910, 0xf66c3dd5),
+ ('GLIBC_2.34', 0x69691b4, 0xc3f3f90c),
+ ('GLIBC_PRIVATE', 0x963cf85, 0x692a260)):
+ for convert in (lambda x: x, lambda x: x.encode('UTF-8')):
+ name = convert(name)
+ actual_elf = glibcelf.elf_hash(name)
+ if actual_elf != expected_elf:
+ error('elf_hash({!r}): {:x} != 0x{:x}'.format(
+ name, actual_elf, expected_elf))
+ actual_gnu = glibcelf.gnu_hash(name)
+ if actual_gnu != expected_gnu:
+ error('gnu_hash({!r}): {:x} != 0x{:x}'.format(
+ name, actual_gnu, expected_gnu))
+
def main():
"""The main entry point."""
parser = argparse.ArgumentParser(
@@ -251,6 +269,7 @@ def main():
check_duplicates()
check_constant_prefixes()
check_constant_values(cc=args.cc)
+ check_hashes()
if errors_encountered > 0:
print("note: errors encountered:", errors_encountered)
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index de0509130ed9ad47..5c8f46f590722384 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -1158,5 +1158,24 @@ class Image:
self._stringtab[sh_link] = strtab
return strtab
+def elf_hash(s):
+ """Computes the ELF hash of the string."""
+ acc = 0
+ for ch in s:
+ if type(ch) is not int:
+ ch = ord(ch)
+ acc = ((acc << 4) + ch) & 0xffffffff
+ top = acc & 0xf0000000
+ acc = (acc ^ (top >> 24)) & ~top
+ return acc
+
+def gnu_hash(s):
+ """Computes the GNU hash of the string."""
+ h = 5381
+ for ch in s:
+ if type(ch) is not int:
+ ch = ord(ch)
+ h = (h * 33 + ch) & 0xffffffff
+ return h
__all__ = [name for name in dir() if name[0].isupper()]

View File

@ -0,0 +1,439 @@
commit f40c7887d3cc9bb0b56576ed9edbe505ff8058c0
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Sep 22 12:10:41 2022 +0200
scripts: Extract glibcpp.py from check-obsolete-constructs.py
The C tokenizer is useful separately.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
diff --git a/scripts/check-obsolete-constructs.py b/scripts/check-obsolete-constructs.py
index 89d21dea6e788783..7c7a092e440a3258 100755
--- a/scripts/check-obsolete-constructs.py
+++ b/scripts/check-obsolete-constructs.py
@@ -24,193 +24,14 @@
"""
import argparse
-import collections
+import os
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).
- """
+# Make available glibc Python modules.
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
- 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()
+import glibcpp
#
# Base and generic classes for individual checks.
@@ -446,7 +267,7 @@ class HeaderChecker:
typedef_checker = ObsoleteTypedefChecker(self, self.fname)
- for tok in tokenize_c(contents, self):
+ for tok in glibcpp.tokenize_c(contents, self):
typedef_checker.examine(tok)
def main():
diff --git a/scripts/glibcpp.py b/scripts/glibcpp.py
new file mode 100644
index 0000000000000000..b44c6a4392dde8ce
--- /dev/null
+++ b/scripts/glibcpp.py
@@ -0,0 +1,212 @@
+#! /usr/bin/python3
+# Approximation to C preprocessing.
+# Copyright (C) 2019-2022 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+"""
+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.
+"""
+
+import collections
+import re
+
+# 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()

View File

@ -0,0 +1,598 @@
commit e6e6184bed490403811771fa527eb95b4ae53c7c
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Sep 22 12:10:41 2022 +0200
scripts: Enhance glibcpp to do basic macro processing
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Conflicts:
support/Makefile
(spurious tests sorting change upstream)
diff --git a/scripts/glibcpp.py b/scripts/glibcpp.py
index b44c6a4392dde8ce..455459a609eab120 100644
--- a/scripts/glibcpp.py
+++ b/scripts/glibcpp.py
@@ -33,7 +33,9 @@ Accepts non-ASCII characters only within comments and strings.
"""
import collections
+import operator
import re
+import sys
# Caution: The order of the outermost alternation matters.
# STRING must be before BAD_STRING, CHARCONST before BAD_CHARCONST,
@@ -210,3 +212,318 @@ def tokenize_c(file_contents, reporter):
yield tok
pos = mo.end()
+
+class MacroDefinition(collections.namedtuple('MacroDefinition',
+ 'name_token args body error')):
+ """A preprocessor macro definition.
+
+ name_token is the Token_ for the name.
+
+ args is None for a macro that is not function-like. Otherwise, it
+ is a tuple that contains the macro argument name tokens.
+
+ body is a tuple that contains the tokens that constitue the body
+ of the macro definition (excluding whitespace).
+
+ error is None if no error was detected, or otherwise a problem
+ description associated with this macro definition.
+
+ """
+
+ @property
+ def function(self):
+ """Return true if the macro is function-like."""
+ return self.args is not None
+
+ @property
+ def name(self):
+ """Return the name of the macro being defined."""
+ return self.name_token.text
+
+ @property
+ def line(self):
+ """Return the line number of the macro defintion."""
+ return self.name_token.line
+
+ @property
+ def args_lowered(self):
+ """Return the macro argument list as a list of strings"""
+ if self.function:
+ return [token.text for token in self.args]
+ else:
+ return None
+
+ @property
+ def body_lowered(self):
+ """Return the macro body as a list of strings."""
+ return [token.text for token in self.body]
+
+def macro_definitions(tokens):
+ """A generator for C macro definitions among tokens.
+
+ The generator yields MacroDefinition objects.
+
+ tokens must be iterable, yielding Token_ objects.
+
+ """
+
+ macro_name = None
+ macro_start = False # Set to false after macro name and one otken.
+ macro_args = None # Set to a list during the macro argument sequence.
+ in_macro_args = False # True while processing macro identifier-list.
+ error = None
+ body = []
+
+ for token in tokens:
+ if token.context == 'define' and macro_name is None \
+ and token.kind == 'IDENT':
+ # Starting up macro processing.
+ if macro_start:
+ # First identifier is the macro name.
+ macro_name = token
+ else:
+ # Next token is the name.
+ macro_start = True
+ continue
+
+ if macro_name is None:
+ # Drop tokens not in macro definitions.
+ continue
+
+ if token.context != 'define':
+ # End of the macro definition.
+ if in_macro_args and error is None:
+ error = 'macro definition ends in macro argument list'
+ yield MacroDefinition(macro_name, macro_args, tuple(body), error)
+ # No longer in a macro definition.
+ macro_name = None
+ macro_start = False
+ macro_args = None
+ in_macro_args = False
+ error = None
+ body.clear()
+ continue
+
+ if macro_start:
+ # First token after the macro name.
+ macro_start = False
+ if token.kind == 'PUNCTUATOR' and token.text == '(':
+ macro_args = []
+ in_macro_args = True
+ continue
+
+ if in_macro_args:
+ if token.kind == 'IDENT' \
+ or (token.kind == 'PUNCTUATOR' and token.text == '...'):
+ # Macro argument or ... placeholder.
+ macro_args.append(token)
+ if token.kind == 'PUNCTUATOR':
+ if token.text == ')':
+ macro_args = tuple(macro_args)
+ in_macro_args = False
+ elif token.text == ',':
+ pass # Skip. Not a full syntax check.
+ elif error is None:
+ error = 'invalid punctuator in macro argument list: ' \
+ + repr(token.text)
+ elif error is None:
+ error = 'invalid {} token in macro argument list'.format(
+ token.kind)
+ continue
+
+ if token.kind not in ('WHITESPACE', 'BLOCK_COMMENT'):
+ body.append(token)
+
+ # Emit the macro in case the last line does not end with a newline.
+ if macro_name is not None:
+ if in_macro_args and error is None:
+ error = 'macro definition ends in macro argument list'
+ yield MacroDefinition(macro_name, macro_args, tuple(body), error)
+
+# Used to split UL etc. suffixes from numbers such as 123UL.
+RE_SPLIT_INTEGER_SUFFIX = re.compile(r'([^ullULL]+)([ullULL]*)')
+
+BINARY_OPERATORS = {
+ '+': operator.add,
+ '<<': operator.lshift,
+}
+
+# Use the general-purpose dict type if it is order-preserving.
+if (sys.version_info[0], sys.version_info[1]) <= (3, 6):
+ OrderedDict = collections.OrderedDict
+else:
+ OrderedDict = dict
+
+def macro_eval(macro_defs, reporter):
+ """Compute macro values
+
+ macro_defs is the output from macro_definitions. reporter is an
+ object that accepts reporter.error(line_number, message) and
+ reporter.note(line_number, message) calls to report errors
+ and error context invocations.
+
+ The returned dict contains the values of macros which are not
+ function-like, pairing their names with their computed values.
+
+ The current implementation is incomplete. It is deliberately not
+ entirely faithful to C, even in the implemented parts. It checks
+ that macro replacements follow certain syntactic rules even if
+ they are never evaluated.
+
+ """
+
+ # Unevaluated macro definitions by name.
+ definitions = OrderedDict()
+ for md in macro_defs:
+ if md.name in definitions:
+ reporter.error(md.line, 'macro {} redefined'.format(md.name))
+ reporter.note(definitions[md.name].line,
+ 'location of previous definition')
+ else:
+ definitions[md.name] = md
+
+ # String to value mappings for fully evaluated macros.
+ evaluated = OrderedDict()
+
+ # String to macro definitions during evaluation. Nice error
+ # reporting relies on determinstic iteration order.
+ stack = OrderedDict()
+
+ def eval_token(current, token):
+ """Evaluate one macro token.
+
+ Integers and strings are returned as such (the latter still
+ quoted). Identifiers are expanded.
+
+ None indicates an empty expansion or an error.
+
+ """
+
+ if token.kind == 'PP_NUMBER':
+ value = None
+ m = RE_SPLIT_INTEGER_SUFFIX.match(token.text)
+ if m:
+ try:
+ value = int(m.group(1), 0)
+ except ValueError:
+ pass
+ if value is None:
+ reporter.error(token.line,
+ 'invalid number {!r} in definition of {}'.format(
+ token.text, current.name))
+ return value
+
+ if token.kind == 'STRING':
+ return token.text
+
+ if token.kind == 'CHARCONST' and len(token.text) == 3:
+ return ord(token.text[1])
+
+ if token.kind == 'IDENT':
+ name = token.text
+ result = eval1(current, name)
+ if name not in evaluated:
+ evaluated[name] = result
+ return result
+
+ reporter.error(token.line,
+ 'unrecognized {!r} in definition of {}'.format(
+ token.text, current.name))
+ return None
+
+
+ def eval1(current, name):
+ """Evaluate one name.
+
+ The name is looked up and the macro definition evaluated
+ recursively if necessary. The current argument is the macro
+ definition being evaluated.
+
+ None as a return value indicates an error.
+
+ """
+
+ # Fast path if the value has already been evaluated.
+ if name in evaluated:
+ return evaluated[name]
+
+ try:
+ md = definitions[name]
+ except KeyError:
+ reporter.error(current.line,
+ 'reference to undefined identifier {} in definition of {}'
+ .format(name, current.name))
+ return None
+
+ if md.name in stack:
+ # Recursive macro definition.
+ md = stack[name]
+ reporter.error(md.line,
+ 'macro definition {} refers to itself'.format(md.name))
+ for md1 in reversed(list(stack.values())):
+ if md1 is md:
+ break
+ reporter.note(md1.line,
+ 'evaluated from {}'.format(md1.name))
+ return None
+
+ stack[md.name] = md
+ if md.function:
+ reporter.error(current.line,
+ 'attempt to evaluate function-like macro {}'.format(name))
+ reporter.note(md.line, 'definition of {}'.format(md.name))
+ return None
+
+ try:
+ body = md.body
+ if len(body) == 0:
+ # Empty expansion.
+ return None
+
+ # Remove surrounding ().
+ if body[0].text == '(' and body[-1].text == ')':
+ body = body[1:-1]
+ had_parens = True
+ else:
+ had_parens = False
+
+ if len(body) == 1:
+ return eval_token(md, body[0])
+
+ # Minimal expression evaluator for binary operators.
+ op = body[1].text
+ if len(body) == 3 and op in BINARY_OPERATORS:
+ if not had_parens:
+ reporter.error(body[1].line,
+ 'missing parentheses around {} expression'.format(op))
+ reporter.note(md.line,
+ 'in definition of macro {}'.format(md.name))
+
+ left = eval_token(md, body[0])
+ right = eval_token(md, body[2])
+
+ if type(left) != type(1):
+ reporter.error(left.line,
+ 'left operand of {} is not an integer'.format(op))
+ reporter.note(md.line,
+ 'in definition of macro {}'.format(md.name))
+ if type(right) != type(1):
+ reporter.error(left.line,
+ 'right operand of {} is not an integer'.format(op))
+ reporter.note(md.line,
+ 'in definition of macro {}'.format(md.name))
+ return BINARY_OPERATORS[op](left, right)
+
+ reporter.error(md.line,
+ 'uninterpretable macro token sequence: {}'.format(
+ ' '.join(md.body_lowered)))
+ return None
+ finally:
+ del stack[md.name]
+
+ # Start of main body of macro_eval.
+ for md in definitions.values():
+ name = md.name
+ if name not in evaluated and not md.function:
+ evaluated[name] = eval1(md, name)
+ return evaluated
diff --git a/support/Makefile b/support/Makefile
index 09b41b0d57e9239a..7749ac24f1ac3622 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -223,11 +223,11 @@ $(objpfx)true-container : $(libsupport)
tests = \
README-testing \
tst-support-namespace \
+ tst-support-process_state \
tst-support_blob_repeat \
tst-support_capture_subprocess \
tst-support_descriptors \
tst-support_format_dns_packet \
- tst-support-process_state \
tst-support_quote_blob \
tst-support_quote_string \
tst-support_record_failure \
@@ -248,6 +248,12 @@ $(objpfx)tst-support_record_failure-2.out: tst-support_record_failure-2.sh \
$(evaluate-test)
endif
+tests-special += $(objpfx)tst-glibcpp.out
+
+$(objpfx)tst-glibcpp.out: tst-glibcpp.py $(..)scripts/glibcpp.py
+ PYTHONPATH=$(..)scripts $(PYTHON) tst-glibcpp.py > $@ 2>&1; \
+ $(evaluate-test)
+
$(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so
tst-support_capture_subprocess-ARGS = -- $(host-test-program-cmd)
diff --git a/support/tst-glibcpp.py b/support/tst-glibcpp.py
new file mode 100644
index 0000000000000000..a2db1916ccfce3c3
--- /dev/null
+++ b/support/tst-glibcpp.py
@@ -0,0 +1,217 @@
+#! /usr/bin/python3
+# Tests for scripts/glibcpp.py
+# Copyright (C) 2022 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+import inspect
+import sys
+
+import glibcpp
+
+# Error counter.
+errors = 0
+
+class TokenizerErrors:
+ """Used as the error reporter during tokenization."""
+
+ def __init__(self):
+ self.errors = []
+
+ def error(self, token, message):
+ self.errors.append((token, message))
+
+def check_macro_definitions(source, expected):
+ reporter = TokenizerErrors()
+ tokens = glibcpp.tokenize_c(source, reporter)
+
+ actual = []
+ for md in glibcpp.macro_definitions(tokens):
+ if md.function:
+ md_name = '{}({})'.format(md.name, ','.join(md.args_lowered))
+ else:
+ md_name = md.name
+ actual.append((md_name, md.body_lowered))
+
+ if actual != expected or reporter.errors:
+ global errors
+ errors += 1
+ # Obtain python source line information.
+ frame = inspect.stack(2)[1]
+ print('{}:{}: error: macro definition mismatch, actual definitions:'
+ .format(frame[1], frame[2]))
+ for md in actual:
+ print('note: {} {!r}'.format(md[0], md[1]))
+
+ if reporter.errors:
+ for err in reporter.errors:
+ print('note: tokenizer error: {}: {}'.format(
+ err[0].line, err[1]))
+
+def check_macro_eval(source, expected, expected_errors=''):
+ reporter = TokenizerErrors()
+ tokens = list(glibcpp.tokenize_c(source, reporter))
+
+ if reporter.errors:
+ # Obtain python source line information.
+ frame = inspect.stack(2)[1]
+ for err in reporter.errors:
+ print('{}:{}: tokenizer error: {}: {}'.format(
+ frame[1], frame[2], err[0].line, err[1]))
+ return
+
+ class EvalReporter:
+ """Used as the error reporter during evaluation."""
+
+ def __init__(self):
+ self.lines = []
+
+ def error(self, line, message):
+ self.lines.append('{}: error: {}\n'.format(line, message))
+
+ def note(self, line, message):
+ self.lines.append('{}: note: {}\n'.format(line, message))
+
+ reporter = EvalReporter()
+ actual = glibcpp.macro_eval(glibcpp.macro_definitions(tokens), reporter)
+ actual_errors = ''.join(reporter.lines)
+ if actual != expected or actual_errors != expected_errors:
+ global errors
+ errors += 1
+ # Obtain python source line information.
+ frame = inspect.stack(2)[1]
+ print('{}:{}: error: macro evaluation mismatch, actual results:'
+ .format(frame[1], frame[2]))
+ for k, v in actual.items():
+ print(' {}: {!r}'.format(k, v))
+ for msg in reporter.lines:
+ sys.stdout.write(' | ' + msg)
+
+# Individual test cases follow.
+
+check_macro_definitions('', [])
+check_macro_definitions('int main()\n{\n{\n', [])
+check_macro_definitions("""
+#define A 1
+#define B 2 /* ignored */
+#define C 3 // also ignored
+#define D \
+ 4
+#define STRING "string"
+#define FUNCLIKE(a, b) (a + b)
+#define FUNCLIKE2(a, b) (a + \
+ b)
+""", [('A', ['1']),
+ ('B', ['2']),
+ ('C', ['3']),
+ ('D', ['4']),
+ ('STRING', ['"string"']),
+ ('FUNCLIKE(a,b)', list('(a+b)')),
+ ('FUNCLIKE2(a,b)', list('(a+b)')),
+ ])
+check_macro_definitions('#define MACRO', [('MACRO', [])])
+check_macro_definitions('#define MACRO\n', [('MACRO', [])])
+check_macro_definitions('#define MACRO()', [('MACRO()', [])])
+check_macro_definitions('#define MACRO()\n', [('MACRO()', [])])
+
+check_macro_eval('#define A 1', {'A': 1})
+check_macro_eval('#define A (1)', {'A': 1})
+check_macro_eval('#define A (1 + 1)', {'A': 2})
+check_macro_eval('#define A (1U << 31)', {'A': 1 << 31})
+check_macro_eval('''\
+#define A (B + 1)
+#define B 10
+#define F(x) ignored
+#define C "not ignored"
+''', {
+ 'A': 11,
+ 'B': 10,
+ 'C': '"not ignored"',
+})
+
+# Checking for evaluation errors.
+check_macro_eval('''\
+#define A 1
+#define A 2
+''', {
+ 'A': 1,
+}, '''\
+2: error: macro A redefined
+1: note: location of previous definition
+''')
+
+check_macro_eval('''\
+#define A A
+#define B 1
+''', {
+ 'A': None,
+ 'B': 1,
+}, '''\
+1: error: macro definition A refers to itself
+''')
+
+check_macro_eval('''\
+#define A B
+#define B A
+''', {
+ 'A': None,
+ 'B': None,
+}, '''\
+1: error: macro definition A refers to itself
+2: note: evaluated from B
+''')
+
+check_macro_eval('''\
+#define A B
+#define B C
+#define C A
+''', {
+ 'A': None,
+ 'B': None,
+ 'C': None,
+}, '''\
+1: error: macro definition A refers to itself
+3: note: evaluated from C
+2: note: evaluated from B
+''')
+
+check_macro_eval('''\
+#define A 1 +
+''', {
+ 'A': None,
+}, '''\
+1: error: uninterpretable macro token sequence: 1 +
+''')
+
+check_macro_eval('''\
+#define A 3*5
+''', {
+ 'A': None,
+}, '''\
+1: error: uninterpretable macro token sequence: 3 * 5
+''')
+
+check_macro_eval('''\
+#define A 3 + 5
+''', {
+ 'A': 8,
+}, '''\
+1: error: missing parentheses around + expression
+1: note: in definition of macro A
+''')
+
+if errors:
+ sys.exit(1)

View File

@ -0,0 +1,208 @@
Partial backport of:
commit 7e1d42400c1b8f03316fe14176133c8853cd3bbe
Author: Joseph Myers <joseph@codesourcery.com>
Date: Fri Nov 30 15:20:41 2018 +0000
Replace gen-as-const.awk by gen-as-const.py.
This patch replaces gen-as-const.awk, and some fragments of the
Makefile code that used it, by a Python script. The point is not such
much that awk is problematic for this particular script, as that I'd
like to build up a general Python infrastructure for extracting
information from C headers, for use in writing tests of such headers.
Thus, although this patch does not set up such infrastructure, the
compute_c_consts function in gen-as-const.py might be moved to a
separate Python module in a subsequent patch as a starting point for
such infrastructure.
The general idea of the code is the same as in the awk version, but no
attempt is made to make the output files textually identical. When
generating a header, a dict of constant names and values is generated
internally then defines are printed in sorted order (rather than the
order in the .sym file, which would have been used before). When
generating a test that the values computed match those from a normal
header inclusion, the test code is made into a compilation test using
_Static_assert, where previously the comparisons were done only when
the test was executed. One fragment of test generation (converting
the previously generated header to use asconst_* prefixes on its macro
names) is still in awk code in the makefiles; only the .sym processing
and subsequent execution of the compiler to extract constants have
moved to the Python script.
Tested for x86_64, and with build-many-glibcs.py.
* scripts/gen-as-const.py: New file.
* scripts/gen-as-const.awk: Remove.
* Makerules ($(common-objpfx)%.h $(common-objpfx)%.h.d): Use
gen-as-const.py.
($(objpfx)test-as-const-%.c): Likewise.
In the downstream version, scripts/gen-as-const.awk is not removed and
still used in Makerules.
diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py
new file mode 100644
index 0000000000000000..b7a5744bb192dd67
--- /dev/null
+++ b/scripts/gen-as-const.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python3
+# Produce headers of assembly constants from C expressions.
+# Copyright (C) 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/>.
+
+# The input to this script looks like:
+# #cpp-directive ...
+# NAME1
+# NAME2 expression ...
+# A line giving just a name implies an expression consisting of just that name.
+
+import argparse
+import os.path
+import re
+import subprocess
+import tempfile
+
+
+def compute_c_consts(sym_data, cc):
+ """Compute the values of some C constants.
+
+ The first argument is a list whose elements are either strings
+ (preprocessor directives) or pairs of strings (a name and a C
+ expression for the corresponding value). Preprocessor directives
+ in the middle of the list may be used to select which constants
+ end up being evaluated using which expressions.
+
+ """
+ out_lines = []
+ started = False
+ for arg in sym_data:
+ if isinstance(arg, str):
+ out_lines.append(arg)
+ continue
+ name = arg[0]
+ value = arg[1]
+ if not started:
+ out_lines.append('void\ndummy (void)\n{')
+ started = True
+ out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
+ ': : \"i\" ((long int) (%s)));'
+ % (name, value))
+ if started:
+ out_lines.append('}')
+ out_lines.append('')
+ out_text = '\n'.join(out_lines)
+ with tempfile.TemporaryDirectory() as temp_dir:
+ c_file_name = os.path.join(temp_dir, 'test.c')
+ s_file_name = os.path.join(temp_dir, 'test.s')
+ with open(c_file_name, 'w') as c_file:
+ c_file.write(out_text)
+ # Compilation has to be from stdin to avoid the temporary file
+ # name being written into the generated dependencies.
+ cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
+ subprocess.check_call(cmd, shell=True)
+ consts = {}
+ with open(s_file_name, 'r') as s_file:
+ for line in s_file:
+ match = re.search('@@@name@@@([^@]*)'
+ '@@@value@@@[^0-9Xxa-fA-F-]*'
+ '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
+ if match:
+ if (match.group(1) in consts
+ and match.group(2) != consts[match.group(1)]):
+ raise ValueError('duplicate constant %s'
+ % match.group(1))
+ consts[match.group(1)] = match.group(2)
+ return consts
+
+
+def gen_test(sym_data):
+ """Generate a test for the values of some C constants.
+
+ The first argument is as for compute_c_consts.
+
+ """
+ out_lines = []
+ started = False
+ for arg in sym_data:
+ if isinstance(arg, str):
+ out_lines.append(arg)
+ continue
+ name = arg[0]
+ value = arg[1]
+ if not started:
+ out_lines.append('#include <stdint.h>\n'
+ '#include <stdio.h>\n'
+ '#include <bits/wordsize.h>\n'
+ '#if __WORDSIZE == 64\n'
+ 'typedef uint64_t c_t;\n'
+ '# define U(n) UINT64_C (n)\n'
+ '#else\n'
+ 'typedef uint32_t c_t;\n'
+ '# define U(n) UINT32_C (n)\n'
+ '#endif\n'
+ 'static int\n'
+ 'do_test (void)\n'
+ '{\n'
+ # Compilation test only, using static assertions.
+ ' return 0;\n'
+ '}\n'
+ '#include <support/test-driver.c>')
+ started = True
+ out_lines.append('_Static_assert (U (asconst_%s) == (c_t) (%s), '
+ '"value of %s");'
+ % (name, value, name))
+ return '\n'.join(out_lines)
+
+
+def main():
+ """The main entry point."""
+ parser = argparse.ArgumentParser(
+ description='Produce headers of assembly constants.')
+ parser.add_argument('--cc', metavar='CC',
+ help='C compiler (including options) to use')
+ parser.add_argument('--test', action='store_true',
+ help='Generate test case instead of header')
+ parser.add_argument('sym_file',
+ help='.sym file to process')
+ args = parser.parse_args()
+ sym_data = []
+ with open(args.sym_file, 'r') as sym_file:
+ for line in sym_file:
+ line = line.strip()
+ if line == '':
+ continue
+ # Pass preprocessor directives through.
+ if line.startswith('#'):
+ sym_data.append(line)
+ continue
+ words = line.split(maxsplit=1)
+ # Separator.
+ if words[0] == '--':
+ continue
+ name = words[0]
+ value = words[1] if len(words) > 1 else words[0]
+ sym_data.append((name, value))
+ if args.test:
+ print(gen_test(sym_data))
+ else:
+ consts = compute_c_consts(sym_data, args.cc)
+ print('\n'.join('#define %s %s' % c for c in sorted(consts.items())))
+
+if __name__ == '__main__':
+ main()

View File

@ -0,0 +1,36 @@
commit 29eb7961197bee68470730aecfdda4d0e206812e
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Sep 5 12:11:19 2022 +0200
elf.h: Remove duplicate definition of VER_FLG_WEAK
This did not cause a warning before because the token sequence for
the two definitions was identical.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/elf/elf.h b/elf/elf.h
index d6506ea1c7160dea..ec09040be639a52a 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1027,7 +1027,8 @@ typedef struct
/* Legal values for vd_flags (version information flags). */
#define VER_FLG_BASE 0x1 /* Version definition of file itself */
-#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier. Also
+ used by vna_flags below. */
/* Versym symbol index values. */
#define VER_NDX_LOCAL 0 /* Symbol is local. */
@@ -1105,10 +1106,6 @@ typedef struct
} Elf64_Vernaux;
-/* Legal values for vna_flags. */
-#define VER_FLG_WEAK 0x2 /* Weak version identifier */
-
-
/* Auxiliary vector. */
/* This vector is normally only used by the program interpreter. The

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
commit d33705c0b020632274318323931695a99753b5be
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Nov 3 12:24:17 2022 +0100
scripts/glibcelf.py: Properly report <elf.h> parsing failures
Without this change, parse failures result in an exception:
Traceback (most recent call last):
File "tst-glibcelf.py", line 23, in <module>
import glibcelf
File "/path/to/git/scripts/glibcelf.py", line 226, in <module>
_elf_h = _parse_elf_h()
File "/path/to/git/scripts/glibcelf.py", line 221, in _parse_elf_h
result = glibcpp.macro_eval(glibcpp.macro_definitions(tokens), reporter)
File "/path/to/git/scripts/glibcpp.py", line 379, in macro_eval
reporter.error(md.line, 'macro {} redefined'.format(md.name))
File "/path/to/git/scripts/glibcelf.py", line 214, in error
errors += 1
UnboundLocalError: local variable 'errors' referenced before assignment
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index 420cb21943b28bba..59aab56ecf9deb3e 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -211,7 +211,7 @@ def _parse_elf_h():
self.errors = 0
def error(self, line, message):
- errors += 1
+ self.errors += 1
print('{}:{}: error: {}'.format(path, line, message))
def note(self, line, message):

View File

@ -0,0 +1,108 @@
Downstream-only adjustments to scripts/glibcelf.py. We do not have
CSKY nor RISC-V constants in <elf.h>, so glibcelf cannot extract
those. PT_AARCH64_* constants are missing as well.
Adjust elf/tst-glibcelf.py to use PT_MIPS_OPTIONS instead of
PT_AARCH64_MEMTAG_MTE for testing. It has the same numeric value
(0x70000002).
diff --git a/elf/tst-glibcelf.py b/elf/tst-glibcelf.py
index a5bff45eae55edea..9cb0861589d6ae2e 100644
--- a/elf/tst-glibcelf.py
+++ b/elf/tst-glibcelf.py
@@ -75,15 +75,17 @@ def check_basic():
if repr(glibcelf.Pt(17609)) != 'Pt(17609)':
error('repr(Pt(17609))')
- if glibcelf.Pt('PT_AARCH64_MEMTAG_MTE') \
- is not glibcelf.Pt.PT_AARCH64_MEMTAG_MTE:
- error('PT_AARCH64_MEMTAG_MTE identity')
- if glibcelf.Pt(0x70000002) is glibcelf.Pt.PT_AARCH64_MEMTAG_MTE:
+ # Note: Upstream uses PT_AARCH64_MEMTAG_MTE instead of PT_MIPS_OPTIONS.
+ # PT_AARCH64_MEMTAG_MTE is not yet available downstream.
+ if glibcelf.Pt('PT_MIPS_OPTIONS') \
+ is not glibcelf.Pt.PT_MIPS_OPTIONS:
+ error('PT_MIPS_OPTIONS identity')
+ if glibcelf.Pt(0x70000002) is glibcelf.Pt.PT_MIPS_OPTIONS:
error('Pt(0x70000002) identity')
- if glibcelf.PtAARCH64(0x70000002) is not glibcelf.Pt.PT_AARCH64_MEMTAG_MTE:
- error('PtAARCH64(0x70000002) identity')
- if glibcelf.Pt.PT_AARCH64_MEMTAG_MTE.short_name != 'AARCH64_MEMTAG_MTE':
- error('PT_AARCH64_MEMTAG_MTE short name')
+ if glibcelf.PtMIPS(0x70000002) is not glibcelf.Pt.PT_MIPS_OPTIONS:
+ error('PtMIPS(0x70000002) identity')
+ if glibcelf.Pt.PT_MIPS_OPTIONS.short_name != 'MIPS_OPTIONS':
+ error('PT_MIPS_OPTIONS short name')
# Special cases for int-like Shn.
if glibcelf.Shn(32) == glibcelf.Shn.SHN_XINDEX:
diff --git a/scripts/glibcelf.py b/scripts/glibcelf.py
index 59aab56ecf9deb3e..5980d7cc906005e2 100644
--- a/scripts/glibcelf.py
+++ b/scripts/glibcelf.py
@@ -306,23 +306,17 @@ class ShtALPHA(Sht):
"""Supplemental SHT_* constants for EM_ALPHA."""
class ShtARM(Sht):
"""Supplemental SHT_* constants for EM_ARM."""
-class ShtCSKY(Sht):
- """Supplemental SHT_* constants for EM_CSKY."""
class ShtIA_64(Sht):
"""Supplemental SHT_* constants for EM_IA_64."""
class ShtMIPS(Sht):
"""Supplemental SHT_* constants for EM_MIPS."""
class ShtPARISC(Sht):
"""Supplemental SHT_* constants for EM_PARISC."""
-class ShtRISCV(Sht):
- """Supplemental SHT_* constants for EM_RISCV."""
_register_elf_h(ShtALPHA, prefix='SHT_ALPHA_', parent=Sht)
_register_elf_h(ShtARM, prefix='SHT_ARM_', parent=Sht)
-_register_elf_h(ShtCSKY, prefix='SHT_CSKY_', parent=Sht)
_register_elf_h(ShtIA_64, prefix='SHT_IA_64_', parent=Sht)
_register_elf_h(ShtMIPS, prefix='SHT_MIPS_', parent=Sht)
_register_elf_h(ShtPARISC, prefix='SHT_PARISC_', parent=Sht)
-_register_elf_h(ShtRISCV, prefix='SHT_RISCV_', parent=Sht)
_register_elf_h(Sht, ranges=True,
skip='SHT_LOSUNW SHT_HISUNW SHT_LOUSER SHT_HIUSER'.split())
@@ -392,8 +386,6 @@ _register_elf_h(Stt, ranges=True)
class Pt(_TypedConstant):
"""ELF program header types. Type of Phdr.p_type."""
prefix = 'PT_'
-class PtAARCH64(Pt):
- """Supplemental PT_* constants for EM_AARCH64."""
class PtARM(Pt):
"""Supplemental PT_* constants for EM_ARM."""
class PtHP(Pt):
@@ -404,15 +396,11 @@ class PtMIPS(Pt):
"""Supplemental PT_* constants for EM_MIPS."""
class PtPARISC(Pt):
"""Supplemental PT_* constants for EM_PARISC."""
-class PtRISCV(Pt):
- """Supplemental PT_* constants for EM_RISCV."""
-_register_elf_h(PtAARCH64, prefix='PT_AARCH64_', parent=Pt)
_register_elf_h(PtARM, prefix='PT_ARM_', parent=Pt)
_register_elf_h(PtHP, prefix='PT_HP_', parent=Pt)
_register_elf_h(PtIA_64, prefix='PT_IA_64_', parent=Pt)
_register_elf_h(PtMIPS, prefix='PT_MIPS_', parent=Pt)
_register_elf_h(PtPARISC, prefix='PT_PARISC_', parent=Pt)
-_register_elf_h(PtRISCV, prefix='PT_RISCV_', parent=Pt)
_register_elf_h(Pt, skip='PT_LOSUNW PT_HISUNW'.split(), ranges=True)
class Dt(_TypedConstant):
@@ -432,8 +420,6 @@ class DtPPC(Dt):
"""Supplemental DT_* constants for EM_PPC."""
class DtPPC64(Dt):
"""Supplemental DT_* constants for EM_PPC64."""
-class DtRISCV(Dt):
- """Supplemental DT_* constants for EM_RISCV."""
class DtSPARC(Dt):
"""Supplemental DT_* constants for EM_SPARC."""
_dt_skip = '''
@@ -456,7 +442,6 @@ _register_elf_h(DtIA_64, prefix='DT_IA_64_', skip=_dt_skip, parent=Dt)
_register_elf_h(DtMIPS, prefix='DT_MIPS_', skip=_dt_skip, parent=Dt)
_register_elf_h(DtPPC, prefix='DT_PPC_', skip=_dt_skip, parent=Dt)
_register_elf_h(DtPPC64, prefix='DT_PPC64_', skip=_dt_skip, parent=Dt)
-_register_elf_h(DtRISCV, prefix='DT_RISCV_', skip=_dt_skip, parent=Dt)
_register_elf_h(DtSPARC, prefix='DT_SPARC_', skip=_dt_skip, parent=Dt)
_register_elf_h(Dt, skip=_dt_skip, ranges=True)
del _dt_skip

View File

@ -0,0 +1,32 @@
commit 7b36d26b22d147ffc347f427f9fd584700578a94
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date: Mon Dec 3 14:40:48 2018 +0100
Fix test-as-const-jmp_buf-ssp.c generation on gnu-i386
hurd's jmp_buf-ssp.sym does not define any symbol.
scripts/gen-as-const.py currently was emitting an empty line in that
case, and the gawk invocation was prepending "asconst_" to it, ending up
with:
.../build/glibc/setjmp/test-as-const-jmp_buf-ssp.c:1:2: error: expected « = », « , », « ; », « asm » or
« __attribute__ » at end of input
1 | asconst_
| ^~~~~~~~
* scripts/gen-as-const.py (main): Avoid emitting empty line when
there is no element in `consts'.
diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py
index b7a5744bb192dd67..cabf401ed15e8367 100644
--- a/scripts/gen-as-const.py
+++ b/scripts/gen-as-const.py
@@ -153,7 +153,7 @@ def main():
print(gen_test(sym_data))
else:
consts = compute_c_consts(sym_data, args.cc)
- print('\n'.join('#define %s %s' % c for c in sorted(consts.items())))
+ print(''.join('#define %s %s\n' % c for c in sorted(consts.items())), end='')
if __name__ == '__main__':
main()

View File

@ -0,0 +1,157 @@
commit 477a02f63751c4b759ddd9454d17f2a7ad120ee3
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Dec 3 22:08:50 2018 +0000
Make gen-as-const.py handle '--' consistently with awk script.
It was reported in
<https://sourceware.org/ml/libc-alpha/2018-12/msg00045.html> that
gen-as-const.py fails to generate test code in the case where a .sym
file has no symbols in it, so resulting in a test failing to link for
Hurd.
The relevant difference from the old awk script is that the old script
treated '--' lines as indicating that the text to do at the start of
the test (or file used to compute constants) should be output at that
point if not already output, as well as treating lines with actual
entries for constants like that. This patch changes gen-as-const.py
accordingly, making it the sole responsibility of the code parsing
.sym files to determine when such text should be output and ensuring
it's always output at some point even if there are no symbols and no
'--' lines, since not outputting it means the test fails to link.
Handling '--' like that also avoids any problems that would arise if
the first entry for a symbol were inside #ifdef (since the text in
question must not be output inside #ifdef).
Tested for x86_64, and with build-many-glibcs.py for i686-gnu. Note
that there are still compilation test failures for i686-gnu
(linknamespace tests, possibly arising from recent posix_spawn-related
changes).
* scripts/gen-as-const.py (compute_c_consts): Take an argument
'START' to indicate that start text should be output.
(gen_test): Likewise.
(main): Generate 'START' for first symbol or '--' line, or at end
of input if not previously generated.
diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py
index cabf401ed15e8367..eb85ef1aa0f4934d 100644
--- a/scripts/gen-as-const.py
+++ b/scripts/gen-as-const.py
@@ -34,28 +34,28 @@ def compute_c_consts(sym_data, cc):
"""Compute the values of some C constants.
The first argument is a list whose elements are either strings
- (preprocessor directives) or pairs of strings (a name and a C
+ (preprocessor directives, or the special string 'START' to
+ indicate this function should insert its initial boilerplate text
+ in the output there) or pairs of strings (a name and a C
expression for the corresponding value). Preprocessor directives
in the middle of the list may be used to select which constants
end up being evaluated using which expressions.
"""
out_lines = []
- started = False
for arg in sym_data:
if isinstance(arg, str):
- out_lines.append(arg)
+ if arg == 'START':
+ out_lines.append('void\ndummy (void)\n{')
+ else:
+ out_lines.append(arg)
continue
name = arg[0]
value = arg[1]
- if not started:
- out_lines.append('void\ndummy (void)\n{')
- started = True
out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
': : \"i\" ((long int) (%s)));'
% (name, value))
- if started:
- out_lines.append('}')
+ out_lines.append('}')
out_lines.append('')
out_text = '\n'.join(out_lines)
with tempfile.TemporaryDirectory() as temp_dir:
@@ -89,32 +89,32 @@ def gen_test(sym_data):
"""
out_lines = []
- started = False
for arg in sym_data:
if isinstance(arg, str):
- out_lines.append(arg)
+ if arg == 'START':
+ out_lines.append('#include <stdint.h>\n'
+ '#include <stdio.h>\n'
+ '#include <bits/wordsize.h>\n'
+ '#if __WORDSIZE == 64\n'
+ 'typedef uint64_t c_t;\n'
+ '# define U(n) UINT64_C (n)\n'
+ '#else\n'
+ 'typedef uint32_t c_t;\n'
+ '# define U(n) UINT32_C (n)\n'
+ '#endif\n'
+ 'static int\n'
+ 'do_test (void)\n'
+ '{\n'
+ # Compilation test only, using static
+ # assertions.
+ ' return 0;\n'
+ '}\n'
+ '#include <support/test-driver.c>')
+ else:
+ out_lines.append(arg)
continue
name = arg[0]
value = arg[1]
- if not started:
- out_lines.append('#include <stdint.h>\n'
- '#include <stdio.h>\n'
- '#include <bits/wordsize.h>\n'
- '#if __WORDSIZE == 64\n'
- 'typedef uint64_t c_t;\n'
- '# define U(n) UINT64_C (n)\n'
- '#else\n'
- 'typedef uint32_t c_t;\n'
- '# define U(n) UINT32_C (n)\n'
- '#endif\n'
- 'static int\n'
- 'do_test (void)\n'
- '{\n'
- # Compilation test only, using static assertions.
- ' return 0;\n'
- '}\n'
- '#include <support/test-driver.c>')
- started = True
out_lines.append('_Static_assert (U (asconst_%s) == (c_t) (%s), '
'"value of %s");'
% (name, value, name))
@@ -134,6 +134,7 @@ def main():
args = parser.parse_args()
sym_data = []
with open(args.sym_file, 'r') as sym_file:
+ started = False
for line in sym_file:
line = line.strip()
if line == '':
@@ -143,12 +144,17 @@ def main():
sym_data.append(line)
continue
words = line.split(maxsplit=1)
+ if not started:
+ sym_data.append('START')
+ started = True
# Separator.
if words[0] == '--':
continue
name = words[0]
value = words[1] if len(words) > 1 else words[0]
sym_data.append((name, value))
+ if not started:
+ sym_data.append('START')
if args.test:
print(gen_test(sym_data))
else:

View File

@ -0,0 +1,483 @@
commit a8110b727e508f7ddf34f940af622e6f95435201
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Dec 10 22:27:13 2018 +0000
Move tst-signal-numbers to Python.
This patch converts the tst-signal-numbers test from shell + awk to
Python.
As with gen-as-const, the point is not so much that shell and awk are
problematic for this code, as that it's useful to build up general
infrastructure in Python for use of a range of code involving
extracting values from C headers. This patch moves some code from
gen-as-const.py to a new glibcextract.py, which also gains functions
relating to listing macros, and comparing the values of a set of
macros from compiling two different pieces of code.
It's not just signal numbers that should have such tests; pretty much
any case where glibc copies constants from Linux kernel headers should
have such tests that the values and sets of constants agree except
where differences are known to be OK. Much the same also applies to
structure layouts (although testing those without hardcoding lists of
fields to test will be more complicated).
Given this patch, another test for a set of macros would essentially
be just a call to glibcextract.compare_macro_consts (plus boilerplate
code - and we could move to having separate text files defining such
tests, like the .sym inputs to gen-as-const, so that only a single
Python script is needed for most such tests). Some such tests would
of course need new features, e.g. where the set of macros changes in
new kernel versions (so you need to allow new macro names on the
kernel side if the kernel headers are newer than the version known to
glibc, and extra macros on the glibc side if the kernel headers are
older). tst-syscall-list.sh could become a Python script that uses
common code to generate lists of macros but does other things with its
own custom logic.
There are a few differences from the existing shell + awk test.
Because the new test evaluates constants using the compiler, no
special handling is needed any more for one signal name being defined
to another. Because asm/signal.h now needs to pass through the
compiler, not just the preprocessor, stddef.h is included as well
(given the asm/signal.h issue that it requires an externally provided
definition of size_t). The previous code defined __ASSEMBLER__ with
asm/signal.h; this is removed (__ASSEMBLY__, a different macro,
eliminates the requirement for stddef.h on some but not all
architectures).
Tested for x86_64, and with build-many-glibcs.py.
* scripts/glibcextract.py: New file.
* scripts/gen-as-const.py: Do not import os.path, re, subprocess
or tempfile. Import glibcexctract.
(compute_c_consts): Remove. Moved to glibcextract.py.
(gen_test): Update reference to compute_c_consts.
(main): Likewise.
* sysdeps/unix/sysv/linux/tst-signal-numbers.py: New file.
* sysdeps/unix/sysv/linux/tst-signal-numbers.sh: Remove.
* sysdeps/unix/sysv/linux/Makefile
($(objpfx)tst-signal-numbers.out): Use tst-signal-numbers.py.
Redirect stderr as well as stdout.
diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py
index eb85ef1aa0f4934d..f85e359394acb1a4 100644
--- a/scripts/gen-as-const.py
+++ b/scripts/gen-as-const.py
@@ -24,68 +24,14 @@
# A line giving just a name implies an expression consisting of just that name.
import argparse
-import os.path
-import re
-import subprocess
-import tempfile
-
-def compute_c_consts(sym_data, cc):
- """Compute the values of some C constants.
-
- The first argument is a list whose elements are either strings
- (preprocessor directives, or the special string 'START' to
- indicate this function should insert its initial boilerplate text
- in the output there) or pairs of strings (a name and a C
- expression for the corresponding value). Preprocessor directives
- in the middle of the list may be used to select which constants
- end up being evaluated using which expressions.
-
- """
- out_lines = []
- for arg in sym_data:
- if isinstance(arg, str):
- if arg == 'START':
- out_lines.append('void\ndummy (void)\n{')
- else:
- out_lines.append(arg)
- continue
- name = arg[0]
- value = arg[1]
- out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
- ': : \"i\" ((long int) (%s)));'
- % (name, value))
- out_lines.append('}')
- out_lines.append('')
- out_text = '\n'.join(out_lines)
- with tempfile.TemporaryDirectory() as temp_dir:
- c_file_name = os.path.join(temp_dir, 'test.c')
- s_file_name = os.path.join(temp_dir, 'test.s')
- with open(c_file_name, 'w') as c_file:
- c_file.write(out_text)
- # Compilation has to be from stdin to avoid the temporary file
- # name being written into the generated dependencies.
- cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
- subprocess.check_call(cmd, shell=True)
- consts = {}
- with open(s_file_name, 'r') as s_file:
- for line in s_file:
- match = re.search('@@@name@@@([^@]*)'
- '@@@value@@@[^0-9Xxa-fA-F-]*'
- '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
- if match:
- if (match.group(1) in consts
- and match.group(2) != consts[match.group(1)]):
- raise ValueError('duplicate constant %s'
- % match.group(1))
- consts[match.group(1)] = match.group(2)
- return consts
+import glibcextract
def gen_test(sym_data):
"""Generate a test for the values of some C constants.
- The first argument is as for compute_c_consts.
+ The first argument is as for glibcextract.compute_c_consts.
"""
out_lines = []
@@ -158,7 +104,7 @@ def main():
if args.test:
print(gen_test(sym_data))
else:
- consts = compute_c_consts(sym_data, args.cc)
+ consts = glibcextract.compute_c_consts(sym_data, args.cc)
print(''.join('#define %s %s\n' % c for c in sorted(consts.items())), end='')
if __name__ == '__main__':
diff --git a/scripts/glibcextract.py b/scripts/glibcextract.py
new file mode 100644
index 0000000000000000..ecc4d5b6cc387c7d
--- /dev/null
+++ b/scripts/glibcextract.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python3
+# Extract information from C headers.
+# Copyright (C) 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/>.
+
+import os.path
+import re
+import subprocess
+import tempfile
+
+
+def compute_c_consts(sym_data, cc):
+ """Compute the values of some C constants.
+
+ The first argument is a list whose elements are either strings
+ (preprocessor directives, or the special string 'START' to
+ indicate this function should insert its initial boilerplate text
+ in the output there) or pairs of strings (a name and a C
+ expression for the corresponding value). Preprocessor directives
+ in the middle of the list may be used to select which constants
+ end up being evaluated using which expressions.
+
+ """
+ out_lines = []
+ for arg in sym_data:
+ if isinstance(arg, str):
+ if arg == 'START':
+ out_lines.append('void\ndummy (void)\n{')
+ else:
+ out_lines.append(arg)
+ continue
+ name = arg[0]
+ value = arg[1]
+ out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
+ ': : \"i\" ((long int) (%s)));'
+ % (name, value))
+ out_lines.append('}')
+ out_lines.append('')
+ out_text = '\n'.join(out_lines)
+ with tempfile.TemporaryDirectory() as temp_dir:
+ c_file_name = os.path.join(temp_dir, 'test.c')
+ s_file_name = os.path.join(temp_dir, 'test.s')
+ with open(c_file_name, 'w') as c_file:
+ c_file.write(out_text)
+ # Compilation has to be from stdin to avoid the temporary file
+ # name being written into the generated dependencies.
+ cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
+ subprocess.check_call(cmd, shell=True)
+ consts = {}
+ with open(s_file_name, 'r') as s_file:
+ for line in s_file:
+ match = re.search('@@@name@@@([^@]*)'
+ '@@@value@@@[^0-9Xxa-fA-F-]*'
+ '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
+ if match:
+ if (match.group(1) in consts
+ and match.group(2) != consts[match.group(1)]):
+ raise ValueError('duplicate constant %s'
+ % match.group(1))
+ consts[match.group(1)] = match.group(2)
+ return consts
+
+
+def list_macros(source_text, cc):
+ """List the preprocessor macros defined by the given source code.
+
+ The return value is a pair of dicts, the first one mapping macro
+ names to their expansions and the second one mapping macro names
+ to lists of their arguments, or to None for object-like macros.
+
+ """
+ with tempfile.TemporaryDirectory() as temp_dir:
+ c_file_name = os.path.join(temp_dir, 'test.c')
+ i_file_name = os.path.join(temp_dir, 'test.i')
+ with open(c_file_name, 'w') as c_file:
+ c_file.write(source_text)
+ cmd = ('%s -E -dM -o %s %s' % (cc, i_file_name, c_file_name))
+ subprocess.check_call(cmd, shell=True)
+ macros_exp = {}
+ macros_args = {}
+ with open(i_file_name, 'r') as i_file:
+ for line in i_file:
+ match = re.fullmatch('#define ([0-9A-Za-z_]+)(.*)\n', line)
+ if not match:
+ raise ValueError('bad -dM output line: %s' % line)
+ name = match.group(1)
+ value = match.group(2)
+ if value.startswith(' '):
+ value = value[1:]
+ args = None
+ elif value.startswith('('):
+ match = re.fullmatch(r'\((.*?)\) (.*)', value)
+ if not match:
+ raise ValueError('bad -dM output line: %s' % line)
+ args = match.group(1).split(',')
+ value = match.group(2)
+ else:
+ raise ValueError('bad -dM output line: %s' % line)
+ if name in macros_exp:
+ raise ValueError('duplicate macro: %s' % line)
+ macros_exp[name] = value
+ macros_args[name] = args
+ return macros_exp, macros_args
+
+
+def compute_macro_consts(source_text, cc, macro_re, exclude_re=None):
+ """Compute the integer constant values of macros defined by source_text.
+
+ Macros must match the regular expression macro_re, and if
+ exclude_re is defined they must not match exclude_re. Values are
+ computed with compute_c_consts.
+
+ """
+ macros_exp, macros_args = list_macros(source_text, cc)
+ macros_set = {m for m in macros_exp
+ if (macros_args[m] is None
+ and re.fullmatch(macro_re, m)
+ and (exclude_re is None
+ or not re.fullmatch(exclude_re, m)))}
+ sym_data = [source_text, 'START']
+ sym_data.extend(sorted((m, m) for m in macros_set))
+ return compute_c_consts(sym_data, cc)
+
+
+def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None):
+ """Compare the values of macros defined by two different sources.
+
+ The sources would typically be includes of a glibc header and a
+ kernel header. Return 1 if there were any differences, 0 if the
+ macro values were the same.
+
+ """
+ macros_1 = compute_macro_consts(source_1, cc, macro_re, exclude_re)
+ macros_2 = compute_macro_consts(source_2, cc, macro_re, exclude_re)
+ if macros_1 == macros_2:
+ return 0
+ print('First source:\n%s\n' % source_1)
+ print('Second source:\n%s\n' % source_2)
+ for name, value in sorted(macros_1.items()):
+ if name not in macros_2:
+ print('Only in first source: %s' % name)
+ elif macros_1[name] != macros_2[name]:
+ print('Different values for %s: %s != %s'
+ % (name, macros_1[name], macros_2[name]))
+ for name in sorted(macros_2.keys()):
+ if name not in macros_1:
+ print('Only in second source: %s' % name)
+ return 1
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index bb055f9d6b841ff5..9c10ee53b26e1b1b 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -113,11 +113,14 @@ tests-special += $(objpfx)tst-signal-numbers.out
# in this context, but signal.c includes signal.h and not much else so it'll
# be conservatively correct.
$(objpfx)tst-signal-numbers.out: \
- ../sysdeps/unix/sysv/linux/tst-signal-numbers.sh \
+ ../sysdeps/unix/sysv/linux/tst-signal-numbers.py \
$(objpfx)signal.o*
- AWK=$(AWK) $(SHELL) ../sysdeps/unix/sysv/linux/tst-signal-numbers.sh \
- $(CC) $(patsubst -DMODULE_NAME=%,-DMODULE_NAME=testsuite,$(CPPFLAGS)) \
- < /dev/null > $@; $(evaluate-test)
+ PYTHONPATH=../scripts \
+ $(PYTHON) ../sysdeps/unix/sysv/linux/tst-signal-numbers.py \
+ --cc="$(CC) $(patsubst -DMODULE_NAME=%, \
+ -DMODULE_NAME=testsuite, \
+ $(CPPFLAGS))" \
+ < /dev/null > $@ 2>&1; $(evaluate-test)
endif
ifeq ($(subdir),socket)
diff --git a/sysdeps/unix/sysv/linux/tst-signal-numbers.py b/sysdeps/unix/sysv/linux/tst-signal-numbers.py
new file mode 100644
index 0000000000000000..48c63d1218e8303d
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-signal-numbers.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python3
+# Test that glibc's signal numbers match the kernel's.
+# Copyright (C) 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/>.
+
+import argparse
+import sys
+
+import glibcextract
+
+
+def main():
+ """The main entry point."""
+ parser = argparse.ArgumentParser(
+ description="Test that glibc's signal numbers match the kernel's.")
+ parser.add_argument('--cc', metavar='CC',
+ help='C compiler (including options) to use')
+ args = parser.parse_args()
+ sys.exit(glibcextract.compare_macro_consts(
+ '#define _GNU_SOURCE 1\n'
+ '#include <signal.h>\n',
+ '#define _GNU_SOURCE 1\n'
+ '#include <stddef.h>\n'
+ '#include <asm/signal.h>\n',
+ args.cc,
+ # Filter out constants that aren't signal numbers.
+ 'SIG[A-Z]+',
+ # Discard obsolete signal numbers and unrelated constants:
+ # SIGCLD, SIGIOT, SIGSWI, SIGUNUSED.
+ # SIGSTKSZ, SIGRTMIN, SIGRTMAX.
+ 'SIG(CLD|IOT|RT(MIN|MAX)|STKSZ|SWI|UNUSED)'))
+
+if __name__ == '__main__':
+ main()
diff --git a/sysdeps/unix/sysv/linux/tst-signal-numbers.sh b/sysdeps/unix/sysv/linux/tst-signal-numbers.sh
deleted file mode 100644
index e1f7be0337c720a6..0000000000000000
--- a/sysdeps/unix/sysv/linux/tst-signal-numbers.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#! /bin/sh
-# Test that glibc's signal numbers match the kernel's.
-# Copyright (C) 2017-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/>.
-
-set -e
-if [ -n "$BASH_VERSION" ]; then set -o pipefail; fi
-LC_ALL=C; export LC_ALL
-
-# We cannot use Linux's asm/signal.h to define signal numbers, because
-# it isn't sufficiently namespace-clean. Instead, this test checks
-# that our signal numbers match the kernel's. This script expects
-# "$@" to be $(CC) $(CPPFLAGS) as set by glibc's Makefiles, and $AWK
-# to be set in the environment.
-
-# Before doing anything else, fail if the compiler doesn't work.
-"$@" -E -xc -dM - < /dev/null > /dev/null
-
-tmpG=`mktemp -t signums_glibc.XXXXXXXXX`
-tmpK=`mktemp -t signums_kernel.XXXXXXXXX`
-trap "rm -f '$tmpG' '$tmpK'" 0
-
-# Filter out constants that aren't signal numbers.
-# If SIGPOLL is defined as SIGIO, swap it around so SIGIO is defined as
-# SIGPOLL. Similarly for SIGABRT and SIGIOT.
-# Discard obsolete signal numbers and unrelated constants:
-# SIGCLD, SIGIOT, SIGSWI, SIGUNUSED.
-# SIGSTKSZ, SIGRTMIN, SIGRTMAX.
-# Then sort the list.
-filter_defines ()
-{
- $AWK '
-/^#define SIG[A-Z]+ ([0-9]+|SIG[A-Z0-9]+)$/ { signals[$2] = $3 }
-END {
- if ("SIGPOLL" in signals && "SIGIO" in signals &&
- signals["SIGPOLL"] == "SIGIO") {
- signals["SIGPOLL"] = signals["SIGIO"]
- signals["SIGIO"] = "SIGPOLL"
- }
- if ("SIGABRT" in signals && "SIGIOT" in signals &&
- signals["SIGABRT"] == "SIGIOT") {
- signals["SIGABRT"] = signals["SIGIOT"]
- signals["SIGIOT"] = "SIGABRT"
- }
- for (sig in signals) {
- if (sig !~ /^SIG(CLD|IOT|RT(MIN|MAX)|STKSZ|SWI|UNUSED)$/) {
- printf("#define %s %s\n", sig, signals[sig])
- }
- }
-}' | sort
-}
-
-# $CC may contain command-line switches, so it should be word-split.
-printf '%s' '#define _GNU_SOURCE 1
-#include <signal.h>
-' |
- "$@" -E -xc -dM - |
- filter_defines > "$tmpG"
-
-printf '%s' '#define _GNU_SOURCE 1
-#define __ASSEMBLER__ 1
-#include <asm/signal.h>
-' |
- "$@" -E -xc -dM - |
- filter_defines > "$tmpK"
-
-if cmp -s "$tmpG" "$tmpK"; then
- exit 0
-else
- diff -u "$tmpG" "$tmpK"
- exit 1
-fi

View File

@ -0,0 +1,98 @@
Partial backport of:
commit cb7be1590e9b18e272e72eb4e910a7ad06a53bd0
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Dec 10 22:56:59 2018 +0000
Use gen-as-const.py to process .pysym files.
This patch eliminates the gen-py-const.awk variant of gen-as-const,
switching to use of gnu-as-const.py (with a new --python option) to
process .pysym files (i.e., to generate nptl_lock_constants.py), as
the syntax of those files is identical to that of .sym files.
Note that the generated nptl_lock_constants.py is *not* identical to
the version generated by the awk script. Apart from the trivial
changes (comment referencing the new script, and output being sorted),
the constant FUTEX_WAITERS, PTHREAD_MUTEXATTR_FLAG_BITS,
PTHREAD_MUTEXATTR_FLAG_PSHARED and PTHREAD_MUTEX_PRIO_CEILING_MASK are
now output as positive rather than negative constants (on x86_64
anyway; maybe not necessarily on 32-bit systems):
< FUTEX_WAITERS = -2147483648
---
> FUTEX_WAITERS = 2147483648
< PTHREAD_MUTEXATTR_FLAG_BITS = -251662336
< PTHREAD_MUTEXATTR_FLAG_PSHARED = -2147483648
---
> PTHREAD_MUTEXATTR_FLAG_BITS = 4043304960
> PTHREAD_MUTEXATTR_FLAG_PSHARED = 2147483648
< PTHREAD_MUTEX_PRIO_CEILING_MASK = -524288
---
> PTHREAD_MUTEX_PRIO_CEILING_MASK = 4294443008
This is because gen-as-const has a cast of the constant value to long
int, which gen-py-const lacks.
I think the positive values are more logically correct, since the
constants in question are in fact unsigned in C. But to reliably
produce gen-as-const.py output for constants that always (in C and
Python) reflects the signedness of values with the high bit of "long
int" set would mean more complicated logic needs to be used in
computing values.
The more correct positive values by themselves produce a failure of
nptl/test-mutexattr-printers, because masking with
~PTHREAD_MUTEXATTR_FLAG_BITS & ~PTHREAD_MUTEX_NO_ELISION_NP now leaves
a bit -1 << 32 in the Python value, resulting in a KeyError exception.
To avoid that, places masking with ~ of one of the constants in
question are changed to mask with 0xffffffff as well (this reflects
how ~ in Python applies to an infinite-precision integer whereas ~ in
C does not do any promotions beyond the width of int).
Tested for x86_64.
* scripts/gen-as-const.py (main): Handle --python option.
* scripts/gen-py-const.awk: Remove.
* Makerules (py-const-script): Use gen-as-const.py.
($(py-const)): Likewise.
* nptl/nptl-printers.py (MutexPrinter.read_status_no_robust): Mask
with 0xffffffff together with ~(PTHREAD_MUTEX_PRIO_CEILING_MASK).
(MutexAttributesPrinter.read_values): Mask with 0xffffffff
together with ~PTHREAD_MUTEXATTR_FLAG_BITS and
~PTHREAD_MUTEX_NO_ELISION_NP.
* manual/README.pretty-printers: Update reference to
gen-py-const.awk.
Only the gen-as-const.py changes are included downstream. We keep using
gen-py-const.awk for the build.
diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py
index f85e359394acb1a4..2f1dff092b98e044 100644
--- a/scripts/gen-as-const.py
+++ b/scripts/gen-as-const.py
@@ -75,6 +75,8 @@ def main():
help='C compiler (including options) to use')
parser.add_argument('--test', action='store_true',
help='Generate test case instead of header')
+ parser.add_argument('--python', action='store_true',
+ help='Generate Python file instead of header')
parser.add_argument('sym_file',
help='.sym file to process')
args = parser.parse_args()
@@ -103,6 +105,13 @@ def main():
sym_data.append('START')
if args.test:
print(gen_test(sym_data))
+ elif args.python:
+ consts = glibcextract.compute_c_consts(sym_data, args.cc)
+ print('# GENERATED FILE\n'
+ '\n'
+ '# Constant definitions.\n'
+ '# See gen-as-const.py for details.\n')
+ print(''.join('%s = %s\n' % c for c in sorted(consts.items())), end='')
else:
consts = glibcextract.compute_c_consts(sym_data, args.cc)
print(''.join('#define %s %s\n' % c for c in sorted(consts.items())), end='')

View File

@ -0,0 +1,178 @@
commit df648905e7d8340bb3e78813fd25e2077b9685d9
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Dec 17 18:29:36 2018 +0000
Add test that MAP_* constants agree with kernel.
Continuing the process of building up and using Python infrastructure
for extracting and using values in headers, this patch adds a test
that MAP_* constants from sys/mman.h agree with those in the Linux
kernel headers. (Other sys/mman.h constants could be added to the
test separately.)
This set of constants has grown over time, so the generic code is
enhanced to allow saying extra constants are OK on either side of the
comparison (where the caller sets those parameters based on the Linux
kernel headers version, compared with the version the headers were
last updated from). Although the test is a custom Python file, my
intention is to move in future to a single Python script for such
tests and text files it takes as inputs, once there are enough
examples to provide a guide to the common cases in such tests (I'd
like to end up with most or all such sets of constants copied from
kernel headers having such tests, and likewise for structure layouts
from the kernel).
The Makefile code is essentially the same as for tst-signal-numbers,
but I didn't try to find an object file to depend on to represent the
dependency on the headers used by the test (the conform/ tests don't
try to represent such header dependencies at all, for example).
Tested with build-many-glibcs.py, and also for x86_64 with older
kernel headers.
* scripts/glibcextract.py (compare_macro_consts): Take parameters
to allow extra macros from first or second sources.
* sysdeps/unix/sysv/linux/tst-mman-consts.py: New file.
* sysdeps/unix/sysv/linux/Makefile [$(subdir) = misc]
(tests-special): Add $(objpfx)tst-mman-consts.out.
($(objpfx)tst-mman-consts.out): New makefile target.
diff --git a/scripts/glibcextract.py b/scripts/glibcextract.py
index ecc4d5b6cc387c7d..06f712ad115e0f9e 100644
--- a/scripts/glibcextract.py
+++ b/scripts/glibcextract.py
@@ -136,12 +136,19 @@ def compute_macro_consts(source_text, cc, macro_re, exclude_re=None):
return compute_c_consts(sym_data, cc)
-def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None):
+def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None,
+ allow_extra_1=False, allow_extra_2=False):
"""Compare the values of macros defined by two different sources.
The sources would typically be includes of a glibc header and a
- kernel header. Return 1 if there were any differences, 0 if the
- macro values were the same.
+ kernel header. If allow_extra_1, the first source may define
+ extra macros (typically if the kernel headers are older than the
+ version glibc has taken definitions from); if allow_extra_2, the
+ second source may define extra macros (typically if the kernel
+ headers are newer than the version glibc has taken definitions
+ from). Return 1 if there were any differences other than those
+ allowed, 0 if the macro values were the same apart from any
+ allowed differences.
"""
macros_1 = compute_macro_consts(source_1, cc, macro_re, exclude_re)
@@ -150,13 +157,19 @@ def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None):
return 0
print('First source:\n%s\n' % source_1)
print('Second source:\n%s\n' % source_2)
+ ret = 0
for name, value in sorted(macros_1.items()):
if name not in macros_2:
print('Only in first source: %s' % name)
+ if not allow_extra_1:
+ ret = 1
elif macros_1[name] != macros_2[name]:
print('Different values for %s: %s != %s'
% (name, macros_1[name], macros_2[name]))
+ ret = 1
for name in sorted(macros_2.keys()):
if name not in macros_1:
print('Only in second source: %s' % name)
- return 1
+ if not allow_extra_2:
+ ret = 1
+ return ret
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 9c10ee53b26e1b1b..863ed80c2a2713d3 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -98,6 +98,15 @@ $(objpfx)tst-sysconf-iov_max: $(objpfx)tst-sysconf-iov_max-uapi.o
$(objpfx)tst-pkey: $(shared-thread-library)
+tests-special += $(objpfx)tst-mman-consts.out
+$(objpfx)tst-mman-consts.out: ../sysdeps/unix/sysv/linux/tst-mman-consts.py
+ PYTHONPATH=../scripts \
+ $(PYTHON) ../sysdeps/unix/sysv/linux/tst-mman-consts.py \
+ --cc="$(CC) $(patsubst -DMODULE_NAME=%, \
+ -DMODULE_NAME=testsuite, \
+ $(CPPFLAGS))" \
+ < /dev/null > $@ 2>&1; $(evaluate-test)
+
endif # $(subdir) == misc
ifeq ($(subdir),time)
diff --git a/sysdeps/unix/sysv/linux/tst-mman-consts.py b/sysdeps/unix/sysv/linux/tst-mman-consts.py
new file mode 100644
index 0000000000000000..1a613beec0da16fb
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-mman-consts.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python3
+# Test that glibc's sys/mman.h constants match the kernel's.
+# Copyright (C) 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/>.
+
+import argparse
+import sys
+
+import glibcextract
+
+
+def linux_kernel_version(cc):
+ """Return the (major, minor) version of the Linux kernel headers."""
+ sym_data = ['#include <linux/version.h>', 'START',
+ ('LINUX_VERSION_CODE', 'LINUX_VERSION_CODE')]
+ val = glibcextract.compute_c_consts(sym_data, cc)['LINUX_VERSION_CODE']
+ val = int(val)
+ return ((val & 0xff0000) >> 16, (val & 0xff00) >> 8)
+
+
+def main():
+ """The main entry point."""
+ parser = argparse.ArgumentParser(
+ description="Test that glibc's sys/mman.h constants "
+ "match the kernel's.")
+ parser.add_argument('--cc', metavar='CC',
+ help='C compiler (including options) to use')
+ args = parser.parse_args()
+ linux_version_headers = linux_kernel_version(args.cc)
+ linux_version_glibc = (4, 19)
+ sys.exit(glibcextract.compare_macro_consts(
+ '#define _GNU_SOURCE 1\n'
+ '#include <sys/mman.h>\n',
+ '#define _GNU_SOURCE 1\n'
+ '#include <linux/mman.h>\n',
+ args.cc,
+ 'MAP_.*',
+ # A series of MAP_HUGE_<size> macros are defined by the kernel
+ # but not by glibc. MAP_UNINITIALIZED is kernel-only.
+ # MAP_FAILED is not a MAP_* flag and is glibc-only, as is the
+ # MAP_ANON alias for MAP_ANONYMOUS. MAP_RENAME, MAP_AUTOGROW,
+ # MAP_LOCAL and MAP_AUTORSRV are in the kernel header for
+ # MIPS, marked as "not used by linux"; SPARC has MAP_INHERIT
+ # in the kernel header, but does not use it.
+ 'MAP_HUGE_[0-9].*|MAP_UNINITIALIZED|MAP_FAILED|MAP_ANON'
+ '|MAP_RENAME|MAP_AUTOGROW|MAP_LOCAL|MAP_AUTORSRV|MAP_INHERIT',
+ linux_version_glibc > linux_version_headers,
+ linux_version_headers > linux_version_glibc))
+
+if __name__ == '__main__':
+ main()

View File

@ -0,0 +1,23 @@
commit 46baeb61e16511f26db1b255e19dc9163f590367
Author: Fangrui Song <maskray@google.com>
Date: Tue Oct 19 09:58:16 2021 -0700
glibcextract.py: Place un-assemblable @@@ in a comment
Unlike GCC, Clang parses asm statements and verifies they are valid
instructions/directives. Place the magic @@@ into a comment to avoid
a parse error.
diff --git a/scripts/glibcextract.py b/scripts/glibcextract.py
index 06f712ad115e0f9e..8f2246aae6a9dfb7 100644
--- a/scripts/glibcextract.py
+++ b/scripts/glibcextract.py
@@ -45,7 +45,7 @@ def compute_c_consts(sym_data, cc):
continue
name = arg[0]
value = arg[1]
- out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
+ out_lines.append('asm ("/* @@@name@@@%s@@@value@@@%%0@@@end@@@ */" '
': : \"i\" ((long int) (%s)));'
% (name, value))
out_lines.append('}')

View File

@ -0,0 +1,45 @@
commit 841afa116e32b3c7195475769c26bf46fd870d32
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Wed Aug 10 16:24:06 2022 -0300
glibcextract.py: Add compile_c_snippet
It might be used on tests to check if a snippet build with the provided
compiler and flags.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
diff --git a/scripts/glibcextract.py b/scripts/glibcextract.py
index 8f2246aae6a9dfb7..0fb50dc8f9c4f7f9 100644
--- a/scripts/glibcextract.py
+++ b/scripts/glibcextract.py
@@ -17,6 +17,7 @@
# License along with the GNU C Library; if not, see
# <http://www.gnu.org/licenses/>.
+import collections
import os.path
import re
import subprocess
@@ -173,3 +174,21 @@ def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None,
if not allow_extra_2:
ret = 1
return ret
+
+CompileResult = collections.namedtuple("CompileResult", "returncode output")
+
+def compile_c_snippet(snippet, cc, extra_cc_args=''):
+ """Compile and return whether the SNIPPET can be build with CC along
+ EXTRA_CC_ARGS compiler flags. Return a CompileResult with RETURNCODE
+ being 0 for success, or the failure value and the compiler output.
+ """
+ with tempfile.TemporaryDirectory() as temp_dir:
+ c_file_name = os.path.join(temp_dir, 'test.c')
+ obj_file_name = os.path.join(temp_dir, 'test.o')
+ with open(c_file_name, 'w') as c_file:
+ c_file.write(snippet + '\n')
+ cmd = cc.split() + extra_cc_args.split() + ['-c', '-o', obj_file_name,
+ c_file_name]
+ r = subprocess.run(cmd, check=False, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ return CompileResult(r.returncode, r.stdout)

View File

@ -0,0 +1,449 @@
1. Added "$(objpfx)tst-cmsghdr: $(libdl)" to socket/Makefile since we still
need $(libdl) in RHEL8.
2. Included stddef.h in socket/tst-cmsghdr-skeleton.c because it uses NULL.
commit 9c443ac4559a47ed99859bd80d14dc4b6dd220a1
Author: Arjun Shankar <arjun@redhat.com>
Date: Tue Aug 2 11:10:25 2022 +0200
socket: Check lengths before advancing pointer in CMSG_NXTHDR
The inline and library functions that the CMSG_NXTHDR macro may expand
to increment the pointer to the header before checking the stride of
the increment against available space. Since C only allows incrementing
pointers to one past the end of an array, the increment must be done
after a length check. This commit fixes that and includes a regression
test for CMSG_FIRSTHDR and CMSG_NXTHDR.
The Linux, Hurd, and generic headers are all changed.
Tested on Linux on armv7hl, i686, x86_64, aarch64, ppc64le, and s390x.
[BZ #28846]
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Conflicts:
socket/Makefile
(usual test backport differences)
diff --git a/bits/socket.h b/bits/socket.h
index 725798882e4b803b..0474613a9c003eeb 100644
--- a/bits/socket.h
+++ b/bits/socket.h
@@ -245,6 +245,12 @@ struct cmsghdr
+ CMSG_ALIGN (sizeof (struct cmsghdr)))
#define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
+/* Given a length, return the additional padding necessary such that
+ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */
+#define __CMSG_PADDING(len) ((sizeof (size_t) \
+ - ((len) & (sizeof (size_t) - 1))) \
+ & (sizeof (size_t) - 1))
+
extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
struct cmsghdr *__cmsg) __THROW;
#ifdef __USE_EXTERN_INLINES
@@ -254,18 +260,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
_EXTERN_INLINE struct cmsghdr *
__NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg))
{
+ /* We may safely assume that __cmsg lies between __mhdr->msg_control and
+ __mhdr->msg_controllen because the user is required to obtain the first
+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs
+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet
+ trust the value of __cmsg->cmsg_len and therefore do not use it in any
+ pointer arithmetic until we check its value. */
+
+ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control;
+ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg;
+
+ size_t __size_needed = sizeof (struct cmsghdr)
+ + __CMSG_PADDING (__cmsg->cmsg_len);
+
+ /* The current header is malformed, too small to be a full header. */
if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr))
- /* The kernel header does this so there may be a reason. */
return (struct cmsghdr *) 0;
+ /* There isn't enough space between __cmsg and the end of the buffer to
+ hold the current cmsg *and* the next one. */
+ if (((size_t)
+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr)
+ < __size_needed)
+ || ((size_t)
+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr
+ - __size_needed)
+ < __cmsg->cmsg_len))
+
+ return (struct cmsghdr *) 0;
+
+ /* Now, we trust cmsg_len and can use it to find the next header. */
__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control
- + __mhdr->msg_controllen)
- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
- /* No more entries. */
- return (struct cmsghdr *) 0;
return __cmsg;
}
#endif /* Use `extern inline'. */
diff --git a/socket/Makefile b/socket/Makefile
index 8975a65c2aabbfbc..a445383f8739351e 100644
--- a/socket/Makefile
+++ b/socket/Makefile
@@ -31,7 +31,12 @@ routines := accept bind connect getpeername getsockname getsockopt \
setsockopt shutdown socket socketpair isfdtype opensock \
sockatmark accept4 recvmmsg sendmmsg sockaddr_un_set
-tests := tst-accept4
+tests := \
+ tst-accept4 \
+ tst-cmsghdr \
+ # tests
+
+$(objpfx)tst-cmsghdr: $(libdl)
tests-internal := \
tst-sockaddr_un_set \
diff --git a/socket/tst-cmsghdr-skeleton.c b/socket/tst-cmsghdr-skeleton.c
new file mode 100644
index 0000000000000000..7accfa6e54708e2a
--- /dev/null
+++ b/socket/tst-cmsghdr-skeleton.c
@@ -0,0 +1,93 @@
+/* Test ancillary data header creation.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* We use the preprocessor to generate the function/macro tests instead of
+ using indirection because having all the macro expansions alongside
+ each other lets the compiler warn us about suspicious pointer
+ arithmetic across subsequent CMSG_{FIRST,NXT}HDR expansions. */
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define RUN_TEST_CONCAT(suffix) run_test_##suffix
+#define RUN_TEST_FUNCNAME(suffix) RUN_TEST_CONCAT (suffix)
+
+static void
+RUN_TEST_FUNCNAME (CMSG_NXTHDR_IMPL) (void)
+{
+ struct msghdr m = {0};
+ struct cmsghdr *cmsg;
+ char cmsgbuf[3 * CMSG_SPACE (sizeof (PAYLOAD))] = {0};
+
+ m.msg_control = cmsgbuf;
+ m.msg_controllen = sizeof (cmsgbuf);
+
+ /* First header should point to the start of the buffer. */
+ cmsg = CMSG_FIRSTHDR (&m);
+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf);
+
+ /* If the first header length consumes the entire buffer, there is no
+ space remaining for additional headers. */
+ cmsg->cmsg_len = sizeof (cmsgbuf);
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg == NULL);
+
+ /* The first header length is so big, using it would cause an overflow. */
+ cmsg = CMSG_FIRSTHDR (&m);
+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf);
+ cmsg->cmsg_len = SIZE_MAX;
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg == NULL);
+
+ /* The first header leaves just enough space to hold another header. */
+ cmsg = CMSG_FIRSTHDR (&m);
+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf);
+ cmsg->cmsg_len = sizeof (cmsgbuf) - sizeof (struct cmsghdr);
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg != NULL);
+
+ /* The first header leaves space but not enough for another header. */
+ cmsg = CMSG_FIRSTHDR (&m);
+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf);
+ cmsg->cmsg_len ++;
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg == NULL);
+
+ /* The second header leaves just enough space to hold another header. */
+ cmsg = CMSG_FIRSTHDR (&m);
+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf);
+ cmsg->cmsg_len = CMSG_LEN (sizeof (PAYLOAD));
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg != NULL);
+ cmsg->cmsg_len = sizeof (cmsgbuf)
+ - CMSG_SPACE (sizeof (PAYLOAD)) /* First header. */
+ - sizeof (struct cmsghdr);
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg != NULL);
+
+ /* The second header leaves space but not enough for another header. */
+ cmsg = CMSG_FIRSTHDR (&m);
+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf);
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg != NULL);
+ cmsg->cmsg_len ++;
+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg);
+ TEST_VERIFY_EXIT (cmsg == NULL);
+
+ return;
+}
diff --git a/socket/tst-cmsghdr.c b/socket/tst-cmsghdr.c
new file mode 100644
index 0000000000000000..68c96d3c9dd2bce8
--- /dev/null
+++ b/socket/tst-cmsghdr.c
@@ -0,0 +1,56 @@
+/* Test ancillary data header creation.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <sys/socket.h>
+#include <gnu/lib-names.h>
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+#define PAYLOAD "Hello, World!"
+
+/* CMSG_NXTHDR is a macro that calls an inline function defined in
+ bits/socket.h. In case the function cannot be inlined, libc.so carries
+ a copy. Both versions need to be tested. */
+
+#define CMSG_NXTHDR_IMPL CMSG_NXTHDR
+#include "tst-cmsghdr-skeleton.c"
+#undef CMSG_NXTHDR_IMPL
+
+static struct cmsghdr * (* cmsg_nxthdr) (struct msghdr *, struct cmsghdr *);
+
+#define CMSG_NXTHDR_IMPL cmsg_nxthdr
+#include "tst-cmsghdr-skeleton.c"
+#undef CMSG_NXTHDR_IMPL
+
+static int
+do_test (void)
+{
+ static void *handle;
+
+ run_test_CMSG_NXTHDR ();
+
+ handle = xdlopen (LIBC_SO, RTLD_LAZY);
+ cmsg_nxthdr = (struct cmsghdr * (*) (struct msghdr *, struct cmsghdr *))
+ xdlsym (handle, "__cmsg_nxthdr");
+
+ run_test_cmsg_nxthdr ();
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h
index 18959139dc7d325b..cc66684061e3e179 100644
--- a/sysdeps/mach/hurd/bits/socket.h
+++ b/sysdeps/mach/hurd/bits/socket.h
@@ -249,6 +249,12 @@ struct cmsghdr
+ CMSG_ALIGN (sizeof (struct cmsghdr)))
#define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
+/* Given a length, return the additional padding necessary such that
+ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */
+#define __CMSG_PADDING(len) ((sizeof (size_t) \
+ - ((len) & (sizeof (size_t) - 1))) \
+ & (sizeof (size_t) - 1))
+
extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
struct cmsghdr *__cmsg) __THROW;
#ifdef __USE_EXTERN_INLINES
@@ -258,18 +264,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
_EXTERN_INLINE struct cmsghdr *
__NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg))
{
+ /* We may safely assume that __cmsg lies between __mhdr->msg_control and
+ __mhdr->msg_controllen because the user is required to obtain the first
+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs
+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet
+ trust the value of __cmsg->cmsg_len and therefore do not use it in any
+ pointer arithmetic until we check its value. */
+
+ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control;
+ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg;
+
+ size_t __size_needed = sizeof (struct cmsghdr)
+ + __CMSG_PADDING (__cmsg->cmsg_len);
+
+ /* The current header is malformed, too small to be a full header. */
if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr))
- /* The kernel header does this so there may be a reason. */
return (struct cmsghdr *) 0;
+ /* There isn't enough space between __cmsg and the end of the buffer to
+ hold the current cmsg *and* the next one. */
+ if (((size_t)
+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr)
+ < __size_needed)
+ || ((size_t)
+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr
+ - __size_needed)
+ < __cmsg->cmsg_len))
+
+ return (struct cmsghdr *) 0;
+
+ /* Now, we trust cmsg_len and can use it to find the next header. */
__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control
- + __mhdr->msg_controllen)
- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
- /* No more entries. */
- return (struct cmsghdr *) 0;
return __cmsg;
}
#endif /* Use `extern inline'. */
diff --git a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h
index c3fbb2110296273c..6b895b89831d2cb5 100644
--- a/sysdeps/unix/sysv/linux/bits/socket.h
+++ b/sysdeps/unix/sysv/linux/bits/socket.h
@@ -302,6 +302,12 @@ struct cmsghdr
+ CMSG_ALIGN (sizeof (struct cmsghdr)))
#define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
+/* Given a length, return the additional padding necessary such that
+ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */
+#define __CMSG_PADDING(len) ((sizeof (size_t) \
+ - ((len) & (sizeof (size_t) - 1))) \
+ & (sizeof (size_t) - 1))
+
extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
struct cmsghdr *__cmsg) __THROW;
#ifdef __USE_EXTERN_INLINES
@@ -311,18 +317,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
_EXTERN_INLINE struct cmsghdr *
__NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg))
{
+ /* We may safely assume that __cmsg lies between __mhdr->msg_control and
+ __mhdr->msg_controllen because the user is required to obtain the first
+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs
+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet
+ trust the value of __cmsg->cmsg_len and therefore do not use it in any
+ pointer arithmetic until we check its value. */
+
+ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control;
+ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg;
+
+ size_t __size_needed = sizeof (struct cmsghdr)
+ + __CMSG_PADDING (__cmsg->cmsg_len);
+
+ /* The current header is malformed, too small to be a full header. */
if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr))
- /* The kernel header does this so there may be a reason. */
return (struct cmsghdr *) 0;
+ /* There isn't enough space between __cmsg and the end of the buffer to
+ hold the current cmsg *and* the next one. */
+ if (((size_t)
+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr)
+ < __size_needed)
+ || ((size_t)
+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr
+ - __size_needed)
+ < __cmsg->cmsg_len))
+
+ return (struct cmsghdr *) 0;
+
+ /* Now, we trust cmsg_len and can use it to find the next header. */
__cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+ CMSG_ALIGN (__cmsg->cmsg_len));
- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control
- + __mhdr->msg_controllen)
- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
- /* No more entries. */
- return (struct cmsghdr *) 0;
return __cmsg;
}
#endif /* Use `extern inline'. */
diff --git a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c
index bab0be6884d9da1c..16594622211c1c8b 100644
--- a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c
+++ b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c
@@ -23,18 +23,38 @@
struct cmsghdr *
__cmsg_nxthdr (struct msghdr *mhdr, struct cmsghdr *cmsg)
{
+ /* We may safely assume that cmsg lies between mhdr->msg_control and
+ mhdr->msg_controllen because the user is required to obtain the first
+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs
+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet
+ trust the value of cmsg->cmsg_len and therefore do not use it in any
+ pointer arithmetic until we check its value. */
+
+ unsigned char * msg_control_ptr = (unsigned char *) mhdr->msg_control;
+ unsigned char * cmsg_ptr = (unsigned char *) cmsg;
+
+ size_t size_needed = sizeof (struct cmsghdr)
+ + __CMSG_PADDING (cmsg->cmsg_len);
+
+ /* The current header is malformed, too small to be a full header. */
if ((size_t) cmsg->cmsg_len < sizeof (struct cmsghdr))
- /* The kernel header does this so there may be a reason. */
- return NULL;
+ return (struct cmsghdr *) 0;
+
+ /* There isn't enough space between cmsg and the end of the buffer to
+ hold the current cmsg *and* the next one. */
+ if (((size_t)
+ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr)
+ < size_needed)
+ || ((size_t)
+ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr
+ - size_needed)
+ < cmsg->cmsg_len))
+
+ return (struct cmsghdr *) 0;
+ /* Now, we trust cmsg_len and can use it to find the next header. */
cmsg = (struct cmsghdr *) ((unsigned char *) cmsg
+ CMSG_ALIGN (cmsg->cmsg_len));
- if ((unsigned char *) (cmsg + 1) > ((unsigned char *) mhdr->msg_control
- + mhdr->msg_controllen)
- || ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len)
- > ((unsigned char *) mhdr->msg_control + mhdr->msg_controllen)))
- /* No more entries. */
- return NULL;
return cmsg;
}
libc_hidden_def (__cmsg_nxthdr)

View File

@ -0,0 +1,96 @@
commit dd2315a866a4ac2b838ea1cb10c5ea1c35d51a2f
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 16 08:27:50 2022 +0200
elf: Run tst-audit-tlsdesc, tst-audit-tlsdesc-dlopen everywhere
The test is valid for all TLS models, but we want to make a reasonable
effort to test the GNU2 model specifically. For example, aarch64
defaults to GNU2, but does not have -mtls-dialect=gnu2, and the test
was not run there.
Suggested-by: Martin Coufal <mcoufal@redhat.com>
Conflicts:
elf/Makefile
(missing tst-align3 backport, missing libdl integration)
diff --git a/elf/Makefile b/elf/Makefile
index 9e721d5d4e0a1cd9..1dd36ba0486e56a0 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -331,6 +331,8 @@ tests += \
tst-addr1 \
tst-align \
tst-align2 \
+ tst-audit-tlsdesc \
+ tst-audit-tlsdesc-dlopen \
tst-audit1 \
tst-audit11 \
tst-audit12 \
@@ -607,6 +609,8 @@ modules-names = \
tst-alignmod2 \
tst-array2dep \
tst-array5dep \
+ tst-audit-tlsdesc-mod1 \
+ tst-audit-tlsdesc-mod2 \
tst-audit11mod1 \
tst-audit11mod2 \
tst-audit12mod1 \
@@ -640,6 +644,7 @@ modules-names = \
tst-auditmanymod7 \
tst-auditmanymod8 \
tst-auditmanymod9 \
+ tst-auditmod-tlsdesc \
tst-auditmod1 \
tst-auditmod9a \
tst-auditmod9b \
@@ -809,23 +814,8 @@ modules-names += tst-gnu2-tls1mod
$(objpfx)tst-gnu2-tls1: $(objpfx)tst-gnu2-tls1mod.so
tst-gnu2-tls1mod.so-no-z-defs = yes
CFLAGS-tst-gnu2-tls1mod.c += -mtls-dialect=gnu2
+endif # $(have-mtls-dialect-gnu2)
-tests += tst-audit-tlsdesc tst-audit-tlsdesc-dlopen
-modules-names += tst-audit-tlsdesc-mod1 tst-audit-tlsdesc-mod2 tst-auditmod-tlsdesc
-$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \
- $(objpfx)tst-audit-tlsdesc-mod2.so \
- $(shared-thread-library)
-CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2
-CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2
-$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) $(libdl)
-$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \
- $(objpfx)tst-audit-tlsdesc-mod2.so
-$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so
-$(objpfx)tst-audit-tlsdesc.out: $(objpfx)tst-auditmod-tlsdesc.so
-tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
-$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so
-tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
-endif
ifeq (yes,$(have-protected-data))
modules-names += tst-protected1moda tst-protected1modb
tests += tst-protected1a tst-protected1b
@@ -2559,5 +2549,23 @@ $(objpfx)tst-tls21.out: $(objpfx)tst-tls21mod.so
$(objpfx)tst-tls21mod.so: $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so)
$(objpfx)tst-rtld-run-static.out: $(objpfx)/ldconfig
+
$(objpfx)tst-dlmopen-gethostbyname: $(libdl)
$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
+$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \
+ $(objpfx)tst-audit-tlsdesc-mod2.so \
+ $(shared-thread-library)
+ifeq (yes,$(have-mtls-dialect-gnu2))
+# The test is valid for all TLS types, but we want to exercise GNU2
+# TLS if possible.
+CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2
+CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2
+endif
+$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) $(libdl)
+$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \
+ $(objpfx)tst-audit-tlsdesc-mod2.so
+$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so
+$(objpfx)tst-audit-tlsdesc.out: $(objpfx)tst-auditmod-tlsdesc.so
+tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
+$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so
+tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so

View File

@ -0,0 +1,202 @@
From d0e357ff45a75553dee3b17ed7d303bfa544f6fe Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Fri, 26 Aug 2022 21:15:43 +0200
Subject: elf: Call __libc_early_init for reused namespaces (bug 29528)
libc_map is never reset to NULL, neither during dlclose nor on a
dlopen call which reuses the namespace structure. As a result, if a
namespace is reused, its libc is not initialized properly. The most
visible result is a crash in the <ctype.h> functions.
To prevent similar bugs on namespace reuse from surfacing,
unconditionally initialize the chosen namespace to zero using memset.
[Note from DJ: Regenerated for new line numbers and context, added
link dependency on libdl]]
diff -rupN a/elf/Makefile b/elf/Makefile
--- a/elf/Makefile 2022-10-05 15:04:11.814901849 -0400
+++ b/elf/Makefile 2022-10-05 17:02:19.858635958 -0400
@@ -367,6 +367,7 @@ tests += \
tst-dlmopen3 \
tst-dlmopen-dlerror \
tst-dlmopen-gethostbyname \
+ tst-dlmopen-twice \
tst-dlopenfail \
tst-dlopenfail-2 \
tst-dlopenrpath \
@@ -671,6 +672,8 @@ modules-names = \
tst-dlmopen1mod \
tst-dlmopen-dlerror-mod \
tst-dlmopen-gethostbyname-mod \
+ tst-dlmopen-twice-mod1 \
+ tst-dlmopen-twice-mod2 \
tst-dlopenfaillinkmod \
tst-dlopenfailmod1 \
tst-dlopenfailmod2 \
@@ -2569,3 +2572,9 @@ $(objpfx)tst-audit-tlsdesc.out: $(objpfx
tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so
tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
+
+
+$(objpfx)tst-dlmopen-twice: $(libdl)
+$(objpfx)tst-dlmopen-twice.out: \
+ $(objpfx)tst-dlmopen-twice-mod1.so \
+ $(objpfx)tst-dlmopen-twice-mod2.so
diff -rupN a/elf/dl-open.c b/elf/dl-open.c
--- a/elf/dl-open.c 2022-10-05 15:04:11.635894932 -0400
+++ b/elf/dl-open.c 2022-10-05 15:10:31.667638060 -0400
@@ -836,11 +836,14 @@ _dl_open (const char *file, int mode, co
_dl_signal_error (EINVAL, file, NULL, N_("\
no more namespaces available for dlmopen()"));
}
- else if (nsid == GL(dl_nns))
- {
- __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
- ++GL(dl_nns);
- }
+
+ if (nsid == GL(dl_nns))
+ ++GL(dl_nns);
+
+ /* Initialize the new namespace. Most members are
+ zero-initialized, only the lock needs special treatment. */
+ memset (&GL(dl_ns)[nsid], 0, sizeof (GL(dl_ns)[nsid]));
+ __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
_dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
}
diff -rupN a/elf/tst-dlmopen-twice-mod1.c b/elf/tst-dlmopen-twice-mod1.c
--- a/elf/tst-dlmopen-twice-mod1.c 1969-12-31 19:00:00.000000000 -0500
+++ b/elf/tst-dlmopen-twice-mod1.c 2022-10-05 15:10:31.671638216 -0400
@@ -0,0 +1,37 @@
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 1.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("info: tst-dlmopen-twice-mod1.so loaded");
+ fflush (stdout);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ puts ("info: tst-dlmopen-twice-mod1.so about to be unloaded");
+ fflush (stdout);
+}
+
+/* Large allocation. The second module does not have this, so it
+ should load libc at a different address. */
+char large_allocate[16 * 1024 * 1024];
diff -rupN a/elf/tst-dlmopen-twice-mod2.c b/elf/tst-dlmopen-twice-mod2.c
--- a/elf/tst-dlmopen-twice-mod2.c 1969-12-31 19:00:00.000000000 -0500
+++ b/elf/tst-dlmopen-twice-mod2.c 2022-10-05 15:10:31.676638411 -0400
@@ -0,0 +1,50 @@
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 2.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <ctype.h>
+#include <stdio.h>
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("info: tst-dlmopen-twice-mod2.so loaded");
+ fflush (stdout);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ puts ("info: tst-dlmopen-twice-mod2.so about to be unloaded");
+ fflush (stdout);
+}
+
+int
+run_check (void)
+{
+ puts ("info: about to call isalpha");
+ fflush (stdout);
+
+ volatile char ch = 'a';
+ if (!isalpha (ch))
+ {
+ puts ("error: isalpha ('a') is not true");
+ fflush (stdout);
+ return 1;
+ }
+ return 0;
+}
diff -rupN a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c
--- a/elf/tst-dlmopen-twice.c 1969-12-31 19:00:00.000000000 -0500
+++ b/elf/tst-dlmopen-twice.c 2022-10-05 15:10:31.679638528 -0400
@@ -0,0 +1,34 @@
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Main.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", RTLD_NOW);
+ xdlclose (handle);
+ handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW);
+ int (*run_check) (void) = xdlsym (handle, "run_check");
+ TEST_COMPARE (run_check (), 0);
+ xdlclose (handle);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,98 @@
From 2c42257314536b94cc8d52edede86e94e98c1436 Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Fri, 14 Oct 2022 11:02:25 +0200
Subject: [PATCH] elf: Do not completely clear reused namespace in dlmopen (bug
29600)
Content-type: text/plain; charset=UTF-8
The data in the _ns_debug member must be preserved, otherwise
_dl_debug_initialize enters an infinite loop. To be conservative,
only clear the libc_map member for now, to fix bug 29528.
Fixes commit d0e357ff45a75553dee3b17ed7d303bfa544f6fe
("elf: Call __libc_early_init for reused namespaces (bug 29528)"),
by reverting most of it.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
---
elf/dl-open.c | 14 ++++++--------
elf/tst-dlmopen-twice.c | 28 ++++++++++++++++++++++++----
2 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 46e8066fd8..e7db5e9642 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -836,15 +836,13 @@ _dl_open (const char *file, int mode, co
_dl_signal_error (EINVAL, file, NULL, N_("\
no more namespaces available for dlmopen()"));
}
+ else if (nsid == GL(dl_nns))
+ {
+ __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
+ ++GL(dl_nns);
+ }
- if (nsid == GL(dl_nns))
- ++GL(dl_nns);
-
- /* Initialize the new namespace. Most members are
- zero-initialized, only the lock needs special treatment. */
- memset (&GL(dl_ns)[nsid], 0, sizeof (GL(dl_ns)[nsid]));
- __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
-
+ GL(dl_ns)[nsid].libc_map = NULL;
_dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
}
/* Never allow loading a DSO in a namespace which is empty. Such
diff --git a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c
index 449f3c8fa9..70c71fe19c 100644
--- a/elf/tst-dlmopen-twice.c
+++ b/elf/tst-dlmopen-twice.c
@@ -16,18 +16,38 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <support/xdlfcn.h>
+#include <stdio.h>
#include <support/check.h>
+#include <support/xdlfcn.h>
-static int
-do_test (void)
+/* Run the test multiple times, to check finding a new namespace while
+ another namespace is already in use. This used to trigger bug 29600. */
+static void
+recurse (int depth)
{
- void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", RTLD_NOW);
+ if (depth == 0)
+ return;
+
+ printf ("info: running at depth %d\n", depth);
+ void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so",
+ RTLD_NOW);
xdlclose (handle);
handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW);
int (*run_check) (void) = xdlsym (handle, "run_check");
TEST_COMPARE (run_check (), 0);
+ recurse (depth - 1);
xdlclose (handle);
+}
+
+static int
+do_test (void)
+{
+ /* First run the test without nesting. */
+ recurse (1);
+
+ /* Then with nesting. The constant needs to be less than the
+ internal DL_NNS namespace constant. */
+ recurse (10);
return 0;
}
--
2.31.1

View File

@ -0,0 +1,39 @@
commit 02ca25fef2785974011e9c5beecc99b900b69fd7
Author: Fabian Vogt <fvogt@suse.de>
Date: Wed Jul 27 11:44:07 2022 +0200
nscd: Fix netlink cache invalidation if epoll is used [BZ #29415]
Processes cache network interface information such as whether IPv4 or IPv6
are enabled. This is only checked again if the "netlink timestamp" provided
by nscd changed, which is triggered by netlink socket activity.
However, in the epoll handler for the netlink socket, it was missed to
assign the new timestamp to the nscd database. The handler for plain poll
did that properly, copy that over.
This bug caused that e.g. processes which started before network
configuration got unusuable addresses from getaddrinfo, like IPv6 only even
though only IPv4 is available:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1041
It's a bit hard to reproduce, so I verified this by checking the timestamp
on calls to __check_pf manually. Without this patch it's stuck at 1, now
it's increasing on network changes as expected.
Signed-off-by: Fabian Vogt <fvogt@suse.de>
diff --git a/nscd/connections.c b/nscd/connections.c
index 98182007646a33d5..19039bdbb210466a 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -2286,7 +2286,8 @@ main_loop_epoll (int efd)
sizeof (buf))) != -1)
;
- __bump_nl_timestamp ();
+ dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
+ = __bump_nl_timestamp ();
}
# endif
else

View File

@ -0,0 +1,472 @@
commit c6fad4fa149485a307207f707e5851216f190fc8
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Mar 19 18:32:28 2020 -0300
stdio: Remove memory leak from multibyte convertion [BZ#25691]
This is an updated version of a previous patch [1] with the
following changes:
- Use compiler overflow builtins on done_add_func function.
- Define the scratch +utstring_converted_wide_string using
CHAR_T.
- Added a testcase and mention the bug report.
Both default and wide printf functions might leak memory when
manipulate multibyte characters conversion depending of the size
of the input (whether __libc_use_alloca trigger or not the fallback
heap allocation).
This patch fixes it by removing the extra memory allocation on
string formatting with conversion parts.
The testcase uses input argument size that trigger memory leaks
on unpatched code (using a scratch buffer the threashold to use
heap allocation is lower).
Checked on x86_64-linux-gnu and i686-linux-gnu.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
[1] https://sourceware.org/pipermail/libc-alpha/2017-June/082098.html
(cherry picked from commit 3cc4a8367c23582b7db14cf4e150e4068b7fd461)
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index ae412e4b8444aea2..dab56b6ba2c7bdbe 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -31,6 +31,7 @@
#include <locale/localeinfo.h>
#include <stdio.h>
#include <scratch_buffer.h>
+#include <intprops.h>
/* This code is shared between the standard stdio implementation found
in GNU C library and the libio implementation originally found in
@@ -64,23 +65,40 @@
} while (0)
#define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED)
-#define done_add(val) \
- do { \
- unsigned int _val = val; \
- assert ((unsigned int) done < (unsigned int) INT_MAX); \
- if (__glibc_unlikely (INT_MAX - done < _val)) \
- { \
- done = -1; \
- __set_errno (EOVERFLOW); \
- goto all_done; \
- } \
- done += _val; \
- } while (0)
+/* Add LENGTH to DONE. Return the new value of DONE, or -1 on
+ overflow (and set errno accordingly). */
+static inline int
+done_add_func (size_t length, int done)
+{
+ if (done < 0)
+ return done;
+ int ret;
+ if (INT_ADD_WRAPV (done, length, &ret))
+ {
+ __set_errno (EOVERFLOW);
+ return -1;
+ }
+ return ret;
+}
+
+#define done_add(val) \
+ do \
+ { \
+ /* Ensure that VAL has a type similar to int. */ \
+ _Static_assert (sizeof (val) == sizeof (int), "value int size"); \
+ _Static_assert ((__typeof__ (val)) -1 < 0, "value signed"); \
+ done = done_add_func ((val), done); \
+ if (done < 0) \
+ goto all_done; \
+ } \
+ while (0)
#ifndef COMPILE_WPRINTF
# define vfprintf _IO_vfprintf_internal
# define CHAR_T char
+# define CHAR_T char
# define UCHAR_T unsigned char
+# define OTHER_CHAR_T wchar_t
# define INT_T int
typedef const char *THOUSANDS_SEP_T;
# define L_(Str) Str
@@ -88,22 +106,10 @@ typedef const char *THOUSANDS_SEP_T;
# define STR_LEN(Str) strlen (Str)
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
-# define PAD(Padchar) \
- do { \
- if (width > 0) \
- { \
- ssize_t written = _IO_padn (s, (Padchar), width); \
- if (__glibc_unlikely (written != width)) \
- { \
- done = -1; \
- goto all_done; \
- } \
- done_add (written); \
- } \
- } while (0)
# define PUTC(C, F) _IO_putc_unlocked (C, F)
# define ORIENT if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\
return -1
+# define CONVERT_FROM_OTHER_STRING __wcsrtombs
#else
# define vfprintf _IO_vfwprintf
# define CHAR_T wchar_t
@@ -118,21 +124,11 @@ typedef wchar_t THOUSANDS_SEP_T;
# include <_itowa.h>
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
-# define PAD(Padchar) \
- do { \
- if (width > 0) \
- { \
- ssize_t written = _IO_wpadn (s, (Padchar), width); \
- if (__glibc_unlikely (written != width)) \
- { \
- done = -1; \
- goto all_done; \
- } \
- done_add (written); \
- } \
- } while (0)
# define PUTC(C, F) _IO_putwc_unlocked (C, F)
# define ORIENT if (_IO_fwide (s, 1) != 1) return -1
+# define CONVERT_FROM_OTHER_STRING __mbsrtowcs
+# define CHAR_T wchar_t
+# define OTHER_CHAR_T char
# undef _itoa
# define _itoa(Val, Buf, Base, Case) _itowa (Val, Buf, Base, Case)
@@ -141,6 +137,33 @@ typedef wchar_t THOUSANDS_SEP_T;
# define EOF WEOF
#endif
+static inline int
+pad_func (FILE *s, CHAR_T padchar, int width, int done)
+{
+ if (width > 0)
+ {
+ ssize_t written;
+#ifndef COMPILE_WPRINTF
+ written = _IO_padn (s, padchar, width);
+#else
+ written = _IO_wpadn (s, padchar, width);
+#endif
+ if (__glibc_unlikely (written != width))
+ return -1;
+ return done_add_func (width, done);
+ }
+ return done;
+}
+
+#define PAD(Padchar) \
+ do \
+ { \
+ done = pad_func (s, (Padchar), width, done); \
+ if (done < 0) \
+ goto all_done; \
+ } \
+ while (0)
+
#include "_i18n_number.h"
/* Include the shared code for parsing the format string. */
@@ -160,24 +183,115 @@ typedef wchar_t THOUSANDS_SEP_T;
} \
while (0)
-#define outstring(String, Len) \
- do \
- { \
- assert ((size_t) done <= (size_t) INT_MAX); \
- if ((size_t) PUT (s, (String), (Len)) != (size_t) (Len)) \
- { \
- done = -1; \
- goto all_done; \
- } \
- if (__glibc_unlikely (INT_MAX - done < (Len))) \
- { \
- done = -1; \
- __set_errno (EOVERFLOW); \
- goto all_done; \
- } \
- done += (Len); \
- } \
- while (0)
+static inline int
+outstring_func (FILE *s, const UCHAR_T *string, size_t length, int done)
+{
+ assert ((size_t) done <= (size_t) INT_MAX);
+ if ((size_t) PUT (s, string, length) != (size_t) (length))
+ return -1;
+ return done_add_func (length, done);
+}
+
+#define outstring(String, Len) \
+ do \
+ { \
+ const void *string_ = (String); \
+ done = outstring_func (s, string_, (Len), done); \
+ if (done < 0) \
+ goto all_done; \
+ } \
+ while (0)
+
+/* Write the string SRC to S. If PREC is non-negative, write at most
+ PREC bytes. If LEFT is true, perform left justification. */
+static int
+outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
+ int width, bool left, int done)
+{
+ /* Use a small buffer to combine processing of multiple characters.
+ CONVERT_FROM_OTHER_STRING expects the buffer size in (wide)
+ characters, and buf_length counts that. */
+ enum { buf_length = 256 / sizeof (CHAR_T) };
+ CHAR_T buf[buf_length];
+ _Static_assert (sizeof (buf) > MB_LEN_MAX,
+ "buffer is large enough for a single multi-byte character");
+
+ /* Add the initial padding if needed. */
+ if (width > 0 && !left)
+ {
+ /* Make a first pass to find the output width, so that we can
+ add the required padding. */
+ mbstate_t mbstate = { 0 };
+ const OTHER_CHAR_T *src_copy = src;
+ size_t total_written;
+ if (prec < 0)
+ total_written = CONVERT_FROM_OTHER_STRING
+ (NULL, &src_copy, 0, &mbstate);
+ else
+ {
+ /* The source might not be null-terminated. Enforce the
+ limit manually, based on the output length. */
+ total_written = 0;
+ size_t limit = prec;
+ while (limit > 0 && src_copy != NULL)
+ {
+ size_t write_limit = buf_length;
+ if (write_limit > limit)
+ write_limit = limit;
+ size_t written = CONVERT_FROM_OTHER_STRING
+ (buf, &src_copy, write_limit, &mbstate);
+ if (written == (size_t) -1)
+ return -1;
+ if (written == 0)
+ break;
+ total_written += written;
+ limit -= written;
+ }
+ }
+
+ /* Output initial padding. */
+ if (total_written < width)
+ {
+ done = pad_func (s, L_(' '), width - total_written, done);
+ if (done < 0)
+ return done;
+ }
+ }
+
+ /* Convert the input string, piece by piece. */
+ size_t total_written = 0;
+ {
+ mbstate_t mbstate = { 0 };
+ /* If prec is negative, remaining is not decremented, otherwise,
+ it serves as the write limit. */
+ size_t remaining = -1;
+ if (prec >= 0)
+ remaining = prec;
+ while (remaining > 0 && src != NULL)
+ {
+ size_t write_limit = buf_length;
+ if (remaining < write_limit)
+ write_limit = remaining;
+ size_t written = CONVERT_FROM_OTHER_STRING
+ (buf, &src, write_limit, &mbstate);
+ if (written == (size_t) -1)
+ return -1;
+ if (written == 0)
+ break;
+ done = outstring_func (s, (const UCHAR_T *) buf, written, done);
+ if (done < 0)
+ return done;
+ total_written += written;
+ if (prec >= 0)
+ remaining -= written;
+ }
+ }
+
+ /* Add final padding. */
+ if (width > 0 && left && total_written < width)
+ return pad_func (s, L_(' '), width - total_written, done);
+ return done;
+}
/* For handling long_double and longlong we use the same flag. If
`long' and `long long' are effectively the same type define it to
@@ -975,7 +1089,6 @@ static const uint8_t jump_table[] =
LABEL (form_string): \
{ \
size_t len; \
- int string_malloced; \
\
/* The string argument could in fact be `char *' or `wchar_t *'. \
But this should not make a difference here. */ \
@@ -987,7 +1100,6 @@ static const uint8_t jump_table[] =
/* Entry point for printing other strings. */ \
LABEL (print_string): \
\
- string_malloced = 0; \
if (string == NULL) \
{ \
/* Write "(null)" if there's space. */ \
@@ -1004,41 +1116,12 @@ static const uint8_t jump_table[] =
} \
else if (!is_long && spec != L_('S')) \
{ \
- /* This is complicated. We have to transform the multibyte \
- string into a wide character string. */ \
- const char *mbs = (const char *) string; \
- mbstate_t mbstate; \
- \
- len = prec != -1 ? __strnlen (mbs, (size_t) prec) : strlen (mbs); \
- \
- /* Allocate dynamically an array which definitely is long \
- enough for the wide character version. Each byte in the \
- multi-byte string can produce at most one wide character. */ \
- if (__glibc_unlikely (len > SIZE_MAX / sizeof (wchar_t))) \
- { \
- __set_errno (EOVERFLOW); \
- done = -1; \
- goto all_done; \
- } \
- else if (__libc_use_alloca (len * sizeof (wchar_t))) \
- string = (CHAR_T *) alloca (len * sizeof (wchar_t)); \
- else if ((string = (CHAR_T *) malloc (len * sizeof (wchar_t))) \
- == NULL) \
- { \
- done = -1; \
- goto all_done; \
- } \
- else \
- string_malloced = 1; \
- \
- memset (&mbstate, '\0', sizeof (mbstate_t)); \
- len = __mbsrtowcs (string, &mbs, len, &mbstate); \
- if (len == (size_t) -1) \
- { \
- /* Illegal multibyte character. */ \
- done = -1; \
- goto all_done; \
- } \
+ done = outstring_converted_wide_string \
+ (s, (const char *) string, prec, width, left, done); \
+ if (done < 0) \
+ goto all_done; \
+ /* The padding has already been written. */ \
+ break; \
} \
else \
{ \
@@ -1061,8 +1144,6 @@ static const uint8_t jump_table[] =
outstring (string, len); \
if (left) \
PAD (L' '); \
- if (__glibc_unlikely (string_malloced)) \
- free (string); \
} \
break;
#else
@@ -1111,7 +1192,6 @@ static const uint8_t jump_table[] =
LABEL (form_string): \
{ \
size_t len; \
- int string_malloced; \
\
/* The string argument could in fact be `char *' or `wchar_t *'. \
But this should not make a difference here. */ \
@@ -1123,7 +1203,6 @@ static const uint8_t jump_table[] =
/* Entry point for printing other strings. */ \
LABEL (print_string): \
\
- string_malloced = 0; \
if (string == NULL) \
{ \
/* Write "(null)" if there's space. */ \
@@ -1149,51 +1228,12 @@ static const uint8_t jump_table[] =
} \
else \
{ \
- const wchar_t *s2 = (const wchar_t *) string; \
- mbstate_t mbstate; \
- \
- memset (&mbstate, '\0', sizeof (mbstate_t)); \
- \
- if (prec >= 0) \
- { \
- /* The string `s2' might not be NUL terminated. */ \
- if (__libc_use_alloca (prec)) \
- string = (char *) alloca (prec); \
- else if ((string = (char *) malloc (prec)) == NULL) \
- { \
- done = -1; \
- goto all_done; \
- } \
- else \
- string_malloced = 1; \
- len = __wcsrtombs (string, &s2, prec, &mbstate); \
- } \
- else \
- { \
- len = __wcsrtombs (NULL, &s2, 0, &mbstate); \
- if (len != (size_t) -1) \
- { \
- assert (__mbsinit (&mbstate)); \
- s2 = (const wchar_t *) string; \
- if (__libc_use_alloca (len + 1)) \
- string = (char *) alloca (len + 1); \
- else if ((string = (char *) malloc (len + 1)) == NULL) \
- { \
- done = -1; \
- goto all_done; \
- } \
- else \
- string_malloced = 1; \
- (void) __wcsrtombs (string, &s2, len + 1, &mbstate); \
- } \
- } \
- \
- if (len == (size_t) -1) \
- { \
- /* Illegal wide-character string. */ \
- done = -1; \
- goto all_done; \
- } \
+ done = outstring_converted_wide_string \
+ (s, (const wchar_t *) string, prec, width, left, done); \
+ if (done < 0) \
+ goto all_done; \
+ /* The padding has already been written. */ \
+ break; \
} \
\
if ((width -= len) < 0) \
@@ -1207,8 +1247,6 @@ static const uint8_t jump_table[] =
outstring (string, len); \
if (left) \
PAD (' '); \
- if (__glibc_unlikely (string_malloced)) \
- free (string); \
} \
break;
#endif

View File

@ -0,0 +1,160 @@
commit 29b12753b51866b227a6c0ac96c2c6c0e20f3497
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Thu Mar 19 18:35:46 2020 -0300
stdio: Add tests for printf multibyte convertion leak [BZ#25691]
Checked on x86_64-linux-gnu and i686-linux-gnu.
(cherry picked from commit 910a835dc96c1f518ac2a6179fc622ba81ffb159)
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index a10f12ab3ccbd76e..51062a7dbf698931 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -63,6 +63,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
tst-vfprintf-mbs-prec \
tst-scanf-round \
tst-renameat2 \
+ tst-printf-bz25691 \
test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
@@ -71,10 +72,12 @@ tests-special += $(objpfx)tst-unbputc.out $(objpfx)tst-printf.out \
$(objpfx)tst-printf-bz18872-mem.out \
$(objpfx)tst-setvbuf1-cmp.out \
$(objpfx)tst-vfprintf-width-prec-mem.out \
- $(objpfx)tst-printfsz-islongdouble.out
+ $(objpfx)tst-printfsz-islongdouble.out \
+ $(objpfx)tst-printf-bz25691-mem.out
generated += tst-printf-bz18872.c tst-printf-bz18872.mtrace \
tst-printf-bz18872-mem.out \
- tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out
+ tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out \
+ tst-printf-bz25691.mtrace tst-printf-bz25691-mem.out
endif
include ../Rules
@@ -96,6 +99,8 @@ endif
tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace
tst-vfprintf-width-prec-ENV = \
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace
+tst-printf-bz25691-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' > $@; \
diff --git a/stdio-common/tst-printf-bz25691.c b/stdio-common/tst-printf-bz25691.c
new file mode 100644
index 0000000000000000..37b30a3a8a7dc5e2
--- /dev/null
+++ b/stdio-common/tst-printf-bz25691.c
@@ -0,0 +1,108 @@
+/* Test for memory leak with large width (BZ#25691).
+ 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/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdint.h>
+#include <locale.h>
+
+#include <mcheck.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static int
+do_test (void)
+{
+ mtrace ();
+
+ /* For 's' conversion specifier with 'l' modifier the array must be
+ converted to multibyte characters up to the precision specific
+ value. */
+ {
+ /* The input size value is to force a heap allocation on temporary
+ buffer (in the old implementation). */
+ const size_t winputsize = 64 * 1024 + 1;
+ wchar_t *winput = xmalloc (winputsize * sizeof (wchar_t));
+ wmemset (winput, L'a', winputsize - 1);
+ winput[winputsize - 1] = L'\0';
+
+ char result[9];
+ const char expected[] = "aaaaaaaa";
+ int ret;
+
+ ret = snprintf (result, sizeof (result), "%.65537ls", winput);
+ TEST_COMPARE (ret, winputsize - 1);
+ TEST_COMPARE_BLOB (result, sizeof (result), expected, sizeof (expected));
+
+ ret = snprintf (result, sizeof (result), "%ls", winput);
+ TEST_COMPARE (ret, winputsize - 1);
+ TEST_COMPARE_BLOB (result, sizeof (result), expected, sizeof (expected));
+
+ free (winput);
+ }
+
+ /* For 's' converstion specifier the array is interpreted as a multibyte
+ character sequence and converted to wide characters up to the precision
+ specific value. */
+ {
+ /* The input size value is to force a heap allocation on temporary
+ buffer (in the old implementation). */
+ const size_t mbssize = 32 * 1024;
+ char *mbs = xmalloc (mbssize);
+ memset (mbs, 'a', mbssize - 1);
+ mbs[mbssize - 1] = '\0';
+
+ const size_t expectedsize = 32 * 1024;
+ wchar_t *expected = xmalloc (expectedsize * sizeof (wchar_t));
+ wmemset (expected, L'a', expectedsize - 1);
+ expected[expectedsize-1] = L'\0';
+
+ const size_t resultsize = mbssize * sizeof (wchar_t);
+ wchar_t *result = xmalloc (resultsize);
+ int ret;
+
+ ret = swprintf (result, resultsize, L"%.65537s", mbs);
+ TEST_COMPARE (ret, mbssize - 1);
+ TEST_COMPARE_BLOB (result, (ret + 1) * sizeof (wchar_t),
+ expected, expectedsize * sizeof (wchar_t));
+
+ ret = swprintf (result, resultsize, L"%1$.65537s", mbs);
+ TEST_COMPARE (ret, mbssize - 1);
+ TEST_COMPARE_BLOB (result, (ret + 1) * sizeof (wchar_t),
+ expected, expectedsize * sizeof (wchar_t));
+
+ /* Same test, but with an invalid multibyte sequence. */
+ mbs[mbssize - 2] = 0xff;
+
+ ret = swprintf (result, resultsize, L"%.65537s", mbs);
+ TEST_COMPARE (ret, -1);
+
+ ret = swprintf (result, resultsize, L"%1$.65537s", mbs);
+ TEST_COMPARE (ret, -1);
+
+ free (mbs);
+ free (result);
+ free (expected);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,356 @@
commit e1c0c00cc2bdd147bfcf362ada1443bee90465ec
Author: Joseph Myers <joseph@codesourcery.com>
Date: Tue Jul 7 14:54:12 2020 +0000
Remove most vfprintf width/precision-dependent allocations (bug 14231, bug 26211).
The vfprintf implementation (used for all printf-family functions)
contains complicated logic to allocate internal buffers of a size
depending on the width and precision used for a format, using either
malloc or alloca depending on that size, and with consequent checks
for size overflow and allocation failure.
As noted in bug 26211, the version of that logic used when '$' plus
argument number formats are in use is missing the overflow checks,
which can result in segfaults (quite possibly exploitable, I didn't
try to work that out) when the width or precision is in the range
0x7fffffe0 through 0x7fffffff (maybe smaller values as well in the
wprintf case on 32-bit systems, when the multiplication by sizeof
(CHAR_T) can overflow).
All that complicated logic in fact appears to be useless. As far as I
can tell, there has been no need (outside the floating-point printf
code, which does its own allocations) for allocations depending on
width or precision since commit
3e95f6602b226e0de06aaff686dc47b282d7cc16 ("Remove limitation on size
of precision for integers", Sun Sep 12 21:23:32 1999 +0000). Thus,
this patch removes that logic completely, thereby fixing both problems
with excessive allocations for large width and precision for
non-floating-point formats, and the problem with missing overflow
checks with such allocations. Note that this does have the
consequence that width and precision up to INT_MAX are now allowed
where previously INT_MAX / sizeof (CHAR_T) - EXTSIZ or more would have
been rejected, so could potentially expose any other overflows where
the value would previously have been rejected by those removed checks.
I believe this completely fixes bugs 14231 and 26211.
Excessive allocations are still possible in the floating-point case
(bug 21127), as are other integer or buffer overflows (see bug 26201).
This does not address the cases where a precision larger than INT_MAX
(embedded in the format string) would be meaningful without printf's
return value overflowing (when it's used with a string format, or %g
without the '#' flag, so the actual output will be much smaller), as
mentioned in bug 17829 comment 8; using size_t internally for
precision to handle that case would be complicated by struct
printf_info being a public ABI. Nor does it address the matter of an
INT_MIN width being negated (bug 17829 comment 7; the same logic
appears a second time in the file as well, in the form of multiplying
by -1). There may be other sources of memory allocations with malloc
in printf functions as well (bug 24988, bug 16060). From inspection,
I think there are also integer overflows in two copies of "if ((width
-= len) < 0)" logic (where width is int, len is size_t and a very long
string could result in spurious padding being output on a 32-bit
system before printf overflows the count of output characters).
Tested for x86-64 and x86.
(cherry picked from commit 6caddd34bd7ffb5ac4f36c8e036eee100c2cc535)
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 51062a7dbf698931..d76b47bd5f932f69 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -64,6 +64,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
tst-scanf-round \
tst-renameat2 \
tst-printf-bz25691 \
+ tst-vfprintf-width-prec-alloc
test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
diff --git a/stdio-common/bug22.c b/stdio-common/bug22.c
index b26399acb7dfc775..e12b01731e1b4ac8 100644
--- a/stdio-common/bug22.c
+++ b/stdio-common/bug22.c
@@ -42,7 +42,7 @@ do_test (void)
ret = fprintf (fp, "%." SN3 "d", 1);
printf ("ret = %d\n", ret);
- if (ret != -1 || errno != EOVERFLOW)
+ if (ret != N3)
return 1;
ret = fprintf (fp, "%" SN2 "d%" SN2 "d", 1, 1);
diff --git a/stdio-common/tst-vfprintf-width-prec-alloc.c b/stdio-common/tst-vfprintf-width-prec-alloc.c
new file mode 100644
index 0000000000000000..0a74b53a3389d699
--- /dev/null
+++ b/stdio-common/tst-vfprintf-width-prec-alloc.c
@@ -0,0 +1,41 @@
+/* Test large width or precision does not involve large allocation.
+ 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/>. */
+
+#include <stdio.h>
+#include <sys/resource.h>
+#include <support/check.h>
+
+char test_string[] = "test";
+
+static int
+do_test (void)
+{
+ struct rlimit limit;
+ TEST_VERIFY_EXIT (getrlimit (RLIMIT_AS, &limit) == 0);
+ limit.rlim_cur = 200 * 1024 * 1024;
+ TEST_VERIFY_EXIT (setrlimit (RLIMIT_AS, &limit) == 0);
+ FILE *fp = fopen ("/dev/null", "w");
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE (fprintf (fp, "%1000000000d", 1), 1000000000);
+ TEST_COMPARE (fprintf (fp, "%.1000000000s", test_string), 4);
+ TEST_COMPARE (fprintf (fp, "%1000000000d %1000000000d", 1, 2), 2000000001);
+ TEST_COMPARE (fprintf (fp, "%2$.*1$s", 0x7fffffff, test_string), 4);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index dab56b6ba2c7bdbe..6b83ba91a12cdcd5 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -42,10 +42,6 @@
#include <libioP.h>
-/* In some cases we need extra space for all the output which is not
- counted in the width of the string. We assume 32 characters is
- enough. */
-#define EXTSIZ 32
#define ARGCHECK(S, Format) \
do \
{ \
@@ -1295,7 +1291,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
/* Buffer intermediate results. */
CHAR_T work_buffer[WORK_BUFFER_SIZE];
- CHAR_T *workstart = NULL;
CHAR_T *workend;
/* We have to save the original argument pointer. */
@@ -1404,7 +1399,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
UCHAR_T pad = L_(' ');/* Padding character. */
CHAR_T spec;
- workstart = NULL;
workend = work_buffer + WORK_BUFFER_SIZE;
/* Get current character in format string. */
@@ -1496,31 +1490,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
pad = L_(' ');
left = 1;
}
-
- if (__glibc_unlikely (width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
- {
- __set_errno (EOVERFLOW);
- done = -1;
- goto all_done;
- }
-
- if (width >= WORK_BUFFER_SIZE - EXTSIZ)
- {
- /* We have to use a special buffer. */
- size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
- if (__libc_use_alloca (needed))
- workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
- else
- {
- workstart = (CHAR_T *) malloc (needed);
- if (workstart == NULL)
- {
- done = -1;
- goto all_done;
- }
- workend = workstart + width + EXTSIZ;
- }
- }
}
JUMP (*f, step1_jumps);
@@ -1528,31 +1497,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
LABEL (width):
width = read_int (&f);
- if (__glibc_unlikely (width == -1
- || width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
+ if (__glibc_unlikely (width == -1))
{
__set_errno (EOVERFLOW);
done = -1;
goto all_done;
}
- if (width >= WORK_BUFFER_SIZE - EXTSIZ)
- {
- /* We have to use a special buffer. */
- size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
- if (__libc_use_alloca (needed))
- workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
- else
- {
- workstart = (CHAR_T *) malloc (needed);
- if (workstart == NULL)
- {
- done = -1;
- goto all_done;
- }
- workend = workstart + width + EXTSIZ;
- }
- }
if (*f == L_('$'))
/* Oh, oh. The argument comes from a positional parameter. */
goto do_positional;
@@ -1601,34 +1552,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
}
else
prec = 0;
- if (prec > width && prec > WORK_BUFFER_SIZE - EXTSIZ)
- {
- /* Deallocate any previously allocated buffer because it is
- too small. */
- if (__glibc_unlikely (workstart != NULL))
- free (workstart);
- workstart = NULL;
- if (__glibc_unlikely (prec >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
- {
- __set_errno (EOVERFLOW);
- done = -1;
- goto all_done;
- }
- size_t needed = ((size_t) prec + EXTSIZ) * sizeof (CHAR_T);
-
- if (__libc_use_alloca (needed))
- workend = (CHAR_T *) alloca (needed) + prec + EXTSIZ;
- else
- {
- workstart = (CHAR_T *) malloc (needed);
- if (workstart == NULL)
- {
- done = -1;
- goto all_done;
- }
- workend = workstart + prec + EXTSIZ;
- }
- }
JUMP (*f, step2_jumps);
/* Process 'h' modifier. There might another 'h' following. */
@@ -1692,10 +1615,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
/* The format is correctly handled. */
++nspecs_done;
- if (__glibc_unlikely (workstart != NULL))
- free (workstart);
- workstart = NULL;
-
/* Look for next format specifier. */
#ifdef COMPILE_WPRINTF
f = __find_specwc ((end_of_spec = ++f));
@@ -1713,18 +1632,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
/* Hand off processing for positional parameters. */
do_positional:
- if (__glibc_unlikely (workstart != NULL))
- {
- free (workstart);
- workstart = NULL;
- }
done = printf_positional (s, format, readonly_format, ap, &ap_save,
done, nspecs_done, lead_str_end, work_buffer,
save_errno, grouping, thousands_sep);
all_done:
- if (__glibc_unlikely (workstart != NULL))
- free (workstart);
/* Unlock the stream. */
_IO_funlockfile (s);
_IO_cleanup_region_end (0);
@@ -1767,8 +1679,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
/* Just a counter. */
size_t cnt;
- CHAR_T *workstart = NULL;
-
if (grouping == (const char *) -1)
{
#ifdef COMPILE_WPRINTF
@@ -1957,7 +1867,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
char pad = specs[nspecs_done].info.pad;
CHAR_T spec = specs[nspecs_done].info.spec;
- workstart = NULL;
CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE;
/* Fill in last information. */
@@ -1991,27 +1900,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
prec = specs[nspecs_done].info.prec;
}
- /* Maybe the buffer is too small. */
- if (MAX (prec, width) + EXTSIZ > WORK_BUFFER_SIZE)
- {
- if (__libc_use_alloca ((MAX (prec, width) + EXTSIZ)
- * sizeof (CHAR_T)))
- workend = ((CHAR_T *) alloca ((MAX (prec, width) + EXTSIZ)
- * sizeof (CHAR_T))
- + (MAX (prec, width) + EXTSIZ));
- else
- {
- workstart = (CHAR_T *) malloc ((MAX (prec, width) + EXTSIZ)
- * sizeof (CHAR_T));
- if (workstart == NULL)
- {
- done = -1;
- goto all_done;
- }
- workend = workstart + (MAX (prec, width) + EXTSIZ);
- }
- }
-
/* Process format specifiers. */
while (1)
{
@@ -2085,18 +1973,12 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
break;
}
- if (__glibc_unlikely (workstart != NULL))
- free (workstart);
- workstart = NULL;
-
/* Write the following constant string. */
outstring (specs[nspecs_done].end_of_fmt,
specs[nspecs_done].next_fmt
- specs[nspecs_done].end_of_fmt);
}
all_done:
- if (__glibc_unlikely (workstart != NULL))
- free (workstart);
scratch_buffer_free (&argsbuf);
scratch_buffer_free (&specsbuf);
return done;

View File

@ -0,0 +1,86 @@
commit 211a30a92b72a18ea4caa35ed503b70bc644923e
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Nov 8 19:11:51 2021 +0000
Fix memmove call in vfprintf-internal.c:group_number
A recent GCC mainline change introduces errors of the form:
vfprintf-internal.c: In function 'group_number':
vfprintf-internal.c:2093:15: error: 'memmove' specified bound between 9223372036854775808 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Werror=stringop-overflow=]
2093 | memmove (w, s, (front_ptr -s) * sizeof (CHAR_T));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a genuine bug in the glibc code: s > front_ptr is always true
at this point in the code, and the intent is clearly for the
subtraction to be the other way round. The other arguments to the
memmove call here also appear to be wrong; w and s point just *after*
the destination and source for copying the rest of the number, so the
size needs to be subtracted to get appropriate pointers for the
copying. Adjust the memmove call to conform to the apparent intent of
the code, so fixing the -Wstringop-overflow error.
Now, if the original code were ever executed, a buffer overrun would
result. However, I believe this code (introduced in commit
edc1686af0c0fc2eb535f1d38cdf63c1a5a03675, "vfprintf: Reuse work_buffer
in group_number", so in glibc 2.26) is unreachable in prior glibc
releases (so there is no need for a bug in Bugzilla, no need to
consider any backports unless someone wants to build older glibc
releases with GCC 12 and no possibility of this buffer overrun
resulting in a security issue).
work_buffer is 1000 bytes / 250 wide characters. This case is only
reachable if an initial part of the number, plus a grouped copy of the
rest of the number, fail to fit in that space; that is, if the grouped
number fails to fit in the space. In the wide character case,
grouping is always one wide character, so even with a locale (of which
there aren't any in glibc) grouping every digit, a number would need
to occupy at least 125 wide characters to overflow, and a 64-bit
integer occupies at most 23 characters in octal including a leading 0.
In the narrow character case, the multibyte encoding of the grouping
separator would need to be at least 42 bytes to overflow, again
supposing grouping every digit, but MB_LEN_MAX is 16. So even if we
admit the case of artificially constructed locales not shipped with
glibc, given that such a locale would need to use one of the character
sets supported by glibc, this code cannot be reached at present. (And
POSIX only actually specifies the ' flag for grouping for decimal
output, though glibc acts on it for other bases as well.)
With binary output (if you consider use of grouping there to be
valid), you'd need a 15-byte multibyte character for overflow; I don't
know if any supported character set has such a character (if, again,
we admit constructed locales using grouping every digit and a grouping
separator chosen to have a multibyte encoding as long as possible, as
well as accepting use of grouping with binary), but given that we have
this code at all (clearly it's not *correct*, or in accordance with
the principle of avoiding arbitrary limits, to skip grouping on
running out of internal space like that), I don't think it should need
any further changes for binary printf support to go in.
On the other hand, support for large sizes of _BitInt in printf (see
the N2858 proposal) *would* require something to be done about such
arbitrary limits (presumably using dynamic allocation in printf again,
for sufficiently large _BitInt arguments only - currently only
floating-point uses dynamic allocation, and, as previously discussed,
that could actually be replaced by bounded allocation given smarter
code).
Tested with build-many-glibcs.py for aarch64-linux-gnu (GCC mainline).
Also tested natively for x86_64.
(cherry picked from commit db6c4935fae6005d46af413b32aa92f4f6059dce)
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 6b83ba91a12cdcd5..2d434ba45a67911e 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -2101,7 +2101,8 @@ group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
copy_rest:
/* No further grouping to be done. Copy the rest of the
number. */
- memmove (w, s, (front_ptr -s) * sizeof (CHAR_T));
+ w -= s - front_ptr;
+ memmove (w, front_ptr, (s - front_ptr) * sizeof (CHAR_T));
break;
}
else if (*grouping != '\0')

View File

@ -0,0 +1,81 @@
commit 8b915921fbf4d32bf68fc3d637413cf96236b3fd
Author: Andreas Schwab <schwab@suse.de>
Date: Mon Aug 29 15:05:40 2022 +0200
Add test for bug 29530
This tests for a bug that was introduced in commit edc1686af0 ("vfprintf:
Reuse work_buffer in group_number") and fixed as a side effect of commit
6caddd34bd ("Remove most vfprintf width/precision-dependent allocations
(bug 14231, bug 26211).").
(cherry picked from commit ca6466e8be32369a658035d69542d47603e58a99)
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index d76b47bd5f932f69..ac61093660ef9063 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -64,7 +64,9 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
tst-scanf-round \
tst-renameat2 \
tst-printf-bz25691 \
- tst-vfprintf-width-prec-alloc
+ tst-vfprintf-width-prec-alloc \
+ tst-grouping2 \
+ # tests
test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
@@ -91,6 +93,7 @@ $(objpfx)bug14.out: $(gen-locales)
$(objpfx)scanf13.out: $(gen-locales)
$(objpfx)test-vfprintf.out: $(gen-locales)
$(objpfx)tst-grouping.out: $(gen-locales)
+$(objpfx)tst-grouping2.out: $(gen-locales)
$(objpfx)tst-sprintf.out: $(gen-locales)
$(objpfx)tst-sscanf.out: $(gen-locales)
$(objpfx)tst-swprintf.out: $(gen-locales)
diff --git a/stdio-common/tst-grouping2.c b/stdio-common/tst-grouping2.c
new file mode 100644
index 0000000000000000..3024c942a60e51bf
--- /dev/null
+++ b/stdio-common/tst-grouping2.c
@@ -0,0 +1,39 @@
+/* Test printf with grouping and large width (bug 29530)
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <locale.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static int
+do_test (void)
+{
+ const int field_width = 1000;
+ char buf[field_width + 1];
+
+ xsetlocale (LC_NUMERIC, "de_DE.UTF-8");
+
+ /* This used to crash in group_number. */
+ TEST_COMPARE (sprintf (buf, "%'*d", field_width, 1000), field_width);
+ TEST_COMPARE_STRING (buf + field_width - 6, " 1.000");
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,54 @@
commit a23820f6052a740246fdc7dcd9c43ce8eed0c45a
Author: Javier Pello <devel@otheo.eu>
Date: Mon Sep 5 20:09:01 2022 +0200
elf: Fix hwcaps string size overestimation
Commit dad90d528259b669342757c37dedefa8577e2636 added glibc-hwcaps
support for LD_LIBRARY_PATH and, for this, it adjusted the total
string size required in _dl_important_hwcaps. However, in doing so
it inadvertently altered the calculation of the size required for
the power set strings, as the computation of the power set string
size depended on the first value assigned to the total variable,
which is later shifted, resulting in overallocation of string
space. Fix this now by using a different variable to hold the
string size required for glibc-hwcaps.
Signed-off-by: Javier Pello <devel@otheo.eu>
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
index 2fc4ae67a0f5d051..7ac27fd689187edc 100644
--- a/elf/dl-hwcaps.c
+++ b/elf/dl-hwcaps.c
@@ -193,7 +193,7 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
/* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
and a "/" suffix once stored in the result. */
hwcaps_counts.maximum_length += strlen (GLIBC_HWCAPS_PREFIX) + 1;
- size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
+ size_t hwcaps_sz = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
+ hwcaps_counts.total_length);
/* Count the number of bits set in the masked value. */
@@ -229,11 +229,12 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
assert (m == cnt);
/* Determine the total size of all strings together. */
+ size_t total;
if (cnt == 1)
- total += temp[0].len + 1;
+ total = temp[0].len + 1;
else
{
- total += temp[0].len + temp[cnt - 1].len + 2;
+ total = temp[0].len + temp[cnt - 1].len + 2;
if (cnt > 2)
{
total <<= 1;
@@ -255,6 +256,7 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
/* This is the overall result, including both glibc-hwcaps
subdirectories and the legacy hwcaps subdirectories using the
power set construction. */
+ total += hwcaps_sz;
struct r_strlenpair *overall_result
= malloc (*sz * sizeof (*result) + total);
if (overall_result == NULL)

View File

@ -0,0 +1,32 @@
commit acb55dcb892d4321ada6fd9b663b28fada432682
Author: Joseph Myers <joseph@codesourcery.com>
Date: Wed Jan 2 18:35:50 2019 +0000
Update Linux kernel version in tst-mman-consts.py.
This patch updates the Linux kernel version in tst-mman-consts.py to
4.20 (meaning that's the version for which glibc is expected to have
the same constants as the kernel, up to the exceptions listed in the
test). (Once we have more such tests sharing common infrastructure, I
expect the kernel version will be something set in the infrastructure
shared by all such tests, rather than something needing updating
separately for each test for each new kernel version.)
Tested with build-many-glibcs.py.
* sysdeps/unix/sysv/linux/tst-mman-consts.py (main): Expect
constants to match with Linux 4.20.
diff --git a/sysdeps/unix/sysv/linux/tst-mman-consts.py b/sysdeps/unix/sysv/linux/tst-mman-consts.py
index 1a613beec0da16fb..4a2ddd49c4c7282b 100644
--- a/sysdeps/unix/sysv/linux/tst-mman-consts.py
+++ b/sysdeps/unix/sysv/linux/tst-mman-consts.py
@@ -41,7 +41,7 @@ def main():
help='C compiler (including options) to use')
args = parser.parse_args()
linux_version_headers = linux_kernel_version(args.cc)
- linux_version_glibc = (4, 19)
+ linux_version_glibc = (4, 20)
sys.exit(glibcextract.compare_macro_consts(
'#define _GNU_SOURCE 1\n'
'#include <sys/mman.h>\n',

View File

@ -0,0 +1,31 @@
commit c7a26cba2ab949216ac9ef245ca78696815ea4c4
Author: Joseph Myers <joseph@codesourcery.com>
Date: Fri Aug 2 11:36:07 2019 +0000
Update Linux kernel version number in tst-mman-consts.py to 5.2.
The tst-mman-consts.py test includes a kernel version number, to avoid
failures because of newly added constants in the kernel (if kernel
headers are newer than this version of glibc) or missing constants in
the kernel (if kernel headers are older than this version of glibc).
This patch updates it to 5.2 to reflect that the MAP_* constants in
glibc are still current as of that kernel version.
Tested with build-many-glibcs.py.
* sysdeps/unix/sysv/linux/tst-mman-consts.py (main): Update Linux
kernel version number to 5.2.
diff --git a/sysdeps/unix/sysv/linux/tst-mman-consts.py b/sysdeps/unix/sysv/linux/tst-mman-consts.py
index 4a2ddd49c4c7282b..9e326b1f31799a72 100644
--- a/sysdeps/unix/sysv/linux/tst-mman-consts.py
+++ b/sysdeps/unix/sysv/linux/tst-mman-consts.py
@@ -41,7 +41,7 @@ def main():
help='C compiler (including options) to use')
args = parser.parse_args()
linux_version_headers = linux_kernel_version(args.cc)
- linux_version_glibc = (4, 20)
+ linux_version_glibc = (5, 2)
sys.exit(glibcextract.compare_macro_consts(
'#define _GNU_SOURCE 1\n'
'#include <sys/mman.h>\n',

View File

@ -0,0 +1,61 @@
commit 71bdf29ac1de04efcce96bc5ce50af3263851ac7
Author: Joseph Myers <joseph@codesourcery.com>
Date: Mon Sep 30 15:49:25 2019 +0000
Update bits/mman.h constants and tst-mman-consts.py for Linux 5.3.
The Linux 5.3 uapi headers have some rearrangement relating to MAP_*
constants, which includes the effect of adding definitions of MAP_SYNC
on powerpc and sparc. This patch updates the corresponding glibc
bits/mman.h headers accordingly, and updates the Linux kernel version
number in tst-mman-consts.py to reflect that these constants are now
current with that kernel version.
Tested with build-many-glibcs.py.
* sysdeps/unix/sysv/linux/powerpc/bits/mman.h [__USE_MISC]
(MAP_SYNC): New macro.
* sysdeps/unix/sysv/linux/sparc/bits/mman.h [__USE_MISC]
(MAP_SYNC): Likewise.
* sysdeps/unix/sysv/linux/tst-mman-consts.py (main): Update Linux
kernel version number to 5.3.
diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/mman.h b/sysdeps/unix/sysv/linux/powerpc/bits/mman.h
index e652467c8c091381..0e7fa647793ed585 100644
--- a/sysdeps/unix/sysv/linux/powerpc/bits/mman.h
+++ b/sysdeps/unix/sysv/linux/powerpc/bits/mman.h
@@ -36,6 +36,8 @@
# define MAP_NONBLOCK 0x10000 /* Do not block on IO. */
# define MAP_STACK 0x20000 /* Allocation is for a stack. */
# define MAP_HUGETLB 0x40000 /* Create huge page mapping. */
+# define MAP_SYNC 0x80000 /* Perform synchronous page
+ faults for the mapping. */
# define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED but do not unmap
underlying mapping. */
#endif
diff --git a/sysdeps/unix/sysv/linux/sparc/bits/mman.h b/sysdeps/unix/sysv/linux/sparc/bits/mman.h
index 3a3ffb994631e2b6..03f6f732bb5efbe2 100644
--- a/sysdeps/unix/sysv/linux/sparc/bits/mman.h
+++ b/sysdeps/unix/sysv/linux/sparc/bits/mman.h
@@ -36,6 +36,8 @@
# define MAP_NONBLOCK 0x10000 /* Do not block on IO. */
# define MAP_STACK 0x20000 /* Allocation is for a stack. */
# define MAP_HUGETLB 0x40000 /* Create huge page mapping. */
+# define MAP_SYNC 0x80000 /* Perform synchronous page
+ faults for the mapping. */
# define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED but do not unmap
underlying mapping. */
#endif
diff --git a/sysdeps/unix/sysv/linux/tst-mman-consts.py b/sysdeps/unix/sysv/linux/tst-mman-consts.py
index 9e326b1f31799a72..42914e4e0ba84712 100644
--- a/sysdeps/unix/sysv/linux/tst-mman-consts.py
+++ b/sysdeps/unix/sysv/linux/tst-mman-consts.py
@@ -41,7 +41,7 @@ def main():
help='C compiler (including options) to use')
args = parser.parse_args()
linux_version_headers = linux_kernel_version(args.cc)
- linux_version_glibc = (5, 2)
+ linux_version_glibc = (5, 3)
sys.exit(glibcextract.compare_macro_consts(
'#define _GNU_SOURCE 1\n'
'#include <sys/mman.h>\n',

View File

@ -0,0 +1,101 @@
This change is equivalent to this upstream change:
commit 22a46dee24351fd5f4f188ad80554cad79c82524
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Nov 8 14:15:02 2022 +0100
Linux: Support __IPC_64 in sysvctl *ctl command arguments (bug 29771)
Old applications pass __IPC_64 as part of the command argument because
old glibc did not check for unknown commands, and passed through the
arguments directly to the kernel, without adding __IPC_64.
Applications need to continue doing that for old glibc compatibility,
so this commit enables this approach in current glibc.
For msgctl and shmctl, if no translation is required, make
direct system calls, as we did before the time64 changes. If
translation is required, mask __IPC_64 from the command argument.
For semctl, the union-in-vararg argument handling means that
translation is needed on all architectures.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
The downstream versions of shmctl and msgctl did not produce
errors because they lacked a -1 error return path. There is no
translation requirement downstream on any architecture, so we
can remove the switch from shmctl and msgctl.
For semctl, we have to do the varargs translation, so this patch adds
the same masking as the upstream commit.
diff --git a/sysdeps/unix/sysv/linux/msgctl.c b/sysdeps/unix/sysv/linux/msgctl.c
index 3362f4562f58f28b..7280cba31a8815a2 100644
--- a/sysdeps/unix/sysv/linux/msgctl.c
+++ b/sysdeps/unix/sysv/linux/msgctl.c
@@ -29,20 +29,6 @@
int
__new_msgctl (int msqid, int cmd, struct msqid_ds *buf)
{
- switch (cmd)
- {
- case IPC_RMID:
- case IPC_SET:
- case IPC_STAT:
- case MSG_STAT:
- case MSG_STAT_ANY:
- case IPC_INFO:
- case MSG_INFO:
- break;
- default:
- __set_errno (EINVAL);
- return -1;
- }
#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
return INLINE_SYSCALL_CALL (msgctl, msqid, cmd | __IPC_64, buf);
#else
diff --git a/sysdeps/unix/sysv/linux/semctl.c b/sysdeps/unix/sysv/linux/semctl.c
index 03c56c69a5412c82..16d3f04fadd039ab 100644
--- a/sysdeps/unix/sysv/linux/semctl.c
+++ b/sysdeps/unix/sysv/linux/semctl.c
@@ -42,6 +42,13 @@ __new_semctl (int semid, int semnum, int cmd, ...)
union semun arg = { 0 };
va_list ap;
+ /* Some applications pass the __IPC_64 flag in cmd, to invoke
+ previously unsupported commands back when there was no EINVAL
+ error checking in glibc. Mask the flag for the switch statements
+ below. msgctl_syscall adds back the __IPC_64 flag for the actual
+ system call. */
+ cmd &= ~__IPC_64;
+
/* Get the argument only if required. */
switch (cmd)
{
diff --git a/sysdeps/unix/sysv/linux/shmctl.c b/sysdeps/unix/sysv/linux/shmctl.c
index 00768bc47614f9aa..25c5152944a6fcf3 100644
--- a/sysdeps/unix/sysv/linux/shmctl.c
+++ b/sysdeps/unix/sysv/linux/shmctl.c
@@ -33,22 +33,6 @@
int
__new_shmctl (int shmid, int cmd, struct shmid_ds *buf)
{
- switch (cmd)
- {
- case IPC_RMID:
- case SHM_LOCK:
- case SHM_UNLOCK:
- case IPC_SET:
- case IPC_STAT:
- case SHM_STAT:
- case SHM_STAT_ANY:
- case IPC_INFO:
- case SHM_INFO:
- break;
- default:
- __set_errno (EINVAL);
- break;
- }
#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
return INLINE_SYSCALL_CALL (shmctl, shmid, cmd | __IPC_64, buf);
#else

View File

@ -0,0 +1,354 @@
commit 2fe64148a81f0d78050c302f34a6853d21f7cae4
Author: DJ Delorie <dj@redhat.com>
Date: Mon Mar 28 23:53:33 2022 -0400
Allow for unpriviledged nested containers
If the build itself is run in a container, we may not be able to
fully set up a nested container for test-container testing.
Notably is the mounting of /proc, since it's critical that it
be mounted from within the same PID namespace as its users, and
thus cannot be bind mounted from outside the container like other
mounts.
This patch defaults to using the parent's PID namespace instead of
creating a new one, as this is more likely to be allowed.
If the test needs an isolated PID namespace, it should add the "pidns"
command to its init script.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
nss/tst-reload2.c
(not in RHEL-8)
support/Makefile
(RHEL-8 missing some routines in libsupport-routines)
diff --git a/elf/tst-pldd.c b/elf/tst-pldd.c
index f381cb0fa7e6b93d..45ac033a0f897088 100644
--- a/elf/tst-pldd.c
+++ b/elf/tst-pldd.c
@@ -85,6 +85,8 @@ in_str_list (const char *libname, const char *const strlist[])
static int
do_test (void)
{
+ support_need_proc ("needs /proc/sys/kernel/yama/ptrace_scope and /proc/$child");
+
/* Check if our subprocess can be debugged with ptrace. */
{
int ptrace_scope = support_ptrace_scope ();
diff --git a/nptl/tst-pthread-getattr.c b/nptl/tst-pthread-getattr.c
index 273b6073abe9cb60..f1c0b39f3a27724c 100644
--- a/nptl/tst-pthread-getattr.c
+++ b/nptl/tst-pthread-getattr.c
@@ -28,6 +28,8 @@
#include <unistd.h>
#include <inttypes.h>
+#include <support/support.h>
+
/* There is an obscure bug in the kernel due to which RLIMIT_STACK is sometimes
returned as unlimited when it is not, which may cause this test to fail.
There is also the other case where RLIMIT_STACK is intentionally set as
@@ -152,6 +154,8 @@ check_stack_top (void)
static int
do_test (void)
{
+ support_need_proc ("Reads /proc/self/maps to get stack size.");
+
pagesize = sysconf (_SC_PAGESIZE);
return check_stack_top ();
}
diff --git a/support/Makefile b/support/Makefile
index 636d69c4f8e7e139..e184fccbe7d2310c 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -59,6 +59,7 @@ libsupport-routines = \
support_format_hostent \
support_format_netent \
support_isolate_in_subprocess \
+ support_need_proc \
support_process_state \
support_ptrace \
support_openpty \
diff --git a/support/support.h b/support/support.h
index 96833bd4e992e6d3..1466eb29f840fa59 100644
--- a/support/support.h
+++ b/support/support.h
@@ -81,6 +81,11 @@ char *support_quote_string (const char *);
regular file open for writing, and initially empty. */
int support_descriptor_supports_holes (int fd);
+/* Predicates that a test requires a working /proc filesystem. This
+ call will exit with UNSUPPORTED if /proc is not available, printing
+ WHY_MSG as part of the diagnostic. */
+void support_need_proc (const char *why_msg);
+
/* Error-checking wrapper functions which terminate the process on
error. */
diff --git a/support/support_need_proc.c b/support/support_need_proc.c
new file mode 100644
index 0000000000000000..9b4eab7539b2d6c3
--- /dev/null
+++ b/support/support_need_proc.c
@@ -0,0 +1,35 @@
+/* Indicate that a test requires a working /proc.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <unistd.h>
+#include <support/check.h>
+#include <support/support.h>
+
+/* We test for /proc/self/maps since that's one of the files that one
+ of our tests actually uses, but the general idea is if Linux's
+ /proc/ (procfs) filesystem is mounted. If not, the process exits
+ with an UNSUPPORTED result code. */
+
+void
+support_need_proc (const char *why_msg)
+{
+#ifdef __linux__
+ if (access ("/proc/self/maps", R_OK))
+ FAIL_UNSUPPORTED ("/proc is not available, %s", why_msg);
+#endif
+}
diff --git a/support/test-container.c b/support/test-container.c
index 9975c8cb7bc9a955..2bce4db841ff7668 100644
--- a/support/test-container.c
+++ b/support/test-container.c
@@ -95,6 +95,7 @@ int verbose = 0;
* mytest.root/mytest.script has a list of "commands" to run:
syntax:
# comment
+ pidns <comment>
su
mv FILE FILE
cp FILE FILE
@@ -120,6 +121,8 @@ int verbose = 0;
details:
- '#': A comment.
+ - 'pidns': Require a separate PID namespace, prints comment if it can't
+ (default is a shared pid namespace)
- 'su': Enables running test as root in the container.
- 'mv': A minimal move files command.
- 'cp': A minimal copy files command.
@@ -143,7 +146,7 @@ int verbose = 0;
* Simple, easy to review code (i.e. prefer simple naive code over
complex efficient code)
- * The current implementation ist parallel-make-safe, but only in
+ * The current implementation is parallel-make-safe, but only in
that it uses a lock to prevent parallel access to the testroot. */
@@ -222,11 +225,37 @@ concat (const char *str, ...)
return bufs[n];
}
+/* Like the above, but put spaces between words. Caller frees. */
+static char *
+concat_words (char **words, int num_words)
+{
+ int len = 0;
+ int i;
+ char *rv, *p;
+
+ for (i = 0; i < num_words; i ++)
+ {
+ len += strlen (words[i]);
+ len ++;
+ }
+
+ p = rv = (char *) xmalloc (len);
+
+ for (i = 0; i < num_words; i ++)
+ {
+ if (i > 0)
+ p = stpcpy (p, " ");
+ p = stpcpy (p, words[i]);
+ }
+
+ return rv;
+}
+
/* Try to mount SRC onto DEST. */
static void
trymount (const char *src, const char *dest)
{
- if (mount (src, dest, "", MS_BIND, NULL) < 0)
+ if (mount (src, dest, "", MS_BIND | MS_REC, NULL) < 0)
FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
}
@@ -709,6 +738,9 @@ main (int argc, char **argv)
gid_t original_gid;
/* If set, the test runs as root instead of the user running the testsuite. */
int be_su = 0;
+ int require_pidns = 0;
+ const char *pidns_comment = NULL;
+ int do_proc_mounts = 0;
int UMAP;
int GMAP;
/* Used for "%lld %lld 1" so need not be large. */
@@ -991,6 +1023,12 @@ main (int argc, char **argv)
{
be_su = 1;
}
+ else if (nt >= 1 && strcmp (the_words[0], "pidns") == 0)
+ {
+ require_pidns = 1;
+ if (nt > 1)
+ pidns_comment = concat_words (the_words + 1, nt - 1);
+ }
else if (nt == 3 && strcmp (the_words[0], "mkdirp") == 0)
{
long int m;
@@ -1048,7 +1086,8 @@ main (int argc, char **argv)
#ifdef CLONE_NEWNS
/* The unshare here gives us our own spaces and capabilities. */
- if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
+ if (unshare (CLONE_NEWUSER | CLONE_NEWNS
+ | (require_pidns ? CLONE_NEWPID : 0)) < 0)
{
/* Older kernels may not support all the options, or security
policy may block this call. */
@@ -1059,6 +1098,11 @@ main (int argc, char **argv)
check_for_unshare_hints ();
FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
}
+ /* We're about to exit anyway, it's "safe" to call unshare again
+ just to see if the CLONE_NEWPID caused the error. */
+ else if (require_pidns && unshare (CLONE_NEWUSER | CLONE_NEWNS) >= 0)
+ FAIL_EXIT1 ("unable to unshare pid ns: %s : %s", strerror (errno),
+ pidns_comment ? pidns_comment : "required by test");
else
FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
}
@@ -1074,6 +1118,15 @@ main (int argc, char **argv)
trymount (support_srcdir_root, new_srcdir_path);
trymount (support_objdir_root, new_objdir_path);
+ /* It may not be possible to mount /proc directly. */
+ if (! require_pidns)
+ {
+ char *new_proc = concat (new_root_path, "/proc", NULL);
+ xmkdirp (new_proc, 0755);
+ trymount ("/proc", new_proc);
+ do_proc_mounts = 1;
+ }
+
xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
devmount (new_root_path, "null");
devmount (new_root_path, "zero");
@@ -1136,42 +1189,60 @@ main (int argc, char **argv)
maybe_xmkdir ("/tmp", 0755);
- /* Now that we're pid 1 (effectively "root") we can mount /proc */
- maybe_xmkdir ("/proc", 0777);
- if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
- FAIL_EXIT1 ("Unable to mount /proc: ");
-
- /* We map our original UID to the same UID in the container so we
- can own our own files normally. */
- UMAP = open ("/proc/self/uid_map", O_WRONLY);
- if (UMAP < 0)
- FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
-
- sprintf (tmp, "%lld %lld 1\n",
- (long long) (be_su ? 0 : original_uid), (long long) original_uid);
- write (UMAP, tmp, strlen (tmp));
- xclose (UMAP);
-
- /* We must disable setgroups () before we can map our groups, else we
- get EPERM. */
- GMAP = open ("/proc/self/setgroups", O_WRONLY);
- if (GMAP >= 0)
+ if (require_pidns)
{
- /* We support kernels old enough to not have this. */
- write (GMAP, "deny\n", 5);
- xclose (GMAP);
+ /* Now that we're pid 1 (effectively "root") we can mount /proc */
+ maybe_xmkdir ("/proc", 0777);
+ if (mount ("proc", "/proc", "proc", 0, NULL) != 0)
+ {
+ /* This happens if we're trying to create a nested container,
+ like if the build is running under podman, and we lack
+ priviledges.
+
+ Ideally we would WARN here, but that would just add noise to
+ *every* test-container test, and the ones that care should
+ have their own relevent diagnostics.
+
+ FAIL_EXIT1 ("Unable to mount /proc: "); */
+ }
+ else
+ do_proc_mounts = 1;
}
- /* We map our original GID to the same GID in the container so we
- can own our own files normally. */
- GMAP = open ("/proc/self/gid_map", O_WRONLY);
- if (GMAP < 0)
- FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
+ if (do_proc_mounts)
+ {
+ /* We map our original UID to the same UID in the container so we
+ can own our own files normally. */
+ UMAP = open ("/proc/self/uid_map", O_WRONLY);
+ if (UMAP < 0)
+ FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
+
+ sprintf (tmp, "%lld %lld 1\n",
+ (long long) (be_su ? 0 : original_uid), (long long) original_uid);
+ write (UMAP, tmp, strlen (tmp));
+ xclose (UMAP);
+
+ /* We must disable setgroups () before we can map our groups, else we
+ get EPERM. */
+ GMAP = open ("/proc/self/setgroups", O_WRONLY);
+ if (GMAP >= 0)
+ {
+ /* We support kernels old enough to not have this. */
+ write (GMAP, "deny\n", 5);
+ xclose (GMAP);
+ }
- sprintf (tmp, "%lld %lld 1\n",
- (long long) (be_su ? 0 : original_gid), (long long) original_gid);
- write (GMAP, tmp, strlen (tmp));
- xclose (GMAP);
+ /* We map our original GID to the same GID in the container so we
+ can own our own files normally. */
+ GMAP = open ("/proc/self/gid_map", O_WRONLY);
+ if (GMAP < 0)
+ FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
+
+ sprintf (tmp, "%lld %lld 1\n",
+ (long long) (be_su ? 0 : original_gid), (long long) original_gid);
+ write (GMAP, tmp, strlen (tmp));
+ xclose (GMAP);
+ }
if (change_cwd)
{

View File

@ -0,0 +1,24 @@
commit b2cd93fce666fdc8c9a5c64af2741a8a6940ac99
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Fri Mar 25 11:16:49 2022 -0300
elf: Fix wrong fscanf usage on tst-pldd
To take in consideration the extra '\0'.
Checked on x86_64-linux-gnu.
diff --git a/elf/tst-pldd.c b/elf/tst-pldd.c
index 45ac033a0f897088..ab89798e250fdccc 100644
--- a/elf/tst-pldd.c
+++ b/elf/tst-pldd.c
@@ -115,7 +115,8 @@ do_test (void)
TEST_VERIFY (out != NULL);
/* First line is in the form of <pid>: <full path of executable> */
- TEST_COMPARE (fscanf (out, "%u: " STRINPUT (512), &pid, buffer), 2);
+ TEST_COMPARE (fscanf (out, "%u: " STRINPUT (sizeof (buffer) - 1), &pid,
+ buffer), 2);
TEST_COMPARE (pid, *target_pid_ptr);
TEST_COMPARE (strcmp (basename (buffer), "tst-pldd"), 0);

View File

@ -0,0 +1,37 @@
commit c353689e49e72f3aafa1a9e68d4f7a4f33a79cbe
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Tue Jul 5 12:58:40 2022 -0300
elf: Fix wrong fscanf usage on tst-pldd
The fix done b2cd93fce666fdc8c9a5c64af2741a8a6940ac99 does not really
work since macro strification does not expand the sizeof nor the
arithmetic operation.
Checked on x86_64-linux-gnu.
diff --git a/elf/tst-pldd.c b/elf/tst-pldd.c
index ab89798e250fdccc..52c0a75be5a808d1 100644
--- a/elf/tst-pldd.c
+++ b/elf/tst-pldd.c
@@ -108,15 +108,16 @@ do_test (void)
loader and libc. */
{
pid_t pid;
- char buffer[512];
-#define STRINPUT(size) "%" # size "s"
+#define BUFFERLEN 511
+ char buffer[BUFFERLEN + 1];
+#define STRINPUT(size) XSTRINPUT(size)
+#define XSTRINPUT(size) "%" # size "s"
FILE *out = fmemopen (pldd.out.buffer, pldd.out.length, "r");
TEST_VERIFY (out != NULL);
/* First line is in the form of <pid>: <full path of executable> */
- TEST_COMPARE (fscanf (out, "%u: " STRINPUT (sizeof (buffer) - 1), &pid,
- buffer), 2);
+ TEST_COMPARE (fscanf (out, "%u: " STRINPUT (BUFFERLEN), &pid, buffer), 2);
TEST_COMPARE (pid, *target_pid_ptr);
TEST_COMPARE (strcmp (basename (buffer), "tst-pldd"), 0);

View File

@ -0,0 +1,45 @@
commit eb4181e9f4a512de37dad4ba623c921671584dea
Author: Vladislav Khmelevsky <och95@yandex.ru>
Date: Thu Nov 17 12:47:29 2022 +0400
elf: Fix rtld-audit trampoline for aarch64
This patch fixes two problems with audit:
1. The DL_OFFSET_RV_VPCS offset was mixed up with DL_OFFSET_RG_VPCS,
resulting in x2 register value nulling in RG structure.
2. We need to preserve the x8 register before function call, but
don't have to save it's new value and restore it before return.
Anyway the final restore was using OFFSET_RV instead of OFFSET_RG value
which is wrong (althoug doesn't affect anything).
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/sysdeps/aarch64/dl-trampoline.S b/sysdeps/aarch64/dl-trampoline.S
index a83e7fc5f97047e2..b4b9c86224785a2c 100644
--- a/sysdeps/aarch64/dl-trampoline.S
+++ b/sysdeps/aarch64/dl-trampoline.S
@@ -282,12 +282,11 @@ _dl_runtime_profile:
stp x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1]
stp x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2]
stp x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3]
- str x8, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4]
stp q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0]
stp q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1]
stp q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2]
stp q6, q7, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*3]
- str xzr, [X29, #OFFSET_RV + DL_OFFSET_RG_VPCS]
+ str xzr, [X29, #OFFSET_RV + DL_OFFSET_RV_VPCS]
/* Setup call to pltexit */
ldp x0, x1, [x29, #OFFSET_SAVED_CALL_X0]
@@ -299,7 +298,6 @@ _dl_runtime_profile:
ldp x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1]
ldp x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2]
ldp x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3]
- ldr x8, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*4]
ldp q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0]
ldp q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1]
ldp q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2]

View File

@ -0,0 +1,297 @@
Maintain an explicit order of tunables, so that the tunable_list
array and the tunable_id_t constant can remain consistent over time.
Related to this upstream bug:
Internal tunables ABI depends on awk array iteration order
<https://sourceware.org/bugzilla/show_bug.cgi?id=30027>
The new dl-tunables.list files are already on the sysdeps search
path, which is why the existing Makeconfig rule picks them up.
The files for RHEL 8.7.z were created by applying the gen-tunables.awk
part of this patch to RHEL 8.7.0 (glibc-2.28-211.el8_7, to be precise).
The sysdeps/unix/sysv/linux/**/dl-tunables.list files were created
based on the generated error message during the RHEL 8.7.z build.
Afterwards, the glibc.rtld.dynamic_sort tunable was added at the
end of the files, for the RHEL 8.8.0 build.
Going forward, new tunables will have to be added manually to the end
of those files. Existing tunables should not be deleted. For
deletion, the script would have to be extended to be able to create
gaps in the tunable_list array.
diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
index 622199061a140ccd..8ebfb560976ead41 100644
--- a/scripts/gen-tunables.awk
+++ b/scripts/gen-tunables.awk
@@ -12,6 +12,7 @@ BEGIN {
tunable=""
ns=""
top_ns=""
+ tunable_order_count = 0
}
# Skip over blank lines and comments.
@@ -78,6 +79,37 @@ $1 == "}" {
next
}
+$1 == "@order" {
+ if (top_ns != "") {
+ printf("%s:%d: error: invalid @order directive inside namespace %s\n",
+ FILENAME, FNR, top_ns) > "/dev/stderr"
+ exit 1
+ }
+ if (NF != 2) {
+ printf("%s:%d: error: invalid argument count in @order directive\n",
+ FILENAME, FNR) > "/dev/stderr"
+ exit 1
+ }
+ order_arg = $2
+ if (split(order_arg, indices, /\./) != 3) {
+ printf("%s:%d: error: invalid tunable syntax in @order directive\n",
+ FILENAME, FNR) > "/dev/stderr"
+ exit 1
+ }
+ t = indices[1]
+ n = indices[2]
+ m = indices[3]
+ if ((t, n, m) in tunable_order) {
+ printf("%s:%d: error: duplicate\"@order %s\"\n" \
+ FILENAME, FNR, order_arg) > "/dev/stderr"
+ exit 1
+ }
+ ++tunable_order_count
+ tunable_order[t,n,m] = tunable_order_count
+ tunable_order_list[tunable_order_count] = t SUBSEP n SUBSEP m
+ next
+}
+
# Everything else, which could either be a tunable without any attributes or a
# tunable attribute.
{
@@ -137,6 +169,31 @@ END {
exit 1
}
+ missing_order = 0
+ for (tnm in types) {
+ if (!(tnm in tunable_order)) {
+ if (!missing_order) {
+ print "error: Missing @order directives:" > "/dev/stderr"
+ missing_order = 1
+ }
+ split(tnm, indices, SUBSEP)
+ printf("@order %s.%s.%s\n", indices[1], indices[2], indices[3]) \
+ > "/dev/stderr"
+ }
+ }
+ for (i = 1; i <= tunable_order_count; ++i) {
+ tnm = tunable_order_list[i]
+ if (!(tnm in types)) {
+ split(tnm, indices, SUBSEP)
+ printf("error: tunable in \"@order %s.%s.%s\" not known\n", \
+ indices[1], indices[2], indices[3]) > "/dev/stderr"
+ missing_order = 1
+ }
+ }
+ if (missing_order) {
+ exit 1
+ }
+
print "/* AUTOGENERATED by gen-tunables.awk. */"
print "#ifndef _TUNABLES_H_"
print "# error \"Do not include this file directly.\""
@@ -147,7 +204,8 @@ END {
# Now, the enum names
print "\ntypedef enum"
print "{"
- for (tnm in types) {
+ for (i = 1; i <= tunable_order_count; ++i) {
+ tnm = tunable_order_list[i]
split (tnm, indices, SUBSEP);
t = indices[1];
n = indices[2];
@@ -159,7 +217,8 @@ END {
# Finally, the tunable list.
print "\n#ifdef TUNABLES_INTERNAL"
print "static tunable_t tunable_list[] attribute_relro = {"
- for (tnm in types) {
+ for (i = 1; i <= tunable_order_count; ++i) {
+ tnm = tunable_order_list[i]
split (tnm, indices, SUBSEP);
t = indices[1];
n = indices[2];
diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list b/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list
new file mode 100644
index 0000000000000000..5c3c5292025607a1
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list
@@ -0,0 +1,26 @@
+# Order of tunables in RHEL 8.7.0.
+@order glibc.rtld.nns
+@order glibc.elision.skip_lock_after_retries
+@order glibc.malloc.trim_threshold
+@order glibc.malloc.perturb
+@order glibc.cpu.name
+@order glibc.elision.tries
+@order glibc.elision.enable
+@order glibc.malloc.mxfast
+@order glibc.elision.skip_lock_busy
+@order glibc.malloc.top_pad
+@order glibc.cpu.hwcap_mask
+@order glibc.malloc.mmap_max
+@order glibc.elision.skip_trylock_internal_abort
+@order glibc.malloc.tcache_unsorted_limit
+@order glibc.elision.skip_lock_internal_abort
+@order glibc.malloc.arena_max
+@order glibc.malloc.mmap_threshold
+@order glibc.malloc.tcache_count
+@order glibc.malloc.arena_test
+@order glibc.rtld.optional_static_tls
+@order glibc.malloc.tcache_max
+@order glibc.malloc.check
+
+# Tunables added in RHEL 8.8.0
+@order glibc.rtld.dynamic_sort
diff --git a/sysdeps/unix/sysv/linux/i386/dl-tunables.list b/sysdeps/unix/sysv/linux/i386/dl-tunables.list
new file mode 100644
index 0000000000000000..b9cad4af62d9f2e5
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/dl-tunables.list
@@ -0,0 +1,33 @@
+# Order of tunables in RHEL 8.7.0.
+@order glibc.rtld.nns
+@order glibc.elision.skip_lock_after_retries
+@order glibc.malloc.trim_threshold
+@order glibc.malloc.perturb
+@order glibc.cpu.x86_shared_cache_size
+@order glibc.elision.tries
+@order glibc.elision.enable
+@order glibc.cpu.x86_rep_movsb_threshold
+@order glibc.malloc.mxfast
+@order glibc.elision.skip_lock_busy
+@order glibc.malloc.top_pad
+@order glibc.cpu.x86_rep_stosb_threshold
+@order glibc.cpu.x86_non_temporal_threshold
+@order glibc.cpu.x86_shstk
+@order glibc.cpu.hwcap_mask
+@order glibc.malloc.mmap_max
+@order glibc.elision.skip_trylock_internal_abort
+@order glibc.malloc.tcache_unsorted_limit
+@order glibc.cpu.x86_ibt
+@order glibc.cpu.hwcaps
+@order glibc.elision.skip_lock_internal_abort
+@order glibc.malloc.arena_max
+@order glibc.malloc.mmap_threshold
+@order glibc.cpu.x86_data_cache_size
+@order glibc.malloc.tcache_count
+@order glibc.malloc.arena_test
+@order glibc.rtld.optional_static_tls
+@order glibc.malloc.tcache_max
+@order glibc.malloc.check
+
+# Tunables added in RHEL 8.8.0
+@order glibc.rtld.dynamic_sort
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list
new file mode 100644
index 0000000000000000..ee1e6fca95e1f2da
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list
@@ -0,0 +1,26 @@
+# Order of tunables in RHEL 8.7.0.
+@order glibc.rtld.nns
+@order glibc.elision.skip_lock_after_retries
+@order glibc.malloc.trim_threshold
+@order glibc.malloc.perturb
+@order glibc.elision.tries
+@order glibc.elision.enable
+@order glibc.malloc.mxfast
+@order glibc.elision.skip_lock_busy
+@order glibc.malloc.top_pad
+@order glibc.cpu.hwcap_mask
+@order glibc.malloc.mmap_max
+@order glibc.elision.skip_trylock_internal_abort
+@order glibc.malloc.tcache_unsorted_limit
+@order glibc.elision.skip_lock_internal_abort
+@order glibc.malloc.arena_max
+@order glibc.malloc.mmap_threshold
+@order glibc.cpu.cached_memopt
+@order glibc.malloc.tcache_count
+@order glibc.malloc.arena_test
+@order glibc.rtld.optional_static_tls
+@order glibc.malloc.tcache_max
+@order glibc.malloc.check
+
+# Tunables added in RHEL 8.8.0
+@order glibc.rtld.dynamic_sort
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list b/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list
new file mode 100644
index 0000000000000000..099e28d8f8e67944
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list
@@ -0,0 +1,25 @@
+# Order of tunables in RHEL 8.7.0.
+@order glibc.rtld.nns
+@order glibc.elision.skip_lock_after_retries
+@order glibc.malloc.trim_threshold
+@order glibc.malloc.perturb
+@order glibc.elision.tries
+@order glibc.elision.enable
+@order glibc.malloc.mxfast
+@order glibc.elision.skip_lock_busy
+@order glibc.malloc.top_pad
+@order glibc.cpu.hwcap_mask
+@order glibc.malloc.mmap_max
+@order glibc.elision.skip_trylock_internal_abort
+@order glibc.malloc.tcache_unsorted_limit
+@order glibc.elision.skip_lock_internal_abort
+@order glibc.malloc.arena_max
+@order glibc.malloc.mmap_threshold
+@order glibc.malloc.tcache_count
+@order glibc.malloc.arena_test
+@order glibc.rtld.optional_static_tls
+@order glibc.malloc.tcache_max
+@order glibc.malloc.check
+
+# Tunables added in RHEL 8.8.0
+@order glibc.rtld.dynamic_sort
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list b/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list
new file mode 100644
index 0000000000000000..b9cad4af62d9f2e5
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list
@@ -0,0 +1,33 @@
+# Order of tunables in RHEL 8.7.0.
+@order glibc.rtld.nns
+@order glibc.elision.skip_lock_after_retries
+@order glibc.malloc.trim_threshold
+@order glibc.malloc.perturb
+@order glibc.cpu.x86_shared_cache_size
+@order glibc.elision.tries
+@order glibc.elision.enable
+@order glibc.cpu.x86_rep_movsb_threshold
+@order glibc.malloc.mxfast
+@order glibc.elision.skip_lock_busy
+@order glibc.malloc.top_pad
+@order glibc.cpu.x86_rep_stosb_threshold
+@order glibc.cpu.x86_non_temporal_threshold
+@order glibc.cpu.x86_shstk
+@order glibc.cpu.hwcap_mask
+@order glibc.malloc.mmap_max
+@order glibc.elision.skip_trylock_internal_abort
+@order glibc.malloc.tcache_unsorted_limit
+@order glibc.cpu.x86_ibt
+@order glibc.cpu.hwcaps
+@order glibc.elision.skip_lock_internal_abort
+@order glibc.malloc.arena_max
+@order glibc.malloc.mmap_threshold
+@order glibc.cpu.x86_data_cache_size
+@order glibc.malloc.tcache_count
+@order glibc.malloc.arena_test
+@order glibc.rtld.optional_static_tls
+@order glibc.malloc.tcache_max
+@order glibc.malloc.check
+
+# Tunables added in RHEL 8.8.0
+@order glibc.rtld.dynamic_sort

View File

@ -0,0 +1,81 @@
Move _dl_dso_sort_algo out of _rtld_global_ro. It is only used
locally in elf/dl-sort-maps.c. This avoids changing the internal
_rtld_global_ro ABI.
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index 6f5c17b47b98fbc7..aeb79b40b45054c0 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -290,12 +290,21 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
}
}
+/* DSO sort algorithm to use. */
+enum dso_sort_algorithm
+ {
+ dso_sort_algorithm_original,
+ dso_sort_algorithm_dfs
+ };
+
+static enum dso_sort_algorithm _dl_dso_sort_algo;
+
void
_dl_sort_maps_init (void)
{
int32_t algorithm = TUNABLE_GET (glibc, rtld, dynamic_sort, int32_t, NULL);
- GLRO(dl_dso_sort_algo) = algorithm == 1 ? dso_sort_algorithm_original
- : dso_sort_algorithm_dfs;
+ _dl_dso_sort_algo = (algorithm == 1 ? dso_sort_algorithm_original
+ : dso_sort_algorithm_dfs);
}
void
@@ -309,7 +318,7 @@ _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
PTR_MANGLE/DEMANGLE, further impairing performance of small, common
input cases. A simple if-case with direct function calls appears to
be the fastest. */
- if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original))
+ if (__glibc_likely (_dl_dso_sort_algo == dso_sort_algorithm_original))
_dl_sort_maps_original (maps, nmaps, force_first, for_fini);
else
_dl_sort_maps_dfs (maps, nmaps, force_first, for_fini);
diff --git a/elf/dl-support.c b/elf/dl-support.c
index ae03aec9764e29d3..e9943e889ef447ad 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -155,8 +155,6 @@ size_t _dl_phnum;
uint64_t _dl_hwcap __attribute__ ((nocommon));
uint64_t _dl_hwcap2 __attribute__ ((nocommon));
-enum dso_sort_algorithm _dl_dso_sort_algo;
-
/* The value of the FPU control word the kernel will preset in hardware. */
fpu_control_t _dl_fpu_control = _FPU_DEFAULT;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 2c1b4c47c6a6c643..29bbde3e83e37d7e 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -240,13 +240,6 @@ enum allowmask
};
-/* DSO sort algorithm to use (check dl-sort-maps.c). */
-enum dso_sort_algorithm
- {
- dso_sort_algorithm_original,
- dso_sort_algorithm_dfs
- };
-
struct audit_ifaces
{
void (*activity) (uintptr_t *, unsigned int);
@@ -640,8 +633,6 @@ struct rtld_global_ro
platforms. */
EXTERN uint64_t _dl_hwcap2;
- EXTERN enum dso_sort_algorithm _dl_dso_sort_algo;
-
#ifdef SHARED
/* We add a function table to _rtld_global which is then used to
call the function instead of going through the PLT. The result

View File

@ -17,6 +17,8 @@
set -evx
tar_tmp="$(mktemp)"
declare -A libc_dlink_tmp_list
ldso_annobin_sym_tmp_list=""
# Prefer a separately installed debugedit over the RPM-integrated one.
if command -v debugedit >/dev/null ; then
@ -26,7 +28,7 @@ else
fi
cleanup () {
rm -f "$tar_tmp"
rm -f "$tar_tmp" ${libc_dlink_tmp_list[@]} $ldso_annobin_sym_tmp_list
}
trap cleanup 0
@ -51,6 +53,15 @@ full_list="$ldso_list $libc_list $libdl_list $libpthread_list $librt_list"
# Run the debuginfo extraction.
"$script_path" "$@"
# libc.so.6: Extract the .gnu_debuglink section
for f in $libc_list
do
dlink_tmp="$(mktemp)"
libc_dlink_tmp_list["$f"]="$dlink_tmp"
objcopy -j.gnu_debuglink --set-section-flags .gnu_debuglink=alloc \
-O binary "$sysroot_path/$f" "$dlink_tmp"
done
# Restore the original files.
(cd "$sysroot_path"; tar xf "$tar_tmp")
(cd "$sysroot_path"; ls -l $full_list)
@ -61,6 +72,20 @@ do
objcopy --merge-notes "$sysroot_path/$p"
done
# libc.so.6: Restore the .gnu_debuglink section
for f in ${!libc_dlink_tmp_list[@]}
do
dlink_tmp="${libc_dlink_tmp_list[$f]}"
objcopy --add-section .gnu_debuglink="$dlink_tmp" "$sysroot_path/$f"
done
# ld.so does not have separated debuginfo and so the debuginfo file
# generated by find-debuginfo is redundant. Therefore, remove it.
for ldso_debug in `find "$sysroot_path" -name 'ld-*.so*.debug' -type f`
do
rm -f "$ldso_debug"
done
# libc.so.6 and other shared objects: Reduce to valuable symbols.
# Eliminate file symbols, annobin symbols, and symbols used by the
# glibc build to implement hidden aliases (__EI_*). We would also
@ -103,6 +128,14 @@ debug_base_name=${last_arg:-$RPM_BUILD_ROOT}
for p in $ldso_list
do
$debugedit -b "$debug_base_name" -d "$debug_dest_name" -n "$sysroot_path/$p"
# Remove the .annobin* symbols (and only them).
ldso_annobin_sym_tmp="$(mktemp)"
ldso_annobin_sym_tmp_list+=" $ldso_annobin_sym_tmp"
if nm --format=posix "$sysroot_path/$p" | cut -d' ' -f1 \
| grep '^\.annobin' > "$ldso_annobin_sym_tmp"; then
objcopy --strip-symbols="$ldso_annobin_sym_tmp" "$sysroot_path/$p"
fi
done
# Apply single-file DWARF optimization.

View File

@ -1,6 +1,6 @@
%define glibcsrcdir glibc-2.28
%define glibcversion 2.28
%define glibcrelease 211%{?dist}
%define glibcrelease 225%{?dist}
# Pre-release tarballs are pulled in from git using a command that is
# effectively:
#
@ -968,6 +968,69 @@ Patch775: glibc-rh2104907.patch
Patch776: glibc-rh2119304-1.patch
Patch777: glibc-rh2119304-2.patch
Patch778: glibc-rh2119304-3.patch
Patch779: glibc-rh2118667.patch
Patch780: glibc-rh2122498.patch
Patch781: glibc-rh2125222.patch
Patch782: glibc-rh1871383-1.patch
Patch783: glibc-rh1871383-2.patch
Patch784: glibc-rh1871383-3.patch
Patch785: glibc-rh1871383-4.patch
Patch786: glibc-rh1871383-5.patch
Patch787: glibc-rh1871383-6.patch
Patch788: glibc-rh1871383-7.patch
Patch789: glibc-rh2122501-1.patch
Patch790: glibc-rh2122501-2.patch
Patch791: glibc-rh2122501-3.patch
Patch792: glibc-rh2122501-4.patch
Patch793: glibc-rh2122501-5.patch
Patch794: glibc-rh2121746-1.patch
Patch795: glibc-rh2121746-2.patch
Patch796: glibc-rh2116938.patch
Patch797: glibc-rh2109510-1.patch
Patch798: glibc-rh2109510-2.patch
Patch799: glibc-rh2109510-3.patch
Patch800: glibc-rh2109510-4.patch
Patch801: glibc-rh2109510-5.patch
Patch802: glibc-rh2109510-6.patch
Patch803: glibc-rh2109510-7.patch
Patch804: glibc-rh2109510-8.patch
Patch805: glibc-rh2109510-9.patch
Patch806: glibc-rh2109510-10.patch
Patch807: glibc-rh2109510-11.patch
Patch808: glibc-rh2109510-12.patch
Patch809: glibc-rh2109510-13.patch
Patch810: glibc-rh2109510-14.patch
Patch811: glibc-rh2109510-15.patch
Patch812: glibc-rh2109510-16.patch
Patch813: glibc-rh2109510-17.patch
Patch814: glibc-rh2109510-18.patch
Patch815: glibc-rh2109510-19.patch
Patch816: glibc-rh2109510-20.patch
Patch817: glibc-rh2109510-21.patch
Patch818: glibc-rh2109510-22.patch
Patch819: glibc-rh2109510-23.patch
Patch820: glibc-rh2139875-1.patch
Patch821: glibc-rh2139875-2.patch
Patch822: glibc-rh2139875-3.patch
Patch823: glibc-rh1159809-1.patch
Patch824: glibc-rh1159809-2.patch
Patch825: glibc-rh1159809-3.patch
Patch826: glibc-rh1159809-4.patch
Patch827: glibc-rh1159809-5.patch
Patch828: glibc-rh1159809-6.patch
Patch829: glibc-rh1159809-7.patch
Patch830: glibc-rh1159809-8.patch
Patch831: glibc-rh1159809-9.patch
Patch832: glibc-rh1159809-10.patch
Patch833: glibc-rh1159809-11.patch
Patch834: glibc-rh1159809-12.patch
Patch835: glibc-rh2141989.patch
Patch836: glibc-rh2142937-1.patch
Patch837: glibc-rh2142937-2.patch
Patch838: glibc-rh2142937-3.patch
Patch839: glibc-rh2144568.patch
Patch840: glibc-rh2154914-1.patch
Patch841: glibc-rh2154914-2.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -2798,6 +2861,52 @@ fi
%files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
%changelog
* Fri Jan 20 2023 Florian Weimer <fweimer@redhat.com> - 2.28-225
- Enforce a specififc internal ordering for tunables (#2154914)
* Wed Nov 30 2022 Arjun Shankar <arjun@redhat.com> - 2.28-224
- Fix rtld-audit trampoline for aarch64 (#2144568)
* Fri Nov 25 2022 Arjun Shankar <arjun@redhat.com> - 2.28-223
- Backport upstream fixes to tst-pldd (#2142937)
* Tue Nov 22 2022 Florian Weimer <fweimer@redhat.com> - 2.28-222
- Restore IPC_64 support in sysvipc *ctl functions (#2141989)
* Fri Nov 18 2022 Florian Weimer <fweimer@redhat.com> - 2.28-221
- Switch to fast DSO dependency sorting algorithm (#1159809)
* Thu Nov 3 2022 Florian Weimer <fweimer@redhat.com> - 2.28-220
- Explicitly switch to --with-default-link=no (#2109510)
- Define MAP_SYNC on ppc64le (#2139875)
* Mon Oct 24 2022 Arjun Shankar <arjun@redhat.com> - 2.28-219
- Fix -Wstrict-overflow warning when using CMSG_NXTHDR macro (#2116938)
* Fri Oct 14 2022 DJ Delorie <dj@redhat.com> - 2.28-218
- Fix dlmopen/dlclose/dlmopen sequence and libc initialization (#2121746)
* Thu Oct 13 2022 Arjun Shankar <arjun@redhat.com> - 2.28-217
- Fix memory corruption in printf with thousands separators and large
integer width (#2122501)
* Wed Oct 05 2022 Arjun Shankar <arjun@redhat.com> - 2.28-216
- Retain .gnu_debuglink section for libc.so.6 (#2115830)
- Remove .annobin* symbols from ld.so
- Remove redundant ld.so debuginfo file
* Wed Sep 28 2022 DJ Delorie <dj@redhat.com> - 2.28-215
- Improve malloc implementation (#1871383)
* Tue Sep 20 2022 Florian Weimer <fweimer@redhat.com> - 2.28-214
- Fix hwcaps search path size computation (#2125222)
* Tue Sep 20 2022 Florian Weimer <fweimer@redhat.com> - 2.28-213
- Fix nscd netlink cache invalidation if epoll is used (#2122498)
* Tue Sep 20 2022 Florian Weimer <fweimer@redhat.com> - 2.28-212
- Run tst-audit-tlsdesc, tst-audit-tlsdesc-dlopen everywhere (#2118667)
* Thu Aug 25 2022 Florian Weimer <fweimer@redhat.com> - 2.28-211
- Preserve GLRO (dl_naudit) internal ABI (#2119304)
- Avoid s390x ABI change due to z16 recognition on s390x (#2119304)