Live data from Hacker News

How safe is Zig?

scattered-thoughts.net

151–160 of 259 posts

Re: How safe is Zig?

#151

Safe enough. You can use `std.testing.allocator` and it will report leaks etc in your test cases. What rust does sounds like a good idea in theory. In practice it rejects too many valid programs, over-complicates the language, and makes me feel like a circus animal being trained to jump through hoops. Zigs solution is hands down better for actually getting work done, plus it's so dead simple to use arena allocation a…

>Zigs solution is hands down better for actually getting work done Rust has seen significant usage in large companies; they wouldn't be using it unless it was usable for "real work". >Full disclaimer, I'm pretty bad at systems programming. Zig is the only one I've used where I didn't feel like memory management was a massive headache. I'd say this about Rust, though. Rust's mental model is very straightforward if you…

Rust has seen significant usage in large companies; they wouldn't be using it unless it was usable for "real work".

I didn't say it wasn't usable. I said I found Zig more usable.

I'd say this about Rust, though. Rust's mental model is very straightforward if you accept the borrow-checker and stop fighting it. Can you list any examples of what you think is a headache...?

Mate, I didn't start learning rust in order to wage war against the borrow checker. I had no idea what the hell it wanted a lot of the time. Each time I fixed an error I thought I got it, and each time I was wrong. The grind got boring.

As for specific examples no, I've tried to put rust out of my mind. I certainly can't remember specific issues from 3 months ago.

I've found that jumping through those hoops leads to things running in production that don't make me get up in the middle of the night. Can you show me a "valid program" that Rust rejects?

Yeah that's how rust as sold, the compiler is your friend and stuff will compile and it will never fail.

In reality the compiler was so irritating I hardly got anything done at all. The output wasn't super reliable software, it was no software.

Re: How safe is Zig?

#152

Earlier quoted context omitted.

That's just a logic error, and not memory unsafety which might risk UB or vulnerabilities. The type system enforces that if we use-after-"free" (remember, we're not free'ing or malloc'ing), we just get a different instance of the same type, which is memory-safe. You do bring up a valid broader concern. Ironically, this is a reason that GC'd systems can sometimes be better for privacy than Ada or Rust which uses a lot…

But, Java has exactly the same behaviour, the typical List in Java is ArrayList which sure enough has an indexed get() method. There seems to be no practical difference here. Rust can do a reference to UserAccount, and Java can do an index into an ArrayList of UserAccounts. Or vice versa. As you wish.

In Java, you can hold onto a reference to the UserAccount directly, for as long as you want.

The borrow checker, however, forces you to hold onto an index instead.

Re: How safe is Zig?

#153

A meta point to make here but I don’t quite understand the pushback that Rust has gotten. How often does a language come around that flat out eliminates certain errors statically, and at the same time manages to stay in that low-level-capable pocket? And doesn’t require a PhD (or heck, a scholarly stipend) to use? Honestly that might be a once in a lifetime kind of thing. But not requiring a PhD (hyperbole) is not en…

