Discussion Thread: DIP 1035-- at system Variables--Final Review

Stanislav Blinov stanislav.blinov at gmail.com
Wed Feb 23 23:07:02 UTC 2022


On Wednesday, 23 February 2022 at 18:16:17 UTC, Paul Backus wrote:

> Having spent some more time scratching my head over this, I now 
> realize what I was missing: it is indeed possible to open a 
> file descriptor that can corrupt *arbitrary* memory in a 
> process's address space, using something like `/proc/self/mem`.

Yes, or you may use e.g. `memfd_create`. And you can inherit such 
an fd from a parent process. Or receive a shared memory 
descriptor from another process.

> Maybe I'm an idiot for missing this the first time around; I 
> can only ask that you take pity on me. :)

Never! How dare you make me question myself!!! :)

> This means that calling `write` on a fd is only memory safe if 
> you have previously verified that the file the fd refers to is 
> "well behaved" (i.e., satisfies a particular invariant). It 
> follows that the fd itself must be stored in a `@system` 
> variable in order to ensure that the invariant is maintained in 
> `@safe` code.

Yup.

> I don't think adding `scope` checking to the fd makes any 
> difference here, though. *Reading* from `/proc/self/mem` in 
> `@safe` code is perfectly fine, even if you are reading from 
> uninitialized or deallocated memory. The reason such reads are 
> UB when done through pointers is that *dereferencing an invalid 
> pointer* is UB, not because reading from the memory is UB.

Well, results of `read`ing from some types of fds are also not 
specified. So, if I'm not mistaken, performing such a read *and* 
then using the resulting "data" would be undefined behavior 
(provided the program even gets there).

As for `scope` checks themselves - as Dennis mentions, double 
`close` looks dissimilar to double free. Yet it *is* subject to a 
superset of that - use after free, as are `read` and `write`. You 
may well safely "dangle" an fd and not invoke UB by calling those 
functions with it, but only up to the point when the program 
opens another descriptor. Calling `close` on a dangled fd, which 
would then succeed, would be a mere bug and not invoke UB, but 
attempting to `write` or `read`+use may.

So I do think that fds could still be a good example material for 
the DIP.

> (I'm also not sure if it's possible in practice to tell whether 
> a file is "well behaved". If not, that means we have to either 
> accept that `write` is *always* `@system`, or allow a permanent 
> loophole in `@safe`. But that's a separate issue.)

I don't think that should be necessary in concrete cases, as the 
onus of ensuring the implicit invariant would lie on the 
implementation of, in this case, `File` - e.g. making it 
non-copyable (or reference-counted), ensuring that the 
constructor opens an appropriate kind of file, etc. etc. That way 
the only way to make it unsafe would be to corrupt the given 
instance of `File` itself, which means there's a memory safety 
issue somewhere else in the program (for example, that same 
void-initialization).


More information about the Digitalmars-d mailing list