lkml.org 
[lkml]   [2022]   [Mar]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 21/30] riscv: Fix auipc+jalr relocation range checks
    Date
    From: Emil Renner Berthing <kernel@esmil.dk>

    commit 0966d385830de3470b7131db8e86c0c5bc9c52dc upstream.

    RISC-V can do PC-relative jumps with a 32bit range using the following
    two instructions:

    auipc t0, imm20 ; t0 = PC + imm20 * 2^12
    jalr ra, t0, imm12 ; ra = PC + 4, PC = t0 + imm12

    Crucially both the 20bit immediate imm20 and the 12bit immediate imm12
    are treated as two's-complement signed values. For this reason the
    immediates are usually calculated like this:

    imm20 = (offset + 0x800) >> 12
    imm12 = offset & 0xfff

    ..where offset is the signed offset from the auipc instruction. When
    the 11th bit of offset is 0 the addition of 0x800 doesn't change the top
    20 bits and imm12 considered positive. When the 11th bit is 1 the carry
    of the addition by 0x800 means imm20 is one higher, but since imm12 is
    then considered negative the two's complement representation means it
    all cancels out nicely.

    However, this addition by 0x800 (2^11) means an offset greater than or
    equal to 2^31 - 2^11 would overflow so imm20 is considered negative and
    result in a backwards jump. Similarly the lower range of offset is also
    moved down by 2^11 and hence the true 32bit range is

    [-2^31 - 2^11, 2^31 - 2^11)

    Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
    Fixes: e2c0cdfba7f6 ("RISC-V: User-facing API")
    Cc: stable@vger.kernel.org
    Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    arch/riscv/kernel/module.c | 21 ++++++++++++++++-----
    1 file changed, 16 insertions(+), 5 deletions(-)

    --- a/arch/riscv/kernel/module.c
    +++ b/arch/riscv/kernel/module.c
    @@ -21,6 +21,19 @@
    #include <asm/pgtable.h>
    #include <asm/sections.h>

    +/*
    + * The auipc+jalr instruction pair can reach any PC-relative offset
    + * in the range [-2^31 - 2^11, 2^31 - 2^11)
    + */
    +static bool riscv_insn_valid_32bit_offset(ptrdiff_t val)
    +{
    +#ifdef CONFIG_32BIT
    + return true;
    +#else
    + return (-(1L << 31) - (1L << 11)) <= val && val < ((1L << 31) - (1L << 11));
    +#endif
    +}
    +
    static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v)
    {
    if (v != (u32)v) {
    @@ -103,7 +116,7 @@ static int apply_r_riscv_pcrel_hi20_rela
    ptrdiff_t offset = (void *)v - (void *)location;
    s32 hi20;

    - if (offset != (s32)offset) {
    + if (!riscv_insn_valid_32bit_offset(offset)) {
    pr_err(
    "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
    me->name, (long long)v, location);
    @@ -205,10 +218,9 @@ static int apply_r_riscv_call_plt_rela(s
    Elf_Addr v)
    {
    ptrdiff_t offset = (void *)v - (void *)location;
    - s32 fill_v = offset;
    u32 hi20, lo12;

    - if (offset != fill_v) {
    + if (!riscv_insn_valid_32bit_offset(offset)) {
    /* Only emit the plt entry if offset over 32-bit range */
    if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
    offset = module_emit_plt_entry(me, v);
    @@ -232,10 +244,9 @@ static int apply_r_riscv_call_rela(struc
    Elf_Addr v)
    {
    ptrdiff_t offset = (void *)v - (void *)location;
    - s32 fill_v = offset;
    u32 hi20, lo12;

    - if (offset != fill_v) {
    + if (!riscv_insn_valid_32bit_offset(offset)) {
    pr_err(
    "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
    me->name, (long long)v, location);

    \
     
     \ /
      Last update: 2022-03-14 12:41    [W:6.754 / U:0.072 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site