This issue is an extension of my comment here with a more precise design in a location better suiting something that's actually entirely orthogonal to the proposed direction of #742. To recap slightly, there are a number of motivations that this issue is designed to resolve:
- Most hosts (to my knowledge) do not attempt to sandbox
wasi:filesystem APIs. For example Node's wasip1 shim explicitly says it doesn't sandbox. My personal understanding of the situation is that it's just extremely difficult to write a sandboxed implementation with the current APIs that wasi:filesystem requires hosts to provide. To me, subjectively again, this is not a great property of wasi:filesystem if the goal is to provide a sandbox and most hosts just don't do that.
- Embedders generally expect
wasi:filesystem to be sandboxed, however. Additionally embedders expect the ability to weave together multiple host directories into the guest at various paths. Embedders also generally expect the ability to make host directories either read-only or entirely read/write. Embedders expect all modifications to be neatly contained within these directories, however.
- No guest, to my knowledge, handles path resolution correctly with multiple preopens. For example with
/a and /b preopens the guest path /a/../b/foo.txt does not resolve on any guest -- again, as far as I know.
- No host nor guest, to my knowledge handles overlapping preopens in a standard or compatible way. If a guest has preopens
/foo and /foo/bar, then behavior is probably quite host specific, embedder-specific, and guest-specific.
- To my knowledge there is no existing portable OS API that can be used to exposed a woven-together filesystem to a guest as a single directory. Basically I do not think that these problems all exist for lack of a "oh we can just use that" solution. These, I believe, are all genuinely very hard problems to solve and with no preexisting solution suitable for hosts. (someone please correct me if I'm wrong!)
In my comment I also outlined why I don't think that #742 is a viable solution to at least a subset of these concerns. Notably implement #742 would involve putting even more complexity into hosts for path resolution and such, and if many hosts already aren't sandboxing anything today they'll almost certainly not provide another VFS-style layer on top of all of that.
One thing I've articulated to myself at least is that to have reasonable semantics for a filesystem there needs to be a single "thing" that does path resolution. Right now this is spread across the guest (picking a preopen) and the host (operation on a sub-path from a preopen). The basic idea behind this issue is to do all path resolution in the guest. What I mean by this is that wasi:filesystem interface surface area would never perform path resolution. Instead it would only have extremely primitive operations like "open this directory in this other directory" or "open this file in a directory" or "remove this file within this directory" -- things like that. Notably the name of the path being operated on would still be passed to APIs, but notably the path could not contain ., .., or /. This means that names passed to APIs are required to effectively be file names themselves.
Another aspect of this hypothetical new wasi:filesystem is that while symlinks would remain they would be followed in-guest as opposed to the host. Hosts would implement everything with the O_NOFOLLOW equivalent, for example. If you tried to open a file which is a symlink, that'd just return an error saying "nope, you gotta readlink that".
As-is, this sort of design I believe would neatly solve (1) and (2). Hosts would be required to validate paths, but the validation is trivial. Hosts would be required to pass O_NOFOLLOW everywhere and not operate on symlinks, but it should be quite easy to write conformance tests for this. If directories have no "parent" operation, then everything is trivially sandboxed because by definition guests can only recurse within the directories they are given. Hosts wanting to provide read-only views can also quite easily do so by rejecting any write-operations on such preopens. Overall, the theory at least, is that an API like this is significantly easier for hosts to implement and also a significantly easier-to-sandbox implementation as well.
As-is, this would additionally solve (3) and most (4). If guests do path resolution then they get to handle everything and correctly operating on multiple preopens is expected to fall out naturally. For example a guest-relative parent of /a exists (it's /, and it's just that if you try to open that it'll be a guest-level error). Additionally guests would be able to handle overlapping directories in at least a way that's consistent across hosts, albeit probably still a bit odd between guest languages.
API-wise what I'm thinking would more-or-less look the same as today's wasi:filesystem. Minor differences could include returning a descriptor from create-directory-at, returning resources for child directories in the stream returned by read-directory, removing all path-flags usage (nofollow is now the only option), adjusting relative APIs like link-at to perhaps say "put this descriptor at this path" instead of "put this path at this other path", etc. Largely things would look the same, but semantically they'd be totally different because the guest would have to do a lot more work.
Guests, by and large, expect a POSIX-like API to operate on files. Things like open, read, write, mkdir, etc. This proposal would place a significant burden on guests to implement this POSIX-like API on top of this hypothetical idea. Guests would have to do all the path resolution themselves, they'd have to maintain in-guest parent pointers for directories (if the host doesn't do that), and they'd have to handle symlinks by using readlink-at and then interpreting the result.
I do not want to paint this approach as a silver bullet, as it's far from it. Open questions in my mind include:
- How difficult would it be to actually build an API like this on existing hosts today? This
wasi:filesystem is a significant departure from the POSIX-like interfaces most hosts support today, for example. One counter-argument to this concern, though, is that a fully sandboxed host is already a significant implementation and my hunch is that this design of wasi:filesystem would be much easier to implement than that.
- What would the performance of this API look like? My expectation is that naively this would be significantly slower than native hosts for traversing files. Opening
/a/b/c/d.txt, for example, would mean opening b within /a, then using that to open c, then using that to open d.txt, and if any of those is a symlink guests would have to readlink-at-it and then handle the result. This is a significant departure from "probably one syscall on the host in fast paths today". For example Wasmtime will use O_RESOLVE_BENEATH with openat today, but that's fundamentally incompatible with this wasi:filesystem design.
- Naively I expect that the implemetation in at least wasi-libc to be quite complicated of how to do all this. I don't know if it would be reasonable to expect that implementation complexity to be reflected into other guest languages not based on wasi-libc.
- I've no idea how we would transition from today's
wasi:filesystem to this hypothetical design. Best I can think of is "just deprecate everything" and add a bunch of parallel APIs. Not great, though.
Anyway, lots of possible upsides for this (every host can sandbox now!) and also lots of downsides (complicated for guests, still leaves lots of open questions for wasi:filesystem like "are paths case sensitive"). I wanted to write all of this down in an official issue at least as having this buried in #742 is probably not a good spot for this.
This issue is an extension of my comment here with a more precise design in a location better suiting something that's actually entirely orthogonal to the proposed direction of #742. To recap slightly, there are a number of motivations that this issue is designed to resolve:
wasi:filesystemAPIs. For example Node's wasip1 shim explicitly says it doesn't sandbox. My personal understanding of the situation is that it's just extremely difficult to write a sandboxed implementation with the current APIs thatwasi:filesystemrequires hosts to provide. To me, subjectively again, this is not a great property ofwasi:filesystemif the goal is to provide a sandbox and most hosts just don't do that.wasi:filesystemto be sandboxed, however. Additionally embedders expect the ability to weave together multiple host directories into the guest at various paths. Embedders also generally expect the ability to make host directories either read-only or entirely read/write. Embedders expect all modifications to be neatly contained within these directories, however./aand/bpreopens the guest path/a/../b/foo.txtdoes not resolve on any guest -- again, as far as I know./fooand/foo/bar, then behavior is probably quite host specific, embedder-specific, and guest-specific.In my comment I also outlined why I don't think that #742 is a viable solution to at least a subset of these concerns. Notably implement #742 would involve putting even more complexity into hosts for path resolution and such, and if many hosts already aren't sandboxing anything today they'll almost certainly not provide another VFS-style layer on top of all of that.
One thing I've articulated to myself at least is that to have reasonable semantics for a filesystem there needs to be a single "thing" that does path resolution. Right now this is spread across the guest (picking a preopen) and the host (operation on a sub-path from a preopen). The basic idea behind this issue is to do all path resolution in the guest. What I mean by this is that
wasi:filesysteminterface surface area would never perform path resolution. Instead it would only have extremely primitive operations like "open this directory in this other directory" or "open this file in a directory" or "remove this file within this directory" -- things like that. Notably the name of the path being operated on would still be passed to APIs, but notably the path could not contain.,.., or/. This means that names passed to APIs are required to effectively be file names themselves.Another aspect of this hypothetical new
wasi:filesystemis that while symlinks would remain they would be followed in-guest as opposed to the host. Hosts would implement everything with theO_NOFOLLOWequivalent, for example. If you tried to open a file which is a symlink, that'd just return an error saying "nope, you gotta readlink that".As-is, this sort of design I believe would neatly solve (1) and (2). Hosts would be required to validate paths, but the validation is trivial. Hosts would be required to pass
O_NOFOLLOWeverywhere and not operate on symlinks, but it should be quite easy to write conformance tests for this. If directories have no "parent" operation, then everything is trivially sandboxed because by definition guests can only recurse within the directories they are given. Hosts wanting to provide read-only views can also quite easily do so by rejecting any write-operations on such preopens. Overall, the theory at least, is that an API like this is significantly easier for hosts to implement and also a significantly easier-to-sandbox implementation as well.As-is, this would additionally solve (3) and most (4). If guests do path resolution then they get to handle everything and correctly operating on multiple preopens is expected to fall out naturally. For example a guest-relative parent of
/aexists (it's/, and it's just that if you try to open that it'll be a guest-level error). Additionally guests would be able to handle overlapping directories in at least a way that's consistent across hosts, albeit probably still a bit odd between guest languages.API-wise what I'm thinking would more-or-less look the same as today's
wasi:filesystem. Minor differences could include returning adescriptorfromcreate-directory-at, returning resources for child directories in the stream returned byread-directory, removing allpath-flagsusage (nofollow is now the only option), adjusting relative APIs likelink-atto perhaps say "put thisdescriptorat this path" instead of "put this path at this other path", etc. Largely things would look the same, but semantically they'd be totally different because the guest would have to do a lot more work.Guests, by and large, expect a POSIX-like API to operate on files. Things like
open,read,write,mkdir, etc. This proposal would place a significant burden on guests to implement this POSIX-like API on top of this hypothetical idea. Guests would have to do all the path resolution themselves, they'd have to maintain in-guest parent pointers for directories (if the host doesn't do that), and they'd have to handle symlinks by usingreadlink-atand then interpreting the result.I do not want to paint this approach as a silver bullet, as it's far from it. Open questions in my mind include:
wasi:filesystemis a significant departure from the POSIX-like interfaces most hosts support today, for example. One counter-argument to this concern, though, is that a fully sandboxed host is already a significant implementation and my hunch is that this design ofwasi:filesystemwould be much easier to implement than that./a/b/c/d.txt, for example, would mean openingbwithin/a, then using that to openc, then using that to opend.txt, and if any of those is a symlink guests would have toreadlink-at-it and then handle the result. This is a significant departure from "probably one syscall on the host in fast paths today". For example Wasmtime will useO_RESOLVE_BENEATHwithopenattoday, but that's fundamentally incompatible with thiswasi:filesystemdesign.wasi:filesystemto this hypothetical design. Best I can think of is "just deprecate everything" and add a bunch of parallel APIs. Not great, though.Anyway, lots of possible upsides for this (every host can sandbox now!) and also lots of downsides (complicated for guests, still leaves lots of open questions for
wasi:filesystemlike "are paths case sensitive"). I wanted to write all of this down in an official issue at least as having this buried in #742 is probably not a good spot for this.