Skip to content

Validate LDDW second-half instruction fields#764

Open
ppx123-web wants to merge 2 commits intoiovisor:mainfrom
ppx123-web:fix-lddw-second-half-validation
Open

Validate LDDW second-half instruction fields#764
ppx123-web wants to merge 2 commits intoiovisor:mainfrom
ppx123-web:fix-lddw-second-half-validation

Conversation

@ppx123-web
Copy link

Patch for issue 757
Fix the lddw instructon validation.

Copilot AI review requested due to automatic review settings February 10, 2026 10:21
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens uBPF’s instruction validation to fully enforce the RFC 9669 requirements for the second half of an LDDW (64-bit immediate load), preventing malformed programs from loading successfully.

Changes:

  • Add validation that the second half of LDDW has dst == 0, src == 0, and offset == 0.
  • Add a new negative test case covering an LDDW second-half instruction with a non-zero dst field.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
vm/ubpf_vm.c Strengthens LDDW validation by enforcing zeroed register/offset fields in the second half.
tests/errors/err-lddw-second-half-invalid.data Adds regression coverage ensuring malformed LDDW second halves are rejected with the expected error.

Comment on lines 1867 to +1872
if (i + 1 >= num_insts || insts[i + 1].opcode != 0) {
*errmsg = ubpf_error("incomplete lddw at PC %d", i);
return false;
}
if (insts[i + 1].dst != 0 || insts[i + 1].src != 0 || insts[i + 1].offset != 0) {
*errmsg = ubpf_error("invalid lddw second half at PC %d", i + 1);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The incomplete lddw at PC %d error is also returned when insts[i+1].opcode != 0, which isn’t an “incomplete” LDDW but an invalid second-half instruction. Consider splitting the condition so the out-of-range case reports "incomplete lddw" while the opcode-mismatch case reports "invalid lddw second half" (at PC i+1) to make failures easier to diagnose and align with the expected semantics of the LDDW second half.

Copilot uses AI. Check for mistakes.
@Alan-Jowett
Copy link
Collaborator

Thanks for this contribution, @ppx123-web! The fix looks correct and aligns well with RFC 9669 — nice work. 👍

One small ask: the current test covers the case where dst != 0 in the second half. Would you be up for adding a couple more test cases that exercise src != 0 and offset != 0 individually? Something like:

err-lddw-second-half-invalid-src.data

-- raw
0x0000000000000018
0x0000000000001000
0x0000000000000095
-- error
Failed to load code: invalid lddw second half at PC 1

err-lddw-second-half-invalid-offset.data

-- raw
0x0000000000000018
0x0000000000010000
0x0000000000000095
-- error
Failed to load code: invalid lddw second half at PC 1

No pressure at all — we're happy to merge as-is and add them ourselves if you'd prefer. Just wanted to flag it in case you'd like to round it out. Thanks again!

@ppx123-web
Copy link
Author

I am sorry that I missed the reply, I will do this.

Exercise each reserved field (dst, src, offset) individually and in
combination per RFC 9669, ensuring the validator rejects non-zero values
in the LDDW pseudo-instruction's second slot.

New test cases:
- invalid-src: src != 0 only
- invalid-offset: offset != 0 only
- invalid-dst-offset: dst != 0 + offset != 0
- invalid-src-offset: src != 0 + offset != 0
- invalid-all: dst != 0 + src != 0 + offset != 0

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@ppx123-web
Copy link
Author

I have added the test cases.
And when adding tests, I also find a small issue about lddw checking.

LDDW first-half src_reg validation is overly restrictive

Summary

The LDDW validation in vm/ubpf_vm.c:1863 rejects all non-zero src_reg values on the first instruction slot. Per RFC 9669 Section 5.4, src_reg values 0x0–0x6 are valid opcode subtypes. The current check conflates two distinct cases: unsupported-but-valid (0x1–0x6) and truly invalid (0x7–0xF).

Current code

// vm/ubpf_vm.c:1862-1866
case EBPF_OP_LDDW:
    if (inst.src != 0) {
        *errmsg = ubpf_error("invalid source register for LDDW at PC %d", i);
        return false;
    }

What the RFC says

RFC 9669, Table 12 defines src_reg as an opcode subtype, not a source register:

src_reg pseudocode type
0x0 dst = (next_imm << 32) | imm integer to integer
0x1 dst = map_by_fd(imm) map fd to map
0x2 dst = map_val(map_by_fd(imm)) + next_imm map fd to data address
0x3 dst = var_addr(imm) variable id to data address
0x4 dst = code_addr(imm) integer to code address
0x5 dst = map_by_idx(imm) map index to map
0x6 dst = map_val(map_by_idx(imm)) + next_imm map index to data address

Values 0x7–0xF are undefined and should be rejected as invalid. Values 0x1–0x6 are valid per the spec but not implemented in ubpf (no kernel map infrastructure).

Problem

A program with src_reg=1 (a valid map_by_fd LDDW used by the Linux kernel loader) gets the same "invalid source register" error as a program with src_reg=15 (genuinely malformed). This is misleading — one is a well-formed program that ubpf doesn't support, the other is a broken program.

Suggested fix

Split the check into two:

case EBPF_OP_LDDW:
    if (inst.src > 6) {
        *errmsg = ubpf_error("invalid source register for LDDW at PC %d", i);
        return false;
    }
    if (inst.src != 0) {
        *errmsg = ubpf_error("unsupported LDDW variant (src=%d) at PC %d", inst.src, i);
        return false;
    }
  • src 7–15: "invalid source register" — the program violates the RFC
  • src 1–6: "unsupported LDDW variant" — the program is valid but ubpf can't run it

Both still reject at load time. No interpreter changes needed. The only difference is a more accurate error message.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants