Re: aarch64: Memory tagging modes proposal

Yury Khrustalev <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
On Thu, Aug 20, 2026 at 10:52:10AM -0300, Adhemerval Zanella Netto wrote:
> 
> On 18/08/26 10:52, Yury Khrustalev wrote:
> > On Tue, Aug 18, 2026 at 10:22:34AM -0300, Adhemerval Zanella Netto wrote:
> >>
> >>>>> ...
> >>>>>
> >>>>> The set of rules below:
> >>>>>
> >>>>> 1) Respects binary marking when it's present.
> >>>>> 2) Tunable cannot disable tagging, only change MTE mode.
> >>
> >> I just realized that this can be arguably a security hazard, like downgrade a
> >> DT_AARCH64_MEMTAG_MODE set to 'sync' to 'async'.
> > 
> > I thought we resolved this with system-wide tunables. If switching from
> > 'sync' to 'async' is an issue on some system, this tunable should be
> > configured as not final in /etc/tunables.conf. In my proposal "downgrading"
> > is allowed but switching off is not.
> > 
> > If we ignore the tunable when marking is present, the question is what
> > to do if the user wants to used different mode (e.g. to use 'sync' when
> > marking is set to 'async')?
> 
> My understanding from 1. ("Respects binary marking when it's present.") is
> we should *always* use the DT_AARCH64_MEMTAG_MODE specified and not allow
> a downgrade through tunables.  Iff the marking is not available then the
> tunable would be free to select a mode different than default.

OK, I guess this will simplify configuration. I just thought it might
be inconvenient for the user: if, for example, they have a program with
in-built DT_AARCH64_MEMTAG_MODE=1 (async) and they see MTE-related error,
they might want to re-run the program with sync to see where exactly it
happens.

Anyway, I think we can just follow the rule that binary marking
overrides everything.

> I think we should not allow a downgrade from 'sync' to 'async', regardless
> of system-wide tunable, because the binary marking is a hint that MTE is
> being used a hardening and not as debug feature.

I don't agree that binary marking is a hint for hardening but it's not
important for this discussion.

> > ...
> >
> >> , my understanding is
> >> in theory we can add a tunable that will essentially mimic what the 
> >> DT_AARCH64_MEMTAG_* does for process startup (prctrl plus PROT_MTE on mmap calls).
> > 
> > This initial setup is always done of tagging needs to be enabled for at
> > least one of the components (heap, stack, etc). It's not a problem to do
> > it but there would be no stack tagging if there is no code instrumentation,
> > and we can't do anything about it on the Glibc side. That is why I think
> > it would be better to be explicit that we don't enable stack tagging
> > unless there is corresponding binary marking.
> 
> I tend to agree, I just raised the options to add an option to enable MTE (even
> if the binary and direct dependencies does not have any marking) to allows
> dlopen DSO with the marking.  This works in theory (and android allows it), albeit
> I am not sure how common it would be for Linux/GNU (maybe adding an extra hardening
> for plugins or NSS modules).

I see your point, but the ABI spec says that binary marking for the
"scope" of memtag protection only applies to the main binary. So, even
if we have a tunable to request stack tagging, I think it should be an
error if this is requested but the main binary is not marked with the
DT_AARCH64_MEMTAG_STACK flag.

> > 
> > ...
> >
> >>> DSOs are not supposed to have DT_AARCH64_MEMTAG_{HEAP,STACK,GLOBALS}
> >>> marking according to the spec, so if they do, it's an error like you
> >>> suggest. I also agree that even if we decided to load such a DSO, we
> >>> should not alter the state of the process.
> >>
> >> Without the marking on DSO it would be *really* hard to avoid loading DSO that
> >> *required* STACK/GLOBAL support if the binary does not enable it.
> > 
> > Well, this is what the spec says. I don't want to spend time trying to
> > change is as it didn't work out in the past. This is what we have to
> > live with.
> 
> I don't recall that spec prohibits marking on DSO,

See section 6:

  The presence of the DT_AARCH64_MEMTAG_STACK dynamic array entry indicates that
  stack allocations should be protected with memory tagging. <...> This entry is
  only valid on the main executable.

> and doing would restrict a
> lot PIE and DSO with _entry calls (like libc.so itself).

You don't need to convince me that the memtag ABI spec does not work
with GNU/Linux, but I've tried changing it and got very strong
resistance, so we have what we have...

> 
> In fact, this neither what gcc nor clang implements now:

Well, there is spec and there is an implementation that partially
follows the spec :)

> ...
>
> We need DSO with proper marking to avoid the potential hard-to-debug issues during
> process loading and dlopen operations.

We do, but we first need a new spec. We are not going to implement an
unwritten spec here please.

> >> We can map to some like 'glibc.cpu.mtemode=sync+async' to mimic the kernel,
> >> but it makes tunable parsing a bit more complex and exposes how Linux
> >> implements it instead of hardware support.
> > 
> > I don't want Turing-complete expressions in tunables :)
> > 
> > Is there a macro for asymmetric in the UAPI headers?
> 
> Unfortunately there is none.

