automerge icon indicating copy to clipboard operation
automerge copied to clipboard

Error: recursive use of an object detected which would lead to unsafe aliasing in rust

Open rf- opened this issue 3 years ago • 2 comments

If I load this doc and mutate it in this specific way, I run into an error:

const Automerge = require('automerge-js');
Automerge.use(require('automerge-wasm'));

const doc = Automerge.load(require('fs').readFileSync('unsafe_aliasing_doc'));

Automerge.change(doc, (doc) => {
  doc.versions['c29cccdee60b4803a29b77b3dabdebb8'].fillChoicesBySlot['7,5,down,5'].rejected['test'] = true
});

// Uncaught:
// Error: recursive use of an object detected which would lead to unsafe aliasing in rust
//     at module.exports.__wbindgen_throw (node_modules/automerge-wasm/nodejs/bindgen.js:1547:11)
//     at wasm://wasm/00346bd2:wasm-function[1480]:0xc275c
//     at wasm://wasm/00346bd2:wasm-function[1482]:0xc2778
//     at wasm://wasm/00346bd2:wasm-function[1277]:0xc0e6d
//     at Automerge.rollback (node_modules/automerge-wasm/nodejs/bindgen.js:571:24)
//     at _change (node_modules/automerge-js/dist/cjs/index.js:112:15)
//     at Object.change (node_modules/automerge-js/dist/cjs/index.js:62:16)

The doc was created with automerge-js 0.1.3 and automerge-wasm 0.1.5, but the repro also works on automerge-js 0.1.5—my actual app is still on 0.1.3 for now because of #399. Loading the doc and applying this change with upstream Automerge appear to work fine.

rf- avatar Jul 10 '22 02:07 rf-

I tried executing your code here against your file and no error was thrown. If you're still seeing this error could you make a repo with a package.json and the data file checked in so we can pin all the package versions exactly

orionz avatar Aug 11 '22 23:08 orionz

Huh, that's odd, thanks for checking it out. I can still repro on latest versions: https://gist.github.com/rf-/fc03ab71c1c0ca4b35f0a630e4cab285

rf- avatar Aug 12 '22 18:08 rf-

This was a little tricky to chase down. First off, the "recursive use" error is a red herring. This error is actually caused by our error handling code which calls rollback. We'll need to fix that. On top of that the panic hook which we install in automerge_wasm::init is never called in this code because we call automerge_wasm::load, this means that we never got the proper Rust backtrace. However, after fixing all these problems I was able to narrow down what was going on.

We can reproduce the problem with the following test case:

#[test]
fn insert_after_many_deletes() {
    let mut doc = AutoCommit::new();
    let obj = doc.put_object(&ROOT, "object", ObjType::Map).unwrap();
    for i in 0..50 {
        doc.put(&obj, format!("{}", i), i).unwrap();
        doc.delete(&obj, format!("{}", i)).unwrap();
    }
}

The issue is that Automerge::put uses the same logic as Automerge::get to determine where in the OpSet to insert the new operation. This logic skips invisible ops - i.e. deleted operations. This means the index can be completely wrong, as we see here.

I'm working on a fix now.

alexjg avatar Sep 06 '22 11:09 alexjg