https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128299

            Bug ID: 128299
           Summary: [RISC-V] Zicfilp: tablejump_cfi emits x7
                    software-guarded jr t2 without bounds check when
                    default is unreachable
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: 220245569 at seu dot edu.cn
                CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
  Target Milestone: ---
            Target: riscv64-*-*

Summary
-------
Under `-O2 -march=rv64gc_zicfilp -fcf-protection=branch`, when a `switch`
statement has `default: __builtin_unreachable()`, GCC elides the unsigned index
bounds check (`bgtu`) while still dispatching the jump table via `jr t2` (`rs1
= x7`).

In `gcc/stmt.cc:1345-1357`, `expand_case` sets `default_label = NULL` when the
default destination is unreachable, causing `do_tablejump` to omit the range
check. On RISC-V, `tablejump_cfi<mode>` (`gcc/config/riscv/riscv.md:3969-4013`)
uses `mv t2, %0; jr t2` (`x7` software-guarded branch), which suppresses
hardware `ELP = LP_EXPECTED` checking at the target.

The RISC-V Zicfilp specification (`cfi.adoc` ยง2.1) permits `x7`
software-guarded
branches for switch tables only when the compiler both places the table in
read-only memory AND performs bounds checking on the index. Eliding the bounds
check allows an out-of-range index to jump to an arbitrary offset without an
`lpad` check. (Note: LLVM recently fixed the identical issue in PR #234733.)

Reproducer
----------
$ cat trigger.c
extern int h0(void), h1(void), h2(void), h3(void), h4(void), h5(void);

int dispatch(unsigned x) {
    switch (x) {
    case 0: return h0();
    case 1: return h1();
    case 2: return h2();
    case 3: return h3();
    case 4: return h4();
    case 5: return h5();
    default: __builtin_unreachable();
    }
}

Compile flag: riscv64-linux-gnu-gcc -O2 -march=rv64gc_zicfilp -mabi=lp64d
-fcf-protection=branch -S trigger.c

$ cat trigger.s
dispatch:
        lpad    0
        lui     a5,%hi(.L4)
        slli    a0,a0,2                 # no bgtu bounds check on a0!
        addi    a5,a5,%lo(.L4)
        add     a0,a0,a5
        lw      t2,0(a0)
        jr      t2                      # rs1 = x7 suppresses hardware lpad
check

Suggested Fix
-------------
Preserve the jump-table bounds check in `expand_case` / `do_tablejump` when
forward-edge CFI (`flag_cf_protection & CF_BRANCH`) is enabled.

Reply via email to