-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
unix/weak: pass arguments to syscall at the given type #79196
Conversation
It looks like Rustc is already very restrictive about the types you can pass to a C-variadic function. It doesn't even allow a extern "C" {
fn a(_: i32, ...);
}
fn main() {
unsafe { a(123, 0u8) };
}
(C happily passes a We should try to figure out if this restriction is too strict or just right. (I don't think there's any syscall taking a If this built-in restriction is not too loose or strict, we can just remove the |
Seems like the original |
That check is here: rust/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs Lines 372 to 375 in 2bf93bd
Looks like the Miri-side check that all arguments have |
@alexcrichton @adrian-budau why was this cast introduced in the first place, do you remember? |
That was 2 years ago so I'm not entirely sure of the context, but I think I just commented how an |
As best as I can remember: It wouldn't compile if I didn't cast some arguments. The standard says all arguments should fit in the register size, so casting to |
Well, it seems to compile fine though... |
It wasn't my intention to support such types ( |
There's only five uses of
Looks like it isn't very restrictive. Even passing a |
RE: #78785 (comment)
The callee It's hard to even puzzle this out from glibc sources, because all arches are implemented directly in assembly, but in musl you can see that it simply reads 6 unconditional https://git.musl-libc.org/cgit/musl/tree/src/misc/syscall.c I think it's not strictly necessary to cast the arguments like that, as long as you follow the C promotion rules for varargs then it will work out, but it seems on the safer side to just pass
If we choose not to cast to unsafe fn $name($($arg_name:$t),*) -> $ret { |
Seems like I am too attached to type systems to fully wrap my head around this.^^ If you prefer we can also hack something in Miri to accept both 32bit and 64bit integers here (I presume we should truncate them to 32bit as who knows which junk might be in the "upper half" in case the caller just passed 32bit). |
Can you explain the issue from Miri's side? Why does it care? |
That seems like an argument for casting to |
When Miri convert an interpreter value to something it works on, it needs to state the size that the value should have. We always double-check that the actual size of the value matches what the interpreter expects. In many cases this is required as mismatching size is UB; furthermore this approach has caught countless bugs.
So Miri should require the type to be Miri needs to be written in a way such that if it does not complain about UB, whatever it does is guaranteed to match whatever happens on any possible real implementation. This is easy to do when we enforce that the size exactly matches what is says in the syscall manpage. If we truly want to support code that relies on further details of the ABI, we need to be really careful to not accidentally accept incorrect code. Right now, the approach in Miri when implementing some FFI function or syscall is that we check the manpage and enforce that all argument types match what it says there. This is easy to explain and obviously sound (in the sense that we will not accept incorrect code). If we diverge from that I'd like to make sure I understand how and why it is okay to do so. |
But I don't understand what you're trying to match against in this case, when we're calling long syscall(long number, ...); Is Miri trying to interpret that |
I guess this is regarding the syscall interception here? I'm not sure what to suggest about that... |
An option could be to avoid |
How is that too aggressive? At a conceptual level this is what the implementation also does, if we do not want to rely on ABI details. It matches on the syscall number and then interprets the arguments. Are you saying the syscall manpages's use of types is entirely meaningless?
This specific problem arises here: (The flag value actually does not matter, so the only reason we call |
Could Miri just validate that whatever argument type is there, that it's legal to cast
We're not calling |
I guess my point is that |
That's fair. However, I assume that if the type is, e.g., And 128bit types might have a different calling convention entirely. Can we accept those as well? And furthermore, I think this means we rely on the fact that for all ABIs, when you pass a 64bit value to a syscall which expects a 32bit argument, things will just get truncated. I cannot tell if that is obviously true for every single ABI that Rust supports now or in the future. I hope you see why I don't consider it easy to just accept two different sizes of arguments for one function/operation.^^ |
When we currently cast From Miri's side, that sounds reasonable to check the lower bound size, so you know the "useful" bits are well defined -- especially if you're considering
There are concerns in
We already have a typed |
Oh, I thought this would be docs about the underlying syscall. Thanks for pointing that out. Where do I find information for the types expected by the individual syscalls?
My question wasn't about what to do in this specific case, but what to do in general. Miri has to work not just for whatever the standard library currently happens to do on x86_64. Or I guess we could make Miri accept 64bit arguments but requires the higher bits to be 0, but that seems... odd?
"there are examples of 64-bit args that are a problem for 32-bit targets" sounds like we should not accept 64bit arguments for 32bit parameters in general. Maybe ptr-sized arguments for 32bit parameters work in general, at least if the ptr size is >= 32bit?
That's fair, I can remove the roundtrip. But that does not answer my question: can we be sure that, in any ABI Rust supports now or in the future, passing a |
I may have spoken too quickly -- The pure reference would be in the kernel source:
Then if you look at the example SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
unsigned int, flags) That wrapper can vary between arches, but the generic one defines
But you're not trying to emulate all possible
I guess you could treat it sign-extended, so all ones are also fine when expecting a smaller signed type.
I'd say you should not accept anything larger than the target size -- not because it's impossible, but because that presents a challenge we don't need to deal with right now. 64-bit arguments for 32-bit parameters are fine on 64-bit targets, just using a full register.
I'm saying they are effectively passed to the kernel in registers (~ |
All that said, I'm okay with dropping the cast, assuming it gets through CI of course. If it breaks, then I guess we'll find out why #56779 wanted it, and we can instead add a comment explaining this to our future selves. |
I see, thanks for digging that up. This almost sounds like Miri should require all arguments to be ptr-sized.^^ So it is the other way around then -- if we pass things as
So, that'd be landing the PR as-is (I removed the roundtrip), and only taking further action if that fails in bors? |
@bors r+ I don't expect any problems with this change. There's very few usages of this macro, and it only affects Linux. (It will break |
📌 Commit d8d763d has been approved by |
⌛ Testing commit d8d763d with merge acaf1ab89e3cfe64f42b879fd87813bbc979f6df... |
Not |
⌛ Testing commit d8d763d with merge 3d61742233888555f7a33bb32b0277b1070d5af4... |
Bors, what are you doing? (: I edited the |
Editing the comment re-sends the command. Probably it interpreted the re- |
This comment has been minimized.
This comment has been minimized.
(That is the first job being cancelled, I guess?) |
☀️ Test successful - checks-actions |
Tested on commit rust-lang/rust@172acf8. Direct link to PR: <rust-lang/rust#79196> 💔 miri on linux: test-pass → test-fail (cc @oli-obk @eddyb @RalfJung).
Given that we know the type the argument should have, it seems a bit strange not to use that information.
r? @m-ou-se @cuviper