# Is synthesizing zero sized values safe?

**URL:** <https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506>\
**Category:** Uncategorized\
**Created:** [December 18, 2019, 11:19pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506 "2019-12-18T23:19:13Z")\
**Posts on this page:** 13\
**Page:** 2

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [December 20, 2019, 9:23pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/21 "2019-12-20T21:23:26Z")

</div>

You can look into my [`generic-field-projection`](https://github.com/KrishnaSannasi/generic-field-projection) crate where I use a mixure of type level lists and runtime checks which get optimized away to get safe unique paths.

(look for [`ProjectToSet`](https://github.com/KrishnaSannasi/generic-field-projection/blob/72446a997369dc78c370146b940ddab6ee000e1d/core/src/lib.rs#L72-L78), [`Field`](https://github.com/KrishnaSannasi/generic-field-projection/blob/72446a997369dc78c370146b940ddab6ee000e1d/core/src/lib.rs#L203-L216), [`FindOverlap`](https://github.com/KrishnaSannasi/generic-field-projection/blob/72446a997369dc78c370146b940ddab6ee000e1d/core/src/project.rs#L30-L70), [`project::from_mut`](https://github.com/KrishnaSannasi/generic-field-projection/blob/master/core/src/project/from_mut.rs))

Here's how it works, `ProjectToSet` projects a reference to a set of fields as (which are represented as types that implement the `Field` trait). The `Field` trait is unsafe to implement, and provides a function `name`. This function provides a unique path that identifies the field as an iterator. (for example, `foo.bar.x` would be `["bar", "x"]` as an iterator). This iterator is build of `std::iter::once` and `std::iter::Chain`, making it really easy for LLVM to see what is going on.

`project::from_mut` then calls `FindOverlap` to see if any of these "names" overlap (meaning that the fields would alias). `FindOverlap` is a type that simulates generic closures. It goes through every field-type in the set pair-wise and figures out if the names overlap. If any of the names overlap, then it return true. If `FindOverlap` returns true, then `project::from_mut` will raise a panic and be on it's way. Otherwise it will do the projection.

For all cases that I tried (up to 16 fields), LLVM was able to see through all the checks and reduce `project::from_mut` to just the pointer arithmetic. (through simple inlineing and const propogation)

This is fairly complex, but in the end you get robust, efficient, and safe code which I find to be worth the cost.

---

<div class="post-metadata">

**Author:** ![matt1985](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matt1985/32/4772_2.png) [@matt1985](https://internals.rust-lang.org/u/matt1985)\
**Post date:** [December 20, 2019, 9:59pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/22 "2019-12-20T21:59:21Z")

</div>

My crate mostly uses the `fp!( .a.b, .a.c )` macro (the first `.` is optional) to construct disjoint field paths safely,since it's intended for emulating structural types,rather than for generically manipulating fields.

"emulating structural types" in this case means that the name of the field is concrete,while the type that you get the field from is generic or a `dyn Trait`.

> [@RustyYato](#):
>
> You can look into my [`generic-field-projection`](https://github.com/KrishnaSannasi/generic-field-projection) crate where I use a mixure of type level lists and runtime checks which get optimized away to get safe unique paths.

In the future I might do something like your crate if I decide to improve support for generic operations on field paths.

---

<div class="post-metadata">

**Author:** ![Lokathor](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lokathor/32/2463_2.png) [@Lokathor](https://internals.rust-lang.org/u/Lokathor)\
**Post date:** [December 20, 2019, 10:26pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/23 "2019-12-20T22:26:37Z")

</div>

I was assuming no such thing, i was simply wrong 😜

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [December 22, 2019, 1:26pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/24 "2019-12-22T13:26:26Z")

</div>

As has been said already, in general conjuring ZST is UB, as is evident by the fact that `!` is a ZST. So you need to have special knowledge that your ZST is inhabited to be allowed to conjure it.

Closure types can be uninhabited if they capture a `!` (or `Void`-style empty enum). I'm afraid I won't have time to do an in-depth review of a full crate. But if there's a small-ish self-contained code snippet demonstrating the key pattern I could take a look at that.

> [@Manishearth](#):
>
> I don't have to deal with never-sized types here, since [the crate I'm writing](https://github.com/Manishearth/mitosis/blob/master/src/lib.rs) does have access to values of this type before it kicks off the series of events that lead to the value eventually being conjured.

This sounds to me like you want `MaybeUnint`; this is exactly the kind of pattern it works well for: delaying the point when we actually assert to have a valid inhabitant of a type.

---

<div class="post-metadata">

**Author:** ![felix.s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/felix.s/32/8073_2.png) [@felix.s](https://internals.rust-lang.org/u/felix.s)\
**Post date:** [December 22, 2019, 2:25pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/25 "2019-12-22T14:25:24Z")

</div>

What if the closure captures a zero-sized proof token that happens to only be valid in the current process? The closure would be zero-sized as well and it would slip past your check.

Also, it seems that ~~if a closure captures an uninhabited type, then~~ it would be possible to trigger UB just by launching the binary with an appropriate command-line argument‌ ~~, even if all code paths that would lead to this from _within_ the program are dead~~.

Not sure how seriously the latter problem deserves to be treated. It reminds me of one time when I wanted to write a program that uses `mmap` to access a read-only file and was wondering what would happen if the file was to be modified after all, while the program is running.

(Edit: I took a better look at how it works. The problem reduces to receiving a bogus pointer over the IPC channel; uninhabited types don't make things any worse here.)

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [December 22, 2019, 4:50pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/26 "2019-12-22T16:50:11Z")

</div>

> [@felix.s](#):
>
> What if the closure captures a zero-sized proof token that happens to only be valid in the current process? The closure would be zero-sized as well and it would slip past your check.

Indeed, that is the more subtle alternative to "closure that captures uninhabited ZST". There's little you can conclude from making sure that the size of the closure environment is 0.

---

<div class="post-metadata">

**Author:** ![Manishearth](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/manishearth/32/50_2.png) [@Manishearth](https://internals.rust-lang.org/u/Manishearth)\
**Post date:** [December 22, 2019, 6:15pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/27 "2019-12-22T18:15:08Z")

</div>

It can't capture an uninhabited type, I have an instance of it, I just don't have an instance of it in the spawned process.

---

<div class="post-metadata">

**Author:** ![felix.s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/felix.s/32/8073_2.png) [@felix.s](https://internals.rust-lang.org/u/felix.s)\
**Post date:** [December 23, 2019, 10:20am UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/28 "2019-12-23T10:20:39Z")

</div>

I was thinking of a situation where, with some uninhabited `F`, an attacker would be able to pass the command-line argument and an address of `run_func<F, A, B>` and tell your binary to run that. But then, if `F` is uninhabited, the compiler may choose not to monomorphise `run_func<F, A, B>` in the first place (because it's only referred to in `spawn<F, A, B>`, which is dead code because it receives an argument of uninhabited type `F`), so it just reduces to the question of whether you should be able to trust function pointers coming from outside the process.

The crate still assumes that `.text` section of the executable is a monolith whose layout is the same in each loaded image (even if it may be loaded at different offsets each time); it would fail to work in the presence of a strong form of ASLR that randomises all functions' locations relative to one another each time the executable is loaded (so that the relative address of `run_func` differs between the parent and the child process), or with a hypothetical JIT implementation of Rust that performs monomorphisation at runtime (the pointer may not even exist in the child process).

---

<div class="post-metadata">

**Author:** ![Manishearth](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/manishearth/32/50_2.png) [@Manishearth](https://internals.rust-lang.org/u/Manishearth)\
**Post date:** [December 23, 2019, 4:32pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/29 "2019-12-23T16:32:41Z")

</div>

Again, uninhabited types are not a problem here. The spawn function takes an instance of the type as an argument.

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [December 23, 2019, 7:29pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/30 "2019-12-23T19:29:05Z")

</div>

Hmm, this looks like a good opportunity to suggest something that has been on my mind for a while: a trait to generalize over capture-less closures and fn "items":

```rust
trait FnItem<Args> : Fn<Args> {
    const fn_item: Self;
}

```

This way, "synthesizing" and then calling such a closure from its type `F` only, would be as simple as doing: `F::fn_item(...)`.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [December 26, 2019, 3:46pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/31 "2019-12-26T15:46:54Z")

</div>

> [@Manishearth](#):
>
> Again, uninhabited types are not a problem here. The spawn function takes an instance of the type as an argument.

Oh I see. Well, in terms of the validity invariant, there are only two possible ZSTs: inhabited and uninhabited. (After all, there is no data that the invariant could depend on.)

So if you know that the type is inhabited, I think you won't cause UB by synthesizing instances of it. But of course there might still be used-defined invariants attached to it that cannot actually be transported across process boundaries (that's what you are doing here, right?). For example, I could imagine a ZST that serves as a witness that some singleton has been initialized -- presenting the ZST means re-initialization can be skipped. But of course there is nothing ensuring that the singleton was initialized in the target process as well.

So I don't think that in general you can safely send any ZST to another process.

---

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [December 26, 2019, 4:32pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/32 "2019-12-26T16:32:46Z")

</div>

> [@RalfJung](#):
>
> For example, I could imagine a ZST that serves as a witness that some singleton has been initialized -- presenting the ZST means re-initialization can be skipped

For an example of something similar to this, [`qcell::TCellOwner`](https://docs.rs/qcell/0.4.0/qcell/struct.TCellOwner.html) must be unique per process. It updates a global it is initialized and dropped to check this invariant. If you send it to another proccess, you could safely obtain two instances and that can lead to aliasing unique references.

---

<div class="post-metadata">

**Author:** ![Manishearth](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/manishearth/32/50_2.png) [@Manishearth](https://internals.rust-lang.org/u/Manishearth)\
**Post date:** [March 25, 2020, 4:32pm UTC](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506/33 "2020-03-25T16:32:48Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

[Previous page](https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506.md?page=1)
