diff --git a/sysdeps/aarch64/dl-gcs.c b/sysdeps/aarch64/dl-gcs.c index 870336f2bb..28febf93cd 100644 --- a/sysdeps/aarch64/dl-gcs.c +++ b/sysdeps/aarch64/dl-gcs.c @@ -15,10 +15,101 @@ License along with the GNU C Library; if not, see . */ +#include #include -/* 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); diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h index 361fc593da..abca2be7fa 100644 --- a/sysdeps/aarch64/dl-prop.h +++ b/sysdeps/aarch64/dl-prop.h @@ -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; diff --git a/sysdeps/aarch64/linkmap.h b/sysdeps/aarch64/linkmap.h index df6d3c66e8..e56c890aea 100644 --- a/sysdeps/aarch64/linkmap.h +++ b/sysdeps/aarch64/linkmap.h @@ -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. */ };