Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions abe/cpabe/tkn20/internal/tkn/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ func removeLen32Prefixed(data []byte) (next []byte, remainder []byte, err error)
return nil, nil, fmt.Errorf("data too short")
}
itemLen := int(binary.LittleEndian.Uint32(data))
if itemLen < 0 {
return nil, nil, fmt.Errorf("negative data length: possible overflow")
}
Comment thread
cjpatton marked this conversation as resolved.
if (4 + itemLen) > len(data) {
return nil, nil, fmt.Errorf("data too short")
}
Comment on lines +80 to 85

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Overflow check is incomplete — addition of offset and length can still wrap on 32-bit targets

The new negative-length check (itemLen < 0 at abe/cpabe/tkn20/internal/tkn/util.go:80) catches uint32 values ≥ 2³¹ on 32-bit platforms, but the subsequent addition (4 + itemLen at abe/cpabe/tkn20/internal/tkn/util.go:83) can still wrap negative for large positive values, so the bounds check passes and the slice operation panics on attacker-controlled ciphertext.

Impact: On 32-bit targets (including WASM, which CI tests), a crafted ciphertext with a length field near 2³¹ can crash the decryption routine.

Detailed overflow mechanism on 32-bit int

On 32-bit platforms (GOOS=js GOARCH=wasm), int is 32 bits. If the encoded uint32 length is e.g. 0x7FFFFFFC (2147483644), it passes the itemLen < 0 check since it's positive as int32. Then 4 + 2147483644 overflows int32 to -2147483648, which is less than len(data), so the (4 + itemLen) > len(data) check passes. The subsequent slice data[4 : 4+itemLen] uses the overflowed negative value as the high bound, causing a runtime panic.

This function is called from DecryptCCA (abe/cpabe/tkn20/internal/tkn/bk.go:156) on attacker-controlled ciphertext input. The REVIEW.md rule states: "No panic on attacker-controlled input."

A robust fix would avoid int arithmetic overflow entirely, e.g.:

itemLen := int(binary.LittleEndian.Uint32(data))
if itemLen < 0 || itemLen > len(data)-4 {
    return nil, nil, fmt.Errorf("data too short")
}

Since len(data) >= 4 is already guaranteed by the earlier check, len(data)-4 is non-negative, and comparing itemLen against it avoids any addition overflow.

Suggested change
if itemLen < 0 {
return nil, nil, fmt.Errorf("negative data length: possible overflow")
}
if (4 + itemLen) > len(data) {
return nil, nil, fmt.Errorf("data too short")
}
if itemLen < 0 || itemLen > len(data)-4 {
return nil, nil, fmt.Errorf("data too short")
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@mdosch this looks useful?

Comment on lines +80 to 85

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟨 Incomplete overflow guard still allows denial-of-service via crafted ciphertext on 32-bit targets

The new itemLen < 0 check at abe/cpabe/tkn20/internal/tkn/util.go:80 catches uint32 values ≥ 2³¹ on 32-bit platforms, but the expression 4 + itemLen at line 83 can still overflow for large positive int32 values (e.g., 0x7FFFFFFC). This causes the bounds check to pass, and the subsequent slice operation panics. Since removeLen32Prefixed is called from DecryptCCA (abe/cpabe/tkn20/internal/tkn/bk.go:156) on attacker-controlled ciphertext, a crafted 4-byte length header can crash the process on WASM and other 32-bit targets.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Expand Down
Loading