mirror of git://sourceware.org/git/glibc.git
debug: Improve '%n' fortify detection (BZ 30932)
The 7bb8045ec0
path made the '%n' fortify check ignore EMFILE errors
while trying to open /proc/self/maps, and this added a security
issue where EMFILE can be attacker-controlled thus making it
ineffective for some cases.
The EMFILE failure is reinstated but with a different error
message. Also, to improve the false positive of the hardening for
the cases where no new files can be opened, the
_dl_readonly_area now uses _dl_find_object to check if the
memory area is within a writable ELF segment. The procfs method is
still used as fallback.
Checked on x86_64-linux-gnu and i686-linux-gnu.
Reviewed-by: Arjun Shankar <arjun@redhat.com>
This commit is contained in:
parent
1894e219dc
commit
ed6a68bac7
|
@ -633,7 +633,7 @@ link-libc-printers-tests = $(link-libc-rpath) \
|
||||||
$(link-libc-tests-after-rpath-link)
|
$(link-libc-tests-after-rpath-link)
|
||||||
|
|
||||||
# This is how to find at build-time things that will be installed there.
|
# This is how to find at build-time things that will be installed there.
|
||||||
rpath-dirs = math elf dlfcn nss nis rt resolv mathvec support misc
|
rpath-dirs = math elf dlfcn nss nis rt resolv mathvec support misc debug
|
||||||
rpath-link = \
|
rpath-link = \
|
||||||
$(common-objdir):$(subst $(empty) ,:,$(patsubst ../$(subdir),.,$(rpath-dirs:%=$(common-objpfx)%)))
|
$(common-objdir):$(subst $(empty) ,:,$(patsubst ../$(subdir),.,$(rpath-dirs:%=$(common-objpfx)%)))
|
||||||
else # build-static
|
else # build-static
|
||||||
|
|
|
@ -75,6 +75,7 @@ routines = \
|
||||||
readlink_chk \
|
readlink_chk \
|
||||||
readlinkat_chk \
|
readlinkat_chk \
|
||||||
readonly-area \
|
readonly-area \
|
||||||
|
readonly-area-fallback \
|
||||||
realpath_chk \
|
realpath_chk \
|
||||||
recv_chk \
|
recv_chk \
|
||||||
recvfrom_chk \
|
recvfrom_chk \
|
||||||
|
@ -180,9 +181,15 @@ CPPFLAGS-tst-longjmp_chk3.c += $(no-fortify-source) -D_FORTIFY_SOURCE=1
|
||||||
CPPFLAGS-tst-realpath-chk.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
CPPFLAGS-tst-realpath-chk.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
CPPFLAGS-tst-chk-cancel.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
CPPFLAGS-tst-chk-cancel.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
CFLAGS-tst-sprintf-fortify-rdonly.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
CFLAGS-tst-sprintf-fortify-rdonly.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
|
CFLAGS-tst-sprintf-fortify-rdonly-mod.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
|
CFLAGS-tst-sprintf-fortify-rdonly-dlopen.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||||
|
|
||||||
|
$(objpfx)tst-sprintf-fortify-rdonly: \
|
||||||
|
$(objpfx)tst-sprintf-fortify-rdonly-mod.so \
|
||||||
|
$(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||||
|
|
||||||
# _FORTIFY_SOURCE tests.
|
# _FORTIFY_SOURCE tests.
|
||||||
# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
|
# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
|
||||||
# preprocessor conditions based on tst-fortify.c.
|
# preprocessor conditions based on tst-fortify.c.
|
||||||
|
@ -303,6 +310,11 @@ tests-container += \
|
||||||
tst-fortify-syslog \
|
tst-fortify-syslog \
|
||||||
# tests-container
|
# tests-container
|
||||||
|
|
||||||
|
modules-names += \
|
||||||
|
tst-sprintf-fortify-rdonly-dlopen \
|
||||||
|
tst-sprintf-fortify-rdonly-mod \
|
||||||
|
# modules-names
|
||||||
|
|
||||||
ifeq ($(have-ssp),yes)
|
ifeq ($(have-ssp),yes)
|
||||||
tests += tst-ssp-1
|
tests += tst-ssp-1
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -16,18 +16,19 @@
|
||||||
<https://www.gnu.org/licenses/>. */
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <ldsodefs.h>
|
||||||
|
|
||||||
/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
|
enum readonly_error_type
|
||||||
Return -1 if it is writable. */
|
|
||||||
|
|
||||||
int
|
|
||||||
__readonly_area (const void *ptr, size_t size)
|
__readonly_area (const void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
/* We cannot determine in general whether memory is writable or not.
|
switch (GLRO(dl_readonly_area (ptr, size)))
|
||||||
This must be handled in a system-dependent manner. to not
|
{
|
||||||
unconditionally break code we need to return here a positive
|
case dl_readonly_area_rdonly:
|
||||||
answer. This disables this security measure but that is the
|
return readonly_noerror;
|
||||||
price people have to pay for using systems without a real
|
case dl_readonly_area_writable:
|
||||||
implementation of this interface. */
|
return readonly_area_writable;
|
||||||
return 1;
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return __readonly_area_fallback (ptr, size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
#include "tst-sprintf-fortify-rdonly-mod.c"
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* Testcase for BZ 30932.
|
||||||
|
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static const char *str2 = "F";
|
||||||
|
static char writeable_format[10] = "%s";
|
||||||
|
static char relro_format[10] __attribute__ ((section (".data.rel.ro"))) =
|
||||||
|
"%s%n%s%n";
|
||||||
|
|
||||||
|
void
|
||||||
|
init_writable (void)
|
||||||
|
{
|
||||||
|
strcpy (writeable_format + 2, "%n%s%n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sprintf_writable (int *n1, int *n2)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
return sprintf (buf, writeable_format, str2, n1, str2, n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sprintf_relro (int *n1, int *n2)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
return sprintf (buf, relro_format, str2, n1, str2, n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sprintf_writable_malloc (int *n1, int *n2)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
char *buf2_malloc = strdup (writeable_format);
|
||||||
|
if (buf2_malloc == NULL)
|
||||||
|
abort ();
|
||||||
|
return sprintf (buf, buf2_malloc, str2, n1, str2, n2);
|
||||||
|
}
|
|
@ -27,16 +27,64 @@
|
||||||
#include <support/check.h>
|
#include <support/check.h>
|
||||||
#include <support/support.h>
|
#include <support/support.h>
|
||||||
#include <support/temp_file.h>
|
#include <support/temp_file.h>
|
||||||
|
#include <support/xdlfcn.h>
|
||||||
|
|
||||||
jmp_buf chk_fail_buf;
|
static sigjmp_buf chk_fail_buf;
|
||||||
bool chk_fail_ok;
|
static volatile int ret;
|
||||||
|
static bool chk_fail_ok;
|
||||||
|
|
||||||
const char *str2 = "F";
|
static void
|
||||||
char buf2[10] = "%s";
|
handler (int sig)
|
||||||
|
{
|
||||||
|
if (chk_fail_ok)
|
||||||
|
{
|
||||||
|
chk_fail_ok = false;
|
||||||
|
longjmp (chk_fail_buf, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_exit (127);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FORTIFY_FAIL \
|
||||||
|
do { printf ("Failure on line %d\n", __LINE__); ret = 1; } while (0)
|
||||||
|
#define CHK_FAIL_START \
|
||||||
|
chk_fail_ok = true; \
|
||||||
|
if (! sigsetjmp (chk_fail_buf, 1)) \
|
||||||
|
{
|
||||||
|
#define CHK_FAIL_END \
|
||||||
|
chk_fail_ok = false; \
|
||||||
|
FORTIFY_FAIL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *str2 = "F";
|
||||||
|
static char writeable_format[10] = "%s";
|
||||||
|
static char relro_format[10] __attribute__ ((section (".data.rel.ro"))) =
|
||||||
|
"%s%n%s%n";
|
||||||
|
|
||||||
|
extern void init_writable (void);
|
||||||
|
extern int sprintf_writable (int *, int *);
|
||||||
|
extern int sprintf_relro (int *, int *);
|
||||||
|
extern int sprintf_writable_malloc (int *, int *);
|
||||||
|
|
||||||
|
#define str(__x) # __x
|
||||||
|
void (*init_writable_dlopen)(void);
|
||||||
|
int (*sprintf_writable_dlopen)(int *, int *);
|
||||||
|
int (*sprintf_rdonly_dlopen)(int *, int *);
|
||||||
|
int (*sprintf_writable_malloc_dlopen)(int *, int *);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
do_test (void)
|
do_test (void)
|
||||||
{
|
{
|
||||||
|
set_fortify_handler (handler);
|
||||||
|
|
||||||
|
{
|
||||||
|
void *h = xdlopen ("tst-sprintf-fortify-rdonly-dlopen.so", RTLD_NOW);
|
||||||
|
init_writable_dlopen = xdlsym (h, str(init_writable));
|
||||||
|
sprintf_writable_dlopen = xdlsym (h, str(sprintf_writable));
|
||||||
|
sprintf_rdonly_dlopen = xdlsym (h, str(sprintf_relro));
|
||||||
|
sprintf_writable_malloc_dlopen = xdlsym (h, str(sprintf_writable_malloc));
|
||||||
|
}
|
||||||
|
|
||||||
struct rlimit rl;
|
struct rlimit rl;
|
||||||
int max_fd = 24;
|
int max_fd = 24;
|
||||||
|
|
||||||
|
@ -63,20 +111,94 @@ do_test (void)
|
||||||
}
|
}
|
||||||
TEST_VERIFY_EXIT (nfiles != 0);
|
TEST_VERIFY_EXIT (nfiles != 0);
|
||||||
|
|
||||||
/* When the format string is writable and contains %n,
|
strcpy (writeable_format + 2, "%n%s%n");
|
||||||
with -D_FORTIFY_SOURCE=2 it causes __chk_fail. However, if libc can not
|
init_writable ();
|
||||||
open procfs to check if the input format string in within a writable
|
init_writable_dlopen ();
|
||||||
memory segment, the fortify version can not perform the check. */
|
|
||||||
char buf[128];
|
|
||||||
int n1;
|
|
||||||
int n2;
|
|
||||||
|
|
||||||
strcpy (buf2 + 2, "%n%s%n");
|
/* writeable_format is at a writable part of .bss segment, so libc should be
|
||||||
if (sprintf (buf, buf2, str2, &n1, str2, &n2) != 2
|
able to check it without resorting to procfs. */
|
||||||
|| n1 != 1 || n2 != 2)
|
{
|
||||||
FAIL_EXIT1 ("sprintf failed: %s %d %d", buf, n1, n2);
|
char buf[128];
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
CHK_FAIL_START
|
||||||
|
sprintf (buf, writeable_format, str2, &n1, str2, &n2);
|
||||||
|
CHK_FAIL_END
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
/* Same as before, but from an library. */
|
||||||
|
{
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
CHK_FAIL_START
|
||||||
|
sprintf_writable (&n1, &n2);
|
||||||
|
CHK_FAIL_END
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
CHK_FAIL_START
|
||||||
|
sprintf_writable_dlopen (&n1, &n2);
|
||||||
|
CHK_FAIL_END
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relro_format is at a readonly part of .bss segment, so '%n' in format input
|
||||||
|
should not trigger a fortify failure. */
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
if (sprintf (buf, relro_format, str2, &n1, str2, &n2) != 2
|
||||||
|
|| n1 != 1 || n2 != 2)
|
||||||
|
FAIL_EXIT1 ("sprintf failed: %s %d %d", buf, n1, n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same as before, but from an library. */
|
||||||
|
{
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
if (sprintf_relro (&n1, &n2) != 2 || n1 != 1 || n2 != 2)
|
||||||
|
FAIL_EXIT1 ("sprintf failed: %d %d", n1, n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
if (sprintf_rdonly_dlopen (&n1, &n2) != 2 || n1 != 1 || n2 != 2)
|
||||||
|
FAIL_EXIT1 ("sprintf failed: %d %d", n1, n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* However if the format string is placed on a writable memory not covered
|
||||||
|
by ELF segments, libc needs to resort to procfs. */
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
char *buf2_malloc = xstrdup (writeable_format);
|
||||||
|
CHK_FAIL_START
|
||||||
|
sprintf (buf, buf2_malloc, str2, &n1, str2, &n2);
|
||||||
|
CHK_FAIL_END
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same as before, but from an library. */
|
||||||
|
{
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
CHK_FAIL_START
|
||||||
|
sprintf_writable_malloc (&n1, &n2);
|
||||||
|
CHK_FAIL_END
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int n1;
|
||||||
|
int n2;
|
||||||
|
CHK_FAIL_START
|
||||||
|
sprintf_writable_malloc_dlopen (&n1, &n2);
|
||||||
|
CHK_FAIL_END
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <support/test-driver.c>
|
#include <support/test-driver.c>
|
||||||
|
|
|
@ -72,6 +72,7 @@ dl-routines = \
|
||||||
dl-open \
|
dl-open \
|
||||||
dl-origin \
|
dl-origin \
|
||||||
dl-printf \
|
dl-printf \
|
||||||
|
dl-readonly-area \
|
||||||
dl-reloc \
|
dl-reloc \
|
||||||
dl-runtime \
|
dl-runtime \
|
||||||
dl-scope \
|
dl-scope \
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* Check if range is within a read-only from a loaded ELF object.
|
||||||
|
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <ldsodefs.h>
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check_relro (const struct link_map *l, uintptr_t start, uintptr_t end)
|
||||||
|
{
|
||||||
|
if (l->l_relro_addr != 0)
|
||||||
|
{
|
||||||
|
uintptr_t relro_start = ALIGN_DOWN (l->l_addr + l->l_relro_addr,
|
||||||
|
GLRO(dl_pagesize));
|
||||||
|
uintptr_t relro_end = ALIGN_DOWN (l->l_addr + l->l_relro_addr
|
||||||
|
+ l->l_relro_size,
|
||||||
|
GLRO(dl_pagesize));
|
||||||
|
/* RELRO is caved out from a RW segment, so the next range is either
|
||||||
|
RW or nonexistent. */
|
||||||
|
return relro_start <= start && end <= relro_end
|
||||||
|
? dl_readonly_area_rdonly : dl_readonly_area_writable;
|
||||||
|
|
||||||
|
}
|
||||||
|
return dl_readonly_area_writable;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum dl_readonly_area_error_type
|
||||||
|
_dl_readonly_area (const void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
struct dl_find_object dlfo;
|
||||||
|
if (_dl_find_object ((void *)ptr, &dlfo) != 0)
|
||||||
|
return dl_readonly_area_not_found;
|
||||||
|
|
||||||
|
const struct link_map *l = dlfo.dlfo_link_map;
|
||||||
|
uintptr_t ptr_start = (uintptr_t) ptr;
|
||||||
|
uintptr_t ptr_end = ptr_start + size;
|
||||||
|
|
||||||
|
for (const ElfW(Phdr) *ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
|
||||||
|
if (ph->p_type == PT_LOAD)
|
||||||
|
{
|
||||||
|
/* For segments with alignment larger than the page size,
|
||||||
|
_dl_map_segment allocates additional space that is mark as
|
||||||
|
PROT_NONE (so we can ignore). */
|
||||||
|
uintptr_t from = l->l_addr
|
||||||
|
+ ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize));
|
||||||
|
uintptr_t to = l->l_addr
|
||||||
|
+ ALIGN_UP (ph->p_vaddr + ph->p_filesz, GLRO(dl_pagesize));
|
||||||
|
|
||||||
|
/* Found an entry that at least partially covers the area. */
|
||||||
|
if (from < ptr_end && to > ptr_start)
|
||||||
|
{
|
||||||
|
if (ph->p_flags & PF_W)
|
||||||
|
return check_relro (l, ptr_start, ptr_end);
|
||||||
|
|
||||||
|
if ((ph->p_flags & PF_R) == 0)
|
||||||
|
return dl_readonly_area_writable;
|
||||||
|
|
||||||
|
if (from <= ptr_start && to >= ptr_end)
|
||||||
|
return dl_readonly_area_rdonly;
|
||||||
|
else if (from <= ptr_start)
|
||||||
|
size -= to - ptr_start;
|
||||||
|
else if (to >= ptr_end)
|
||||||
|
size -= ptr_end - from;
|
||||||
|
else
|
||||||
|
size -= to - from;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size == 0 ? dl_readonly_area_rdonly : dl_readonly_area_not_found;
|
||||||
|
}
|
|
@ -371,6 +371,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
|
||||||
._dl_error_free = _dl_error_free,
|
._dl_error_free = _dl_error_free,
|
||||||
._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
|
._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
|
||||||
._dl_libc_freeres = __rtld_libc_freeres,
|
._dl_libc_freeres = __rtld_libc_freeres,
|
||||||
|
._dl_readonly_area = _dl_readonly_area,
|
||||||
};
|
};
|
||||||
/* If we would use strong_alias here the compiler would see a
|
/* If we would use strong_alias here the compiler would see a
|
||||||
non-hidden definition. This would undo the effect of the previous
|
non-hidden definition. This would undo the effect of the previous
|
||||||
|
|
|
@ -368,6 +368,21 @@ struct abort_msg_s
|
||||||
extern struct abort_msg_s *__abort_msg;
|
extern struct abort_msg_s *__abort_msg;
|
||||||
libc_hidden_proto (__abort_msg)
|
libc_hidden_proto (__abort_msg)
|
||||||
|
|
||||||
|
enum readonly_error_type
|
||||||
|
{
|
||||||
|
readonly_noerror,
|
||||||
|
readonly_area_writable,
|
||||||
|
readonly_procfs_inaccessible,
|
||||||
|
readonly_procfs_open_fail,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern enum readonly_error_type __readonly_area (const void *ptr,
|
||||||
|
size_t size)
|
||||||
|
attribute_hidden;
|
||||||
|
extern enum readonly_error_type __readonly_area_fallback (const void *ptr,
|
||||||
|
size_t size)
|
||||||
|
attribute_hidden;
|
||||||
|
|
||||||
# if IS_IN (rtld)
|
# if IS_IN (rtld)
|
||||||
extern __typeof (unsetenv) unsetenv attribute_hidden;
|
extern __typeof (unsetenv) unsetenv attribute_hidden;
|
||||||
extern __typeof (__strtoul_internal) __strtoul_internal attribute_hidden;
|
extern __typeof (__strtoul_internal) __strtoul_internal attribute_hidden;
|
||||||
|
|
|
@ -576,7 +576,8 @@ static const uint8_t jump_table[] =
|
||||||
|
|
||||||
/* Handle positional format specifiers. */
|
/* Handle positional format specifiers. */
|
||||||
static void printf_positional (struct Xprintf_buffer *buf,
|
static void printf_positional (struct Xprintf_buffer *buf,
|
||||||
const CHAR_T *format, int readonly_format,
|
const CHAR_T *format,
|
||||||
|
enum readonly_error_type readonly_format,
|
||||||
va_list ap, va_list *ap_savep,
|
va_list ap, va_list *ap_savep,
|
||||||
int nspecs_done, const UCHAR_T *lead_str_end,
|
int nspecs_done, const UCHAR_T *lead_str_end,
|
||||||
CHAR_T *work_buffer, int save_errno,
|
CHAR_T *work_buffer, int save_errno,
|
||||||
|
@ -626,9 +627,7 @@ Xprintf_buffer (struct Xprintf_buffer *buf, const CHAR_T *format,
|
||||||
/* For the %m format we may need the current `errno' value. */
|
/* For the %m format we may need the current `errno' value. */
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
|
|
||||||
/* 1 if format is in read-only memory, -1 if it is in writable memory,
|
enum readonly_error_type readonly_format = readonly_noerror;
|
||||||
0 if unknown. */
|
|
||||||
int readonly_format = 0;
|
|
||||||
|
|
||||||
/* Initialize local variables. */
|
/* Initialize local variables. */
|
||||||
grouping = (const char *) -1;
|
grouping = (const char *) -1;
|
||||||
|
@ -1045,7 +1044,7 @@ do_positional:
|
||||||
|
|
||||||
static void
|
static void
|
||||||
printf_positional (struct Xprintf_buffer * buf, const CHAR_T *format,
|
printf_positional (struct Xprintf_buffer * buf, const CHAR_T *format,
|
||||||
int readonly_format,
|
enum readonly_error_type readonly_format,
|
||||||
va_list ap, va_list *ap_savep, int nspecs_done,
|
va_list ap, va_list *ap_savep, int nspecs_done,
|
||||||
const UCHAR_T *lead_str_end,
|
const UCHAR_T *lead_str_end,
|
||||||
CHAR_T *work_buffer, int save_errno,
|
CHAR_T *work_buffer, int save_errno,
|
||||||
|
|
|
@ -324,16 +324,24 @@ LABEL (form_pointer):
|
||||||
LABEL (form_number):
|
LABEL (form_number):
|
||||||
if ((mode_flags & PRINTF_FORTIFY) != 0)
|
if ((mode_flags & PRINTF_FORTIFY) != 0)
|
||||||
{
|
{
|
||||||
if (! readonly_format)
|
if (readonly_format == readonly_noerror)
|
||||||
{
|
readonly_format = __readonly_area (format, ((STR_LEN (format) + 1)
|
||||||
extern int __readonly_area (const void *, size_t)
|
* sizeof (CHAR_T)));
|
||||||
attribute_hidden;
|
switch (readonly_format)
|
||||||
readonly_format
|
{
|
||||||
= __readonly_area (format, ((STR_LEN (format) + 1)
|
case readonly_area_writable:
|
||||||
* sizeof (CHAR_T)));
|
__libc_fatal ("*** %n in writable segments detected ***\n");
|
||||||
}
|
/* The format is not within ELF segments and opening /proc/self/maps
|
||||||
if (readonly_format < 0)
|
failed because there are too many files. */
|
||||||
__libc_fatal ("*** %n in writable segment detected ***\n");
|
case readonly_procfs_open_fail:
|
||||||
|
__libc_fatal ("*** procfs could not open ***\n");
|
||||||
|
/* The /proc/self/maps can not be opened either because it is not
|
||||||
|
available or the process does not have the right permission. Since
|
||||||
|
it should not be attacker-controlled we can avoid failure. */
|
||||||
|
case readonly_procfs_inaccessible:
|
||||||
|
case readonly_noerror:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Answer the count of characters written. */
|
/* Answer the count of characters written. */
|
||||||
void *ptrptr = process_arg_pointer ();
|
void *ptrptr = process_arg_pointer ();
|
||||||
|
|
|
@ -276,6 +276,12 @@ struct audit_ifaces
|
||||||
struct audit_ifaces *next;
|
struct audit_ifaces *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum dl_readonly_area_error_type
|
||||||
|
{
|
||||||
|
dl_readonly_area_rdonly,
|
||||||
|
dl_readonly_area_writable,
|
||||||
|
dl_readonly_area_not_found,
|
||||||
|
};
|
||||||
|
|
||||||
/* Test whether given NAME matches any of the names of the given object. */
|
/* Test whether given NAME matches any of the names of the given object. */
|
||||||
extern int _dl_name_match_p (const char *__name, const struct link_map *__map)
|
extern int _dl_name_match_p (const char *__name, const struct link_map *__map)
|
||||||
|
@ -676,6 +682,10 @@ struct rtld_global_ro
|
||||||
dlopen. */
|
dlopen. */
|
||||||
int (*_dl_find_object) (void *, struct dl_find_object *);
|
int (*_dl_find_object) (void *, struct dl_find_object *);
|
||||||
|
|
||||||
|
/* Implementation of _dl_readonly_area, used in fortify routines to check
|
||||||
|
if memory area is within a read-only ELF segment. */
|
||||||
|
enum dl_readonly_area_error_type (*_dl_readonly_area) (const void *, size_t);
|
||||||
|
|
||||||
/* Dynamic linker operations used after static dlopen. */
|
/* Dynamic linker operations used after static dlopen. */
|
||||||
const struct dlfcn_hook *_dl_dlfcn_hook;
|
const struct dlfcn_hook *_dl_dlfcn_hook;
|
||||||
|
|
||||||
|
@ -1284,6 +1294,10 @@ extern void _dl_show_scope (struct link_map *new, int from)
|
||||||
extern struct link_map *_dl_find_dso_for_object (const ElfW(Addr) addr);
|
extern struct link_map *_dl_find_dso_for_object (const ElfW(Addr) addr);
|
||||||
rtld_hidden_proto (_dl_find_dso_for_object)
|
rtld_hidden_proto (_dl_find_dso_for_object)
|
||||||
|
|
||||||
|
extern enum dl_readonly_area_error_type _dl_readonly_area (const void *ptr,
|
||||||
|
size_t size)
|
||||||
|
attribute_hidden;
|
||||||
|
|
||||||
/* Initialization which is normally done by the dynamic linker. */
|
/* Initialization which is normally done by the dynamic linker. */
|
||||||
extern void _dl_non_dynamic_init (void)
|
extern void _dl_non_dynamic_init (void)
|
||||||
attribute_hidden;
|
attribute_hidden;
|
||||||
|
|
|
@ -20,11 +20,8 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <mach.h>
|
#include <mach.h>
|
||||||
|
|
||||||
/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
|
enum readonly_error_type
|
||||||
Return -1 if it is writable. */
|
__readonly_area_fallback (const void *ptr, size_t size)
|
||||||
|
|
||||||
int
|
|
||||||
__readonly_area (const char *ptr, size_t size)
|
|
||||||
{
|
{
|
||||||
vm_address_t region_address = (uintptr_t) ptr;
|
vm_address_t region_address = (uintptr_t) ptr;
|
||||||
vm_size_t region_length = size;
|
vm_size_t region_length = size;
|
||||||
|
@ -46,11 +43,11 @@ __readonly_area (const char *ptr, size_t size)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (protection & VM_PROT_WRITE)
|
if (protection & VM_PROT_WRITE)
|
||||||
return -1;
|
return readonly_area_writable;
|
||||||
|
|
||||||
if (region_address - (uintptr_t) ptr >= size)
|
if (region_address - (uintptr_t) ptr >= size)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return readonly_noerror;
|
||||||
}
|
}
|
|
@ -23,11 +23,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "libio/libioP.h"
|
#include "libio/libioP.h"
|
||||||
|
|
||||||
/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
|
enum readonly_error_type
|
||||||
Return -1 if it is writable. */
|
__readonly_area_fallback (const void *ptr, size_t size)
|
||||||
|
|
||||||
int
|
|
||||||
__readonly_area (const char *ptr, size_t size)
|
|
||||||
{
|
{
|
||||||
const void *ptr_end = ptr + size;
|
const void *ptr_end = ptr + size;
|
||||||
|
|
||||||
|
@ -42,11 +39,11 @@ __readonly_area (const char *ptr, size_t size)
|
||||||
to the /proc filesystem if it is set[ug]id. There has
|
to the /proc filesystem if it is set[ug]id. There has
|
||||||
been no willingness to change this in the kernel so
|
been no willingness to change this in the kernel so
|
||||||
far. */
|
far. */
|
||||||
|| errno == EACCES
|
|| errno == EACCES)
|
||||||
/* Process has reached the maximum number of open files. */
|
return readonly_procfs_inaccessible;
|
||||||
|| errno == EMFILE)
|
/* Process has reached the maximum number of open files or another
|
||||||
return 1;
|
unusual error. */
|
||||||
return -1;
|
return readonly_procfs_open_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need no locking. */
|
/* We need no locking. */
|
||||||
|
@ -98,7 +95,5 @@ __readonly_area (const char *ptr, size_t size)
|
||||||
fclose (fp);
|
fclose (fp);
|
||||||
free (line);
|
free (line);
|
||||||
|
|
||||||
/* If the whole area between ptr and ptr_end is covered by read-only
|
return size == 0 ? readonly_noerror : readonly_area_writable;
|
||||||
VMAs, return 1. Otherwise return -1. */
|
|
||||||
return size == 0 ? 1 : -1;
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue