Skip to content

Fix demux_matrix freeing memory inside function blocks - #1803

Merged
mkskeller merged 1 commit into
data61:masterfrom
rdeviti:demux-matrix-function-blocks
Nov 9, 2026
Merged

mkskeller merged 1 commit into
data61:masterfrom
rdeviti:demux-matrix-function-blocks

Conversation

@rdeviti

@rdeviti rdeviti commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

I found this when a RecursiveORAM of 1,024 entries returned wrong values for most of the entries I read back. oram_tutorial is affected too: it prints 0 instead of 1 with every protocol I tried.

demux_matrix frees its temporary matrices a and b before it returns, so that the compiler can give their memory to arrays and variables created later. LinearORAM calls it in its method blocks _read, _write and _access, whose code is generated at the first access and then called by every access. In these blocks, freeing a and b is wrong: their memory is freed at the first access, but every later access still writes a and b to it, and so overwrites any array or variable placed in it in the meantime. The contents of the LinearORAM are not affected, because they are allocated before its first access. So the bug only shows in memory allocated after the first access to a LinearORAM, and only if that memory is still needed after a later access. For example, with Scripts/compile-run.py -E ring, -E sy-rep-ring, -E rep-field, -E semi2k or -E semi, the following prints [1, 0, 0, 0] instead of [5, 5, 5, 5]:

from Compiler.oram import LinearORAM
o = LinearORAM(16)
o[sint(0)] = sint(1)
x = sint.Array(4)
x.assign_all(5)
o[sint(1)] = sint(2)
print_ln('%s', x.reveal())

Here, x gets the memory of b, which the second access to o overwrites.

The same happens in the test where I found the bug, which uses a RecursiveORAM of 1,024 entries directly. Its constructor first writes to the position map, which is a LinearORAM, to initialise it, and then allocates read_value, read_non_empty and state, which get the memory of a. Every access to the RecursiveORAM updates the position map, and every update writes a into these variables. With the same five protocols, when I write 128 of its entries, overwrite a third of them and read them back, most of the values are wrong.

Fix

demux_matrix now frees a and b only outside function blocks.

Testing

With the fix, the snippet prints [5, 5, 5, 5], oram_tutorial prints 1, and the RecursiveORAM of 1,024 entries reads back correctly, with the five protocols. Rounds and communication are unchanged.

The fix changes the bytecode only where demux_matrix is called inside a function block, for example in dijkstra_example, gale-shapley_tutorial, gc_oram and oram_tutorial. dijkstra_example and gale-shapley_tutorial print the same output with the fix.

@mkskeller
mkskeller merged commit d3268aa into data61:master Nov 9, 2026
3 checks passed
@mkskeller

Copy link
Copy Markdown
Member

Thank you

@rdeviti
rdeviti deleted the demux-matrix-function-blocks branch November 9, 2026 17:22
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