Skip to content

Commit 724a834

Browse files
author
Matthieu Longo
committed
aarch64: check GCS feature in GNU properties of input dynamic objects
The Guarded Control Stack (GCS) feature requires that two things: - at static link time, all the input objects of a link unit have to be compatible with GCS. - at runtime, the executable and the shared libraries which it depends on have to be compatible with GCS. Both of those criteria are checked with the GCS feature stored in the GNU property note. The previous patch, adding support for the GCS feature check in GNU note properties for input objects, ignored the input dynamic objects. Although this support was better than no check, it was still delaying the detection of compatibility issues up to the runtime linker. In order to help the developer in detecting such an incompatibility issue as early as possible, this patch adds a check for input dynamic objects lacking the GCS marking. This check can be controlled via the linker option '-z gcs-report-dynamic[=none|warning|error]'. By default, if the option is omitted, it inherits the value from '-z gcs-report'. However, the inherited value is capped to 'warning' as a user might want to only report errors in the currently built module, and not the shared dependencies. If a user also wants to error on GCS issues in the shared libraries, '-z gcs-report-dynamic=error' will have to be specified explicitly.
1 parent 480a115 commit 724a834

File tree

5 files changed

+185
-31
lines changed

5 files changed

+185
-31
lines changed

bfd/elfnn-aarch64.c

+14
Original file line numberDiff line numberDiff line change
@@ -5035,8 +5035,22 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
50355035
}
50365036

50375037
elf_aarch64_tdata (output_bfd)->sw_protections = *sw_protections;
5038+
/* Inherit the value from '-z gcs-report' if the option '-z gcs-report-dynamic'
5039+
was not set on the command line. However, the inheritance mechanism is
5040+
capped to avoid inheriting the error level from -g gcs-report as the user
5041+
might want to continue to build a module without rebuilding all the shared
5042+
libraries. If a user also wants to error GCS issues in the shared
5043+
libraries, '-z gcs-report-dynamic=error' will have to be specified
5044+
explicitly. */
5045+
if (sw_protections->gcs_report_dynamic == MARKING_UNSET)
5046+
elf_aarch64_tdata (output_bfd)->sw_protections.gcs_report_dynamic
5047+
= (sw_protections->gcs_report == MARKING_ERROR)
5048+
? MARKING_WARN
5049+
: sw_protections->gcs_report;
5050+
50385051
elf_aarch64_tdata (output_bfd)->n_bti_issues = 0;
50395052
elf_aarch64_tdata (output_bfd)->n_gcs_issues = 0;
5053+
elf_aarch64_tdata (output_bfd)->n_gcs_dynamic_issues = 0;
50405054

50415055
setup_plt_values (link_info, sw_protections->plt_type);
50425056
}

bfd/elfxx-aarch64.c

+106-19
Original file line numberDiff line numberDiff line change
@@ -764,11 +764,9 @@ _bfd_aarch64_report_summary_merge_issues (struct bfd_link_info *info)
764764
{
765765
const struct elf_aarch64_obj_tdata * tdata
766766
= elf_aarch64_tdata (info->output_bfd);
767-
aarch64_feature_marking_report bti_report = tdata->sw_protections.bti_report;
768-
aarch64_feature_marking_report gcs_report = tdata->sw_protections.gcs_report;
769767

770768
if (tdata->n_bti_issues > GNU_PROPERTY_ISSUES_MAX
771-
&& bti_report != MARKING_NONE)
769+
&& tdata->sw_protections.bti_report != MARKING_NONE)
772770
{
773771
const char *msg
774772
= (tdata->sw_protections.bti_report == MARKING_ERROR)
@@ -780,7 +778,7 @@ _bfd_aarch64_report_summary_merge_issues (struct bfd_link_info *info)
780778
}
781779

