powerpc: Fix stack buffer overflow in _dl_reloc_overflow/_dl_error_localentry (bug 34541)

Consolidate the 32-bit and 64-bit implementation and use
_dl_exception_create_format to create an exception message of the
correct size.

Co-authored-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
Florian Weimer
2026-09-08 17:24:58 -03:00
committed by Adhemerval Zanella
co-authored by Adhemerval Zanella
parent 76c3d4472f
commit a9d335d922
18 changed files with 396 additions and 75 deletions
+2
View File
@@ -130,12 +130,14 @@ _dl_exception_create_format (struct dl_exception *exception, const char *objname
case 'z':
if (p[1] == 'x')
{
va_arg (ap, unsigned long int);
length += LONG_WIDTH / 4;
++p;
break;
}
[[fallthrough]];
case 'x':
va_arg (ap, unsigned int);
length += INT_WIDTH / 4;
break;
default:
+5
View File
@@ -90,6 +90,11 @@ do_test (void)
TEST_LONG ("fffffffe", "test", "%zx", (size_t)~1ul);
TEST_LONG ("fffffffe-test", "test", "%zx-test", (size_t)~1ul);
TEST ("0000007b-test", NULL, "%x-%s", 123, "test");
TEST ("0000007b-test-000001c8", NULL, "%x-%s-%x", 123, "test", 456);
TEST_LONG ("fffffffd-test", NULL, "%lx-%s", (long int)~2ul, "test");
TEST_LONG ("fffffffe-test", NULL, "%zx-%s", (size_t)~1ul, "test");
struct support_capture_subprocess result;
result = support_capture_subprocess (do_test_invalid_conversion, NULL);
support_capture_subprocess_check (&result, "dl-exception",
+7 -1
View File
@@ -4,7 +4,7 @@ endif
ifeq ($(subdir),elf)
# extra shared linker files to link into dl-allobjs.so and libc
sysdep-dl-routines += dl-machine hwcapinfo
sysdep-dl-routines += dl-machine dl-reloc_overflow hwcapinfo
sysdep_routines += dl-machine hwcapinfo
# extra shared linker files to link only into dl-allobjs.so
sysdep-rtld-routines += dl-machine hwcapinfo
@@ -14,6 +14,12 @@ mod-tlsopt-powerpc.so-no-z-defs = yes
tests += tst-tlsopt-powerpc
$(objpfx)tst-tlsopt-powerpc: $(objpfx)mod-tlsopt-powerpc.so
tests += tst-reloc-overflow
modules-names += tst-reloc-overflow-mod tst-reloc-overflow-lib
$(objpfx)tst-reloc-overflow: $(libdl)
$(objpfx)tst-reloc-overflow.out: $(objpfx)tst-reloc-overflow-mod.so
$(objpfx)tst-reloc-overflow-mod.so: $(objpfx)tst-reloc-overflow-lib.so
tests-static += tst-cache-ppc-static
tests-internal += tst-cache-ppc-static
+41
View File
@@ -0,0 +1,41 @@
/* Relocation overflow reporting for POWER.
Copyright (C) 1995-2026 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 Library General Public License as
published by the Free Software Foundation; either version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, see <https://www.gnu.org/licenses/>. */
#include <ldsodefs.h>
#include <dl-machine.h>
void
_dl_reloc_overflow (struct link_map *map, const char *name,
ElfW(Addr) *const reloc_addr, const ElfW(Sym) *refsym)
{
struct dl_exception exc;
if (refsym != NULL)
{
const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
_dl_exception_create_format
(&exc, map->l_name, "%s reloc at 0x%lx for symbol `%s' out of range",
name, (unsigned long int) reloc_addr, strtab + refsym->st_name);
}
else
_dl_exception_create_format
(&exc, map->l_name, "%s reloc at 0x%lx out of range",
name, (unsigned long int) reloc_addr);
_dl_signal_exception (0, &exc, NULL);
}
-25
View File
@@ -23,7 +23,6 @@
#include <ldsodefs.h>
#include <elf/dynamic-link.h>
#include <dl-machine.h>
#include <_itoa.h>
/* Stuff for the PLT. */
#define PLT_INITIAL_ENTRY_WORDS 18
@@ -377,30 +376,6 @@ __elf_machine_fixup_plt (struct link_map *map,
return finaladdr;
}
void
_dl_reloc_overflow (struct link_map *map,
const char *name,
Elf32_Addr *const reloc_addr,
const Elf32_Sym *refsym)
{
char buffer[128];
char *t;
t = stpcpy (buffer, name);
t = stpcpy (t, " relocation at 0x00000000");
_itoa_word ((unsigned) reloc_addr, t, 16, 0);
if (refsym)
{
const char *strtab;
strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
t = stpcpy (t, " for symbol `");
t = stpcpy (t, strtab + refsym->st_name);
t = stpcpy (t, "'");
}
t = stpcpy (t, " out of range");
_dl_signal_error (0, map->l_name, NULL, buffer);
}
void
__process_machine_rela (struct link_map *map,
const Elf32_Rela *reloc,
+6 -6
View File
@@ -261,6 +261,12 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
#define ARCH_LA_PLTENTER ppc32_gnu_pltenter
#define ARCH_LA_PLTEXIT ppc32_gnu_pltexit
/* Call _dl_signal_error when a resolved value overflows a relocated area. */
extern void attribute_hidden _dl_reloc_overflow (struct link_map *map,
const char *name,
ElfW(Addr) *const reloc_addr,
const ElfW(Sym) *refsym);
#endif /* dl_machine_h */
#ifdef RESOLVE_MAP
@@ -277,12 +283,6 @@ extern void __process_machine_rela (struct link_map *map,
int rinfo, bool skip_ifunc)
attribute_hidden;
/* Call _dl_signal_error when a resolved value overflows a relocated area. */
extern void _dl_reloc_overflow (struct link_map *map,
const char *name,
Elf32_Addr *const reloc_addr,
const Elf32_Sym *refsym) attribute_hidden;
/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
LOADADDR is the load address of the object; INFO is an array indexed
by DT_* of the .dynamic section info. */
+5 -38
View File
@@ -16,51 +16,18 @@
License along with the GNU C Library; see the file COPYING.LIB. If
not, see <https://www.gnu.org/licenses/>. */
#include <string.h>
#include <unistd.h>
#include <ldsodefs.h>
#include <_itoa.h>
#include <dl-machine.h>
void
_dl_reloc_overflow (struct link_map *map,
const char *name,
Elf64_Addr *const reloc_addr,
const Elf64_Sym *refsym)
{
char buffer[1024];
char *t;
t = stpcpy (buffer, name);
/* Notice that _itoa_word() writes characters from the higher address to the
lower address, requiring the destination string to reserve all the
required size before the call. */
t = stpcpy (t, " reloc at 0x0000000000000000");
_itoa_word ((unsigned long) reloc_addr, t, 16, 0);
if (refsym)
{
const char *strtab;
strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
t = stpcpy (t, " for symbol `");
t = stpcpy (t, strtab + refsym->st_name);
t = stpcpy (t, "'");
}
t = stpcpy (t, " out of range");
_dl_signal_error (0, map->l_name, NULL, buffer);
}
#if _CALL_ELF == 2
void
_dl_error_localentry (struct link_map *map, const Elf64_Sym *refsym)
{
char buffer[1024];
char *t;
const char *strtab;
const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
t = stpcpy (buffer, "expected localentry:0 `");
t = stpcpy (t, strtab + refsym->st_name);
t = stpcpy (t, "'");
_dl_signal_error (0, map->l_name, NULL, buffer);
struct dl_exception exc;
_dl_exception_create_format (&exc, map->l_name, "expected localentry:0 `%s'",
strtab + refsym->st_name);
_dl_signal_exception (0, &exc, NULL);
}
#endif
+6 -5
View File
@@ -590,6 +590,12 @@ ppc_init_fake_thread_pointer (void)
ppc_init_fake_thread_pointer ();
#endif /* ENABLE_STATIC_PIE && !defined SHARED && !IS_IN (rtld) */
/* Call _dl_signal_error when a resolved value overflows a relocated area. */
extern void attribute_hidden _dl_reloc_overflow (struct link_map *map,
const char *name,
ElfW(Addr) *const reloc_addr,
const ElfW(Sym) *refsym);
#endif /* dl_machine_h */
#ifdef RESOLVE_MAP
@@ -606,11 +612,6 @@ ppc_init_fake_thread_pointer (void)
#define dont_expect(X) __builtin_expect ((X), 0)
extern void attribute_hidden _dl_reloc_overflow (struct link_map *map,
const char *name,
Elf64_Addr *const reloc_addr,
const Elf64_Sym *refsym);
static inline void __attribute__ ((always_inline))
elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc,
void *const reloc_addr_arg)
+14
View File
@@ -237,4 +237,18 @@ ifeq (no,$(build-hardcoded-path-in-tests))
tests-container += tst-glibc-hwcaps-cache
endif
tests += tst-localentry
modules-names += tst-localentry-mod tst-localentry-callee \
tst-localentry-callee-v0
# Both callees share a soname, so the module records a DT_NEEDED on
# tst-localentry-callee.so while being linked against the v0 definition.
LDFLAGS-tst-localentry-callee-v0.so += -Wl,-soname,tst-localentry-callee.so
LDFLAGS-tst-localentry-mod.so += -Wl,--plt-localentry
$(objpfx)tst-localentry: $(libdl)
$(objpfx)tst-localentry.out: $(objpfx)tst-localentry-mod.so \
$(objpfx)tst-localentry-callee.so
$(objpfx)tst-localentry-mod.so: $(objpfx)tst-localentry-callee-v0.so
endif # $(subdir) == elf
@@ -0,0 +1,36 @@
/* Callee with a local entry point of zero, used at link time.
Copyright (C) 2026 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 "tst-localentry-name.h"
/* The modules required st_other to have localentry:0, because that is what
makes ld optimize the module's call under --plt-localentry.
This is written in assembly to assure the code generation decision
regardless of compiler flags (like stack protection) which change how
the leaf function returns the value. */
.abiversion 2
.section ".text"
.globl LOCALENTRY_SYMBOL
.type LOCALENTRY_SYMBOL, @function
LOCALENTRY_SYMBOL:
.localentry LOCALENTRY_SYMBOL, 0
li 3, 42
blr
.size LOCALENTRY_SYMBOL, .-LOCALENTRY_SYMBOL
@@ -0,0 +1,35 @@
/* Callee with a non-zero local entry point, substituted at run time.
Copyright (C) 2026 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 "tst-localentry-name.h"
/* The same symbol with a global entry sequence that sets up r2, so st_other
says localentry:8. Resolving the module's optimized call to this
definition is the ABI violation ld.so has to reject. */
.abiversion 2
.section ".text"
.globl LOCALENTRY_SYMBOL
.type LOCALENTRY_SYMBOL, @function
LOCALENTRY_SYMBOL:
0: addis 2, 12, .TOC.-0b@ha
addi 2, 2, .TOC.-0b@l
.localentry LOCALENTRY_SYMBOL, .-LOCALENTRY_SYMBOL
li 3, 43
blr
.size LOCALENTRY_SYMBOL, .-LOCALENTRY_SYMBOL
@@ -0,0 +1,27 @@
/* Module whose PLT call was optimized for a localentry:0 callee.
Copyright (C) 2026 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 "tst-localentry-name.h"
extern int LOCALENTRY_SYMBOL (void);
int
call_it (void)
{
return LOCALENTRY_SYMBOL ();
}
@@ -0,0 +1,41 @@
/* Overly long symbol name shared by the local entry point test.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _TST_LOCALENTRY_NAME_H
#define _TST_LOCALENTRY_NAME_H 1
#define CAT_(a, b) a##b
#define CAT(a, b) CAT_(a, b)
#define N16 xxxxxxxxxxxxxxxx
#define N32 CAT (N16, N16)
#define N64 CAT (N32, N32)
#define N128 CAT (N64, N64)
#define N256 CAT (N128, N128)
#define N512 CAT (N256, N256)
#define N1024 CAT (N512, N512)
#define N2048 CAT (N1024, N1024)
#define N4096 CAT (N2048, N2048)
#define N8192 CAT (N4096, N4096)
#define LOCALENTRY_SYMBOL CAT (tst_localentry_symbol_, N8192)
#define STR_(x) #x
#define STR(x) STR_(x)
#endif
@@ -0,0 +1,40 @@
/* Check the message for a local entry point mismatch (bug 34541).
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <string.h>
#include <support/check.h>
#include "tst-localentry-name.h"
static int
do_test (void)
{
void *h = dlopen ("tst-localentry-mod.so", RTLD_NOW);
TEST_VERIFY (h == NULL);
const char *msg = dlerror ();
TEST_VERIFY_EXIT (msg != NULL);
TEST_VERIFY (strstr (msg, STR (LOCALENTRY_SYMBOL)) != NULL);
TEST_VERIFY (strstr (msg, "expected localentry:0") != NULL);
return 0;
}
#include <support/test-driver.c>
+21
View File
@@ -0,0 +1,21 @@
/* Definition of the symbol the relocation overflow test refers to.
Copyright (C) 2026 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 "tst-reloc-overflow-name.h"
int OVERFLOW_SYMBOL = 42;
+29
View File
@@ -0,0 +1,29 @@
/* Module with an out of range R_PPC*_ADDR16 relocation.
Copyright (C) 2026 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 "tst-reloc-overflow-name.h"
/* _dl_reloc_overflow is only reached for relocation types that store an
address into a field narrower than a pointer. */
.data
.globl refword
.type refword, @object
refword:
.short OVERFLOW_SYMBOL
.size refword, 2
+41
View File
@@ -0,0 +1,41 @@
/* Overly long symbol name shared by the relocation overflow test.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _TST_RELOC_OVERFLOW_NAME_H
#define _TST_RELOC_OVERFLOW_NAME_H 1
#define CAT_(a, b) a##b
#define CAT(a, b) CAT_(a, b)
#define N16 xxxxxxxxxxxxxxxx
#define N32 CAT (N16, N16)
#define N64 CAT (N32, N32)
#define N128 CAT (N64, N64)
#define N256 CAT (N128, N128)
#define N512 CAT (N256, N256)
#define N1024 CAT (N512, N512)
#define N2048 CAT (N1024, N1024)
#define N4096 CAT (N2048, N2048)
#define N8192 CAT (N4096, N4096)
#define OVERFLOW_SYMBOL CAT (tst_reloc_overflow_symbol_, N8192)
#define STR_(x) #x
#define STR(x) STR_(x)
#endif
+40
View File
@@ -0,0 +1,40 @@
/* Check the message for an out of range relocation (bug 34541).
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <string.h>
#include <support/check.h>
#include "tst-reloc-overflow-name.h"
static int
do_test (void)
{
void *h = dlopen ("tst-reloc-overflow-mod.so", RTLD_NOW);
TEST_VERIFY (h == NULL);
const char *msg = dlerror ();
TEST_VERIFY_EXIT (msg != NULL);
TEST_VERIFY (strstr (msg, STR (OVERFLOW_SYMBOL)) != NULL);
TEST_VERIFY (strstr (msg, "out of range") != NULL);
return 0;
}
#include <support/test-driver.c>