https://sourceware.org/bugzilla/show_bug.cgi?id=34957

            Bug ID: 34957
           Summary: [RISC-V] GNU ld partially relaxes a TLS-LE group and
                    leaves a load through stale a0
           Product: binutils
           Version: 2.48 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: basavesh.shivakumar at wsu dot edu
  Target Milestone: ---

Created attachment 17250
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17250&action=edit
repro

GNU ld can perform only part of a RISC-V local-exec TLS relaxation when the
`R_RISCV_TPREL_HI20` and `R_RISCV_TPREL_ADD` relocations carry adjacent
`R_RISCV_RELAX` markers but the following `R_RISCV_TPREL_LO12_I` relocation
does not.

The attached standalone reproducer is generated by GNU as from documented
`.option relax` and `.option norelax` scopes. The relevant input is:

```asm
.option push
.option relax
ct_load:
  lui a0, %tprel_hi(tvar)
  add a0, a0, tp, %tprel_add(tvar)
.option norelax
  ld a0, %tprel_lo(tvar)(a0)
.option pop
  ret
```

The `lui` in the input overwrites `a0`, so the incoming value of `a0` is dead.
With relaxation enabled, GNU ld deletes the `lui` and `add` but retains the
load:

```asm
ct_load:
  ld a0,0(a0)
  ret
```

This revives the incoming value of `a0` and changes program semantics. The
wrong-value witness passes a pointer to a word containing 13 while the TLS word
contains 7. The affected executable returns `13 ^ 7 = 10`; both the same input
linked with `--no-relax` and a canonical all-marked TLS-LE sequence return 0.

Expected behavior: the linker should not delete only the address-producing
part of this relocation group. It should either preserve the sequence or
relax it in a way that retains the TLS address semantics.

I reproduced this with GNU ld 2.46 and with official Binutils master commit
`f735688fcb997db4821ed6f9df40425b31b0cd0c`, fetched on 2026-11-05. The
fresh master build identifies itself as GNU ld 2.47.50.20261105.

There is also a bounded constant-time consequence. The reproducer includes an
optional equal-value witness in which incoming `a0` selects one of two
different addresses containing the same value. Both inputs have the same
functional result and execute the same linked instruction sequence, but the
retained load address follows the incoming selection. This is an architectural
address-dependence observation, not a remote timing measurement. I am not
claiming key recovery or a known affected deployed cryptographic instance.

The likely fix area is the RISC-V TLS local-exec relaxation logic: relaxation
should require a complete, consistently marked relocation group before
deleting any address-producing instruction.

This is related to, but distinct from, bug 34846. Bug 34846 concerns accepting
a LO12-only TLS-LE form in a shared object. This report concerns partial
relaxation deleting marked address producers while leaving an unmarked
consumer in an executable.

Reproduction:

```sh
tar -xzf gnu-ld-riscv-tlsle-partial-relax-reproducer.tar.gz
cd gnu-ld-riscv-tlsle-partial-relax-reproducer
./repro.sh
```

The script requires GNU RISC-V binutils and `qemu-riscv64`. It assembles and
links every input locally, prints the relevant disassembly, and checks the
affected case and both controls.

Credit: Basavesh Ammanaghatta Shivakumar and Swarn Priya, Washington State
University.

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to