Re: [Xen-devel] [PATCH v5 06/16] livepatch: ARM/x86: Check displacement of old_addr and new_addr
On 09/23/2016 04:59 PM, Konrad Rzeszutek Wilk wrote: On Fri, Sep 23, 2016 at 11:37:27AM -0400, Konrad Rzeszutek Wilk wrote: On Fri, Sep 23, 2016 at 03:36:27PM +0100, Ross Lagerwall wrote: On 09/21/2016 06:32 PM, Konrad Rzeszutek Wilk wrote: If the distance is too big we are in trouble - as our relocation distance can surely be clipped, or still have a valid width - but cause an overflow of distance. On various architectures the maximum displacement for a unconditional branch/jump varies. ARM32 is +/- 32MB, ARM64 is +/- 128MB while x86 for 32-bit relocations is +/- 2G. Note: On x86 we could use the 64-bit jmpq instruction which would provide much bigger displacement to do a jump, but we would still have issues with the new function not being able to reach any of the old functions (as all the relocations would assume 32-bit displacement). And "furthermore would require an register or memory location to load/store the address to." (From Jan). On ARM the conditional branch supports even a smaller displacement but fortunately we are not using that. Wouldn't this be simpler as a BUILD_BUG_ON() in arch_livepatch_init()? Something like BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - XEN_VIRT_START) > ARCH_LIVEPATCH_RANGE)? And BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - XEN_VIRT_START) > ARCH_LIVEPATCH_RANGE) And something similar for ARM... What does that have to with the payload? The displacement calculations(checks) are done when we load the payload. Ooh, you are thinking of just making sure that the displacement in virtual addresses _should_ be OK. And that BUILD_BUG_ON would certainly do it. But my thinking was more of the payload either having some wacky relocations (say having an initial addendum that along with the XEN_VIRT_START -> XEN_VIRT_END) causes us to be way past the right place (especially with ARM32 where we only have 32MB distance). And having this extra check makes me sleep better at night. Assuming that you had build checked the maximum possible displacement in virtual address space, then the only way a payload would fail the check is if either old_addr or new_addr doesn't point to hypervisor code or payload code (otherwise it would be in the range), but instead points to some other bit of memory. In that case, the payload is so completely broken you've got bigger problems... There are many ways that payloads that can be broken. This checks only a special case of the payload being broken -- and only if the incorrect bit exceeds a certain range... I don't really object to the check but it seems like an unnecessarily special case. Cheers, -- Ross Lagerwall ___ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCH v5 06/16] livepatch: ARM/x86: Check displacement of old_addr and new_addr
On Fri, Sep 23, 2016 at 11:37:27AM -0400, Konrad Rzeszutek Wilk wrote: > On Fri, Sep 23, 2016 at 03:36:27PM +0100, Ross Lagerwall wrote: > > On 09/21/2016 06:32 PM, Konrad Rzeszutek Wilk wrote: > > > If the distance is too big we are in trouble - as our relocation > > > distance can surely be clipped, or still have a valid width - but > > > cause an overflow of distance. > > > > > > On various architectures the maximum displacement for a unconditional > > > branch/jump varies. ARM32 is +/- 32MB, ARM64 is +/- 128MB while x86 > > > for 32-bit relocations is +/- 2G. > > > > > > Note: On x86 we could use the 64-bit jmpq instruction which > > > would provide much bigger displacement to do a jump, but we would > > > still have issues with the new function not being able to reach > > > any of the old functions (as all the relocations would assume 32-bit > > > displacement). And "furthermore would require an register or > > > memory location to load/store the address to." (From Jan). > > > > > > On ARM the conditional branch supports even a smaller displacement > > > but fortunately we are not using that. > > > > Wouldn't this be simpler as a BUILD_BUG_ON() in arch_livepatch_init()? > > > > Something like BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - > > XEN_VIRT_START) > ARCH_LIVEPATCH_RANGE)? > > > > And BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - XEN_VIRT_START) > > > ARCH_LIVEPATCH_RANGE) > > > > And something similar for ARM... > > What does that have to with the payload? The displacement calculations(checks) > are done when we load the payload. Ooh, you are thinking of just making sure that the displacement in virtual addresses _should_ be OK. And that BUILD_BUG_ON would certainly do it. But my thinking was more of the payload either having some wacky relocations (say having an initial addendum that along with the XEN_VIRT_START -> XEN_VIRT_END) causes us to be way past the right place (especially with ARM32 where we only have 32MB distance). And having this extra check makes me sleep better at night. Adding the BUILD_BUG_ON as a futher check is fine, but as a different patch. > > > > -- > > Ross Lagerwall ___ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCH v5 06/16] livepatch: ARM/x86: Check displacement of old_addr and new_addr
On Fri, Sep 23, 2016 at 03:36:27PM +0100, Ross Lagerwall wrote: > On 09/21/2016 06:32 PM, Konrad Rzeszutek Wilk wrote: > > If the distance is too big we are in trouble - as our relocation > > distance can surely be clipped, or still have a valid width - but > > cause an overflow of distance. > > > > On various architectures the maximum displacement for a unconditional > > branch/jump varies. ARM32 is +/- 32MB, ARM64 is +/- 128MB while x86 > > for 32-bit relocations is +/- 2G. > > > > Note: On x86 we could use the 64-bit jmpq instruction which > > would provide much bigger displacement to do a jump, but we would > > still have issues with the new function not being able to reach > > any of the old functions (as all the relocations would assume 32-bit > > displacement). And "furthermore would require an register or > > memory location to load/store the address to." (From Jan). > > > > On ARM the conditional branch supports even a smaller displacement > > but fortunately we are not using that. > > Wouldn't this be simpler as a BUILD_BUG_ON() in arch_livepatch_init()? > > Something like BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - > XEN_VIRT_START) > ARCH_LIVEPATCH_RANGE)? > > And BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - XEN_VIRT_START) > > ARCH_LIVEPATCH_RANGE) > > And something similar for ARM... What does that have to with the payload? The displacement calculations(checks) are done when we load the payload. > > -- > Ross Lagerwall ___ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCH v5 06/16] livepatch: ARM/x86: Check displacement of old_addr and new_addr
On 09/21/2016 06:32 PM, Konrad Rzeszutek Wilk wrote: If the distance is too big we are in trouble - as our relocation distance can surely be clipped, or still have a valid width - but cause an overflow of distance. On various architectures the maximum displacement for a unconditional branch/jump varies. ARM32 is +/- 32MB, ARM64 is +/- 128MB while x86 for 32-bit relocations is +/- 2G. Note: On x86 we could use the 64-bit jmpq instruction which would provide much bigger displacement to do a jump, but we would still have issues with the new function not being able to reach any of the old functions (as all the relocations would assume 32-bit displacement). And "furthermore would require an register or memory location to load/store the address to." (From Jan). On ARM the conditional branch supports even a smaller displacement but fortunately we are not using that. Wouldn't this be simpler as a BUILD_BUG_ON() in arch_livepatch_init()? Something like BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - XEN_VIRT_START) > ARCH_LIVEPATCH_RANGE)? And BUILD_BUG_ON(((XEN_VIRT_END - NR_CPUS * PAGE_SIZE) - XEN_VIRT_START) > ARCH_LIVEPATCH_RANGE) And something similar for ARM... -- Ross Lagerwall ___ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCH v5 06/16] livepatch: ARM/x86: Check displacement of old_addr and new_addr
Hi Konrad, On 21/09/16 18:32, Konrad Rzeszutek Wilk wrote: diff --git a/xen/include/asm-arm/livepatch.h b/xen/include/asm-arm/livepatch.h index 929c7d9..482d74f 100644 --- a/xen/include/asm-arm/livepatch.h +++ b/xen/include/asm-arm/livepatch.h @@ -6,6 +6,8 @@ #ifndef __XEN_ARM_LIVEPATCH_H__ #define __XEN_ARM_LIVEPATCH_H__ +#include /* For SZ_* macros. */ + /* On ARM32,64 instructions are always 4 bytes long. */ #define ARCH_PATCH_INSN_SIZE 4 @@ -15,6 +17,15 @@ */ extern void *vmap_of_xen_text; +/* These ranges are only for unconditional branches. */ +#ifdef CONFIG_ARM_32 +/* ARM32: A4.3 IN ARM DDI 0406C.j - we are using only ARM instructions in Xen.*/ NIT: There is no j revision of the ARMv7 manual. The latest on is 0406C.c. With that: Reviewed-by: Julien Grall Regards, -- Julien Grall ___ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel
[Xen-devel] [PATCH v5 06/16] livepatch: ARM/x86: Check displacement of old_addr and new_addr
If the distance is too big we are in trouble - as our relocation distance can surely be clipped, or still have a valid width - but cause an overflow of distance. On various architectures the maximum displacement for a unconditional branch/jump varies. ARM32 is +/- 32MB, ARM64 is +/- 128MB while x86 for 32-bit relocations is +/- 2G. Note: On x86 we could use the 64-bit jmpq instruction which would provide much bigger displacement to do a jump, but we would still have issues with the new function not being able to reach any of the old functions (as all the relocations would assume 32-bit displacement). And "furthermore would require an register or memory location to load/store the address to." (From Jan). On ARM the conditional branch supports even a smaller displacement but fortunately we are not using that. Signed-off-by: Konrad Rzeszutek Wilk --- Cc: Andrew Cooper Cc: Jan Beulich Cc: Stefano Stabellini Cc: Julien Grall v3: New submission. v4: s/arch_livepatch_verify_distance/livepatch_verify_distance/ s/LIVEPATCH_ARCH_RANGE/ARCH_LIVEPATCH_RANGE/ v5: Updated commit description with Jan's comment Ditch the casting of long on calculating offset. Move most of the docs in "livepatch: Initial ARM64 support." Drop the cast on assigning ARCH_LIVEPATCH_RANGE to an variable. --- docs/misc/livepatch.markdown| 3 +++ xen/arch/arm/arm64/livepatch.c | 1 + xen/common/livepatch.c | 4 xen/include/asm-arm/livepatch.h | 11 +++ xen/include/asm-x86/livepatch.h | 3 +++ xen/include/xen/livepatch.h | 17 - 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/misc/livepatch.markdown b/docs/misc/livepatch.markdown index ff2cfb8..16f45f3 100644 --- a/docs/misc/livepatch.markdown +++ b/docs/misc/livepatch.markdown @@ -1151,6 +1151,9 @@ with proper offset is used for an unconditional branch to the new code. This means that that `old_size` **MUST** be at least four bytes if patching in trampoline. +The instruction offset is limited on ARM32 to +/- 32MB to displacement +and on ARM64 to +/- 128MB displacement. + The new code is placed in the 8M - 10M virtual address space while the Xen code is in 2M - 4M. That gives us enough space. diff --git a/xen/arch/arm/arm64/livepatch.c b/xen/arch/arm/arm64/livepatch.c index 7cb1812..d3fc9ac 100644 --- a/xen/arch/arm/arm64/livepatch.c +++ b/xen/arch/arm/arm64/livepatch.c @@ -40,6 +40,7 @@ void arch_livepatch_apply(struct livepatch_func *func) else insn = aarch64_insn_gen_nop(); +/* Verified in livepatch_verify_distance. */ ASSERT(insn != AARCH64_BREAK_FAULT); new_ptr = func->old_addr - (void *)_start + vmap_of_xen_text; diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c index 66f23e0..f2f866c 100644 --- a/xen/common/livepatch.c +++ b/xen/common/livepatch.c @@ -545,6 +545,10 @@ static int prepare_payload(struct payload *payload, rc = resolve_old_address(f, elf); if ( rc ) return rc; + +rc = livepatch_verify_distance(f); +if ( rc ) +return rc; } sec = livepatch_elf_sec_by_name(elf, ".livepatch.hooks.load"); diff --git a/xen/include/asm-arm/livepatch.h b/xen/include/asm-arm/livepatch.h index 929c7d9..482d74f 100644 --- a/xen/include/asm-arm/livepatch.h +++ b/xen/include/asm-arm/livepatch.h @@ -6,6 +6,8 @@ #ifndef __XEN_ARM_LIVEPATCH_H__ #define __XEN_ARM_LIVEPATCH_H__ +#include /* For SZ_* macros. */ + /* On ARM32,64 instructions are always 4 bytes long. */ #define ARCH_PATCH_INSN_SIZE 4 @@ -15,6 +17,15 @@ */ extern void *vmap_of_xen_text; +/* These ranges are only for unconditional branches. */ +#ifdef CONFIG_ARM_32 +/* ARM32: A4.3 IN ARM DDI 0406C.j - we are using only ARM instructions in Xen.*/ +#define ARCH_LIVEPATCH_RANGE SZ_32M +#else +/* ARM64: C1.3.2 in ARM DDI 0487A.j */ +#define ARCH_LIVEPATCH_RANGE SZ_128M +#endif + #endif /* __XEN_ARM_LIVEPATCH_H__ */ /* diff --git a/xen/include/asm-x86/livepatch.h b/xen/include/asm-x86/livepatch.h index 5e04aa1..7dfc2e7 100644 --- a/xen/include/asm-x86/livepatch.h +++ b/xen/include/asm-x86/livepatch.h @@ -6,7 +6,10 @@ #ifndef __XEN_X86_LIVEPATCH_H__ #define __XEN_X86_LIVEPATCH_H__ +#include /* For SZ_* macros. */ + #define ARCH_PATCH_INSN_SIZE 5 +#define ARCH_LIVEPATCH_RANGE SZ_2G #endif /* __XEN_X86_LIVEPATCH_H__ */ diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h index b7f66d4..b7b84e7 100644 --- a/xen/include/xen/livepatch.h +++ b/xen/include/xen/livepatch.h @@ -12,6 +12,7 @@ struct livepatch_elf_sym; struct xen_sysctl_livepatch_op; #include +#include /* For -ENOSYS or -EOVERFLOW */ #ifdef CONFIG_LIVEPATCH /* @@ -79,6 +80,21 @@ unsigned int livepatch_insn_len(const struct livepatch_func *func) return ARCH_PATCH_INSN_SIZE; } + +static inline int livepatch_verify_distance(const struct livepatch_func *func) +{ +long offset; +long range = ARCH_LIVEPATCH_RANGE;