26 ms·
How Safe Is Zig?
- oblio 6y agoInteresting comparison. Long term we badly need something to replace C (or at least minimize its usage drastically), so perfect should not be the enemy of good. I hope something like Zig gets widespread adoption, including in embedded/IoT/automotive environments. Especially automotive. We're moving more and more life-and-death scenario-type tools into software.
- the_duke 6y agoConsidering that Rust exists, I really don't hope that Zig gets much adoption, at least until the language improves a lot in some key aspects. There definitely is a design space for a simpler language than Rust that is easier to write, but Zig is too far on the side of C and has lots of trivially introducable unsafety. It's an improvement over C , but imo not enough.
- fwsgonzo 6y agoOne thing I noticed is that Zig is not 1.0 yet, so perhaps it will still improve in those aspects you are thinking about?
- 1_player 6y agoRust instead is too far on the side of C++ I have tried to like the language, but sadly having to think about types and lifetimes robs precious energy which should be devoted to thinking about business rules and what am I actually trying to achieve. In some niches Rust is perfect, but in every language thread on HN there's often someone that suggests to use Rust whatever the use case. C, in that respect, is more flexible and gets out of the way much more, of course while being unsafer, but it's easier to keep your mind on the goal and not figuring out the best memory safe approach for this piece of logic. Which is why I'm very excited for Zig. I don't want another C++. Give me safer C, thanks.
- adrianN 6y agoIn C you also have to think about lifetimes and types, only that the compiler is helping you a lot less to get things right.
- 1_player 6y agoOf course, but you don't need a lot of work to make the C compiler happy. The result might not be 100% safe, secure and mathematically proven, but sometimes you need to deliver, fast, not create the safest 1k lines of code on Earth. To be honest I haven't used C in a long time, but I've been looking for a low level language that sparks as much joy as C does. Go, Rust ain't it, IMO.
- flukus 6y agoIn C you have to think about lifetimes (and to a lesser extent types) at specific times, typically after the "make it work" step of writing code. Rust on the other hand forces you to think of these things from the start and this inhibits solving the actual problem first. Now there's an argument the front loading these decisions may be beneficial overall but I don't find that compelling either. After the "make it work" stage there's thinking about performance, comments, logging, etc and you usually need to think about lifetimes as they change at this point as well, so front loading it has only added work overall.
- tene 6y agoThis description doesn't match my experience of writing Rust at all. During the exploratory phase of my projects, I very rarely run into nontrivial lifetime issues, and when I do, I can just put whatever data into an Arc and then it just works. Most of my exploratory code is just objects that own their data. Later in a project, while doing a bunch of refactoring to handle performance, logging, etc. , I have a much easier time letting the compiler tell me when I've made mistakes with a value's lifetime rather than trying to keep the whole program in my head for the duration of the refactor. I don't spend a lot of time thinking about lifetime issues during either phase of my projects. Mostly I just write the same sort of thing that I'd have written in other languages, and the compiler tells me when I've made a mistake.
- orthoxerox 6y agoRight. It's a better C, but it has to compete with C's ubiquity: there's make and cc on most *nix boxes. JetBrains went out of their way to make migrating to Kotlin as easy as possible: you can literally upgrade your Java project file by file.
- HourglassFR 6y ago> JetBrains went out of their way to make migrating to Kotlin as easy as possible: you can literally upgrade your Java project file by file. Yes, offering a good path to switching (and conversely keeping the old voodoo part of the code no one wants to touch) is the way to go. And as I understand it, Zig offers this possibility as well.
- 1_player 6y agoWhich is why Zig shipping with a functional C compiler with cross platform support is a brilliant idea. Just run `zig cc` and you don't even need `clang` around. https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace...
- vips7L 6y agoAnd you can directly import C headers into zig files. It's quite amazing.
- pjmlp 6y agoKotlin has settled its future by marring with Android. As Java evolves and Kotlin needs to cater to Mountain View masters, upgrading the Java file won't be enough as many modern features don't exist on ART.
- unscaled 6y agoIt was true for a while that Kotlin got stuck on JVM 8 for its target bytecode, but it's no longer true, Kotlin supports all versions up to 15 now: https://kotlinlang.org/docs/compiler-reference.html#jvm-target-version https://kotlinlang.org/docs/compiler-reference.html#jvm-targ... I think the first Kotlin version to use post-Java 9 bytecode is the Kotlin 1.4.20 using invokedynamic for string concatenation: https://blog.jetbrains.com/kotlin/2020/11/kotlin-1-4-20-released/ https://blog.jetbrains.com/kotlin/2020/11/kotlin-1-4-20-rele... Kotlin sealed classes are planned to be rendered as JVM sealed classes when JVM sealed classes go out of preview (probably Java 17), and the same is planned for mapping Kotlin value classes as Project Valhalla user-defined primitive types. All these features obviously won't be available if you target Android, but they will still be there for you if you don't.
- ttt0 6y agoI personally don't care about Rust at all, I already have C++ for that. But I'm very interested in Zig.
- roca 6y agoWe are, and I hope for life-and-death situations people are willing to work a little bit harder to get the extra protections Rust provides ... or much harder, and formally verify their code in which the language you use no longer matters as much.
- 10000truths 6y agoPrevious thread: https://news.ycombinator.com/item?id=26527848 https://news.ycombinator.com/item?id=26527848
- childintime 6y agohttps://news.ycombinator.com/item?id=26521539 https://news.ycombinator.com/item?id=26521539
- quietbritishjim 6y agoFrom the table: > null pointer dereference ... [C] none; [Zig] runtime; [Rust] runtime Assuming this is talking normal "safe" Rust, I think I disagree with this. The Rust analogue of a pointer in safe code is a reference, not a Rust pointer, and these can't null at all. You could use an Option<> of a reference, and Rust will internally use null to represent the None (empty) case, but an attept to use the option without checking for None will result in an error at compile time, not runtime. Yes you could convert that into a runtime error, but if it was an error condition for that variable to be None then (depending on the context) you could choose not to use an Option at all and then it would be a compile-time error at the call site to attempt to put None into it. I don't think I understand what is meant by "type confusion". Surely this would also cause compile-time errors? Even C++ would give compile time errors for this unless you use a cast! (C, unlike C++, lets you implicitly convert from void* to any other pointer type so you don't need a cast to get pointer confusion.) Could someone think of an example of what might be meant here, and how it would cause a runtime error?
- the_duke 6y agoZig has a nullable pointer type, which will have null checks. But only in ReleaseSafe build mode, and it's trivial to construct a non nullable pointer that is null or uninitialised. So I agree with your disagreement.
- quietbritishjim 6y agoI'm not familiar with Zig, and I'm afraid I didn't quite follow your comment. Are you saying that "runtime" should instead be "none" for Zig?
- masklinn 6y agoDepends on the exact guarantees e.g. technically it's trivial to construct a nullable reference in Rust, but it's also `unsafe` and flagrantly UB so...
- jamii 6y ago
- flohofwoe 6y agoOne thing that's important to note is that Zig in general enforces much more correctness than C and also has much better stdlib APIs. IME many memory corruption issues in C (and C++) are actually "secondary effects" of C's and C++'s general "sloppiness" and bad stdlib APIs. One thing I found where Zig is currently worse than C compilers: returning a pointer to a stack variable generates a warning in "modern" C and C++ compilers, while Zig lets this slip through. I hope that "trivial" things like this will be fixed on the way to 1.0
- Rendello 6y agoI ran into this in Zig and I was surprised that it wasn't caught. Since I'm newer to lower-level programming I didn't automatically think to look for this and I was lost for a day or two
- AndyKelley 6y agoWe're working on this one- it requires careful cooperation with the language specification to make it legal to be a compile error.
- Rendello 6y agoAwesome stuff! I love this language
- fwsgonzo 6y agoI don't have much experience with Zig, but one thing that stuck out to me was that I was able to build for RISC-V with a one-liner. I didn't have to change or do anything at all to make this happen. That's so cool. In contrast, I have yet to be able to build any RISC-V binaries with Rust. It just doesn't work. Sure, I could see some potential things like writing a custom JSON to describe the env and maybe build using a cross-compilation toolchain. But after a certain amount of time and no answers, it was not worth my time anymore. https://stackoverflow.com/questions/64308644/rust-unable-to-build-64-bit-risc-v-binary https://stackoverflow.com/questions/64308644/rust-unable-to-... If you think you have the answer ^
- pansa2 6y ago> Temporal memory safety and data race safety. [...] Unique to rust. [...] add a significant amount of complexity to the language. Rust seems to be a very complex language. Is all that complexity essential to providing memory safety without GC? Or would it be possible to have a significantly simpler language that is equally safe? A language that’s “safe & C-like” compared to Rust’s “safe & C++-like”?
- EwanToo 6y agoYou can write very simple Rust, it's worth trying it out if you haven't already. I'm not a rust developer, but I've ported over a handful of python or golang projects to see how it works. I managed to write the code without understanding much about things like the borrow checker. I'm certain my code is not as performant or elegant as it could be using some of the more complex tools and concepts in the language, but it is possible.
- adrianN 6y ago"You can write <adjective> <programming language>" is an answer that usually get rebutted by pointing out that a) you need to read a lot more code than you write and b) other people might not write <adjective> code or c) other people might have a different definition of <adjective>.
- genuine_smiles 6y agoThis is true. It’s also true that team using languages with more feature then they need can just take the parts that they need. It’s not quite as ideal as having a language that’s perfectly suited to your use cases, but it works well enough. For example, I’ve been writing JavaScript for 3+ years. I have yet to use the prototype chain directly, only through the use of the `class` keyword, and I’ve only reviewed code using it once. I hear C++ is similar in that teams use a slice of the available language features.
- pansa2 6y ago> I'm certain my code is not as performant If you don’t need maximum-possible performance, though, why use Rust/C/C++ at all? Wouldn’t a better choice be Go/Java/C#?
- Snetry 6y agoIn the recent latest Zig Showtime stream Andrew showed off things like use after free, double free and so on detection
- The_rationalist 6y agoThe matrix is incomplete: What about use after returns? Memory leaks? Cycles? Stack under/over-flows And more from https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools https://github.com/google/sanitizers/wiki/AddressSanitizerCo...
- pron 6y agoOne thing that's important to emphasise: sound safety, i.e. using a safe language, is no one's goal; rather, it is a means to end. What people want is correct programs. The question is, then, does a language help write correct programs? Ensuring safety from important classes of bugs with sound guarantees is one way to help write correct programs, and both Zig and Rust use it; ensuring safety from from important classes of bugs with sound guarantees based on runtime checks is another way, and both Zig and Rust do it, too; a simple language that's easy to understand and analyse is another very important way to help write correct programs, and both Zig and Rust try to be simpler than their predecessors; making it easy to write tests and run them frequently is another way to get more correct programs that both languages try to employ. Both languages drastically differ in the use of those techniques from either C or C++ because they are both languages that put a very strong emphasise writing correct programs, but they also differ a lot from each other in how they balance those techniques. It is impossible to tell without careful empirical research which helps write correct programs more than the other, and it is also possible that different people find it easier to write correct programs in either Zig or Rust. Rust certainly provides stronger guarantees that prevent temporal memory bugs than Zig, so let's assume Rust programs will contain zero, and a Zig program will contain more than zero but much less than C or C++. But that delta is insufficient to determine that Rust's balance of techniques reduces more bugs overall. Also, Zig already has decent checks for use-after-free, and they'll get better, and not having uninitialised memory is also very easy to do (and verify) in Zig, despite there not being any checks. Even if, like other runtime checks, it is turned off in production, it still helps catch errors in that category.
- HourglassFR 6y agoI've seen you make that argument a few times on Zig related discutions but I'm not sure I buy it. It essentially boils down to: simpler language => easier to reason about and build tools for => less bugs. While the thought as merits, the empirical evidence we have indicates that yes it is possible to achieve good software with faulty languages with strong rules and tooling but it is certainly not as straightforward as you make it seem. In the end though, for the Zig case I agree that the jury is still out. But if I was a betting man, my money would not be on it, even though personnaly prefer Zig.
- mtzet 6y agoI think this is a roughly fair assesment, but I also think it's important to contextualize memory safety. Ultimately, the goal here is to produce /correct/ software. Memory safety is a subset of this, but there are other aspects to correctness as well. I really like zig's approach of explicitness and fast iteration cycles. Fast compile times and the very flexible build system makes me hopeful for a really slick workflow for embedded development,where zig code can be used to deploy and test as well. For my own use I think it's a clear win. On the hand, the amount of damage poorly architected zig code can cause is about as large as for poor c code. For typical enterprise code the rust compiler will make sure that many bad decisions will not even compile. There's still a risk of towering abstractions, but at least I could avoid spending as much time debugging hideous race conditions.
- jorangreef 6y agoIt's important to clarify that memory safety is only one aspect of writing safe, secure software. To this list then I would also want to add and compare: OOM-safety under overload conditions, and fine-grained error handling safety, in particular because error handling tends to be one of the leading causes of faults in distributed systems [1]. To be fair, I was surprised that Rust did not have checked arithmetic on by default and that this needs to be turned on via compiler setting or linted against. The presence of integer overflow in a program can facilitate a whole range of exploits, even with memory safety. [1] - https://www.eecg.utoronto.ca/~yuan/papers/failure_analysis_osdi14.pdf https://www.eecg.utoronto.ca/~yuan/papers/failure_analysis_o...
- littlestymaar 6y ago> The presence of integer overflow in a program can facilitate a whole range of exploits, even with memory safety. Do you have examples that do not involve array indexing? (which are uncommon in Rust because iterators exist and are faster).
- jorangreef 6y agoSure, almost anything that uses numbers, e.g. to control access or parse data structures or do "something important", here are two off the top of my head: * Rate limits for an OTP, which could be trivially reset through overflow. * Parsing zip files, where the hostile content is self-referencing, to sneak in cyclic references for a DoS, or to change file extension type for code execution after bypassing a content filter, or to change output destination (e.g. as part of a symbolic link or directory traversal) to overwrite system files. Unchecked arithmetic for me is by far one of the scariest exploit vectors because it's so easy to do, and one of the first things a trained attacker would look for. Programs in any language are always exploitable, in some way or another. Memory safety is no guarantee that a program is "safe" let alone correct.
- littlestymaar 6y agoThanks for the examples.
- ajross 6y agoFrom the very top of the article: > 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): ...with a link to a five year old opinion post to oss-security as a reference for "not recommended". To wit: "There is other stuff in this space that might be relevant, but I don't want to talk about it so I'll just make up a reason. Moving on..." That kind of logic tells me instantly that this is spin and not a serious analysis. I know next to nothing about Zig, but I know I shouldn't trust this post to tell me about it.
- sunflowerdeath 6y agoI think, it is very important, that people started to actually discuss and compare different approaches to safety, instead of just saying that since Rust is safer, we should throw out all of c/c++ code and rewrite everything in rust.
- fulafel 6y agoOh we had this long before Rust, and most of C++ usage in new applications was displaced by safer (among other things) languages. I think the biggest thing was that university curriculums and mainstream app development platforms (like Microsoft) stopped pushing it as hard when the level of horror got past a certain point. It used to be pretty bad. Business apps being written using MS "Active Template Library" in C++ and then used as signed ActiveX plugins on IE6-only web pages etc.
- smolder 6y agoSafety (memory and otherwise) isn't new, but during my CS curriculum, including a course on programming language theory, there was little/no mention of techniques to ensure safety in the space between C++ and Java. I probably would have pointed toward formal verification if someone said they needed safety guarantees in the absence of garbage collection and a potentially slow or bloated runtime. Though I believe there were some languages with features to that end, at least research languages, they weren't that well represented. I think Rust's presence brought attention to the possibilities there, and an increasing number of people see the value of investigating and developing that niche.
- pjmlp 6y agoMicrosoft still is the main company pushing it hard (C++ use) despite all security reports, most likely due to how the Windows and Office teams don't accept anything else. So basically you have the DevTools and Azure teams pushing for .NET, Java and other safer languages, while Azure Sphere has a C only SDK and WinUI/UWP push C++ above anything else, with some C++ only APIs. Politics.
- com2kid 6y agoIt is amusing to me because last time I was using C, the problems rust solves weren't the problems I had in C. Deeply embedded code doesn't use malloc, doesn't use threading. I could use a better type system, ala Ada, being able to say "this variable is of type distance in meters, this variable is of type time in milliseconds", that'd have cut the # of bugs by a huge amount. But simple, unsexy, type system changes like that aren't what language designers are focused on. Who here has never confused Milliseconds and Seconds when passing a variable around? Trivial for a compiler to catch with a half decent type system, but few modern languages bother to try. Even when writing modern code in newer languages, I rarely directly use threads, and if I need to pass data between them 95% of the time I can get away just doing a deep copy to avoid the hassles of sharing data between threads! Obviously Rust is meant solving different problems than the ones I face, I have friends who frequently write highly threaded code, but in my day to day, Rust doesn't offer much more safety. (However, Zig does look super cool and interesting!)
- scoutt 6y agoI wonder in which category Rust's RefCell runtime-borrow-checks fall into.
- jedisct1 6y agoIn the “it’s necessary but let’s act as if it didn’t exist” category.
- junon 6y agoOnly as safe as the creator decides. Andrew has shot down discussions about DOS vulnerabilities in the standard library and the cult-like discord community loves to dig on anyone who brings such things up. I will be staying away from Zig exactly for that purpose. Great idea but I can't get behind a maintainer that adamantly refuses to even discuss proper, safe standard library design. EDIT: Yep, the HN crowd tends to be the same. Downvote me all you want please :) We'll see over time.
- fctorial 6y agoCan you share a link?
- junon 6y agohttps://github.com/ziglang/zig/pull/4929#issuecomment-649191491 https://github.com/ziglang/zig/pull/4929#issuecomment-649191... The bulk of the conversation happened in Discord around that time. Initial attempts to bring this up were met with "zig is perfect"-type conversation, none of which was very technical. Finally, the conversation grew to be so large and fiery that Andrew had to step in and say "everyone play nice, now!" and then head back out. Then this PR was filed. Invalid user input should not be classed as undefined behavior and concluding that the standard library's UTF decoder shouldn't be used if you want a safe execution is just absurd. There were a few other run-ins on discord in the same vein. It made a few people at the time leave, including myself. Andrew's smart. Zig is a cool idea. But I don't like when this laisse-faire attitude is taken when designing a programming language that places so much emphasis on being safe.
- kbd 6y ago> Then this PR was filed. Invalid user input should not be classed as undefined behavior and concluding that the standard library's UTF decoder shouldn't be used if you want a safe execution is just absurd. Just to copy Andrew's final words in here: > I think the entire std.unicode needs an audit both in terms of API design and performance. This module is not yet what it will become before stabilization. But this commit is not where this is going. The PR wanted to make a function that takes runtime values only take comptime values. I read Andrew's response as saying "this all needs to be looked at before 1.0 but this isn't the way to fix this", which seems to me to be an entirely reasonable thing to reject a PR with.
- dnautics 6y agoThe recommended way to deal with use-after-free and double-free in the language is to do it in test. You can pretty trivially get "asan"-like behaviours out of the zig std library. A good demonstration is here: https://www.youtube.com/watch?v=4nVhByP-npU&t=3h12m https://www.youtube.com/watch?v=4nVhByP-npU&t=3h12m I kind of like this philosophy, because in a sly way it's a carrot to get you to write tests. Come for the memory safety, stay for the robustness. As a bonus, the beginning 2 hours of the video is a fantastic and honest discussion about the role of emotional empathy in tech communities and tech employment (while also acknowledging that it is possible to be an asshole and deliver good tech).
- jamii 6y agoThat's also the recommended way to deal with it in c. It hasn't been effective in preventing vulnerabilities based on use-after-free. If the GPA is practical to use in production, that will be a different story. But it doesn't sound like it's there yet.
- dnautics 6y agoTrue, UAF still kind of sucks in zig. My prediction is that we are going to eventually get some sort of formal verification engine for zig as a third party tool.
- pjmlp 6y agoUnless the test proves all use cases in production, it is no better than C with ASAN.
- AndyKelley 6y agoTo be clear - you get this same UAF protection in ReleaseSafe mode by default with GeneralPurposeAllocator and PageAllocator. It's not just a test thing. The testing system just chooses some nice defaults for you.
- dnautics 6y agothanks for the clarification! Also, you can arbitrarily compose GPA backended by other allocators that aren't pageallocator, in which case you might lose the early-exit segfault behavior.
- smasher164 6y agoI think if Zig wanted to, it could introduce lightweight linear types using the concept of proof variables and interleaving from ATS. Since resource management is explicit in Zig anyways, there's not much additional overhead in "consuming" proof values to signal that you've dealt with a resource.
- tptacek 6y agoI don't know Zig, at all, but I do know Rust and C, and I know what a UAF bug looks like. What does a UAF bug look like in Zig? That a modern memory-safe language could be vulnerable to the C-language UAF pattern is a surprising claim.
- trishume 6y agoZig is a C-like language that isn't memory safe. A UAF bug looks the same as it does in C where you manually call the allocator to get a block of memory, manually free it, then try to perform an access via a pointer to it.
- jorangreef 6y agoTo be fair, Zig provides a spectrum of memory safety as the comparison table in the post makes clear. Sure, it isn't 100% memory safe, and especially not around UAF, but it's still orders of magnitude safer than C. In the safety department, it's not at all a "C-like language" in that respect. It's a massive leap forward.
- tptacek 6y agoType confusion and memory lifecycle flaws are probably the dominant source of exploitable vulnerabilities at this point. I'm surprised to see it suggested that Zig is weak to them.
- jorangreef 6y agoHey Thomas, I would have thought you would have said that at this point JavaScript or Postel's law were probably the dominant source of exploitable vulnerabilities. You're right though, Zig is weak to them, but it's not all or nothing as with C. It's a spectrum, and having spent some time with the language, I think that for Zig's goals, it makes the right set of trade-offs.
- tptacek 6y ago
- setpatchaddress 6y ago> 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. Zig vs Rust vs C aside, this cannot be a serious position for a software developer to take in 2021 CE.
- dnautics 6y agoit's about context. Suppose you just document "please take this into account" because it's a low-level FFI library, that's being called by a high-level PL, which will make sure that your memory management is sane. An analogy: "non-threadsafe" code; typically you just tag 'non-threadsafe' and if someone misuses it, it's on them.
- Nycto 6y agoSomeone on Reddit added Nim to the table, which I found interesting: https://uploads.peterme.net/nimsafe.html https://uploads.peterme.net/nimsafe.html (Source: https://www.reddit.com/r/nim/comments/maj1lz/nim_safety_in_comparision_to_c_zig_and_rust/ https://www.reddit.com/r/nim/comments/maj1lz/nim_safety_in_c...)
- andrepd 6y agoWhich is a bit silly because Nim is a garbage collected language, and hence competing in a different league.
- k__ 6y agoIsn't the Nim GC optional?
- ampdepolymerase 6y agoIt is optional in the sense that D's GC is also optional. Technically true but you have to go out of the way to make it work for you, average libraries off the shelf cannot be easily utilized.
- rayman22201 6y agoThis is a response to both you and the grandfather comment. Nim now has ARC/ORC, which is a reference counting scheme similar to Swift. This is not a "generational GC" like Java, Go, or D. The entire standard library and most average libraries, "just work" with it, and it will be the default near future. https://nim-lang.org/blog/2020/12/08/introducing-orc.html https://nim-lang.org/blog/2020/12/08/introducing-orc.html
- pjmlp 6y agoThe league of being safe, without use after free, letting the developer focus on productivity, while providing the language features to do C style programming when required.
- spiritplumber 6y agoMove Zig for great justice!
- daakus 6y agoSafety for me is confidence to use the thing. For me in my own code, but also others on my team that may work on this code. I mostly have experience building things in GC languages. But with Rust I managed to safely use [1]: - stack references in threads - kept mmap references alive until threads finish work - zero copy xml parsing (from mmaped data!) - SSE/AVX enabled searching The Rust language empowered me to do these things with a high degree of confidence. Not one segfault or core dump, just lots of compiler errors. I played with Zig. Admittedly, the small ecosystem aspect is something all languages go thru, and it would be a better experience with a Zig specific libraries. But Zig doesn't empower library authors to make a large category of bugs impossible, and leaves it to documentation. This is like C, I don't have enough confidence in myself to use it. Brilliant people are building powerful, safe-ish, reusable libraries in Rust. For mere mortals like me, this is Awesome. [1]: https://gist.github.com/daaku/58557e2545612df8f40b13b66b7d3bd0 https://gist.github.com/daaku/58557e2545612df8f40b13b66b7d3b...
- burntsushi 6y agoHi, author of the aho-corasick crate here. Your use of it piqued my interest and caused me to take a closer look. I believe your use of `unsafe` on this line is unsound: https://gist.github.com/daaku/58557e2545612df8f40b13b66b7d3bd0#file-main-rs-L32 https://gist.github.com/daaku/58557e2545612df8f40b13b66b7d3b... Namely, there is no guarantee that the bytes between `<page>` and `</page>` will be valid UTF-8. It may be the case that you only run this program with UTF-8 input, in which case, UB is never triggered. But it's worth pointing out here since there is nothing actually stopping your program from hitting UB. Also, as long as you're bringing in the twoway crate, you might as well use it on lines 43 and 48 since you're just searching for a single needle.
- daakus 6y agoThe bytes are assumed to be utf8 (I was using the safer `from_utf8` prior to confirming the data was utf8). I brought in `twoway` when I couldn't find a way to `rfind` using `aho-corasick`. I'll switch the use over for consistency. Thanks for the quick code review! PS: Thanks for ripgrep too!
- dathinab 6y ago> null pointer dereference (C)none (Zig)runtime (Rust)runtime Not really rusts pointers are the `&`/`&mut` references which are compiler time proven to be not just non null but actually differentiable and potentially writable in the given context. Which are MUCH stronger guarantees then just "not null". > 2. only when using tagged unions Which in rust are the default types, unions didn't exist for quite a while and require the use of unsafe making them heavily discouraged to be used. Besides that it's not that rust enums are tagged unions where you at runtime check a tag and then access them, or where you "panic/throw an exception" when you try to access the wrong type, but incoperated into the type system and language given quite a different experience to classical tagged unions. Lastly type confusions applies to more then just "tagged union style access" but also subtype stile access in which case rust can use trait objects instead of sum types. Besides that many of the ways listed zig can archive more safety (weather applicable or not) are also applicable to C. And some of the checks Zig do can be "somewhat" archived in C too by combining non standard compiler options and code analysis tools. Don't get me wrong. Zig is a very interesting language and I would argue the spiritual successor of C in how it's designed. Still I guess the main ways to add more safety (and similar) to languages like Zig (or C) is to compile them to WebAsm. The module isolation while still being able to call other functions without to much overhead which can be archived with WebAsm might lead to quite interesting trends in the future.
- AndyKelley 6y agoThe word "none" in this blog post is misleading, for the rows use after free, double free, and uninitialized memory. Criticism is of course welcome but let's make sure we get all the facts on the table so we're not arguing a straw man. Copied here [from lobsters](https://lobste.rs/s/v5y4jb/how_safe_is_zig#c_vddk9j https://lobste.rs/s/v5y4jb/how_safe_is_zig#c_vddk9j): With regards to use after free and double free, this is solved in practice for heap allocations. The basic building block of heap allocation, page_allocator, uses a global, atomic, monotonically increasing address hint to mmap, ensuring that virtual address pages are not reused until the entire virtual address space has been exhausted. In practice, this is a very long time for 64-bit applications. The standard library GeneralPurposeAllocator in safe build modes follows a similar strategy for large allocations and for small allocations, does not re-use slots. Similarly, an ArenaAllocator backed by page_allocator does not re-use any virtual addresses. This covers all the use cases of heap allocation, and so I think it’s worth making the safety table a bit more detailed to take this scenario into account. Additionally, as far as stack allocations go, there is a plan to do escape analysis and add this (optional) safety for stack allocations as well. As far as initialized memory goes, zig forces you to initialize all variable declarations. So an uninitialized memory has the word undefined in it. And in safe build modes, this writes 0xaa bytes to the memory. This is not enough to be considered “safe” but I think it’s enough that the word “none” is not quite correct. As for data races, this is an area where Rust completely crushes Zig in terms of safety, hands down. I do want to note that the safety story in zig is [under active development](https://github.com/ziglang/zig/projects/3 https://github.com/ziglang/zig/projects/3) and will be worth checking back in on in a year or two and see what has changed :)
- dathinab 6y agoJust somewhat related to the article but I think one thing which is often misunderstood about rust's is it's borrow checker. The borrow checker is not about memory safety but about aliasing guaranteed. It just happen that combining this with deterministic destructors (/RAII) happend to enable reliable "automatic-manual-memory-management" (or however to callit). And combining it with some clever auto traits (Send/Sync) happen to prevent data races (if no unsafe is used, like always). But the benefits are not limited to just that. Not just memory-resource management but also other resource management profits from this design. Similar while Send/Sync is about multi threaded data race prevention there are also problems in single threaded patterns which are quite similar to that e.g. "racing" between iterating a collection and changing it in the body of the iteration, and the aliasing guarantees make sure you don't have such problems either. Similar rusts main pointer type (`&`) does not only provide compiler time non-null guarantees but also provides compiler time guarantees about how the data can be accessed (dereferencable, writeable etc.). And then there is the choice to use the type system to prevent application logic bugs in many ways. So the bullet points in the table miss many dimensions . But then zig is still a grate language, but trying to convince people that it's good enough by telling them that not reusing allocations seem to not be the best way. Instead look at arguments why people still use C today (not C++!) what they conceptually like about it and you might realize many of the parts still apply to Zig. Honestly Zig seems to be a grate choice for webasm or similar sandboxed systems where the potential damage of use-after free or double frees can be massively reduced.
- StefanKarpinski 6y agoIt’s interesting to note and probably underappreciated how many of these safety issues are addressed simply by having a garbage collector — eliminating manual memory management prevents all of these kinds of bugs except for data races. Of course there are situations where you cannot afford a GC, but for how many programs is avoiding a GC worth all the additional language complexity? Preventing data races is no small thing, but this observation certainly suggests that the approach of GC + tasks & channels + a good race detector is more powerful than it is commonly given credit for — think about how much user-facing language complexity it replaces. This sounds like a pitch for Go, and to some extent it is, but Julia takes very much the same approach for the same reasons.
- oleganza 6y agoThe power of a software is in composing things via powerful abstractions. When you write a useful library in a PL with GC, you automatically make it unavailable in all applications that can't use GC. What's worse, other libraries that could've built something around yours have to choose whether to limit their applicability to applications with GC or find another way. But if you have a clever language that navigates this tradeoff and lets you build powerful zero-cost abstractions (C++, Rust, Zig), then it attracts significantly more talent that compounds.
- pjmlp 6y agoThere are many ways to compose software and libraries, it doesn't need to be static linking all the time. Everyone praises UNIX for pipes in the shell and forgets about OS composition APIs.
- StefanKarpinski 6y agoThat’s a fair point. Something we’re very interested in is exposing libraries written in Julia without the full runtime, ditching the JIT or the GC if you don’t need them. It would be easy to write a libm replacement in Julia, for example, without using either of those. Regarding talent, it depends on the kind of talent you’re talking about. Yes, systems programmers like Rust. On the other hand, needing to deal with a strict borrow checker excludes a very large number of people with numerical computing, data science and machine learning expertise (not all, but definitely most). So it cuts both ways.
- WalterBright 6y agoThe D programming language: out-of-bounds heap read/write: runtime, some cases at compile time null pointer dereference: relies on hardware protection type confusion : compile time integer overflow: wrap-around semantics use after free: prototype protection in @live functions, not a problem when GC is used double free: prototype protection in @live functions, not a problem when GC is used invalid stack read/write: compile time uninitialized memory: compile time data race: read/write to shared memory can only be done via library functions
- sedatk 6y ago> null pointer dereference: relies on hardware protection > integer overflow: wrap-around semantics Interesting choice of words to not say "none".
- WalterBright 6y agoHardware seg faults for accessing the first page in memory are the norm for modern machines. Back in the 16 bit real mode DOS days, writing to the first K of memory would trash the interrupt vector table. But that was a 1980 design.
- pjmlp 6y agoThat doesn't work in WebAssembly, so you will get exactly the same corruption as in MS-DOS days.
- WalterBright 6y agoI don't know anything about WebAssembly, but that sounds like a major design flaw in it.
- pjmlp 6y agoWebAssembly has several flaws, one of them is lack of bounds checking for memory access inside of the same linear segment, so while it is sandboxed it cannot prevent that the data turns into garbage, and thus be open to attacks that change the outcome of public APIs based on internal memory state. But hey, it sells better than Flash or PNaCL.
- mwkaufma 6y agoSafety without context is Apples-to-Oranges. I could write a compiler which simply rejects any possible source-text you send it and check "compile time" for every box and offer "greater safety guarantees" than Rust.
- snicker7 6y agoOn a complete tangent: scattered-thoughts.net is one of my favourite technical blogs. Author has a lot of great insight in many areas.
- lenkite 6y agoI cannot live without RAII. Zig is just a no-go for me without automatic resource cleanup. A lot of people forget cleanup and destroy safety without RAII. https://github.com/ziglang/zig/issues/782 https://github.com/ziglang/zig/issues/782 Zig is apparently a PL for folks with perfect memory who never make mistakes like those described in that ticket. "Lesser" programmers like me can choose another language.
- Nuoji 6y agoA problem for Zig is the combination of unsigned overflow UB, implicit widening and always performing operations on the type size of the operands. For example, let’s say you have `a = b + c` with b/c being i8 and a i32. This calculation is first performed as an 8 bit add, then extended to 32 bits. This is true for both Rust and Zig, but Rust requires an explicit cast to widen the result of `b + c`, making it obvious that an extension happens and that `b + c` is not performed in 32 bits. In Zig there is no such indication- you need to look up the definition of b and c. Other problems occur as well, that both C and Rust avoid in their own ways. Hopefully Zig can improve this situation. See more here: https://c3.handmade.network/blogs/p/7651-overflow_trapping_in_practice#23988 https://c3.handmade.network/blogs/p/7651-overflow_trapping_i...