Live data from Hacker News

How (memory) safe is Zig? (2021)

scattered-thoughts.net

61–70 of 88 posts

Re: How (memory) safe is Zig? (2021)

#61
post #58

Earlier quoted context omitted.

why do you expect compile time static analysis to fail at this? unless youre loading a precompiled asset?

I don't. I think compile time static analysis is great. Upthread you said this: > there's no reason why the borrow checker must be in the compiler proper. On a technical front, I completely agree. But there's an ecosystem benefit to having the borrow checker as part of the compiler. If the borrow checker wasn't in the compiler proper, lots of people would "accidentally forget" to run it. As a result, lots of librarie…

> lots of people would "accidentally forget" to run it

yeah, like how the sel4 guys accidentally forget to run their static analysis all the time.

You put a badge on CI. If you "forget to run" the static analysis, then people get on you for not running it. Or people get on you if you don't have the badge. Just like how people get on people for not writing programs in rust.

Re: How (memory) safe is Zig? (2021)

#62

Earlier quoted context omitted.

Rust attempts to enforce guarantees statically, but in practice fails, because of pervasive use of `unsafe`. Fil-C doesn't "add a runtime". C already has a runtime (loader, crt, compiler runtime, libc, etc)

> but in practice fails, because of pervasive use of `unsafe`. Yes, in `unsafe` code typically dynamic checks or careful manual review is needed. However, most code is not `unsafe` and `unsafe` code is wrapped in safe APIs. I'm aware C already has a runtime, this adds to it.

> Yes, in `unsafe` code typically dynamic checks or careful manual review is needed. However, most code is not `unsafe` and `unsafe` code is wrapped in safe APIs.

Those are the excuses I heard from C++ programmers for years.

Memory safety is about guarantees enforced by the compiler. `unsafe` isn't that.

Re: How (memory) safe is Zig? (2021)

#63
post #14

Earlier quoted context omitted.