Unsafe Rust is an esoteric language without iron-clad guarantees, and type-level programming and async Rust is an esoteric metalanguage (https://hirrolot.github.io/posts/rust-is-hard-or-the-misery-...). For example, matklad made a recent blog post on "Caches In Rust" (https://matklad.github.io/2022/06/11/caches-in-rust.html). The cache is built around https://docs.rs/elsa, which is built around https://docs.rs/stable_deref_trait/latest/stable_deref_trait..., which is unsound for Box and violates stacked borrows in its current form: https://github.com/Storyyeller/stable_deref_trait/issues/15

There is a recurring trend of sound C programs turning into unsound Rust programs, because shared mutability is often necessary but it's difficult to avoid creating &mut, and Stacked Borrows places strict conditions on constructing &mut T (they invalidate some but not all aliasing *const T).

Re: How safe is Zig?

#154

Earlier quoted context omitted.

Nothing Is Perfect is a common refrain and non-argument. If option A has 20 defects and option B has the superset of 25 defects then option A is better—the fact that option A has defects at all is completely besides the point with regards to relative measurements.

Zig keeps overflow checks in the main release mode (ReleaseSafe), Rust defines ints as naturally wrapping in release. This means that Rust is not a strict superset of Zig in terms of safety, if you want to go down that route. I personally am not interested at all in abstract discussions about sets of errors. Reality is much more complicated, each error needs to be evaluated with regards to the probability of causing…

releasesafe is the main release mode because zig has a small community that is largely ideologically aligned with it.

I have absolutely no faith that, in a future where Zig is popular, it will remain so. "well it passed our unit tests and it didn't fall down when we fuzzed it, so lets squeeze a couple free % extra perf out of it and ship" already in this post we have people talking about how safer mallocs or sanitizers are too much of a hit to expect people to use in the wild.

Re: How safe is Zig?

#155

A meta point to make here but I don’t quite understand the pushback that Rust has gotten. How often does a language come around that flat out eliminates certain errors statically, and at the same time manages to stay in that low-level-capable pocket? And doesn’t require a PhD (or heck, a scholarly stipend) to use? Honestly that might be a once in a lifetime kind of thing. But not requiring a PhD (hyperbole) is not en…

It's pretty simple. Rust's safety features (and other language choices) have a productivity cost. For me I found the cost surprisingly high, and I'm not alone (though I'm sure I'll get replies from people who say the borrow checker never bothers them anymore and made them a better programmer, let's just agree there's room to disagree).

Although I'm a big fan of safety, since experiencing Rust my opinion is that low-pause GC is a better direction for the future of safe but high performance programming. And there's also a niche for languages that aren't absolutely safe which I think Zig looks like a great fit for.

Re: How safe is Zig?

#156
post #64
post #60

Earlier quoted context omitted.

Has an exploitable buffer bleed (I'm happy with this coinage!) happened in any recent memory safe codebase?

I worked on a static analysis tool to detect bleeds in outgoing email attachments, looking for non-zero padding in the ZIP file format. It caught different banking/investment systems written in memory safe languages leaking server RAM. You could sometimes see the whole intranet web page, that the teller or broker used to generate and send the statement, leaking through. Bleeds terrify me, no matter the language. The…

This error is possible in Rust, but not easy to cause.

Uninitialised memory is considered unsafe. There aren't any loopholes even for "just bytes". You can't accidentally make an uninitialised buffer, and you can't expose one without explicit `unsafe{}`.

Sych unsafe code is typically wrapped in safe interfaces, so the risk is limited to implementation side, and not spread to every usage site.

For filling buffers the language pushes towards use of growable Vec with capacity that tracks how much has been initialised, or to collect straight from an iterator, which is foolproof.

Custom buffers are not even used that often, because things like zip writing or compression use io::Write streaming interface.

Re: How safe is Zig?

#157

Earlier quoted context omitted.

Nothing Is Perfect is a common refrain and non-argument. If option A has 20 defects and option B has the superset of 25 defects then option A is better—the fact that option A has defects at all is completely besides the point with regards to relative measurements.

> If option A has 20 defects and option B has the superset of 25 defects then option A is better Only if "defect count" is what you care for. What if you don't give a fuck about defect count, but prefer simplicity to explore/experiment quickly, ease of use, time to market, and so on?

Then you don't want Zig or Rust. Use a language with a GC. Exploratory programming is a lot more pleasant when you don't have to worry about calling free() at the right time. I've had success with PHP and Elixir for productive, exploratory programming, not just because of their GCs, but also because they both support REPL-driven development and hot code reloading.

Re: How safe is Zig?

#158

This was a great read, with an important point: there's always a tradeoff to be made, and we can make it (e.g. never freeing memory to obtain temporal memory safety without static lifetime checking). One thought: > Never calling free (practical for many embedded programs, some command-line utilities, compilers etc) This works well for compilers and embedded systems, but please don't do it command-line tools that are…

> This was a great read, with an important point: there's always a tradeoff to be made, and we can make it (e.g. never freeing memory to obtain temporal memory safety without static lifetime checking). I.e. we can choose to risk running out of memory? I don’t understand how this is a viable strategy unless you know you only will process a certain input size.

Yes. There are many domains where you know exactly how much memory you’ll need (even independent of input size), so just “leaking” everything is a perfectly valid technique.

Re: How safe is Zig?

#159

I have one trick up my sleeve for memory safety of locals. I'm looking forward to experimenting with it during an upcoming release cycle of Zig. However, this release cycle (0.10.0) is all about polishing the self-hosted compiler and shipping it. I'll be sure to make a blog post about it exploring the tradeoffs - it won't be a silver bullet - and I'm sure it will be a lively discussion. The idea is (1) escape analysi…

I feel like the most user friendly solution for Use After Free is to just use Reference Counting. Basically, just copy Objective-C's "weak_ptr" design.

For every allocation, also store on the heap a "reference object", that keeps track of this new reference.

  struct reference_t {
   // the ptr returned by malloc
   void* ptr;
   // the stack/heap location that ptr was written to.
   // i.e  the reference location.
   void* referenceAddr; 
  }
Reference track: every time ptr is copied or overwritten, create and delete reference objects as needed.

If ptr is freed, visit every reference and call *referenceAddr = NULL, turning all of these references into NULL pointers.

Re: How safe is Zig?

#160
post #156
post #64

Earlier quoted context omitted.

I worked on a static analysis tool to detect bleeds in outgoing email attachments, looking for non-zero padding in the ZIP file format. It caught different banking/investment systems written in memory safe languages leaking server RAM. You could sometimes see the whole intranet web page, that the teller or broker used to generate and send the statement, leaking through. Bleeds terrify me, no matter the language. The…

This error is possible in Rust, but not easy to cause. Uninitialised memory is considered unsafe. There aren't any loopholes even for "just bytes". You can't accidentally make an uninitialised buffer, and you can't expose one without explicit `unsafe{}`. Sych unsafe code is typically wrapped in safe interfaces, so the risk is limited to implementation side, and not spread to every usage site. For filling buffers the…

It's actually not very hard to cause. It would happen if your system reuses buffers without actually going through the allocator. Something like this:

    let mut buf = vec![0; DEFAULT_BUFFER_SIZE];
    let database_frame_len = database_socket.read(&mut buf)?; // use 1
    let database_result = Database::parse(&buf[..database_frame_len])?;
    let html_template = HtmlTemplate::new(database_result);
    let html_len = html_template.write(&mut buf)?; // use 2
    socket.write(&buf[..html_len]);
The above code makes the tenuous assumption that HtmlTemplate::write actually clobbers everything it's supposed to write. But it doesn't even require unsafe code for it to not do that, because the underlying buffer is entirely initialized according to the type system.

The only advice I can really give to avoid this kind of bug is: don't reuse buffers unless it's actually a bottleneck.

Post reply on HN