When there is one, we can always extend Glibc. I thought this was the
common convention in Glibc: use new constants and flags only after they
appear in the kernel.

> The original kernel patch did added one, but it
> was later revamp during review.
> 
> I think 'sync+async' makes more sense than 'asymm'

I'm not sure what you mean by 'sync+async' but it is not the same as
'asymm'.

> because 'asymm' is enabled
> iff kernel/chip support (armv8.7), otherwise kernel fallbacks to either sync
> or async. But I think we can discuss this later.

Yes, let's discuss this later.

> > 
> > ...
> >
> >> For the rules you
> >> defined, tunables can not disable tagging (2.), and stack/global are only
> >> gated through markings (4.). So 'none' here only gates whether enable
> >> *heap* tagging for a process without marking. It is still confusing and
> >> conflate two different MTE functionalities. 
> > 
> > It's the same as having 0 for the GCS or BTI tunables. I just follow the
> > same approach. Not sure why it's confusing.
> 
> It is just that it seems redundant, not really 'wrong'.

OK, I can think of how to avoid having 'none', I just prefer to follow
the existing convention.

> > 
> > ...
> >
> > Because 'auto' is one of the modes that kernel supports. Also, see my
> > comment about not wanting to have expressions in the tunable values.
> 
> Right, but 'auto' is a property of the 'kernel' not really glibc. And I think
> it would be better to avoid query the kernel in this case, since kernel allows
> a per-cpu mode.

What do you mean by "query the kernel"? We don't. We just need to
support a way to pass all the flags that the prctl syscall supports and

  PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC

is one of them and it means "mode preferred by the system" which I
described as 'auto' for brevity.

> So 'auto' is already covered by the marking presence

I prefer that selection of the mode does not depend on marking. I don't
think that marking is going to be used a lot and we certainly need to
support configuration for unmarked binaries as well.

Also, there is no 'auto' in the spec (that is the only place where
marking is described). I invented that lack of mode marking means 'auto'
just for completeness.

> (which glibc will use it
> anyway),

Correction: Glibc *may* use it, but it must also support unmarked binaries.

> >>> ...
> >>>
> >>> 'auto' means use mode preferred by CPU as described in:
> >>> https://www.kernel.org/doc/html/next/arm64/memory-tagging-extension.html
> >>
> >> Yes, but what does it gain for the glibc usercase?
> > 
> > It allows to use one of the supported modes. I believe it might be the
> > most commonly used in practice if MTE is enabled system-wide.
> 
> But, what is difference between let the process use kernel default (transparent
> to the caller, no tunable being used), or using the support 'sync' or 'async'?

sync means PR_MTE_TCF_SYNC
async means PR_MTE_TCF_ASYNC
auto means PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC

all of the above is described in the kernel UAPI docs:

https://www.kernel.org/doc/html/next/arm64/memory-tagging-extension.html

and all I want is to support every such configuration: if they exist,
they are useful for someone, and I don't want to prevent them from using
it even if I don't know why they need it.

> For instance, I have a binary with stack marking but no mode.

So, given the way this is implemented in compilers, I think it will be
very difficult to achieve no mode marking in the binary.

> I have:
> 
>  1. No tunable being used - kernel default, which is set by administrator on
>     per-cpu mode.
> 
>  2. system-wide tunable - 'async' or 'sync' and no override.
> 
>  3. No system-wide - tunable will be used to select *an specific* mode (sync
>     or async).
> 
> I still can't see what 'auto' allows different to what other combinations already
> provide.

Like I said, Glibc should not add extra layer of interpretation of the
parameters. When you do the prctl syscall, you have 4 combination of the
PR_MTE_TCF_x flags, and all four need to be supported because they are
all valid.

> It would make sense to enable MTE on non-marked binaries, to instruct glibc to
> enable MTE. But my understanding is your proposal does not want to support this
> scenario.

You've misunderstood. This scenario is actually the most important for
me. I only intend to support marking because they are mentioned in the
spec and we need to provide consistent use of them to the users.

> >
> > ...
> >
> > If binary has any memtag-related marking, heap tagging is only enabled
> > iff DT_AARCH64_MEMTAG_HEAP is present, and the mode is selected based on
> > the rules I've described.
> 
> Right, so there will be no way an user can enable memory tagging on non-marked
> binaries?

I'm not sure where you got this impression from because it is the *opposite*
of what I was trying to say.

Again:

 1) Marking is present => scope is determined by the marking (tunables
    have no effect on the scope, and perhaps not even on the mode like
    you suggest).

2) Marking is not present =>
   2.1) mode is 'none' => no memtag protection (this is default, you
   need to opt-in to use memtag protection)
   2.2) mode is anything by 'none' => enable 'standard' protection
   (which currently means 'heap')

This is exhaustive, a complete set of variants.

> ...

Hope this helps,
Yury
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.