Live data from Hacker News

Rootless Pings in Rust

bou.ke

81–87 of 87 posts

Re: Rootless Pings in Rust

#81
post #74
post #70

Earlier quoted context omitted.

Of course it allows invalid combinations. This also compiles: let f = std::fs::File::open("/dev/null").unwrap(); let f: std::os::fd::OwnedFd = f.into(); let socket: std::net::UdpSocket = f.into(); If you convert a high level object into a low level one, and then back up as another type, then what exactly do you expect the language to do about that? > "protected from or not exposed to danger or risk." A computer will…

There are two issues here, and you're talking about a different one from the one I'm interested in. Your main issue seems to be this: > If you convert a high level object into a low level one, and then back up as another type, then what exactly do you expect the language to do about that? One answer to this would be "prevent it entirely". That's probably not practical for a language like Rust today, though, and I don…

> What I care about is that it's necessary to do this in the first place.

I don't think it is. socket2::Socket has send_to() just as much as UdpSocket does.

(disclaimer: I only looked up the docs, I didn't try to modify the code to strip out needless UdpSocket)

> runtime-unsafe

That's not a thing. You always need to check for errors. seccomp could be blocking your syscalls. Hard drives break such that reads return error.

Getting an Err() from a function does not make it "unsafe", runtime or not.

> the types could be designed to prevent the need for doing this in the first place.

If your type system does not allow you to "bring your own fd (to be managed)", then it's not fit for purpose for the kind of problems Rust aims to solve.

A systems language needs to be able to receive a file descriptor from a C library, and work with it.

Re: Rootless Pings in Rust

#82
Since basically all the comments are about how both the author and many commenters are confused about what UDP and DGRAM sockets are, I have corrected the author's code to no longer miscommunicate what protocol is being used.

https://github.com/ThomasHabets/rust-ping-example-corrected

There is no UDP used anywhere in this example. ICMP is not UDP.

I'm not saying my fix is pretty (e.g. uses unwrap(), and ugly destination address parsing), but that's not the point.

Re: Rootless Pings in Rust

#84

Why does Linux require root for this if you can do it anyway?

It doesn't.

For users in the UID range in sysctl `net.ipv4.ping_group_range` the normal ping command uses this non-root way.

Sure, maybe your system still sets suid root on your ping binary, or shows it adding `cap_net_raw` according to `getcap`, but mine does not.

Re: Rootless Pings in Rust

#85
post #81
post #74

Earlier quoted context omitted.

There are two issues here, and you're talking about a different one from the one I'm interested in. Your main issue seems to be this: > If you convert a high level object into a low level one, and then back up as another type, then what exactly do you expect the language to do about that? One answer to this would be "prevent it entirely". That's probably not practical for a language like Rust today, though, and I don…

> What I care about is that it's necessary to do this in the first place. I don't think it is. socket2::Socket has send_to() just as much as UdpSocket does. (disclaimer: I only looked up the docs, I didn't try to modify the code to strip out needless UdpSocket) > runtime-unsafe That's not a thing. You always need to check for errors. seccomp could be blocking your syscalls. Hard drives break such that reads return er…

> > runtime-unsafe

> That's not a thing.

Yes, it is. See all the work on the subject of "make illegal states unrepresentable". Search for that phrase, it's originally from Yaron Minsky at Jane Street Capital, but it's mainly a pithy characterization of a common goal for languages with strong type systems, like Rust, Haskell, or the ML family. Another way this is expressed is as "static debugging" - the idea that you can debug a significant proportion of a program's bugs statically, using the type system.

That's what I'm referring to here. If it's unfamiliar to you, it will likely take some time to get used to, because it's a significantly different paradigm from the common approach of debugging by running programs, encountering runtime errors, and trying to resolve them. But, instead of getting indignant and objecting to what I'm saying, consider that there might be something for you to learn here.

> You always need to check for errors. seccomp could be blocking your syscalls. Hard drives break such that reads return error.

What do you believe the relevance of this is? You need to check for errors that can't be checked for at compile time. That doesn't mean we should abandon the idea of checking for errors at compile time, or improving the scope of issues that we can detect at compile time.

Errors that are prevented at compile time cannot occur at runtime in principle, and that's an extremely powerful invariant in software development, one that any serious software developer should be aware of, and be able to take advantage of.

Type systems allow you to prove properties of programs that otherwise would need to be debugged and tested at runtime, but to take advantage of that, the types need to be designed appropriately.

> If your type system does not allow you to "bring your own fd (to be managed)", then it's not fit for purpose for the kind of problems Rust aims to solve.

This is a failure of imagination, nothing more. An appropriate type schema for this domain will be able to handle the requirements of the domain.

Re: Rootless Pings in Rust

#86
post #59

Earlier quoted context omitted.

> The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing. It comes down to these two lines (using full items paths for clarity): let socket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?; let socket: std::net::UdpSocket = socket.into(); The latter is using this impl: https://docs.rs/socket2/0.6.1/socket2/struct.Socket.html#imp... Basically…

It may be memory safe but it's not using the type system to represent the domain very well. One could imagine a more type-friendly design in which we could write that first line as follows: let socket: Socket = Socket::new()?; Now, the specifics of socket types will be statically checked. Edit: I realized that the issue here is actually the conversion, and that UdpSocket on its own is actually a type-safe representat…

Rust has an RFC about some of the conventions for the Fd conversions. https://rust-lang.github.io/rfcs/3128-io-safety.html

It's unfortunate they did not extend marking the OwnedFd conversions as unsafe due to the focus in the RFC on a single class of unsafety in Fds instead of having a recognition that there are other issues with arbitrary Fd conversions.

Re: Rootless Pings in Rust

#87
post #85
post #81

Earlier quoted context omitted.

> What I care about is that it's necessary to do this in the first place. I don't think it is. socket2::Socket has send_to() just as much as UdpSocket does. (disclaimer: I only looked up the docs, I didn't try to modify the code to strip out needless UdpSocket) > runtime-unsafe That's not a thing. You always need to check for errors. seccomp could be blocking your syscalls. Hard drives break such that reads return er…

> > runtime-unsafe > That's not a thing. Yes, it is. See all the work on the subject of "make illegal states unrepresentable". Search for that phrase, it's originally from Yaron Minsky at Jane Street Capital, but it's mainly a pithy characterization of a common goal for languages with strong type systems, like Rust, Haskell, or the ML family. Another way this is expressed is as "static debugging" - the idea that you…

You're misapplying it.

A language that doesn't let you do safe things, then that's a very different language.

In this case, it would be a language that does not allow creating a UdpSocket object by bringing in your own file descriptor, or it verifies that it's the right type of socket when you do. Which has performance implications without adding any "safe" guarantees.

Say you add this feature, taking the performance hit. Now you need to adjust seccomp policies to allow that. Ok, no biggie. But then I invent UDPv2, and this check fails. The code becomes wrong because of an incorrect assumption about the future.

All without gain. It's not an invalid state, any more than naming a variable "x_squared" but containing x+1 is an invalid state.

You could also imagine stdout to be of a different type if it's line or character buffered, and continue in the direction of a cartesian explosion for all states. Ok… that seems like it'd cause more problems than it'd solve.

> instead of getting indignant

Please don't assume my mental state. You got it wrong.

> This is a failure of imagination, nothing more. An appropriate type schema for this domain will be able to handle the requirements of the domain.

I'm all ears. Note that it also has to support "I got the file descriptor as a libc::c_int from a C library", or it's not fit for purpose.

Post reply on HN