How safe is zig?

Last updated 2021-03-20

Like, on a scale from c to rust?

For various common safety issues, we can look at protections that are present in software as it is typically shipped (ie excluding tools like AddressSanitizer that are not recommended for production use):

issueczig (release-safe)rust (release)
out-of-bounds heap read/writenoneruntimeruntime
null pointer dereferencenoneruntime⁰runtime⁰
type confusionnoneruntime, partial¹runtime²
integer overflownoneruntimeruntime³
use after freenonenone⁴compile time
double freenonenone⁴compile time
invalid stack read/writenonenonecompile time
uninitialized memorynonenonecompile time
data racenonenonecompile time
  1. optional types
  2. tagged unions, doesn't protect against holding a pointer to value while changing tag
  3. tagged unions
  4. not by default, but available via compiler setting or by linting against unchecked arithmetic
  5. this is contentious, see counterarguments here and here

There are two clear groups here:

So we can say that zigs spatial memory safety is roughly comparable to rust, and its temporal memory safety and data race safety are roughly comparable to c.

Zig does has some weak improvements over c with regards to temporal memory safety:

Zig also has a number of tools to help detect violations of temporal memory safety during testing. These are useful, but experience with c indicates that they won't be sufficient to eliminate vulnerabilities.

I tried looking at some public breakdowns of security issues from various projects written in c and c++ (mostly sourced from Alex Gaynors handy summary) to get a sense of the relative frequencies:

This isn't a very clear picture. The percentages vary wildly between projects. The categories are sufficiently vague that I could be classifying them all wrong. Looking only at fixed issues tells us nothing about how easy they are to exploit, but looking at existing exploits limits us to a very small dataset.

It certainly seems like just fixing spatial memory safety (going from c to zig) is a non-trivial improvement. But I'd like to better understand why actual exploits appear here to rely more often on violating temporal memory safety.

Rust bears additional complexity and friction to buy temporal memory safety and data race safety. But sometimes we might be able to buy those more cheaply eg:

Sometimes we might also just choose the bear the cost. For systems with low risk profiles (eg internal software that is never exposed to hostile input) we might decide that debugging the occasional use-after-free is preferable to adding development friction.

There are certainly systems though where none of the above are options. For example, the web spec pretty much mandates that browsers must have complicated ownership models, use pervasive sharing between threads and be constantly exposed to hostile inputs. In such cases it's hard to make an argument for zig.