[PATCH v8 00/20] aarch64: Add support for Guarded Control Stack extension

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Thu Jan 16 14:33:53 GMT 2025



On 16/01/25 10:55, Yury Khrustalev wrote:
> On Thu, Jan 16, 2025 at 10:49:45AM -0300, Adhemerval Zanella Netto wrote:
>>
>> Hi Yuri,
>>
>> I think I reviewed all the remaning patches, and the only contention I have is
>> the inclusion of aarch64_gcs_policy=1 that allows dlopen to disable GCS support
>> if a shared library does not contain the GCS marking.
> 
> I think there is some misunderstanding here. If glibc.cpu.aarch64_gcs is set,
> then dlopen() can never disable GCS. If policy is non-zero, it will be an error,
> and if policy is 0, GCS will remain enabled.

The problem is the silent change of program security semantic at *runtime*,
where auditing is more complex.  On sysdeps/aarch64/dl-gcs.c:

 40 static void
 41 check_gcs (struct link_map *l, const char *program)
 42 {
 43 #ifdef SHARED
 44   /* Ignore GCS marking on ld.so: its properties are not processed.  */
 45   if (is_rtld_link_map (l->l_real))
 46     return;
 47 #endif
 48   bool for_dlopen = program == NULL;
 49   if (!l->l_mach.gcs)
 50     {
 51       if (GLRO(dl_aarch64_gcs_policy) == 2 || for_dlopen)
 52         fail (l, program);
 53       if (GLRO(dl_aarch64_gcs_policy) == 1)
 54         GL(dl_aarch64_gcs) = 0;
 55       else
 56         unsupported ();
 57

For dl_aarch64_gcs_policy=1, it silent changes the dl_aarch64_gcs to 0 which
disable future GCS checks.

In fact, the manual sentence:

  GCS policy @code{1} means that GCS is enabled if @code{glibc.cpu.aarch64_gcs}
  is set and all binaries are GCS-marked. If GCS is required and an incompatible
  library is loaded via @code{dlopen}, it is an error, otherwise any
  incompatible binary will disable GCS. 

should be improve to state that what is disabled is the GCS *check* for future
dlopen, not GCS itself.  And still think this semantic is far from ideal.

In fact, two tunables for GCS support is *really* confusing.  For instance 
aarch64_gcs_policy=0,aarch64_gcs=1 does not make sense (since aarch64_gcs is
ignored if aarch64_gcs_policy is not set).

Assuming GCS is never disabled once enabled (which seems the intention of this
initial patchset), I think we can simplify to just one tunable:

  aarch64_gcs=0  -  GCS is disabled and any GCS-marking is ignored.  The default.
  aarch64_gcs=1  -  GCS is enabled, and taken as a hint at loading time.  If
                    any binary is not GCS-marked, or if kernel does not
                    support it, GCS is not enabled. 
                    dlopen enforces GCS if it was enabled at loading time,
                    otherwise GCS-marking is ignored.
  aarch64_gcs=2  -  GCS is enabled, and it is *enforced*.  If any binary is not
                    GCS-marked, or if kernel does not support, loading fails.
                    dlopen is always enforces GCS.

It means that we can eventually set aarch64_gcs=1 as default in a future release,
and aarch64_gcs=2 might be an option for a hardened distro (or even in some future
release).

> 
>>
>> As I said, we recently removed a similar support for executable stacks
>> (commit 0ca8785a28) because this is silent change of process semantic and
>> it also used as way to enable a RCE on a CVE [1].
>>
>> So I don't think we should explict support options to allow process to move
>> from a hardened to a lower hardened security perpective, specially in a
>> silent manner, and even though a explicit option (the tunable, where use
>> does need to opt-in).
>>
>> It would be good to have some more input for other maintainers.  How x86 does
>> in this regard (I haven't check it).
>>
>> [1] https://www.qualys.com/2023/07/19/cve-2023-38408/rce-openssh-forwarded-ssh-agent.txt
> 
> 
> Thanks,
> Yury
> 



More information about the Libc-alpha mailing list