-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
handle impossible errors from the kernel with error.Unexpected instead of unreachable #6389
Comments
Either that, or we need an |
I think |
Do you have any example of a syscall that have changed its behaviour, except ioring stuff? If its specific to that feature, then do not use this pattern with these apis. |
I haven't looked, but it's not about changing behavior so much as adding behavior, i.e. two error states (one old, one new) map to one error code, and Zig only checks one error state (because that was the only error state that existed at the time in the man pages) or assumes unreachable. |
I don't know much about Linux; maybe I'm spouting nonsense, but couldn't the opposite, hypothetically, be true as well? If a syscall is "upgraded" to loosen its preconditions, then what was previously an error now succeeds. If the program relies on that logic, it breaks semantically (which is its own class of undefined behaviour). To me it seems like the actual solution would be to instead put an upper limit on the supported kernel version (so do a version check and |
Thanks @rohlem, for the interesting inverse example. That's certainly true, but I think at least it wouldn't lead to undefined behavior from a safety point of view? The program simply wouldn't receive an error from the kernel, so this kind of code branch wouldn't be reached. Unless, then again, it could be a safety issue if the program required the kernel to return an error, and fell back to unreachable if it did not.
Perhaps at first glance, but remember the kernel is free to backport features. Ultimately, checking kernel versions is a slippery slope and should be avoided in favor of relying on the error mechanism the kernel already exposes, albeit without using unreachable to assert what the kernel can/cannot return as an error. |
Its expected that OSes have some backward compatibility, if the system has a breaking change thats not our problem (everything would break if for example read syscall suddenly changed). All documented errors should be handled and nothing more. |
My only concern here is with the debug experience. For example the following application: const std = @import("std");
test "close() twice" {
var file = try std.fs.cwd().createFile("aoeuaoeu", .{});
defer file.close();
file.close();
} The debug experience here is excellent:
Let's just make sure it remains excellent after the changes incurred by this proposal. |
Yes, thanks for the example. On second thought I think we should actually leave the status quo as is, because it's a better experience, and safe in Debug and ReleaseSafe even if the kernel does overload errors. It's also something that can be maintained across different kernel versions and they're not going to be overloading errors that often. Alternatively, simply a |
Just to complicate things. Any syscall can produce any error due to seccompbpf or an LSM although usually it would be PERM, but maybe some people would use NOSYS. This is a very common scenario now with sandboxing and containers. Syscalls that act on FDs can produce any error because FDs can point to anything. You can create a file system that represents DNS and start returning NSRCNAMELOOP on writes or reads. It doesn't have to be a mainline kernel, it can be an external module or FUSE. Even with just the mainline kernel, if you actually mapped all the error codes that a syscall like |
On Linux, e.g. in
os.zig
the std lib will often interrogateerrno
and map to Zig errors, orunreachable
if the std lib wants to assert that the std lib implementation would never cause anEINVAL
orEFAULT
, e.g.:However, while implementing #6356, I was about to follow this pattern but then I realized that the kernel often overloads errors in new kernel versions, which is particularly the case for the
io_uring
syscalls.This means that we might think our std lib implementation cannot cause
EINVAL
, and then the kernel adds a new feature which could, leading to undefined behavior instead of a safe error.In other words, we need to start going through the std lib and make this usage of
unreachable
an anti-pattern because there's no way we can assert what the kernel can or cannot be returning like this.The text was updated successfully, but these errors were encountered: