Message ID | 20181005084754.20950-1-kristina.martsenko@arm.com (mailing list archive) |
---|---|
Headers | show |
Series | ARMv8.3 pointer authentication support | expand |
On 05/10/2018 09:47, Kristina Martsenko wrote: > Compile all functions with two ptrauth instructions: paciasp in the > prologue to sign the return address, and autiasp in the epilogue to > authenticate the return address. This should help protect the kernel > against attacks using return-oriented programming. > > CONFIG_ARM64_PTR_AUTH enables pointer auth for both userspace and the > kernel. > > Signed-off-by: Mark Rutland <mark.rutland@arm.com> > Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com> > --- > arch/arm64/Makefile | 4 ++++ > 1 file changed, 4 insertions(+) > > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile > index 106039d25e2f..dbcd43ea99d8 100644 > --- a/arch/arm64/Makefile > +++ b/arch/arm64/Makefile > @@ -56,6 +56,10 @@ KBUILD_AFLAGS += $(lseinstr) $(brokengasinst) > KBUILD_CFLAGS += $(call cc-option,-mabi=lp64) > KBUILD_AFLAGS += $(call cc-option,-mabi=lp64) > > +ifeq ($(CONFIG_ARM64_PTR_AUTH),y) > +KBUILD_CFLAGS += -msign-return-address=all Glad to see this being done and being proposed for mainline. I can see why you would prefer this though have you guys experimented at all with -msign-return-address=non-leaf as well ? Orthogonally and just fair warning - the command lines for this are also being revised to provide ROP and JOP protection using BTI from v8.5-a during the GCC-9 timeframe but I suspect that's a different option. regards Ramana Reviewed-by: Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>
On 05/10/2018 10:01, Ramana Radhakrishnan wrote: > On 05/10/2018 09:47, Kristina Martsenko wrote: >> Compile all functions with two ptrauth instructions: paciasp in the >> prologue to sign the return address, and autiasp in the epilogue to >> authenticate the return address. This should help protect the kernel >> against attacks using return-oriented programming. >> >> CONFIG_ARM64_PTR_AUTH enables pointer auth for both userspace and the >> kernel. >> >> Signed-off-by: Mark Rutland <mark.rutland@arm.com> >> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com> >> --- >> arch/arm64/Makefile | 4 ++++ >> 1 file changed, 4 insertions(+) >> >> diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile >> index 106039d25e2f..dbcd43ea99d8 100644 >> --- a/arch/arm64/Makefile >> +++ b/arch/arm64/Makefile >> @@ -56,6 +56,10 @@ KBUILD_AFLAGS += $(lseinstr) $(brokengasinst) >> KBUILD_CFLAGS += $(call cc-option,-mabi=lp64) >> KBUILD_AFLAGS += $(call cc-option,-mabi=lp64) >> +ifeq ($(CONFIG_ARM64_PTR_AUTH),y) >> +KBUILD_CFLAGS += -msign-return-address=all > > Glad to see this being done and being proposed for mainline. > > I can see why you would prefer this though have you guys experimented at > all with -msign-return-address=non-leaf as well ? I've tried non-leaf and it works too. I'd be fine with switching to it, I'm not sure which would be better for the kernel. What kind of experiments did you have in mind? If I understand correctly, then compared to non-leaf, "all" additionally protects leaf functions that write to the stack. I don't know how many of those there are in the kernel (or will be in the future). I also don't know the additional performance impact of "all", as I don't think we have any v8.3 hardware to test on yet. There is a minor code size impact (0.36% on the current kernel), but I'm not sure how much that matters. > Orthogonally and just fair warning - the command lines for this are also > being revised to provide ROP and JOP protection using BTI from v8.5-a > during the GCC-9 timeframe but I suspect that's a different option. Thanks. I expect it will be a separate Kconfig option to build the kernel with BTI and pointer auth, yes. > Reviewed-by: Ramana Radhakrishnan <ramana.radhakrishnan@arm.com> Thanks! Kristina
On Fri, Oct 5, 2018 at 1:47 AM, Kristina Martsenko <kristina.martsenko@arm.com> wrote: > This series adds support for the ARMv8.3 pointer authentication > extension. The series contains Mark's original patches to enable pointer > authentication for userspace [1], followed by early RFC patches using > pointer authentication in the kernel. It wasn't obvious to me where the PAC mismatch exceptions will be caught. I'm mainly curious to compare the PAC exception handling to the existing stack-protector panic(). Can you point me to which routines manage that? (Perhaps I just missed it in the series...) Thanks for the series! I'm quite excited for ARMv8.3 hardware. :) -Kees
On Fri, Oct 05, 2018 at 09:47:37AM +0100, Kristina Martsenko wrote: > 1) Key support > > This series enables the use of instructions using APIAKey, which is > initialised and maintained per-process (shared by all threads). GCC > currently only makes use of APIAKey. > > This series does not add support for APIBKey, APDAKey, APDBKey, nor > APGAKey. HINT-space instructions using these keys will currently execute > as NOPs. Support for these keys can be added as users appear. > > Note that while we expose the cpuid register (ID_AA64ISAR1_EL1) to > userspace, it only contains one feature for address authentication > (API/APA), so it cannot be used by userspace to tell which keys the > kernel supports. For this the kernel exposes HWCAP bits, one per key > (currently only APIAKey), which must be checked instead. Given that the architecture doesn't provide an identification mechanism for the case where only one of the keys is available, I would much prefer that we expose both of the keys to userspace. Is the only downside of that a possible exception entry overhead if the kernel wants to use pointer authentication as well? Having an initial implementation where the B key operations act as NOPs isn't ideal if we want to support future users -- chances are they'll be put off because deployed kernels don't give them whatever security guarantees they require. It's a bit of a chicken-and-egg problem, so unless we have good reasons to keep the B key hidden, I think we should be exposing it from the start. Will
On 19/10/2018 13:36, Will Deacon wrote: > On Fri, Oct 05, 2018 at 09:47:37AM +0100, Kristina Martsenko wrote: >> 1) Key support >> >> This series enables the use of instructions using APIAKey, which is >> initialised and maintained per-process (shared by all threads). GCC >> currently only makes use of APIAKey. >> >> This series does not add support for APIBKey, APDAKey, APDBKey, nor >> APGAKey. HINT-space instructions using these keys will currently execute >> as NOPs. Support for these keys can be added as users appear. >> >> Note that while we expose the cpuid register (ID_AA64ISAR1_EL1) to >> userspace, it only contains one feature for address authentication >> (API/APA), so it cannot be used by userspace to tell which keys the >> kernel supports. For this the kernel exposes HWCAP bits, one per key >> (currently only APIAKey), which must be checked instead. > > Given that the architecture doesn't provide an identification mechanism > for the case where only one of the keys is available, I would much prefer > that we expose both of the keys to userspace. Is the only downside of > that a possible exception entry overhead if the kernel wants to use pointer > authentication as well? > > Having an initial implementation where the B key operations act as NOPs > isn't ideal if we want to support future users -- chances are they'll > be put off because deployed kernels don't give them whatever security > guarantees they require. It's a bit of a chicken-and-egg problem, so > unless we have good reasons to keep the B key hidden, I think we should > be exposing it from the start. There are patches in flight to get B key signing support in for GCC 9 - so exposing this to user space will be good. Ramana > > Will >
(Sorry for the really late response!) On 15/10/2018 23:42, Kees Cook wrote: > On Fri, Oct 5, 2018 at 1:47 AM, Kristina Martsenko > <kristina.martsenko@arm.com> wrote: >> This series adds support for the ARMv8.3 pointer authentication >> extension. The series contains Mark's original patches to enable pointer >> authentication for userspace [1], followed by early RFC patches using >> pointer authentication in the kernel. > > It wasn't obvious to me where the PAC mismatch exceptions will be > caught. I'm mainly curious to compare the PAC exception handling to > the existing stack-protector panic(). Can you point me to which > routines manage that? (Perhaps I just missed it in the series...) When the PAC authentication fails, it doesn't actually generate an exception, it just flips a bit in the high-order bits of the pointer, making the pointer invalid. Then when the pointer is dereferenced (e.g. as a function return address), it generates the usual type of exception for an invalid address. So when a function return fails in user mode, the exception is handled in __do_user_fault and a forced SIGSEGV is delivered to the task. When a function return fails in kernel mode, the exception is handled in __do_kernel_fault and the task is killed. This is different from stack protector as we don't panic the kernel, we just kill the task. It would be difficult to panic as we don't have a reliable way of knowing that the exception was caused by a PAC authentication failure (we just have an invalid pointer with a specific bit flipped). We also don't print out any PAC-related warning. Thanks, Kristina
On Tue, Nov 13, 2018 at 10:17 AM, Kristina Martsenko <kristina.martsenko@arm.com> wrote: > When the PAC authentication fails, it doesn't actually generate an > exception, it just flips a bit in the high-order bits of the pointer, > making the pointer invalid. Then when the pointer is dereferenced (e.g. > as a function return address), it generates the usual type of exception > for an invalid address. Ah! Okay, thanks. I missed that detail. :) What area of memory ends up being addressable with such bit flips? (i.e. is the kernel making sure nothing executable ends up there?) > So when a function return fails in user mode, the exception is handled > in __do_user_fault and a forced SIGSEGV is delivered to the task. When a > function return fails in kernel mode, the exception is handled in > __do_kernel_fault and the task is killed. > > This is different from stack protector as we don't panic the kernel, we > just kill the task. It would be difficult to panic as we don't have a > reliable way of knowing that the exception was caused by a PAC > authentication failure (we just have an invalid pointer with a specific > bit flipped). We also don't print out any PAC-related warning. There are other "guesses" in __do_kernel_fault(), I think? Could a "PAC mismatch?" warning be included in the Oops if execution fails in the address range that PAC failures would resolve into? -Kees
On 13/11/2018 23:09, Kees Cook wrote: > On Tue, Nov 13, 2018 at 10:17 AM, Kristina Martsenko > <kristina.martsenko@arm.com> wrote: >> When the PAC authentication fails, it doesn't actually generate an >> exception, it just flips a bit in the high-order bits of the pointer, >> making the pointer invalid. Then when the pointer is dereferenced (e.g. >> as a function return address), it generates the usual type of exception >> for an invalid address. > > Ah! Okay, thanks. I missed that detail. :) > > What area of memory ends up being addressable with such bit flips? > (i.e. is the kernel making sure nothing executable ends up there?) The address will be in between the user and kernel address ranges, so it's guaranteed to be invalid and not address any memory. Specifically, assuming a 48-bit VA configuration, user addresses must have bits [55:48] clear and kernel addresses must have bits [63:48] set. When authentication fails it will set two bits in those ranges to "10" or "01", ensuring that the address is no longer a valid user or kernel address. >> So when a function return fails in user mode, the exception is handled >> in __do_user_fault and a forced SIGSEGV is delivered to the task. When a >> function return fails in kernel mode, the exception is handled in >> __do_kernel_fault and the task is killed. >> >> This is different from stack protector as we don't panic the kernel, we >> just kill the task. It would be difficult to panic as we don't have a >> reliable way of knowing that the exception was caused by a PAC >> authentication failure (we just have an invalid pointer with a specific >> bit flipped). We also don't print out any PAC-related warning. > > There are other "guesses" in __do_kernel_fault(), I think? Could a > "PAC mismatch?" warning be included in the Oops if execution fails in > the address range that PAC failures would resolve into? Sounds reasonable to me, I'll add a warning. Thanks, Kristina
On Tue, Nov 13, 2018 at 05:09:00PM -0600, Kees Cook wrote: > On Tue, Nov 13, 2018 at 10:17 AM, Kristina Martsenko > <kristina.martsenko@arm.com> wrote: > > When the PAC authentication fails, it doesn't actually generate an > > exception, it just flips a bit in the high-order bits of the pointer, > > making the pointer invalid. Then when the pointer is dereferenced (e.g. > > as a function return address), it generates the usual type of exception > > for an invalid address. > > Ah! Okay, thanks. I missed that detail. :) > > What area of memory ends up being addressable with such bit flips? > (i.e. is the kernel making sure nothing executable ends up there?) > > > So when a function return fails in user mode, the exception is handled > > in __do_user_fault and a forced SIGSEGV is delivered to the task. When a > > function return fails in kernel mode, the exception is handled in > > __do_kernel_fault and the task is killed. > > > > This is different from stack protector as we don't panic the kernel, we > > just kill the task. It would be difficult to panic as we don't have a > > reliable way of knowing that the exception was caused by a PAC > > authentication failure (we just have an invalid pointer with a specific > > bit flipped). We also don't print out any PAC-related warning. > > There are other "guesses" in __do_kernel_fault(), I think? Could a > "PAC mismatch?" warning be included in the Oops if execution fails in > the address range that PAC failures would resolve into? I'd personally prefer that we didn't try to guess if a fault is due to a failed AUT*, even for logging. Presently, it's not possible to distinguish between a fault resulting from a failed AUT* and a fault which happens to have hte same bits/clear, so there are false positives. The architecture may also change the precise details of the faulting address, and we'd have false negatives in that case. Given that, I think suggesting that a fault is due to a failed AUT* is liable to make things more confusing. Thanks, Mark.
On Wed, Nov 14, 2018 at 3:47 PM, Mark Rutland <mark.rutland@arm.com> wrote: > On Tue, Nov 13, 2018 at 05:09:00PM -0600, Kees Cook wrote: >> On Tue, Nov 13, 2018 at 10:17 AM, Kristina Martsenko >> <kristina.martsenko@arm.com> wrote: >> > When the PAC authentication fails, it doesn't actually generate an >> > exception, it just flips a bit in the high-order bits of the pointer, >> > making the pointer invalid. Then when the pointer is dereferenced (e.g. >> > as a function return address), it generates the usual type of exception >> > for an invalid address. >> >> Ah! Okay, thanks. I missed that detail. :) >> >> What area of memory ends up being addressable with such bit flips? >> (i.e. is the kernel making sure nothing executable ends up there?) >> >> > So when a function return fails in user mode, the exception is handled >> > in __do_user_fault and a forced SIGSEGV is delivered to the task. When a >> > function return fails in kernel mode, the exception is handled in >> > __do_kernel_fault and the task is killed. >> > >> > This is different from stack protector as we don't panic the kernel, we >> > just kill the task. It would be difficult to panic as we don't have a >> > reliable way of knowing that the exception was caused by a PAC >> > authentication failure (we just have an invalid pointer with a specific >> > bit flipped). We also don't print out any PAC-related warning. >> >> There are other "guesses" in __do_kernel_fault(), I think? Could a >> "PAC mismatch?" warning be included in the Oops if execution fails in >> the address range that PAC failures would resolve into? > > I'd personally prefer that we didn't try to guess if a fault is due to a failed > AUT*, even for logging. > > Presently, it's not possible to distinguish between a fault resulting from a > failed AUT* and a fault which happens to have hte same bits/clear, so there are > false positives. The architecture may also change the precise details of the > faulting address, and we'd have false negatives in that case. > > Given that, I think suggesting that a fault is due to a failed AUT* is liable > to make things more confusing. Okay, no worries. It should be pretty clear from the back trace anyway. :) As long as there isn't any way for the pointer bit flips to result in an addressable range, I'm happy. :) -Kees