Skip to content

harden(incoming): read DATA seg.offset as u32 in homa_copy_to_user - #100

Open
randomizedcoder wants to merge 1 commit into
PlatformLab:mainfrom
randomizedcoder:fix/data-offset-validation
Open

randomizedcoder wants to merge 1 commit into
PlatformLab:mainfrom
randomizedcoder:fix/data-offset-validation

Conversation

@randomizedcoder

Copy link
Copy Markdown

Hardening: read DATA seg.offset as u32 in homa_copy_to_user()

int offset = ntohl(h->seg.offset);

The DATA segment offset is an unsigned wire field. homa_add_packet() already reads it as u32 start = ntohl(h->seg.offset); this makes the copy-out path consistent.

⚠️ Honest scope: this is defense-in-depth, not a live bug

I want to be upfront: there is no reachable failure here today. A packet only reaches homa_copy_to_user() after homa_add_packet() has already validated its offset (as u32) and dropped anything with start >= rpc->msgin.length:

u32 start = ntohl(h->seg.offset);
if (start >= rpc->msgin.length || length > (rpc->msgin.length - start)) {
    reason = SKB_DROP_REASON_PKT_TOO_BIG;
    goto ignore;
}

and rpc->msgin.length is itself capped at HOMA_MAX_MESSAGE_LENGTH (1,000,000) by homa_message_in_init(). So offsets that reach the copy-out loop are always small and non-negative — the original int was safe. This change is type-consistency / defense-in-depth only. start_offset, end_offset, and offset + copied are unaffected (values stay well within int).

Fix

-			int offset = ntohl(h->seg.offset);
+			u32 offset = ntohl(h->seg.offset);

Verification

Adds homa_data_pkt__seg_offset_is_unsigned, exercising the signed/unsigned boundary offset 0x80000000: it is dropped as PKT_TOO_BIG and never queued. This is a characterization test — it's green before and after — that locks in the existing guarantee at the sign boundary, complementing the existing offset = 2000000 case in homa_data_pkt__homa_add_packet_returns_error.

homa_data_pkt__seg_offset_is_unsigned ... 1 / 1 passed
homa_incoming (whole fixture) ........... 123 / 123 passed

This one is optional hardening — the original static-analysis finding turned out to be already mitigated upstream of this line; happy to close it if you'd rather not carry the change.

homa_copy_to_user() reads the DATA segment offset into a signed int:

    int offset = ntohl(h->seg.offset);

The offset is an unsigned wire field, so for consistency with
homa_add_packet() (which already uses `u32 start = ntohl(h->seg.offset)`)
read it as u32 here too.

Note: this is a defense-in-depth / type-consistency change, not a live
bug. Packets only reach homa_copy_to_user() after homa_add_packet() has
validated the offset as u32 and dropped anything with
start >= rpc->msgin.length; msgin.length is itself capped at
HOMA_MAX_MESSAGE_LENGTH (1,000,000) by homa_message_in_init(). So offsets
that reach the copy-out path are always small and non-negative -- the old
`int` was safe in practice. start_offset/end_offset and offset+copied are
unaffected (values stay well within int range).

Adds a characterization test, homa_data_pkt__seg_offset_is_unsigned,
covering the sign-boundary offset 0x80000000: it is dropped as
PKT_TOO_BIG and never queued (green before and after -- it locks in the
existing guarantee at the signed/unsigned boundary, complementing the
existing offset-2000000 case). Incoming suite: 123/123.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant