I agree on the last point of the lack of composition here.
While it's true that writers need to be aware of buffering to make use of fancy syscalls, implementing that should be an option, but not a requirement.
Naively this would mean implementing one of two APIs in an interface, which ruins the direct peformance. So I see why the choice was made, but I still hope for something better.
It's probably not possible with zig's current capabilities, but I would ideally like to see a solution that:
- Allows implementations to know at comptime what the interface actually implements and optimize for that (is buffering supported? Can you get access to the buffer inplace for zero copy?).
- For the generic version (which is in the vtable), choose one of the methods and wrap it (at comptime).
There's so many directions to take Zig into (more types? more metaprogramming? closer to metal?) so it's always interesting to see new developments!
> While it's true that writers need to be aware of buffering to make use of fancy syscalls, implementing that should be an option, but not a requirement.
Buffering is implemented and handled in the vtable struct itself, the writers (implentations of the interface) themselves don't actually have to know or care about it other than passing through the user-provided buffer when initializing the vtable.
If you don't want buffering, you can pass a zero-length buffer upon creation, and it'll get optimized out. This optimization doesn't require devirtualization because the buffering happens before any virtual function calls.
I wonder if making this change will improve design of buffering across IO implementers because buffering needs consideration upfront, rather than treatment as some feature bolted on the side?
It’s a good sacrifice if the redesign, whilst being more complicated, is avoiding an oversimplified abstraction which end up restricting optimisation opportunities.
You do need an suid binary to e.g. set a new user id map, since this requires comparing the user id range owned by you to what you're mapping, but you only do it once and it's a simple, secure operation.
That doesn't always fix it. An attacker can race you to make you write something in a place you didn't intend or expect unless the application is incredibly carefully written.
And by "incredibly" I mean beyond the scope of human endeavour :-).
On the mdn page for it they say there are other less legitimate techniques like adding an image which will start loading and delay the page unloading. So if they didn't add beacons it would mean other less performant ways would be (ab)used.
I think you would need a pipe reading from (using splice) a file you're not supposed to write to, which also accepts your input, which you're very unlikely to find, and you would only be able to modify the file being read.
We have atomic bitwise operations already (look at glibc's mutex implementation), and the unit atomic operations work on is a 64-byte cache line. Cache lines are useful because reading 64 bytes isn't really more expensive but it improves sequential memory access by a lot.
While it's true that writers need to be aware of buffering to make use of fancy syscalls, implementing that should be an option, but not a requirement.
Naively this would mean implementing one of two APIs in an interface, which ruins the direct peformance. So I see why the choice was made, but I still hope for something better.
It's probably not possible with zig's current capabilities, but I would ideally like to see a solution that:
- Allows implementations to know at comptime what the interface actually implements and optimize for that (is buffering supported? Can you get access to the buffer inplace for zero copy?).
- For the generic version (which is in the vtable), choose one of the methods and wrap it (at comptime).
There's so many directions to take Zig into (more types? more metaprogramming? closer to metal?) so it's always interesting to see new developments!