Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> If dealing with potentially hostile data, Zig certainly isn't more appropriate than Rust in my opinion, try maybe WUFFS.

Thanks! Great recommendation on WUFFS! And completely agreed, it's also easy to turn on checked arithmetic for Rust (if you know about it, but Rust definitely has an unsafe default there for those that don't, which is surprising to me).

At the same time, WUFFS is not always applicable, for example to writing something like a distributed system where you do still want safety, often the flip side of security. I'm sure you'll also agree it's good to balance out that security is more nuanced than just a rant about memory safety to the extreme. It's great to have positive discussions about languages, to evaluate trade-offs positively.

Counter-intuitively, I do feel also that Zig's explicitness as a language as a whole fits a security mindset well. For example, in `std/mem.zig` there's a very careful divExact assertion around underflow when calling `bytesAsSlice()`. This is just a fantastic way to prevent buffer bleeds, i.e. HeartBleed or CloudBleed, but it's probably uncommon to see in many libraries, and something like a borrow checker wouldn't provide this aspect of memory safety automatically. You can easily get lulled into a false sense of security.

From a security angle, I also like Zig's philosophy around very simple control flow and avoiding unnecessary abstractions, no matter if they're zero-cost. I think this is going to lead to a healthier package ecosystem when it arrives, compared to say NPM, where you get these dependency explosions that are a real headache for supply chain attacks. Attackers always go one level deeper, they attack through the basement, and there's often more low-hanging fruit at hand than a UAF (especially considering that many embedded systems that Zig targets probably do static allocation anyway, so bleeds might often be the worst that can happen). It will be interesting to see how Zig's philosophy around explicitness and avoiding bloat makes a difference here.

> Zig will panic here if using default arithmetic with default release builds. If the attacker wanted to cause a Denial of Service, job done already.

In the security world, a DoS is usually not treated as a P1. Perhaps a P3 at best (if you're lucky as a researcher!). For example, I've submitted one or two DoS MIME bomb samples that can shutdown Gmail servers and got very much an "okay, we'll just not bother about it because we're Gmail and our fleet is so massive". The DoS is probably still out in the wild for Gmail. Even ProtonMail, which has experienced numerous outages, didn't classify it as a P1, although they awarded it.

However, for a read/write exploit (running with the email example, perhaps a directory traversal in Apple Mail), having checked arithmetic convert what could have been a P1 into a P3 is actually exactly what you want because it prevents the exploit from going further (these things are almost always chained).

It also surfaces the bug visibly, you get a crash, you investigate, you fix. So from an attacker's perspective, they're actually less likely in fact to try and trigger it, because then they reveal they're in your system.



Something like WUFFS is exactly what we should be using for Wrangling Untrusted File Formats as it says in the name, even if you've decided to do that in a distributed system. Realistically you're definitely going to get this wrong, so, use a language where the worst case is it doesn't work is a massive improvement over using languages where it's all additional attack surface.

That recent Apple bug where they render PNGs incorrectly can (in principle) happen in WUFFS. The other recent Apple bug where bad guys seize control of your iPhone by sending a malicious image file cannot. One of these things is not like the other.

I think you're missing the point if you expect the borrow checker to care about buffer underflow. Rust has a runtime bounds check to check bounds, the borrow checker is, as its name suggests, checking the borrow rules. The trick (compared to arithmetic overflow) is that the optimiser can often push a bounds check outside a fast loop or eliminate it altogether, so you really can afford to do this in all or almost all your release code unlike checked arithmetic. WUFFS shows that you can do away with both of these runtime checks and be entirely safe if you're not interested in being a general purpose programming language. Which is (part of) why WUFFS gets to be both safer and faster. Both Zig and Rust are intended as general purpose languages.

I don't buy the "surfaces the bug" thing because I have too much experience of real world systems where there's so much noise and mayhem that you are focused on stuff that's causing your real users pain. Even if the DoS means the server falls over and must be manually restarted, the ticket in my queue says "Urgent: Auto-restart server. Watchdog maybe?" not "OMG bad guys are trying to break into our system somehow, find out how ASAP"


> Something like WUFFS is exactly what we should be using for Wrangling Untrusted File Formats as it says in the name, even if you've decided to do that in a distributed system.

No, I was saying earlier that there are limits to WUFFS. The example I gave was that you can't write something like a distributed system (think consensus protocol like Viewstamped Replication, Raft or Paxos) in WUFFS, but where safety is nevertheless still critical, and where you reach that through crystal clear control flow and explicitness. In other words, safety is the other side of the coin to security. Hope it's a little more clear now.

> That recent Apple bug where they render PNGs incorrectly can (in principle) happen in WUFFS. The other recent Apple bug where bad guys seize control of your iPhone by sending a malicious image file cannot. One of these things is not like the other.

Of course.

> I think you're missing the point if you expect the borrow checker to care about buffer underflow.

No, I was stating the obvious, that it can't (or at least not always, but in some cases it can), not that it should.

> I don't buy the "surfaces the bug" thing

I was just trying to convey a little bit about how security works and how hackers (or at least red teamers) think, especially when blue teams are involved. I've found that the more I get into this, it becomes much less about preventing the breach and more about "assume breach, okay, now how do we detect it?". And a software DoS is also really just bottom-of-the-rung, you'll find almost no programs paying out for any findings. You shouldn't worry about them. Asserts are the safe thing to do. They close semantic gaps and make your code much more secure. It's like putting in a thousand trip wires, any thing off and an attacker can't get further. It completely shuts down exploit chaining.


> No, I was saying earlier that there are limits to WUFFS

Of course there are limits to WUFFS, that's why it isn't a general purpose language. You shouldn't implement these distributed protocols in it for the same reason toothpaste isn't a good engine lubricant, you deliberately can't even write "Hello, world" in WUFFS.

And yet, if you find yourself, in your distributed system, Wrangling Untrusted File Formats, you should reach for WUFFS to do that safely. Somewhere between "The device has a single button, it's green, press it" and "We process any PDF, HTML or XML documents sent to this email address" you will realise you need all the help you can get to Wrangle the data safely, and that's why WUFFS.


> Of course there are limits to WUFFS, that's why it isn't a general purpose language. You shouldn't implement these distributed protocols in it for the same reason toothpaste isn't a good engine lubricant, you deliberately can't even write "Hello, world" in WUFFS.

LOL, I would never have thought to do that till now! :)

I think we've always been on the same page regarding WUFFS and file format sanitizers. For me the question here really is, how do we improve the status quo when WUFFS is not an option? i.e. What are sane defaults for general purpose programming languages?

I still maintain that checked arithmetic should be enabled by default in general purpose programming languages, and that's because I believe in the principles behind WUFFS, having worked exactly on these kinds of tools myself.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: