Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240214021925.GC4163@brightrain.aerifal.cx>
Date: Tue, 13 Feb 2024 21:19:25 -0500
From: Rich Felker <dalias@...c.org>
To: Markus Wichmann <nullplan@....net>
Cc: musl@...ts.openwall.com, enh <enh@...gle.com>
Subject: Re: PAC/BTI Support on aarch64

On Tue, Feb 13, 2024 at 06:51:47PM +0100, Markus Wichmann wrote:
> Am Tue, Feb 13, 2024 at 08:47:42AM -0600 schrieb William Roberts:
> > It should. Is there a known minimal tool chain requirement and I can test?
> 
> Typically the first C99 compiler or the first aarch64 compiler,
> whichever is younger.

I think binutils is the relevant component, and that'd be whichever
version of binutils added aarch64.

> > No, anywhere branches are allowed, a BTI instruction must be the first
> > instruction. BTI is just a way for software to say, hey this is a
> > valid jump/branch
> > target, allow it. This reduces the amount of gadgets available to an
> > attacker, which
> > is why libc is such a juicy target, as it's in everything. A lot of
> > things static link it,
> > which effectively turns it off for the whole process.
> >
> 
> So this means there must be a BTI instruction following every single BL
> instruction.
> 
> But in the end this isn't that much different from endbr64 on the PC.
> Whatever happened to those patches, BTW?

What is the situation on x86? Does it use the same kind of per-page
enforcement mode, or is it only global, requiring disabling it if any
DSO lacks support? Is the endbr64 opcode a guaranteed-safe nop on
older ISA levels, or does it need to be conditional?

> > Yes, so the kernel will manage the EL1 register flag for this, and then
> > mprotect sets the PROT_BTI flag during dlopen().
> 
> Well, this is a novelty. This is the first time there will be an
> arch-specific flag in mmap()/mprotect() for the musl dynlinker. So far
> that code has been entirely portable.

Can the flag be used at mmap time, or only in mprotect? It would be a
lot more efficient to do it as part of the mmap, but getting
visibility to the note to know you need it at mmap time seems
difficult and more costly than doing the mprotect later...

I assume we would either add the code conditional on the existence of
a PROT_BTI macro (#ifdef) and define that to the corresponding thing
on other archs in the future, or abstract it with a new name in
arch/$ARCH/reloc.h defined in terms of whatever the arch provides so
as to be a little bit more naming-agnostic.

It should not be #ifdef __aarch64__ or similar.

> > It's important to note, that even when enabling the assembly code files, if the
> > C level source is not built with -mbranch-protection=standard, the feature will
> > remain off for the library.
> >
> 
> Arch-specific compiler flags are not a problem; configure.sh can add
> those as needed.

Yep, that's fine. Possibly a question of whether it should be on by
default or configurable, but if there's essentially no cost,
on-by-default seems fine.

> > I can't think of anything like this offhand, but aarches may want to add prot
> > flags to mprotect calls.
> 
> That hasn't happened yet. Of course, this may be as simple as adding a
> static inline function. The fact that the important information is in a
> note section is yet another novelty, of course. So far, the important
> information (even arch-specific) has been contained in the dynamic
> section.

Yes, that's gratuitously annoying. Ideally it would have been
somewhere easily accessible from the Ehdr so it's available at initial
mmap time... :/

> > it usually
> > #ifdef aarch64
> > if (gnu_notes_bti_set && (prot & PROT_EXEC)) {
> >     prot |= PROT_BTI;
> > else {
> >     prot &= ~PROT_BTI;
> > }
> > #endif
> >
> > mprotect(..., prot);
> >
> 
> So far we have managed to steer clear of conditional inclusion, and I
> think we should try to keep it that way.

Yes. I think reloc.h should define a predicate macro (which may call a
static inline function if the predicate is complex) to check if a DSO
needs branch protection on its PROT_EXEC segments.
src/internal/dynlink.h could provide a default always-false one if
it's not defined. Then dynlink.c can just, when that predicate
evaluates true, loop thru the segments and mprotect any PROT_EXEC ones
to also have PROT_BTI or whatever.

This remains very arch-agnostic and the code should be either directly
usable on other archs, or admit easy generalization if needed.

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.