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