782780
if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX
783-
&& gcs_report != MARKING_NONE)
781+
&& tdata->sw_protections.gcs_report != MARKING_NONE)
784782
{
785783
const char *msg
786784
= (tdata->sw_protections.gcs_report == MARKING_ERROR)
@@ -790,6 +788,71 @@ _bfd_aarch64_report_summary_merge_issues (struct bfd_link_info *info)
790788
"GCS requirements.\n");
791789
info->callbacks->einfo (msg, tdata->n_gcs_issues);
792790
}
791+
792+
if (tdata->n_gcs_dynamic_issues > GNU_PROPERTY_ISSUES_MAX
793+
&& tdata->sw_protections.gcs_report_dynamic != MARKING_NONE)
794+
{
795+
const char *msg
796+
= (tdata->sw_protections.gcs_report_dynamic == MARKING_ERROR)
797+
? _("%Xerror: found a total of %d dynamically-linked objects "
798+
"incompatible with GCS requirements.\n")
799+
: _("warning: found a total of %d dynamically-linked objects "
800+
"incompatible with GCS requirements.\n");
801+
info->callbacks->einfo (msg, tdata->n_gcs_dynamic_issues);
802+
}
803+
}
804+
805+
/* Perform a look-up of a property in an unsorted list of properties. This is
806+
useful when the list of properties of an object has not been sorted yet. */
807+
static uint32_t
808+
_bfd_aarch64_prop_linear_lookup (elf_property_list *properties,
809+
unsigned int pr_type)
810+
{
811+
for (elf_property_list *p = properties; p != NULL; p = p->next)
812+
if (p->property.pr_type == pr_type)
813+
return p->property.u.number;
814+
return 0;
815+
}
816+
817+
/* Compare the GNU properties of the current dynamic object, with the ones of
818+
the output BFD. Today, we only care about GCS feature stored in
819+
GNU_PROPERTY_AARCH64_FEATURE_1. */
820+
static void
821+
_bfd_aarch64_compare_dynamic_obj_prop_against_outprop(
822+
struct bfd_link_info *info,
823+
const uint32_t outprop,
824+
bfd *pbfd)
825+
{
826+
if (!(outprop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
827+
return;
828+
829+
const uint32_t dyn_obj_aarch64_feature_prop =
830+
_bfd_aarch64_prop_linear_lookup (elf_properties (pbfd),
831+
GNU_PROPERTY_AARCH64_FEATURE_1_AND);
832+
if (!(dyn_obj_aarch64_feature_prop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
833+
_bfd_aarch64_elf_check_gcs_report (info, pbfd);
834+
}
835+
836+
/* Check compatibility between the GNU properties of the ouput BFD and the
837+
linked dynamic objects. */
838+
static void
839+
_bfd_aarch64_elf_check_gnu_properties_linked_dynamic_objects (
840+
struct bfd_link_info * info,
841+
const uint32_t outprop)
842+
{
843+
const struct elf_backend_data *bed = get_elf_backend_data (info->output_bfd);
844+
const int elf_machine_code = bed->elf_machine_code;
845+
const unsigned int elfclass = bed->s->elfclass;
846+
847+
for (bfd *pbfd = info->input_bfds; pbfd != NULL; pbfd = pbfd->link.next)
848+
/* Ignore GNU properties from non-ELF objects or ELF objects with different
849+
machine code. */
850+
if ((pbfd->flags & DYNAMIC) != 0
851+
&& (bfd_get_flavour (pbfd) == bfd_target_elf_flavour)
852+
&& (get_elf_backend_data (pbfd)->elf_machine_code == elf_machine_code)
853+
&& (get_elf_backend_data (pbfd)->s->elfclass == elfclass))
854+
_bfd_aarch64_compare_dynamic_obj_prop_against_outprop(info, outprop,
855+
pbfd);
793856
}
794857

795858
/* Find the first input bfd with GNU property and merge it with GPROP. If no
@@ -855,7 +918,7 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info)
855918
/* The property list is sorted in order of type. */
856919
for (elf_property_list *p = elf_properties (pbfd);
857920
(p != NULL)
858-
&& (GNU_PROPERTY_AARCH64_FEATURE_1_AND <= p->property.pr_type);
921+
&& (GNU_PROPERTY_AARCH64_FEATURE_1_AND <= p->property.pr_type);
859922
p = p->next)
860923
{
861924
/* This merge of features should happen only once as all the identical
@@ -872,9 +935,12 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info)
872935
}
873936
}
874937

938+
tdata->gnu_property_aarch64_feature_1_and = outprop;
939+
940+
_bfd_aarch64_elf_check_gnu_properties_linked_dynamic_objects (info, outprop);
941+
875942
_bfd_aarch64_report_summary_merge_issues (info);
876943

877-
tdata->gnu_property_aarch64_feature_1_and = outprop;
878944
return pbfd;
879945
}
880946

@@ -1047,21 +1113,42 @@ void
10471113
_bfd_aarch64_elf_check_gcs_report (struct bfd_link_info *info, bfd *ebfd)
10481114
{
10491115
struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
1116+
bool dynamic_obj = (ebfd->flags & DYNAMIC) != 0;
10501117

1051-
if (tdata->sw_protections.gcs_report == MARKING_NONE)
1052-
return;
1053-
1054-
++tdata->n_gcs_issues;
1055-
1056-
if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)
1057-
return;
1118+
if (dynamic_obj)
1119+
{
1120+
if (tdata->sw_protections.gcs_report_dynamic == MARKING_NONE)
1121+
return;
1122+
++tdata->n_gcs_dynamic_issues;
1123+
if (tdata->n_gcs_dynamic_issues > GNU_PROPERTY_ISSUES_MAX)
1124+
return;
1125+
}
1126+
else
1127+
{
1128+
if (tdata->sw_protections.gcs_report == MARKING_NONE)
1129+
return;
1130+
++tdata->n_gcs_issues;
1131+
if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)
1132+
return;
1133+
}
10581134

1059-
const char *msg
1060-
= (tdata->sw_protections.gcs_report == MARKING_WARN)
1061-
? _("%pB: warning: GCS is required by -z gcs, but this input object file "
1062-
"lacks the necessary property note.\n")
1063-
: _("%X%pB: error: GCS is required by -z gcs, but this input object file "
1064-
"lacks the necessary property note.\n");
1135+
const char *msg;
1136+
if (dynamic_obj)
1137+
msg = (tdata->sw_protections.gcs_report_dynamic == MARKING_WARN)
1138+
? _("%pB: warning: GCS is required by -z gcs, but this shared library "
1139+
"lacks the necessary property note. The dynamic loader might not "
1140+
"enable GCS or refuse to load the program unless all the shared "
1141+
"library dependencies have the GCS marking.\n")
1142+
: _("%X%pB: error: GCS is required by -z gcs, but this shared library "
1143+
"lacks the necessary property note. The dynamic loader might not "
1144+
"enable GCS or refuse to load the program unless all the shared "
1145+
"library dependencies have the GCS marking.\n");
1146+
else
1147+
msg = (tdata->sw_protections.gcs_report == MARKING_WARN)
1148+
? _("%pB: warning: GCS is required by -z gcs, but this input object file "
1149+
"lacks the necessary property note.\n")
1150+
: _("%X%pB: error: GCS is required by -z gcs, but this input object file "
1151+
"lacks the necessary property note.\n");
10651152

10661153
info->callbacks->einfo (msg, ebfd);
10671154
}

bfd/elfxx-aarch64.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ typedef enum
5151
MARKING_ERROR = 2, /* Emit error when the input objects are missing GNU
5252
feature property markings, and the output has the
5353
markings. */
54+
MARKING_UNSET = 3, /* The only purpose of this value is to simulate an
55+
optional to detect when the value was not initialized
56+
from the command line. */
5457
} aarch64_feature_marking_report;
5558

5659
/* To indicate whether GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit is
@@ -64,7 +67,7 @@ typedef enum
6467
} aarch64_gcs_type;
6568

6669
/* A structure to encompass all information about software protections coming
67-
from BTI or PAC related command line options. */
70+
from BTI, PAC and GCS related command line options. */
6871
struct aarch64_protection_opts
6972
{
7073
/* PLT type to use depending on the selected software proctections. */
@@ -78,6 +81,9 @@ struct aarch64_protection_opts
7881

7982
/* Report level for GCS issues. */
8083
aarch64_feature_marking_report gcs_report;
84+
85+
/* Report level for GCS issues with dynamic inputs. */
86+
aarch64_feature_marking_report gcs_report_dynamic;
8187
};
8288
typedef struct aarch64_protection_opts aarch64_protection_opts;
8389

@@ -104,8 +110,11 @@ struct elf_aarch64_obj_tdata
104110
/* Number of reported BTI issues. */
105111
int n_bti_issues;
106112

107-
/* Number of reported GCS issues. */
113+
/* Number of reported GCS issues for non-dynamic objects. */
108114
int n_gcs_issues;
115+
116+
/* Number of reported GCS issues for dynamic objects. */
117+
int n_gcs_dynamic_issues;
109118
};
110119

111120
#define elf_aarch64_tdata(bfd) \

ld/emultempl/aarch64elf.em

+30-4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static aarch64_protection_opts sw_protections = {
3939
.bti_report = MARKING_WARN,
4040
.gcs_type = GCS_IMPLICIT,
4141
.gcs_report = MARKING_WARN,
42+
.gcs_report_dynamic = MARKING_UNSET,
4243
};
4344

4445
#define COMPILE_TIME_STRLEN(s) \
@@ -357,18 +358,20 @@ static bool
357358
aarch64_parse_feature_report_option (const char *_optarg,
358359
const char *report_opt,
359360
const size_t report_opt_len,
361+
bool allow_empty_value,
360362
aarch64_feature_marking_report *level)
361363
{
362364
if (strncmp (_optarg, report_opt, report_opt_len) != 0)
363365
return false;
364366

365-
if (strlen (_optarg) == report_opt_len
366-
|| strcmp (_optarg + report_opt_len, "=warning") == 0)
367+
if (strcmp (_optarg + report_opt_len, "=warning") == 0)
367368
*level = MARKING_WARN;
368369
else if (strcmp (_optarg + report_opt_len, "=none") == 0)
369370
*level = MARKING_NONE;
370371
else if (strcmp (_optarg + report_opt_len, "=error") == 0)
371372
*level = MARKING_ERROR;
373+
else if (allow_empty_value && strlen (_optarg) == report_opt_len)
374+
*level = MARKING_WARN;
372375
else
373376
einfo (_("%X%P: error: unrecognized value '-z %s'\n"), _optarg);
374377

@@ -382,7 +385,7 @@ aarch64_parse_bti_report_option (const char *_optarg)
382385
#define BTI_REPORT_LEN COMPILE_TIME_STRLEN (BTI_REPORT)
383386

384387
return aarch64_parse_feature_report_option (_optarg, BTI_REPORT,
385-
BTI_REPORT_LEN, &sw_protections.bti_report);
388+
BTI_REPORT_LEN, true, &sw_protections.bti_report);
386389

387390
#undef BTI_REPORT
388391
#undef BTI_REPORT_LEN
@@ -395,12 +398,25 @@ aarch64_parse_gcs_report_option (const char *_optarg)
395398
#define GCS_REPORT_LEN COMPILE_TIME_STRLEN (GCS_REPORT)
396399

397400
return aarch64_parse_feature_report_option (_optarg, GCS_REPORT,
398-
GCS_REPORT_LEN, &sw_protections.gcs_report);
401+
GCS_REPORT_LEN, true, &sw_protections.gcs_report);
399402

400403
#undef GCS_REPORT
401404
#undef GCS_REPORT_LEN
402405
}
403406

407+
static bool
408+
aarch64_parse_gcs_report_dynamic_option (const char *_optarg)
409+
{
410+
#define GCS_REPORT_DYNAMIC "gcs-report-dynamic"
411+
#define GCS_REPORT_DYNAMIC_LEN COMPILE_TIME_STRLEN (GCS_REPORT_DYNAMIC)
412+
413+
return aarch64_parse_feature_report_option (_optarg, GCS_REPORT_DYNAMIC,
414+
GCS_REPORT_DYNAMIC_LEN, false, &sw_protections.gcs_report_dynamic);
415+
416+
#undef GCS_REPORT_DYNAMIC
417+
#undef GCS_REPORT_DYNAMIC_LEN
418+
}
419+
404420
static bool
405421
aarch64_parse_gcs_option (const char *_optarg)
406422
{
@@ -494,6 +510,14 @@ PARSE_AND_LIST_OPTIONS='
494510
and output have GCS marking.\n\
495511
error: Emit error when the input objects are missing GCS markings\n\
496512
and output have GCS marking.\n"));
513+
fprintf (file, _("\
514+
-z gcs-report-dynamic=none|warning|error Emit warning/error on mismatch of GCS marking between the current link\n\
515+
unit and input dynamic objects.\n\
516+
none: Does not emit any warning/error messages.\n\
517+
warning: Emit warning when the input objects are missing GCS markings\n\
518+
and output have GCS marking.\n\
519+
error: Emit error when the input objects are missing GCS markings\n\
520+
and output have GCS marking.\n"));
497521
'
498522

499523
PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
@@ -503,6 +527,8 @@ PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
503527
{}
504528
else if (strcmp (optarg, "pac-plt") == 0)
505529
sw_protections.plt_type |= PLT_PAC;
530+
else if (aarch64_parse_gcs_report_dynamic_option (optarg))
531+
{}
506532
else if (aarch64_parse_gcs_report_option (optarg))
507533
{}
508534
else if (aarch64_parse_gcs_option (optarg))

ld/ld.texi

+24-6
Original file line numberDiff line numberDiff line change
@@ -8255,15 +8255,33 @@ mark the output with GCS.
82558255

82568256
@kindex -z gcs-report[=none|warning|error]
82578257
@cindex Control warnings for missing GCS markings.
8258-
The @samp{-z gcs-report[=none|warning|error]} specifies how to report the missing
8259-
GCS markings on inputs, i.e. the GNU_PROPERTY_AARCH64_FEATURE_1_GCS property.
8260-
By default, if the option is omitted and @samp{-z gcs} is provided, warnings are
8261-
emitted.
8258+
The @samp{-z gcs-report[=none|warning|error]} option specifies how to report the
8259+
missing GCS markings on inputs, i.e. the GNU_PROPERTY_AARCH64_FEATURE_1_GCS
8260+
property. By default, if the option is omitted and @samp{-z gcs} is provided,
8261+
warnings are emitted.
82628262
@itemize
82638263
@item @samp{none} disables any warning messages.
82648264
@item @samp{warning} (the default value) emits warning messages when input objects
8265-
composing the link unit are missing GCS markings, or dynamic objects containing
8266-
external symbols used in the link unit.
8265+
composing the link unit are missing GCS markings.
8266+
@item @samp{error} turns the warning messages into errors.
8267+
@end itemize
8268+
If issues are found, a maximum of 20 messages will be emitted, and then a summary
8269+
with the total number of issues will be displayed at the end.
8270+
8271+
@kindex -z gcs-report-dynamic=none|warning|error
8272+
@cindex Control warnings for missing GCS markings on dynamic input objects.
8273+
The @samp{-z gcs-report-dynamic=none|warning|error} option specifies how to
8274+
report the missing GCS markings on dynamic input objects, i.e. the
8275+
GNU_PROPERTY_AARCH64_FEATURE_1_GCS property. By default, if the option is
8276+
omitted, it inherits the value of @samp{-z gcs-report}. However, the inherited
8277+
value is capped to @samp{warning} as some user might want to only report errors
8278+
in the currently built module, and not the shared dependencies. It is therefore
8279+
necessary to use an explicit @samp{-z gcs-report-dynamic=error} option if you
8280+
want the linker to error on GCS issues in the shared libraries.
8281+
@itemize
8282+
@item @samp{none} disables any warning messages.
8283+
@item @samp{warning} emits warning messages when dynamic objects are missing
8284+
GCS markings.
82678285
@item @samp{error} turns the warning messages into errors.
82688286
@end itemize
82698287
If issues are found, a maximum of 20 messages will be emitted, and then a summary

0 commit comments

Comments
 (0)