Skip to content

[bpf-ci-bot] ksock_lsm fails under GCC BPF: verifier retval range lost on sign-extending ctx load #516

Description

@kernel-patches-review-bot

Summary

The ksock_lsm selftest fails when compiled with the GCC BPF backend because the verifier's retval range constraint ([-4095, 0]) is overwritten by coerce_reg_to_size_sx() during post-processing of sign-extending sub-word context loads.

Failure Details

Root Cause Analysis

GCC BPF generates a sign-extending 32-bit load (BPF_LDX | BPF_MEMSX | BPF_W, opcode 0x81) to read the int ret parameter from the LSM trampoline context:

0: (81) r6 = *(s32 *)(r1 +24)  ; R6=scalar(smin=0xffffffff80000000,smax=0x7fffffff)

The verifier correctly identifies this as the LSM retval (via btf_ctx_access setting info->is_retval = true) and applies the return value range [-4095, 0] using __mark_reg_s32_range() in check_mem_access() at kernel/bpf/verifier.c:6441.

However, later in the same function (line 6557), because size=4 < BPF_REG_SIZE=8 and is_ldsx=true, the code calls coerce_reg_to_size_sx(). This function attempts to determine the sign-extension bounds from the register's current s64 range. The range [-4095, 0] has smin=-4095 (top 32 bits = 0xFFFFFFFF) and smax=0 (top 32 bits = 0x00000000). Since these differ, coerce_reg_to_size_sx() conservatively widens the register to [S32_MIN, S32_MAX], destroying the retval range.

The verifier then rejects the program at exit because R0 has range [-2147483648, 2147483647] instead of [-4095, 0].

LLVM/clang avoids this by generating a 64-bit load (BPF_LDX | BPF_MEM | BPF_DW) for the same access, so size == BPF_REG_SIZE and the coercion is skipped.

Relevant commits:

  • c7838e3dc61a ("selftests/bpf: Add ksock kfunc test") — added Aug 13
  • 5e0b273e0a62 ("bpf: Reset register bounds before narrowing retval range in check_mem_access()") — added mark_reg_unknown() before range, but didn't address the post-coercion issue
  • Denylisted in vmtest commit beebabb (Aug 15)

Proposed Fix

Skip coerce_reg_to_size_sx() when the retval range has already been correctly applied. The range set by __mark_reg_s32_range() already represents the valid post-sign-extension bounds (a 32-bit value in [-4095, 0] sign-extends to the same 64-bit range), so the coercion is a no-op at best and destructive at worst.

See: output/0001-bpf-Fix-retval-range-for-sign-extending-ctx-loads.patch

Impact

Without this fix, any LSM BPF program that reads its ret parameter using a sign-extending 32-bit load (as GCC BPF generates) will be rejected by the verifier. This blocks all LSM programs with return value semantics from working under GCC BPF compilation.

The test is currently denylisted in CI (DENYLIST.test_progs-bpf_gcc), masking the issue.

References

  • CI failure log: R6=scalar(smin=0xffffffff80000000,smax=0x7fffffff)At program exit the register R0 has smin=-2147483648 smax=2147483647 should have been in [-4095, 0]
  • kernel/bpf/verifier.c:6439-6444 — retval range application
  • kernel/bpf/verifier.c:6557-6576 — post-load coercion
  • kernel/bpf/verifier.c:5647-5700coerce_reg_to_size_sx() implementation
  • kernel/bpf/btf.c:6895info->is_retval = true for LSM retval

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions