aarch64: Handle GCS marking

- Handle GCS marking
 - Use l_searchlist.r_list for gcs (allows using the
   same function for static exe)

Co-authored-by: Yury Khrustalev <yury.khrustalev@arm.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
This commit is contained in:
Szabolcs Nagy 2023-12-21 23:05:10 +00:00 committed by Yury Khrustalev
parent 8d516b6f85
commit 4d56a5bbd6
3 changed files with 103 additions and 6 deletions

View file

@ -15,10 +15,101 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <unistd.h>
#include <ldsodefs.h>
/* Used to report error when prctl system call to enabled GCS fails. */
/* GCS is disabled. */
#define GCS_POLICY_DISABLED 0
/* Enable GCS, abort if unmarked binary is found. */
#define GCS_POLICY_ENFORCED 1
/* Optionally enable GCS if all startup dependencies are marked. */
#define GCS_POLICY_OPTIONAL 2
/* Override binary marking and always enabled GCS. */
#define GCS_POLICY_OVERRIDE 3
static void
fail (struct link_map *l, const char *program)
{
if (program && program[0])
_dl_fatal_printf ("%s: %s: %s\n", program, l->l_name, "not GCS compatible");
else if (program)
_dl_fatal_printf ("%s\n", "not GCS compatible");
else
_dl_signal_error (0, l->l_name, "dlopen", "not GCS compatible");
}
static void
unsupported (void)
{
_dl_fatal_printf ("%s\n", "unsupported GCS policy");
}
/* This function is called only when binary markings are not
ignored and GCS is supposed to be enabled. This occurs
for the GCS_POLICY_ENFORCED and GCS_POLICY_ENFORCED policies. */
static bool
check_gcs (struct link_map *l, const char *program, bool enforced)
{
/* Binary is marked, all good. */
if (l->l_mach.gcs)
return true;
/* Binary is not marked and loaded via dlopen: abort. */
if (program == NULL)
fail (l, program);
/* Binary is not marked and we enforce GCS: abort. */
if (enforced)
fail (l, program);
/* Binary is not marked but GSC is optional: disable GCS. */
else
{
GL(dl_aarch64_gcs) = 0;
return false;
}
__builtin_unreachable ();
}
/* Iterate over all dependencies and check GCS marking.
This function is called only when binary markings are not
ignored and GCS is supposed to be enabled. This occurs
for the GCS_POLICY_ENFORCED and GCS_POLICY_ENFORCED policies.
We interrupt checking if GCS is optional and we already know
it is going to be disabled. */
static void
check_gcs_depends (struct link_map *l, const char *program, bool enforced)
{
if (check_gcs (l, program, enforced))
for (unsigned int i = 0; i < l->l_searchlist.r_nlist; i++)
if (!check_gcs (l->l_searchlist.r_list[i], program, enforced))
break;
}
/* Apply GCS policy for L and its dependencies.
PROGRAM is NULL when this check is invoked for dl_open. */
void
_dl_gcs_check (struct link_map *l, const char *program)
{
unsigned long policy = GL (dl_aarch64_gcs);
switch (policy)
{
case GCS_POLICY_DISABLED:
case GCS_POLICY_OVERRIDE:
return;
case GCS_POLICY_ENFORCED:
check_gcs_depends (l, program, true);
return;
case GCS_POLICY_OPTIONAL:
check_gcs_depends (l, program, false);
return;
default:
/* All other policy values are not supported: abort. */
unsupported ();
}
}
/* Used to report error when prctl system call to enabled GCS fails. */
void _dl_gcs_enable_failed (int code)
{
_dl_fatal_printf ("failed to enable GCS: %d\n", -code);

View file

@ -24,16 +24,21 @@ extern void _dl_bti_protect (struct link_map *, int) attribute_hidden;
extern void _dl_bti_check (struct link_map *, const char *)
attribute_hidden;
extern void _dl_gcs_check (struct link_map *, const char *)
attribute_hidden;
static inline void __attribute__ ((always_inline))
_rtld_main_check (struct link_map *m, const char *program)
{
_dl_bti_check (m, program);
_dl_gcs_check (m, program);
}
static inline void __attribute__ ((always_inline))
_dl_open_check (struct link_map *m)
{
_dl_bti_check (m, NULL);
_dl_gcs_check (m, NULL);
}
static inline void __attribute__ ((always_inline))
@ -45,10 +50,6 @@ static inline int
_dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
uint32_t datasz, void *data)
{
if (!GLRO(dl_aarch64_cpu_features).bti)
/* Skip note processing. */
return 0;
if (type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
{
/* Stop if the property note is ill-formed. */
@ -57,7 +58,11 @@ _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
unsigned int feature_1 = *(unsigned int *) data;
if (feature_1 & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
_dl_bti_protect (l, fd);
if (GLRO(dl_aarch64_cpu_features).bti)
_dl_bti_protect (l, fd);
if (feature_1 & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
l->l_mach.gcs = 1;
/* Stop if we processed the property note. */
return 0;

View file

@ -23,4 +23,5 @@ struct link_map_machine
ElfW(Addr) plt; /* Address of .plt */
void *tlsdesc_table; /* Address of TLS descriptor hash table. */
bool bti_fail; /* Failed to enable Branch Target Identification. */
bool gcs; /* Guarded Control Stack marking. */
};