Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotation for file descriptors / enforcing integrity of fd #50

Closed
wanderer opened this issue May 31, 2019 · 14 comments
Closed

Annotation for file descriptors / enforcing integrity of fd #50

wanderer opened this issue May 31, 2019 · 14 comments
Labels
discussion A discussion that doesn't yet have a specific conclusion or actionable proposal.

Comments

@wanderer
Copy link

So once we have reference types we can use those types for fd but currently we can also enforce nearly the same behavior as if we had reference types by annotating functions. This would enable the host system to enforce the integrity of fd across module instances.

For example lets say we have to have 2 wasm instances (a, b) that share no memory or tables but instance a imports some exported functions from instance b. If those exported functions expected fd in its augments, those augments would be annotated in a custom section so that when a called b the host would enforce that values were valid fd.

The custom sections could looks something like the following


LANGUAGE_TYPES = {
  'i32': 0x7f,
  'i64': 0x7e,
  'f32': 0x7d,
  'f64': 0x7c,
  'fd': 0x7b,
  'anyFunc': 0x70,
  'func': 0x60,
  'block_type': 0x40,
}

The custom section has a name of "wasi.type".

The typeMap section maps the custom types to functions. And is encoded as follows

Field Type Description
count varuint32 count of type entries to follow
entries map* repeated type entries mapping a function to a custom type

And a map is Encoded as follows and named "wasi.typeMap".

Field Type Description
function varuint32 the index of the function using the custom type
type varuint32 the index of the custom type

Lastly the custom type information for globals are encoded as follows. The section name is "wasi.globals"

Field Type Description
index varuint32 the index of the global
type varuint32 the type of the global
@dumblob
Copy link

dumblob commented Jun 1, 2019

How would you get rid of this once reference types will be available?

@wanderer
Copy link
Author

wanderer commented Jun 3, 2019

@dumblob i think you could just have a tool that removes the custom header and transforms the annotated i32s to reference types.

@dumblob
Copy link

dumblob commented Jun 3, 2019

@wanderer I mean in production - you'll have some "binary app" compiled using the annotated i32s and suddenly the WASI runtime will have just (?) reference types (because if there won't be any "hard" switch to reference types, then annotations become the "living standard" and reference types won't find their use in this case effectively forcing all implementers implement and forever maintain both - annotated fds along with reference types). Unless annotated fds will be "binary" compatible with reference types in which case I'm fine with annotations.

This is the biggest pain of web development - everything changes all the time 😢.

@rylev rylev added the discussion A discussion that doesn't yet have a specific conclusion or actionable proposal. label Jun 3, 2019
@wanderer
Copy link
Author

wanderer commented Jun 5, 2019

@dumblob doesn't that question also apply to the current situation too? Once we have actually references the API will totally change and I assume older binaries will use the older version of the API (once we figure out versioning as well). Annotations are just trying to enforces the unforgeablality of references between modules with what we have now.

@wanderer
Copy link
Author

wanderer commented Jun 5, 2019

@dumblob

forcing all implementers implement and forever maintain both

yeah this is particularly annoying. But forward looking implementations could just go ahead and implement references and translate the annotation to actually references internally. The problem with this approach would be that those implementation would still have to support operations that actually references wouldn't support. I don't have any other ideas here 😶

@dumblob
Copy link

dumblob commented Jun 5, 2019

Once we have actually references the API will totally change and I assume older binaries will use the older version of the API (once we figure out versioning as well).

@wanderer I admit I didn't think of this at all as I assume this won't happen after a "1.0" WASI standard will be published. But I must be missing something and so I'll rather keep waiting until some clarification from the versioning and reference types discussions emerges 😉.

@sunfishcode
Copy link
Member

It's unclear what this mechanism would guard against. If someone forges a file descriptor i32 value, either:

  • it's not valid, and calls to WASI functions with it return __WASI_EBADF and do nothing
  • it's valid, and can be used to access some resource, however it would also pass integrity checks

@wanderer
Copy link
Author

wanderer commented Jun 5, 2019

@sunfishcode so maybe the assumption i made here that isn't entirely clear is that the two instantiates are separate stores and instead of inheriting fds they would have to passed as arguments. Each store would have to have an associated capabilities-list which could be replaced by a table later. The c-list would just be a table of opened fd that the given instance has access to.

