Skip to content

Fix bugs in truncation of vectors - #1797

Merged
mkskeller merged 1 commit into
data61:masterfrom
rdeviti:truncinring-lane-masks
Nov 5, 2026
Merged

mkskeller merged 1 commit into
data61:masterfrom
rdeviti:truncinring-lane-masks

Conversation

@rdeviti

@rdeviti rdeviti commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

This PR fixes two bugs that show up when ring protocols truncate vectors:

  1. floatingpoint.TruncInRing: when its arguments have different sizes, it masks every element of the vector with the same random value before opening it.
  2. sint.trunc_zeros: on a vector, it masks every element with the same random value, and it applies the first element's wrap-around correction to every element, so some elements come back wrong.

The two bugs are in different files, but one call can reach both, so I'm sending them together. I'm opening a PR because I think I have a fix, so this seemed the quickest way to help. I saw that other potential security bugs have been reported in public issues here, so I hope that's ok. If you'd rather I report them another way next time, just let me know.

This snippet reproduces both bugs. get_block calls TruncInRing through SplitInRing to cut the 8-bit block out of each value, and get_slice calls trunc_zeros to split the block into two 4-bit fields:

from Compiler.oram import get_block
x = sint([0xA5, 0x3C, 0xF0])
lo, hi = get_block(x, regint(0), (4, 4), 1).get_slice()
print_ln('%s %s', lo.reveal(), hi.reveal())

With Scripts/compile-run.py -E ring or -E sy-rep-ring, it should print [5, 12, 0] [10, 3, 15]. Because of bug 2, the second list changes between runs, for example to [10, 19, 15] or [10, 3, -1]. Bug 1 doesn't change the output. To see it, print masked in TruncInRing: the differences between its elements, modulo 256, are the same on every run.

Fix

  1. TruncInRing draws the random bits of its mask with get_random_bit(size=rev.size), so the mask has one value per element of rev.
  2. sint.trunc_zeros is decorated with @vectorize, so TruncLeakyInRing runs at the size of the vector.

Testing

I tested with ring and sy-rep-ring only.

  • Snippet: with both fixes, it prints [5, 12, 0] [10, 3, 15] on every run. The differences between the elements of masked, printed in TruncInRing and in TruncLeakyInRing, change on every run with the fixes.
  • More elements: print_ln('%s', sint(list(range(0, 1024, 16))).trunc_zeros(2, 10, False).reveal()) should print [0, 4, ..., 252].
  • Bytecode: unchanged when the arguments have the same size, for example for trunc_zeros on a single value, and identical for all 39 programs in Programs/Source that compile with -R 64 and no arguments.

@mkskeller
mkskeller merged commit 3411e0f into data61:master Nov 5, 2026
3 checks passed
@mkskeller

Copy link
Copy Markdown
Member

Thank you.

@rdeviti
rdeviti deleted the truncinring-lane-masks branch November 5, 2026 13:56
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.

2 participants