We have already amended {get,put}_user so that they access user memory directly via the user capability in PCuABI; now is time to convert copy_*_user too. As a result, the copy will be aborted if the user capability is unsuitable to perform the access (potentially partway, in case the tail of the targeted region is out of bounds).
__arch_copy_{from,to}_user present an additional challenge in that they are implemented fully in assembly. Fortunately, the registers holding the source and destination pointers are mostly used as base registers for load/store instructions. After switching to C64, such instructions operate on C registers instead of X, so it becomes simply a matter of modifying the register aliases in PCuABI; the req_reg_pcuabi macro is introduced for that purpose. Explicit ADD instructions are also used in the user_{ldst,ldp,stp} helpers; those are unproblematic as they can operate on both X and C registers.
In the few situations where the pointers are being somehow inspected, we keep operating on their address only, by using the corresponding X register; srcx and dstx are introduced for that purpose. This is necessary in some cases due to the instruction simply not accepting C registers (e.g. TST), while in others it is rather a matter of convenience, as it means we don't need to convert additional register aliases to capabilities (CMP, SUB).
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/asm/assembler.h | 8 ++++++++ arch/arm64/lib/copy_from_user.S | 31 +++++++++++++++++++++++++++--- arch/arm64/lib/copy_template.S | 10 ++++++---- arch/arm64/lib/copy_to_user.S | 15 ++++++++++++--- 4 files changed, 54 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 6b31eb7687ba..7d4046ded4df 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -34,6 +34,14 @@ wx\n .req w\n .endr
+ .macro req_reg_pcuabi, name, pcuabi_reg, default_reg +#ifdef CONFIG_CHERI_PURECAP_UABI + \name .req \pcuabi_reg +#else + \name .req \default_reg +#endif + .endm + .macro disable_daif msr daifset, #0xf .endm diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 46dc57cd8d34..d35126460624 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -66,22 +66,47 @@
end .req x5 dstin .req x6 -srcin .req x15 +req_reg_pcuabi srcin, c15, x15 SYM_FUNC_START(COPY_FUNC_NAME) add end, x0, x2 mov dstin, x0 +#ifdef CONFIG_CHERI_PURECAP_UABI +.arch morello+c64 + bx #4 + /* + * Having switched to C64, argumentless RET is equivalent to RET CLR. + * Because we have been called from A64, only LR is set. We therefore + * set CLR to a valid capability, derived from PCC (as if we had been + * called from C64). Conveniently this will also automatically switch + * us back to A64 when returning (as the LSB of LR should be unset). + */ + cvtp clr, lr + /* + * Accessing memory via X registers in C64 requires using + * alternate-base loads and stores; unfortunately most loads and stores + * used in copy_template.S do not have an alternate-base counterpart. + * The most straightforward solution is to access memory via C + * registers only. We therefore need to create a valid capability for + * the kernel buffer too, which is done by deriving it from DDC. Since + * X-based accesses are validated against DDC, this is functionally + * equivalent. + */ + cvtd c0, x0 + mov srcin, c1 +#else mov srcin, x1 +#endif #include "copy_template.S" mov x0, #0 // Nothing to copy ret
// Exception fixups -9997: cmp dst, dstin +9997: cmp dstx, dstin b.ne 9998f // Before being absolutely sure we couldn't copy anything, try harder USER(9998f, ldtrb tmp1w, [srcin]) strb tmp1w, [dst], #1 -9998: sub x0, end, dst // bytes not copied +9998: sub x0, end, dstx // bytes not copied ret SYM_FUNC_END(COPY_FUNC_NAME) EXPORT_SYMBOL(COPY_FUNC_NAME) diff --git a/arch/arm64/lib/copy_template.S b/arch/arm64/lib/copy_template.S index 1c54c338d078..a07fa44d4c99 100644 --- a/arch/arm64/lib/copy_template.S +++ b/arch/arm64/lib/copy_template.S @@ -19,8 +19,10 @@ * x1 - src * x2 - n */ -dst .req x0 -src .req x1 +req_reg_pcuabi dst, c0, x0 +dstx .req x0 +req_reg_pcuabi src, c1, x1 +srcx .req x1 count .req x2 tmp1 .req x3 tmp1w .req w3 @@ -50,7 +52,7 @@ Bc_h .req c10 /*When memory length is less than 16, the accessed are not aligned.*/ b.lo .Ltiny15
- neg tmp2, src + neg tmp2, srcx ands tmp2, tmp2, #15/* Bytes to reach alignment. */ b.eq .LSrcAligned sub count, count, tmp2 @@ -79,7 +81,7 @@ Bc_h .req c10 .LSrcAligned: #ifdef COPY_CAPTAGS /* src now 16-byte aligned, copy capability tags if dst also aligned */ - tst dst, #15 + tst dstx, #15 b.eq .LSrcAligned_cpycaps #endif cmp count, #64 diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index a501e7665e77..316349701edf 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -65,23 +65,32 @@
end .req x5 dstin .req x6 -srcin .req x15 +req_reg_pcuabi srcin, c15, x15 SYM_FUNC_START(COPY_FUNC_NAME) add end, x0, x2 mov dstin, x0 +#ifdef CONFIG_CHERI_PURECAP_UABI +.arch morello+c64 + bx #4 + /* See comments in copy_from_user.S */ + cvtp clr, lr + cvtd c1, x1 + mov srcin, c1 +#else mov srcin, x1 +#endif #include "copy_template.S" mov x0, #0 ret
// Exception fixups -9997: cmp dst, dstin +9997: cmp dstx, dstin b.ne 9998f // Before being absolutely sure we couldn't copy anything, try harder ldrb tmp1w, [srcin] USER(9998f, sttrb tmp1w, [dst]) add dst, dst, #1 -9998: sub x0, end, dst // bytes not copied +9998: sub x0, end, dstx // bytes not copied ret SYM_FUNC_END(COPY_FUNC_NAME) EXPORT_SYMBOL(COPY_FUNC_NAME)