Weird that Swift is your totem for "managed/collected runtime" and not Java (or C#/.NET, or Go, or even Javascript). I mean, it fits the bill, but it's hardly the best didactic choice.

I don't think they said anything about that?

The point was that basically no one knows Swift, and everyone knows Java. If you want to point out a memory safe language in the "managed garbage-collected runtime" family, you probably shouldn't pick Swift.

Re: How (memory) safe is Zig? (2021)

#64
post #39

Earlier quoted context omitted.

The stuff Fil-C adds is on the same footing as `unsafe` code in Rust- its implementation isn't checked, but its surface area is designed so that (if the implementation is correct) the rest of the program can't break it. Whether the amount and quality of this kind of code is comparable between the two approaches depends on the specific programs you're writing. Static checking, which can also be applied in more fine-gr…

> The stuff Fil-C adds is on the same footing as `unsafe` code in Rust- its implementation isn't checked, but its surface area is designed so that (if the implementation is correct) the rest of the program can't break it. It’s not the same. The Fil-C runtime is the same runtime in every client of Fil-C. It’s a single common trusted compute base and there’s no reason for it to grow. On the other hand Rust programmers…

Yeah, that's what I meant by "depends on the specific programs you're writing." Confining unsafe Rust to core libraries is totally something people do.

Re: How (memory) safe is Zig? (2021)

#65
post #64

Earlier quoted context omitted.

> The stuff Fil-C adds is on the same footing as `unsafe` code in Rust- its implementation isn't checked, but its surface area is designed so that (if the implementation is correct) the rest of the program can't break it. It’s not the same. The Fil-C runtime is the same runtime in every client of Fil-C. It’s a single common trusted compute base and there’s no reason for it to grow. On the other hand Rust programmers…

Yeah, that's what I meant by "depends on the specific programs you're writing." Confining unsafe Rust to core libraries is totally something people do.

You're equating a core runtime that doesn't grow with libraries written by anyone.

There's no world in which a Fil-C user would write unsafe code. That's not a thing you can do in Fil-C.

Rust users write unsafe code a lot and the language allows it and encourages it even.

Re: How (memory) safe is Zig? (2021)

#66
post #64

Earlier quoted context omitted.

Yeah, that's what I meant by "depends on the specific programs you're writing." Confining unsafe Rust to core libraries is totally something people do.

You're equating a core runtime that doesn't grow with libraries written by anyone. There's no world in which a Fil-C user would write unsafe code. That's not a thing you can do in Fil-C. Rust users write unsafe code a lot and the language allows it and encourages it even.

I mean, again, yeah. I specifically compared the safe API/unsafe implementation aspect, not who writes the unsafe implementation.

To me the interesting thing about Rust's approach is precisely this ability to compose unrelated pieces of trusted code. The type system and dynamic semantics are set up so that things don't just devolve into a yolo-C-style free-for-all when you combine two internally-unsafe APIs: if they are safe independently, they are automatically safe together as well.

The set of internally-unsafe APIs you choose to compose is a separate question on top of that. Maybe Rust, or its ecosystem, or its users, are too lax about this, but I'm not really trying to have that argument. Like I mentioned in my initial comment, I find this interesting even if you just apply it within a single trusted runtime.

Re: How (memory) safe is Zig? (2021)

#67
post #64

Earlier quoted context omitted.

Yeah, that's what I meant by "depends on the specific programs you're writing." Confining unsafe Rust to core libraries is totally something people do.

You're equating a core runtime that doesn't grow with libraries written by anyone. There's no world in which a Fil-C user would write unsafe code. That's not a thing you can do in Fil-C. Rust users write unsafe code a lot and the language allows it and encourages it even.

> Rust users write unsafe code a lot

This isn't the case.

Re: How (memory) safe is Zig? (2021)

#68

Earlier quoted context omitted.

There's also no reason to have a separate borrow checker if it could just be integrated in the compiler. When a compiler has a borrow checker that means the language was already designed to enable borrow checking in the first place. And if a language can let you do borrow checking why would you use a separate tool?

because it gets it out of the fast path compile cycle. do you need a borrow checker for `ls`? Probably not. don't use it. do you need it every time you work through intermediate ideas in a refactor? probably not. just turn it on in CI.

The borrow checker is very fast.

Re: How (memory) safe is Zig? (2021)

#69

Earlier quoted context omitted.

There's also no reason to have a separate borrow checker if it could just be integrated in the compiler. When a compiler has a borrow checker that means the language was already designed to enable borrow checking in the first place. And if a language can let you do borrow checking why would you use a separate tool?

because it gets it out of the fast path compile cycle. do you need a borrow checker for `ls`? Probably not. don't use it. do you need it every time you work through intermediate ideas in a refactor? probably not. just turn it on in CI.

The borrow checker is not the slow part of the Rust compiler and lets me avoid bugs, why would I not always want to use it?

And if you put the borrow checker in the CI you massively increased the latency between writing the code and getting all relevant feedback from the compiler/tooling. This would do the opposite of what you intended.

Re: How (memory) safe is Zig? (2021)

#70
post #43

Earlier quoted context omitted.

> C and C++ are often chosen because they make systems programming possible at all due to their direct access to syscall ABI. Surely Fil-C cannot provide direct access to syscalls without violating the safety guarantee. There must be something ensuring that what the kernel interprets as a pointer is actually a valid pointer. > Fil-C means you cannot claim - as TFA claims - that it’s impossible to make C and C++ safe.…

> Surely Fil-C cannot provide direct access to syscalls without violating the safety guarantee. There must be something ensuring that what the kernel interprets as a pointer is actually a valid pointer. This is exactly what Fil-C does. > all the known disadvantages of C and C++ The main disadvantage of C and C++ is unsafety and fil-C comprehensively fixes that. > edit: I feel bad writing such a dismissive comment, bu…

> This is exactly what Fil-C does.

Okay, I just checked. It does not. I wrote: "There must be something ensuring that what the kernel interprets as a pointer is actually a valid pointer." And sure enough, your runtime manually wraps each Linux syscall to do exactly that:

https://github.com/pizlonator/llvm-project-deluge/blob/6804d...

For harder cases like fcntl, where arguments can be either pointers or integers, you have to enumerate the possible fcntl arguments:

https://github.com/pizlonator/llvm-project-deluge/blob/6804d...

ioctl is even harder because some ioctls take pointers to structs that themselves contain pointers; on Linux that includes v4l2 and mmc. It looks like you don't handle that properly, judging by:

https://github.com/pizlonator/llvm-project-deluge/blob/6804d...

--

My point is: having to go through wrapper functions is not what I'd call "direct" access to "ABI". (Also, the wrappers don’t even wrap the syscall ABI directly; they wrap the libc ABI that in turn wraps syscalls.)

You might object that the wrappers are thin enough that they still count as direct. While that's a matter of definitions, I think my previous comment made it clear what _I_ meant when I questioned "direct", given my followup sentence about "actually a valid pointer".

But beyond quibbles about who meant what, this lack of directness matters because it implicates the portability of your approach.

At least as currently implemented, you rely on compiling almost everything (even libc) inside the sandbox, while having a ‘narrow waist’ of syscall wrappers mediating access between the sandbox and the outside world. That should work for most use cases on Linux, where there's already an assumption that different processes can have completely different library stacks, and static linking is common. Even if you want to do a GUI application you should be able to recompile the entire GTK or Qt stack inside the sandbox, and it doesn’t matter if other apps are using different versions or GTK or Qt.

But what about other operating systems? For server and CLI software you can probably still get away with exposing a small syscall/libc API, similar to Cosmopolitan (though that will still require significant effort for each OS). But for GUIs and platform integration more broadly, you’re expected to use the platform-provided libraries that live in your address space. They are usually proprietary, and even when they’re not, the system isn’t designed for multiple versions of the libraries to coexist.

I know I’m not telling you anything you don’t already know. But it’s an important point, because aside from performance, the _other_ big reason that Rust relies on user-written unsafe code is for FFI. If anyone can write their own FFI bindings, as opposed to making all FFI bindings live in a centralized runtime, then it becomes more feasible to scale the mammoth task of writing safe wrappers for all those ABIs. Your approach explicitly rejects user-written unsafe code, so I don’t know how you can possibly end up with reasonable OS library coverage.

Now sure, you didn’t claim anything about GUIs or portability. Perhaps this is more on topic for my previous comment’s point about “high difficulty interoperating with other native code” which you didn’t rebut. But it’s also relevant to “direct access to syscall ABI”, because if there _were_ some way to provide direct access to syscall ABI while remaining memory-safe, then the same approach would probably extend to other system ABIs. For example, a fully CHERI-aware system actually would allow for that. It’s an unfair comparison, because CHERI assumes cooperation from both the hardware and the OS, while you’re trying to run on existing hardware and OSes. I have no idea if we’ll ever see CHERI in general purpose systems. But in exchange, CHERI achieves something that’s otherwise impossible: combining direct system access and memory safety. And I originally read your comment as claiming to do the impossible.

Post reply on HN