mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-22 21:10:07 +00:00
365 lines
11 KiB
C
365 lines
11 KiB
C
/* x86 CET initializers function.
|
|
Copyright (C) 2018-2024 Free Software Foundation, Inc.
|
|
|
|
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 <errno.h>
|
|
#include <libintl.h>
|
|
#include <ldsodefs.h>
|
|
#include <dl-cet.h>
|
|
#include <sys/single_threaded.h>
|
|
|
|
/* GNU_PROPERTY_X86_FEATURE_1_IBT and GNU_PROPERTY_X86_FEATURE_1_SHSTK
|
|
are defined in <elf.h>, which are only available for C sources.
|
|
X86_FEATURE_1_IBT and X86_FEATURE_1_SHSTK are defined in <sysdep.h>
|
|
which are available for both C and asm sources. They must match. */
|
|
#if GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT
|
|
# error GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT
|
|
#endif
|
|
#if GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
|
|
# error GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
|
|
#endif
|
|
|
|
struct dl_cet_info
|
|
{
|
|
const char *program;
|
|
|
|
/* Check how IBT and SHSTK should be enabled. */
|
|
enum dl_x86_cet_control enable_ibt_type;
|
|
enum dl_x86_cet_control enable_shstk_type;
|
|
|
|
/* If IBT and SHSTK were previously enabled. */
|
|
unsigned int feature_1_enabled;
|
|
|
|
/* If IBT and SHSTK should be enabled. */
|
|
unsigned int enable_feature_1;
|
|
|
|
/* If there are any legacy shared object. */
|
|
unsigned int feature_1_legacy;
|
|
|
|
/* Which shared object is the first legacy shared object. */
|
|
unsigned int feature_1_legacy_ibt;
|
|
unsigned int feature_1_legacy_shstk;
|
|
};
|
|
|
|
/* Check if the object M and its dependencies are legacy object. */
|
|
|
|
static void
|
|
dl_check_legacy_object (struct link_map *m,
|
|
struct dl_cet_info *info)
|
|
{
|
|
unsigned int i;
|
|
struct link_map *l = NULL;
|
|
|
|
i = m->l_searchlist.r_nlist;
|
|
while (i-- > 0)
|
|
{
|
|
/* Check each shared object to see if IBT and SHSTK are enabled. */
|
|
l = m->l_initfini[i];
|
|
|
|
if (l->l_init_called)
|
|
continue;
|
|
|
|
#ifdef SHARED
|
|
/* Skip check for ld.so since it has the features enabled. The
|
|
features will be disabled later if they are not enabled in
|
|
executable. */
|
|
if (l == &GL(dl_rtld_map)
|
|
|| l->l_real == &GL(dl_rtld_map)
|
|
|| (info->program != NULL && l == m))
|
|
continue;
|
|
#endif
|
|
|
|
/* IBT and SHSTK set only if enabled in executable and all DSOs.
|
|
NB: cet_always_on is handled outside of the loop. */
|
|
info->enable_feature_1 &= ((l->l_x86_feature_1_and
|
|
& (GNU_PROPERTY_X86_FEATURE_1_IBT
|
|
| GNU_PROPERTY_X86_FEATURE_1_SHSTK))
|
|
| ~(GNU_PROPERTY_X86_FEATURE_1_IBT
|
|
| GNU_PROPERTY_X86_FEATURE_1_SHSTK));
|
|
if ((info->feature_1_legacy
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT) == 0
|
|
&& ((info->enable_feature_1
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT)
|
|
!= (info->feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT)))
|
|
{
|
|
info->feature_1_legacy_ibt = i;
|
|
info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
}
|
|
|
|
if ((info->feature_1_legacy
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK) == 0
|
|
&& ((info->enable_feature_1
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK)
|
|
!= (info->feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK)))
|
|
{
|
|
info->feature_1_legacy_shstk = i;
|
|
info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
}
|
|
}
|
|
|
|
/* Handle cet_always_on. */
|
|
if ((info->feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT) != 0
|
|
&& info->enable_ibt_type == cet_always_on)
|
|
{
|
|
info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
}
|
|
|
|
if ((info->feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0
|
|
&& info->enable_shstk_type == cet_always_on)
|
|
{
|
|
info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
}
|
|
}
|
|
|
|
#ifdef SHARED
|
|
/* Enable IBT and SHSTK only if they are enabled in executable. Set
|
|
feature bits properly at the start of the program. */
|
|
|
|
static void
|
|
dl_cet_check_startup (struct link_map *m, struct dl_cet_info *info)
|
|
{
|
|
/* NB: IBT and SHSTK may be disabled by environment variable:
|
|
|
|
GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK.
|
|
*/
|
|
if (CPU_FEATURE_USABLE (IBT))
|
|
{
|
|
if (info->enable_ibt_type == cet_always_on)
|
|
info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
else
|
|
info->enable_feature_1 &= ((m->l_x86_feature_1_and
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT)
|
|
| ~GNU_PROPERTY_X86_FEATURE_1_IBT);
|
|
}
|
|
else
|
|
info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
|
|
if (CPU_FEATURE_USABLE (SHSTK))
|
|
{
|
|
if (info->enable_shstk_type == cet_always_on)
|
|
info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
else
|
|
info->enable_feature_1 &= ((m->l_x86_feature_1_and
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK)
|
|
| ~GNU_PROPERTY_X86_FEATURE_1_SHSTK);
|
|
}
|
|
else
|
|
info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
|
|
if (info->enable_feature_1 != 0)
|
|
dl_check_legacy_object (m, info);
|
|
|
|
unsigned int disable_feature_1
|
|
= info->enable_feature_1 ^ info->feature_1_enabled;
|
|
if (disable_feature_1 != 0)
|
|
{
|
|
/* Clear the disabled bits. Sync dl_x86_feature_1 and
|
|
info->feature_1_enabled with info->enable_feature_1. */
|
|
info->feature_1_enabled = info->enable_feature_1;
|
|
GL(dl_x86_feature_1) = info->enable_feature_1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Check feature bits when dlopening the shared object M. */
|
|
|
|
static void
|
|
dl_cet_check_dlopen (struct link_map *m, struct dl_cet_info *info)
|
|
{
|
|
/* Check if there are any legacy objects loaded. */
|
|
if (info->enable_feature_1 != 0)
|
|
{
|
|
dl_check_legacy_object (m, info);
|
|
|
|
/* Skip if there are no legacy shared objects loaded. */
|
|
if (info->feature_1_legacy == 0)
|
|
return;
|
|
}
|
|
|
|
unsigned int disable_feature_1 = 0;
|
|
unsigned int legacy_obj = 0;
|
|
const char *msg = NULL;
|
|
|
|
if ((info->feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT) != 0
|
|
&& (info->feature_1_legacy
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT) != 0)
|
|
{
|
|
/* Don't disable IBT if not single threaded since IBT may be still
|
|
enabled in other threads. */
|
|
if (info->enable_ibt_type != cet_permissive
|
|
|| !SINGLE_THREAD_P)
|
|
{
|
|
legacy_obj = info->feature_1_legacy_ibt;
|
|
msg = N_("rebuild shared object with IBT support enabled");
|
|
}
|
|
else
|
|
disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
|
|
}
|
|
|
|
/* Check the next feature only if there is no error. */
|
|
if (msg == NULL
|
|
&& (info->feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0
|
|
&& (info->feature_1_legacy
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0)
|
|
{
|
|
/* Don't disable SHSTK if not single threaded since SHSTK may be
|
|
still enabled in other threads. */
|
|
if (info->enable_shstk_type != cet_permissive
|
|
|| !SINGLE_THREAD_P)
|
|
{
|
|
legacy_obj = info->feature_1_legacy_shstk;
|
|
msg = N_("rebuild shared object with SHSTK support enabled");
|
|
}
|
|
else
|
|
disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
|
}
|
|
|
|
/* If there is an error, long jump back to the caller. */
|
|
if (msg != NULL)
|
|
_dl_signal_error (0, m->l_initfini[legacy_obj]->l_name, "dlopen",
|
|
msg);
|
|
|
|
if (disable_feature_1 != 0)
|
|
{
|
|
int res = dl_cet_disable_cet (disable_feature_1);
|
|
if (res)
|
|
{
|
|
if ((disable_feature_1
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT) != 0)
|
|
msg = N_("can't disable IBT");
|
|
else
|
|
msg = N_("can't disable SHSTK");
|
|
/* Long jump back to the caller on error. */
|
|
_dl_signal_error (-res, m->l_initfini[legacy_obj]->l_name,
|
|
"dlopen", msg);
|
|
}
|
|
|
|
/* Clear the disabled bits in dl_x86_feature_1. */
|
|
GL(dl_x86_feature_1) &= ~disable_feature_1;
|
|
|
|
THREAD_SETMEM (THREAD_SELF, header.feature_1,
|
|
GL(dl_x86_feature_1));
|
|
}
|
|
}
|
|
|
|
static void
|
|
dl_cet_check (struct link_map *m, const char *program)
|
|
{
|
|
struct dl_cet_info info;
|
|
|
|
/* CET is enabled only if RTLD_START_ENABLE_X86_FEATURES is defined. */
|
|
#if defined SHARED && defined RTLD_START_ENABLE_X86_FEATURES
|
|
/* Set dl_x86_feature_1 to features enabled in the executable. */
|
|
if (program != NULL)
|
|
GL(dl_x86_feature_1) = (m->l_x86_feature_1_and
|
|
& (X86_FEATURE_1_IBT
|
|
| X86_FEATURE_1_SHSTK));
|
|
#endif
|
|
|
|
/* Check how IBT and SHSTK should be enabled. */
|
|
info.enable_ibt_type = GL(dl_x86_feature_control).ibt;
|
|
info.enable_shstk_type = GL(dl_x86_feature_control).shstk;
|
|
|
|
info.feature_1_enabled = GL(dl_x86_feature_1);
|
|
|
|
/* No legacy object check if IBT and SHSTK are always on. */
|
|
if (info.enable_ibt_type == cet_always_on
|
|
&& info.enable_shstk_type == cet_always_on)
|
|
return;
|
|
|
|
/* Check if IBT and SHSTK were enabled. */
|
|
if (info.feature_1_enabled == 0)
|
|
return;
|
|
|
|
info.program = program;
|
|
|
|
/* Check which features should be enabled. */
|
|
info.enable_feature_1 = 0;
|
|
if (info.enable_ibt_type != cet_always_off)
|
|
info.enable_feature_1 |= (info.feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_IBT);
|
|
if (info.enable_shstk_type != cet_always_off)
|
|
info.enable_feature_1 |= (info.feature_1_enabled
|
|
& GNU_PROPERTY_X86_FEATURE_1_SHSTK);
|
|
|
|
/* Start with no legacy objects. */
|
|
info.feature_1_legacy = 0;
|
|
info.feature_1_legacy_ibt = 0;
|
|
info.feature_1_legacy_shstk = 0;
|
|
|
|
#ifdef SHARED
|
|
if (program)
|
|
dl_cet_check_startup (m, &info);
|
|
else
|
|
#endif
|
|
dl_cet_check_dlopen (m, &info);
|
|
}
|
|
|
|
void
|
|
_dl_cet_open_check (struct link_map *l)
|
|
{
|
|
dl_cet_check (l, NULL);
|
|
}
|
|
|
|
/* Set GL(dl_x86_feature_1) to the enabled features and clear the
|
|
active bits of the disabled features. */
|
|
|
|
attribute_hidden void
|
|
_dl_cet_setup_features (unsigned int cet_feature)
|
|
{
|
|
/* NB: cet_feature == GL(dl_x86_feature_1) which is set to features
|
|
enabled from executable, not necessarily supported by kernel. */
|
|
if (cet_feature != 0)
|
|
{
|
|
cet_feature = dl_cet_get_cet_status ();
|
|
if (cet_feature != 0)
|
|
{
|
|
THREAD_SETMEM (THREAD_SELF, header.feature_1, cet_feature);
|
|
|
|
/* Lock CET if IBT or SHSTK is enabled in executable. Don't
|
|
lock CET if IBT or SHSTK is enabled permissively. */
|
|
if (GL(dl_x86_feature_control).ibt != cet_permissive
|
|
&& (GL(dl_x86_feature_control).shstk != cet_permissive))
|
|
dl_cet_lock_cet (cet_feature);
|
|
}
|
|
/* Sync GL(dl_x86_feature_1) with kernel. */
|
|
GL(dl_x86_feature_1) = cet_feature;
|
|
}
|
|
}
|
|
|
|
#ifdef SHARED
|
|
|
|
# ifndef LINKAGE
|
|
# define LINKAGE
|
|
# endif
|
|
|
|
LINKAGE
|
|
void
|
|
_dl_cet_check (struct link_map *main_map, const char *program)
|
|
{
|
|
dl_cet_check (main_map, program);
|
|
}
|
|
#endif /* SHARED */
|