If instance a called my_method(i32) on instance b which was annotated as fd the runtime would use an i32 to as an index into a c-list and then place the fd onto bs c-list then call b.my_method with b index.

If we don't have a mechanism like this then how would to separate store be able to share fds?

@sunfishcode
Copy link
Member

Could you spell out the scenario in more detail? How does a obtain the fd? Who decides whether the capability should be transferred from a's c-list to b's c-list?

Also, another option here is to just accept that forging is possible with integer file descriptors, and wait for reference types to fix this properly.

@sbc100
Copy link
Member

sbc100 commented Jun 5, 2019

Let me see if I understand what you are asking for here @wanderer?

In your scenario there are two separate wasi modules (is this what you mean by store?) that can directly call functions on each other? Right now as of today if you pass and fd (say fd=55) from one to the other it won't be usable on the other side since the loader/kernel didn't assign fd 55 to the other wasi module. Worse still, it might have an fd 55 assigned but it wouldn't represent the same thing.

In the future this will work if we have first class reference types.

In the interim you are suggesting we could build some kind of annotation that would automatically generate the code needed to pass the fd (via some kind of wasi fd passing syscall) before making the call to my_method()?

@wanderer
Copy link
Author

wanderer commented Jun 6, 2019

@sunfishcode

How does a obtain the fd?

It could just be preopened. All the preopend fd's would be added to c-list or table once we have ref types.

Who decides whether the capability should be transferred from a's c-list to b's c-list?

thats the job of the runtime. When a fd is used in a call to another instance then it is transferred.
It would look like this now with annotations.

(module $a
  (import "b" "my_method" (func $my_method (param i32))) ;; annotated as a `fd` 
  (func $main
    i32.const 0 ;; a preopened fd
    call $my_method ;; the fd is now in b's c-list
))

on b's side

(module $b
 (func (export "my_method") (param i32)
    local.get 0 ;; this is the index of the fd on b's clist
    ... 
    ;; do stuff with the fd
   ))

Here is the equivalents with ref-types

(module $a
  (import "b" "my_method" (func $my_method (param i32)))
  (table (export "clist") $clist 1 anyref) ;; the runtime populates this with preopens
  (func $main
     i32.const 0
     table.get $clist
     call $my_method 
   ))
(module $b
  (table $clist 1 anyref)
  (func (export "my_method") (param  anyref)
     (table.set $t (i32.const 0) (local.get 0))
     (i32.const 0)
      ... 
      ;; do stuff with the fd
))

Also, another option here is to just accept that forging is possible with integer file descriptors, and wait for reference types to fix this properly.

yeah annotations are a bit pain but in some usecases, like smart contracts, i think that unforgeability is necessary from the get go or any situation when multiple untrusted stores are interacting.

@wanderer
Copy link
Author

wanderer commented Jun 6, 2019

@sbc100

In your scenario there are two separate wasi modules (is this what you mean by store?)

yes this is what i was thinking. but i guess this scenario would be fine for multiple wasm instances that are linked together in a single wasi module / store too.

In your scenario there are two separate wasi modules (is this what you mean by store?) that can directly call functions on each other?

yep! this at least allows us to have the room to do that. Currently i think it would be hard to implement a system used WASI and that needed to have multiple untrusted programs interacting and sharing capabilities/fd.

@sunfishcode
Copy link
Member

The design principles document describes an approach where we can think of c-lists as WebAssembly tables. With that in mind, the way to have two instances that are linked without sharing is to give them each their own table, aka c-list, and to have tooling and interface-types support for conceptually passing handles from one instance to another, which handle actually copying handles from one table to another.

This avoids the need for special types or special annotations -- instances can access any of the capabilities within their c-lists, but they need not see all c-lists present within a store.

alexcrichton pushed a commit to alexcrichton/WASI that referenced this issue Jan 19, 2022
@sunfishcode
Copy link
Member

WASI is now moving to a model where handles are a first-class type in wit, and instance-local indices are used to refer to them. They're instance-local, so when a handle is passed from one instance to another, the indices are translated. For further questions about the underlying mechanism, please follow up in the component model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion A discussion that doesn't yet have a specific conclusion or actionable proposal.
Projects
None yet
Development

No branches or pull requests

5 participants