GaloisInc / crucible

Crucible is a library for symbolic simulation of imperative programs
627 stars 42 forks source link

`crucible-mir` crashes due to improperly constructing vtable shim types #1222

Open Torrencem opened 1 month ago

Torrencem commented 1 month ago

I've installed saw and mir-json on my system, and I think I have the right version of everything set up.

$ saw --version
1.1
$ saw-rustc --version
test build - extract output path - ["rustc", "--version", "--test", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs"]
rustc 1.69.0-nightly (5e37043d6 2023-01-22)
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/bin/mir-json-rustc-wrapper.rs:86:27
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

(kind of weird version is panicking but you can see the rustc version is correct in there)

When I try to compile the following:

fn add(x: u32, y: u32) -> u32 {
x + y
}

it works fine, I'm able to handle it with the following saw script:

enable_experimental;

m <- mir_load_module "hello.linked-mir.json";

then I run saw-rustc, which succeeds

$ saw-rustc hello.rs
test build - extract output path - ["rustc", "hello.rs", "--test", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs"]
test build - ["rustc", "hello.rs", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--cfg", "crux_top_level", "--crate-type", "rlib"]
warning: function `add` is never used
 --> hello.rs:1:4
  |
1 | fn add(x: u32, y: u32) -> u32 {
  |    ^^^
  |
  = note: `#[warn(dead_code)]` on by default

note: Emitting MIR for hello/a2764e5c::add

warning: 1 warning emitted

linking 19 mir files into hello.linked-mir.json
  inputs: libhello.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcore.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcompiler_builtins.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liballoc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcrucible.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liblibc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_core.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libunwind.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcfg_if.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libminiz_oxide.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libadler.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libhashbrown.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd_detect.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_demangle.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libaddr2line.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libgimli.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libobject.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libmemchr.mir
generated test script hello

when I run saw on this, it succeeds, telling me my installation is at least somewhat working:

$ saw hello.saw
[17:24:19.173] Loading file "/Users/sarah.spall/projects/provers/aes_rust_colin/src/hello.saw"

however, if I then try a more complicated function that uses IO, like the following:

fn main() {
println!("hello world");
}

I can compile this with saw-rustc:

test build - extract output path - ["rustc", "hello.rs", "--test", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs"]
test build - ["rustc", "hello.rs", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--cfg", "crux_top_level", "--crate-type", "rlib"]
warning: function `main` is never used
 --> hello.rs:1:4
  |
1 | fn main() {
  |    ^^^^
  |
  = note: `#[warn(dead_code)]` on by default

note: Emitting MIR for hello/7d20a575::main

note: Emitting MIR for hello/7d20a575::main::{{promoted}}[0]

note: Emitting MIR for hello/7d20a575::main::{{promoted}}[1]

note: Emitting ADT definition for core/0a2bb85a::fmt::Arguments::_adtbd21306cbe4f0b9b[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::ArgumentV1::_adtbd21306cbe4f0b9b[0]

note: Emitting MIR for core/0a2bb85a::fmt::{impl#4}::new_v1::_instbd21306cbe4f0b9b[0]

note: Emitting MIR for core/0a2bb85a::fmt::{impl#4}::new_v1::_instbd21306cbe4f0b9b[0]::{{promoted}}[0]

note: Emitting MIR for core/0a2bb85a::fmt::{impl#4}::new_v1::_instbd21306cbe4f0b9b[0]::{{promoted}}[1]

note: Emitting ADT definition for core/0a2bb85a::option::Option::_adt9c34ccba92936212[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::Argument::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::result::Result::_adtb3f4e538b15dbdda[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::Formatter::_adtbd21306cbe4f0b9b[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::Error::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::marker::PhantomData::_adta595c6a450ad127c[0]

note: Emitting ADT definition for core/0a2bb85a::crucible::any::Any::_adtb7803c2264daf0ec[0]

note: Emitting trait def for Some(dyn std::fmt::Write)

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::FormatSpec::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::Alignment::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::option::Option::_adtaffa7a8b1157c078[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::Count::_adtb7803c2264daf0ec[0]

warning: 1 warning emitted

linking 19 mir files into hello.linked-mir.json
  inputs: libhello.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcore.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcompiler_builtins.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liballoc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcrucible.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liblibc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_core.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libunwind.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcfg_if.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libminiz_oxide.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libadler.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libhashbrown.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd_detect.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_demangle.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libaddr2line.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libgimli.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libobject.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libmemchr.mir
generated test script hello

(and I can provide this mir file if it might be useful). When I run saw hello.saw (same saw file) again for this example I get

[17:26:51.993] Loading file "/Users/sarah.spall/projects/provers/aes_rust_colin/src/hello.saw"
[17:26:54.234] vtable signature mismatch for vtable core/0a2bb85a::ops[0]::function[0]::FnMut[0]::_vtblb704da9d5ff2e622[0] of trait core/0a2bb85a::ops[0]::function[0]::FnMut[0]::_trait1f2906ad28e660ae[0] : StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (IntrinsicRepr MirReference [AnyRepr])]] UnitRepr, FunctionHandleRepr [AnyRepr, IntrinsicRepr MirReference [AnyRepr]] UnitRepr] != StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (IntrinsicRepr MirReference [AnyRepr])]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (IntrinsicRepr MirReference [AnyRepr])]] UnitRepr]
CallStack (from HasCallStack):
  error, called at src/Mir/Trans.hs:980:20 in crucible-mir-0.2-inplace:Mir.Trans
  mkTraitObject, called at src/Mir/Trans.hs:823:11 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast', called at src/Mir/Trans.hs:954:5 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast, called at src/Mir/Trans.hs:1011:30 in crucible-mir-0.2-inplace:Mir.Trans
  evalRval, called at src/Mir/Trans.hs:1246:11 in crucible-mir-0.2-inplace:Mir.Trans
  transStatement, called at src/Mir/Trans.hs:1712:68 in crucible-mir-0.2-inplace:Mir.Trans
  translateBlockBody, called at src/Mir/Trans.hs:1721:28 in crucible-mir-0.2-inplace:Mir.Trans
  registerBlock, called at src/Mir/Trans.hs:1761:10 in crucible-mir-0.2-inplace:Mir.Trans
  genFn, called at src/Mir/Trans.hs:1807:24 in crucible-mir-0.2-inplace:Mir.Trans
  transDefine, called at src/Mir/Trans.hs:2264:30 in crucible-mir-0.2-inplace:Mir.Trans
  transCollection, called at src/Mir/ParseTranslate.hs:76:26 in crucible-mir-0.2-inplace:Mir.ParseTranslate
  translateMIR, called at src/SAWScript/Crucible/MIR/Builtins.hs:528:11 in saw-script-1.1-inplace:SAWScript.Crucible.MIR.Builtins

Any troubleshooting tips / suggestions? is my installation borked or is something else wrong with the way I'm doing things

RyanGlScott commented 1 month ago

Interestingly, I am not able to reproduce this locally on my x86_64-unknown-linux-gnu machine, which leads me to wonder if this issue is platform-specific. (I note that you're using aarch64-apple-darwin.) Would you be able to upload your hello.linked-mir.json file somewhere?

Torrencem commented 1 month ago

Here's the mir json (fair warning, it's 11mb): https://gist.github.com/Torrencem/2da86154e1a6ae7304bd6bc90ad92e53

RyanGlScott commented 1 month ago

@mccleeary-galois (who also uses aarch64-apple-darwin) is also able to reproduce this issue.

Torrencem commented 1 month ago

It's funny, as we were discussing this issue, the first thing we said was "this better not be an aarch64-apple-darwin issue again"

RyanGlScott commented 1 month ago

Thank you for uploading your MIR JSON file! This was invaluable in figuring out what is going on.

First, a general disclaimer: I doubt that SAW will be able to verify code that contains calls to println!, even if we fixed this bug. As such, I'd recommend removing calls to println! in any Rust code that you want to verify (at least, for the time being).

That being said, you're not actually verifying the main() function here—you're just loading it into SAW. Loading Rust code into SAW should never crash, so we should strive to make at least this much work. Here are some takeaways:

Your installation isn't borked.

Thankfully, nothing's wrong on your end. This is a bug in how SAW ingests MIR JSON files.

There's nothing inherently macOS-specific about this bug.

The fact that this bug seemingly only arose on aarch64-apple-darwin is a bit of a red herring. That's simply because there's some code in the std crate which happens to use different implementations depending on what operating system is used, and it just so happens that the macOS implementation triggers this bug, but the Linux implementation doesn't.

Thankfully, it proves relatively straightforward to extract the code which triggers the bug into a minimal form that crashes on all operating systems:

// test.rs
pub fn foo(f: &dyn Fn(i32)) {
    f(42)
}

pub fn bar() {
    foo(&|_| ())
}
// test.saw
enable_experimental;
m <- mir_load_module "test.linked-mir.json";
$ saw-rustc test.rs && ~/Software/saw-1.1/bin/saw test.saw
test build - extract output path - ["rustc", "test.rs", "--test", "--target", "x86_64-unknown-linux-gnu", "--cfg", "crux"]
test build - ["rustc", "test.rs", "--target", "x86_64-unknown-linux-gnu", "--cfg", "crux", "--cfg", "crux_top_level", "--crate-type", "rlib"]
note: Emitting MIR for test/801036f2::bar

note: Emitting MIR for test/801036f2::bar::{{promoted}}[0]

note: Emitting MIR for test/801036f2::foo

note: Emitting trait def for Some(dyn std::ops::Fn(i32))

note: Emitting MIR for test/801036f2::bar::{closure#0}::_insta56a8199f5bc040a[0]

note: Emitting MIR for core/73237d41::ops::function::FnOnce::call_once::_callonce194a5c55a7e36159[0]

linking 1 mir files into test.linked-mir.json
  inputs: libtest.mir
generated test script test
[13:10:27.920] Loading file "/home/ryanscott/Documents/Hacking/Haskell/sandbox/saw-script/test.saw"
[13:10:27.922] vtable signature mismatch for vtable core/73237d41::ops[0]::function[0]::Fn[0]::_vtbl87c30a95762061ee[0] of trait core/73237d41::ops[0]::function[0]::Fn[0]::_traitb9ec75e8d3618d41[0] : StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr] != StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr]
CallStack (from HasCallStack):
  error, called at src/Mir/Trans.hs:980:20 in crucible-mir-0.2-inplace:Mir.Trans
  mkTraitObject, called at src/Mir/Trans.hs:823:11 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast', called at src/Mir/Trans.hs:954:5 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast, called at src/Mir/Trans.hs:1011:30 in crucible-mir-0.2-inplace:Mir.Trans
  evalRval, called at src/Mir/Trans.hs:1246:11 in crucible-mir-0.2-inplace:Mir.Trans
  transStatement, called at src/Mir/Trans.hs:1712:68 in crucible-mir-0.2-inplace:Mir.Trans
  translateBlockBody, called at src/Mir/Trans.hs:1721:28 in crucible-mir-0.2-inplace:Mir.Trans
  registerBlock, called at src/Mir/Trans.hs:1761:10 in crucible-mir-0.2-inplace:Mir.Trans
  genFn, called at src/Mir/Trans.hs:1807:24 in crucible-mir-0.2-inplace:Mir.Trans
  transDefine, called at src/Mir/Trans.hs:2264:30 in crucible-mir-0.2-inplace:Mir.Trans
  transCollection, called at src/Mir/ParseTranslate.hs:76:26 in crucible-mir-0.2-inplace:Mir.ParseTranslate
  translateMIR, called at src/SAWScript/Crucible/MIR/Builtins.hs:528:11 in saw-script-1.1-inplace:SAWScript.Crucible.MIR.Builtins

What is actually going on here?

I haven't made it as far as identifying the actual cause of the underlying bug, but it appears that crucible-mir is improperly constructing the types of vtable shims. Note that you can also reproduce this bug using crux-mir (after annotating bar as a crux-mir test entrypoint):

// test.rs
pub fn foo(f: &dyn Fn(i32)) {
    f(42)
}

#[crux::test]
pub fn bar() {
    foo(&|_| ())
}
$ crux-mir test.rs 
vtable signature mismatch for vtable core/092bc89a::ops[0]::function[0]::Fn[0]::_vtbl437dbfe18b7afdbd[0] of trait core/092bc89a::ops[0]::function[0]::Fn[0]::_trait82b5b5831af5fcf4[0] : StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr] != StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr]
CallStack (from HasCallStack):
  error, called at src/Mir/Trans.hs:980:20 in crucible-mir-0.2-inplace:Mir.Trans
  mkTraitObject, called at src/Mir/Trans.hs:823:11 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast', called at src/Mir/Trans.hs:954:5 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast, called at src/Mir/Trans.hs:1011:30 in crucible-mir-0.2-inplace:Mir.Trans
  evalRval, called at src/Mir/Trans.hs:1246:11 in crucible-mir-0.2-inplace:Mir.Trans
  transStatement, called at src/Mir/Trans.hs:1712:68 in crucible-mir-0.2-inplace:Mir.Trans
  translateBlockBody, called at src/Mir/Trans.hs:1721:28 in crucible-mir-0.2-inplace:Mir.Trans
  registerBlock, called at src/Mir/Trans.hs:1761:10 in crucible-mir-0.2-inplace:Mir.Trans
  genFn, called at src/Mir/Trans.hs:1807:24 in crucible-mir-0.2-inplace:Mir.Trans
  transDefine, called at src/Mir/Trans.hs:2264:30 in crucible-mir-0.2-inplace:Mir.Trans
  transCollection, called at src/Mir/ParseTranslate.hs:76:26 in crucible-mir-0.2-inplace:Mir.ParseTranslate
  translateMIR, called at src/Mir/Language.hs:256:16 in crux-mir-0.8.0.99-inplace:Mir.Language

As such, this is really more of a crucible-mir issue than a SAW one. I'll move this issue over to the Crucible repo and retitle it accordingly.

RyanGlScott commented 1 month ago

@spernsteiner reminded me that the culprit is the spread_arg hack:

https://github.com/GaloisInc/crucible/blob/8cc5e175be47de6f3d74348f6ec6e93e2598386d/crucible-mir/src/Mir/Mir.hs#L126-L143

More specifically, suppose we have this program:

// test.rs
pub fn foo(f: &mut dyn FnMut(i16, i32)) {
    f(27, 42)
}

#[crux::test]
pub fn bar() {
    foo(&mut |_, _| ())
}

If I dump out the internal representation of the FnMut instantiation in the program above (using mir-json), I get:

  "traits": [
    {
      "items": [
        {
          "item_id": "core/092bc89a::ops::function::FnOnce::call_once",
          "kind": "Method",
          "signature": {
            "abi": {
              "kind": "RustCall"
            },
            "inputs": [
              "ty::Dynamic::985de022e3d83671",
              "ty::Tuple::05f82569127efd62"
            ],
            "output": "ty::Tuple::e93222e871854c41"
          }
        },
        {
          "item_id": "core/092bc89a::ops::function::FnMut::call_mut",
          "kind": "Method",
          "signature": {
            "abi": {
              "kind": "RustCall"
            },
            "inputs": [
              "ty::Ref::0751798588f5ec27",
              "ty::Tuple::05f82569127efd62"
            ],
            "output": "ty::Tuple::e93222e871854c41"
          }
        }
      ],
      "name": "core/092bc89a::ops::function::FnMut::_traite54ca09db4b501ca[0]"
    }
  ]

Here, we see that both call_once and call_mut expect a Tuple argument, which corresponds to the pair (i16, i32). So far, so good.

If I look up the vtable used in this program, I see this:

  "vtables": [
    {
      "items": [
        {
          "def_id": "core/092bc89a::ops::function::FnOnce::call_once::_callonce95ae10fa377a8068[0]",
          "item_id": "core/092bc89a::ops::function::FnOnce::call_once"
        },
        {
          "def_id": "test/7578cddf::bar::{closure#0}::_inst4da6783bb0bc5290[0]",
          "item_id": "test/7578cddf::bar::{closure#0}"
        }
      ],
      "name": "core/092bc89a::ops::function::FnMut::_vtbl421e8f554162ba11[0]",
      "trait_id": "core/092bc89a::ops::function::FnMut::_traite54ca09db4b501ca[0]"
    }
  ]

What is peculiar is the type of test/7578cddf::bar::{closure#0}::_inst4da6783bb0bc5290[0] used as the second item in the vtable:

    {
      ...,
      "args": [
        {
          "is_zst": false,
          "mut": {
            "kind": "Mut"
          },
          "name": "_1",
          "ty": "ty::Ref::fcca228e69df3496"
        },
        {
          "is_zst": false,
          "mut": {
            "kind": "Mut"
          },
          "name": "_2",
          "ty": "ty::i16"
        },
        {
          "is_zst": false,
          "mut": {
            "kind": "Mut"
          },
          "name": "_3",
          "ty": "ty::i32"
        }
      ],
      ...,
      "name": "test/7578cddf::bar::{closure#0}::_inst4da6783bb0bc5290[0]",
      "return_ty": "ty::Tuple::e93222e871854c41",
      "spread_arg": null
    }

Unlike call_mut above, this doesn't take a Tuple as an argument. Instead it "unpacks" the tuple, having separate i16 and i32 arguments. This is the root cause of the vtable signature mismatch error reported earlier.

The unpacking (or lack thereof) is related to crucible-mir's hacky treatment of spread_arg in MIR. As the comment above indicates, the current hack works for direct calls to Fn-like things, but not function pointers. In order to make this work, we will likely need to remove the hack and do the right thing.