5 ms·
> Swift was sold to me as a safe language. But I think it would be more accurate to describe it as a thin veneer of safety over a deep pit of peril. The linux v
by estebank 3y ago
> Swift was sold to me as a safe language. But I think it would be more accurate to describe it as a thin veneer of safety over a deep pit of peril. The linux version of swift replicates a large number of native mac frameworks using whatever mix of C and C++ gets the job done.
I still don't understand why this argument is used as much as it is. Every language ends up having to deal with either hardware or system libraries, which means that any language that provides further assurances beyond what the platform does has to have glue code to maintain its invariants. Whenever these languages have to dereference pointers they use runtime checks which would otherwise not hold its invariants. Interfacing with existing libraries is the same. Using a C library from Swift doesn't make it an "unsafe language", it makes it a flexible one that can leverage the existing ecosystem without reinventing every wheel.
- woodruffw 3y agoI don’t know much about Swift, but the “normal” way to handle those invariants in safe programming languages is safe constructions: unsafe operations are wrapped in safe abstractions that cannot violate the language’s semantics, no matter how they’re misused. It’s of course possible to build those abstractions incorrectly, but there’s a large language design space between “you can build safe abstractions” and “all correctly implemented abstractions are safe by construction.” If Swift falls into the first category (maybe it doesn’t!), then this seems like a fair criticism to me.
- estebank 3y agoThe critique was that it relies on C code for cryptography at all, no further investigation on whether the usage of those APIs is limited to providing safe abstractions. That's what I take issue with.
- geodel 3y agoWell, did you see this in article copied from swift website: swift, a general-purpose programming language built using a modern approach to safety, performance, and software design patterns, to create the best available language for uses ranging from systems programming, to mobile and desktop apps, scaling up to cloud services. With these rather expansive pronouncements, one can reasonably assume Swift won't drop to C++ in the very next program after "Hello World".
- kbenson 3y agoNo, the critique (as I understand it) is that it's not using the system library but building it from scratch. From the description it's not binding his BoringSSL .so or .dll that exists on the system and building it, it's compiling its own pinned version and using that. If it was using the system library and a vulnerability was found in it and the distro shipped a fixed package, then the swift coding using it would benefit from that fix without any recompilation needed (for back-patching distros at least, which is from my experience what people use in production systems). Ideally, even if you manually compiled BoringSSL for your system and the distro wasn't providing it, you could build again from a newer version or at worst patch the source of the library you originally built and rebuild it. Automatically building a specific version of a library, which may not be (and likely won't be) the latest version of that library with the latest security fixes, is a real problem. Now, I don't know if BoringSSL was pulled in from vapor, or a dependency vapor relies on, but it is problematic to have the language libraries do this, at least if there isn't an option to do otherwise (and maybe there was, and it just wasn't surfaced through the layers as vapor built its dependencies). This might very well also be a problem with vapor and not swift, as it could be other swift modules do the right thing.
- estebank 3y agoThis is a good call-out, the issue around vendoring, but it seemed to me that the complaint was about using BoringSSL at all. It is a very valid complaint if dependencies are vendoring their dependencies, side-stepping your system. On Rust-land, I've noticed that some libraries like to have a fallback or compile time config flag to use the vendored version, but still provide a way of using the system's version. I appreciate when both options are available because I've encountered some C libs that were very difficult to set-up on first run and the vendored version usually works out of the box.
- kbenson 3y ago> but it seemed to me that the complaint was about using BoringSSL at all. From the article: "but it raises the bigger question of why are we compiling BoringSSL in the first place?" (emphasis mine). I think the "compiled" part of that is doing the heavy lifting. > On Rust-land, I've noticed that some libraries like to have a fallback or compile time config flag to use the vendored version, but still provide a way of using the system's version. This is probably a good middle ground, but it might be more harmful than helpful than if it instead just linked to direction for how to install it yourself (at the simplest level it's almost always ./configure; make; make install), because then at least it's obvious what happened and what libs are being used and you can find them (in /usr/local or wherever). > I appreciate when both options are available because I've encountered some C libs that were very difficult to set-up on first run and the vendored version usually works out of the box. Anything that the install process can automate should be easy enough to provide directions and maybe some simple helper scripts for. If it's too complicated for someone to do, how is it able to be done in an automated fashion by a dependent in a way that seems to work for most people? (and if it doesn't work for most people, why even offer it)
- jchw 3y agoSince Go came up earlier, likely it is being contrasted with Go in this case, where Go has a fast and robust crypto library in the standard library. And yeah, safety is clearly a continuum: hardware is not perfect, operating systems are not perfect, etc. However, being defeatist about this point is not really the way to go. We should aim to replace less safe things, especially in safety-critical parts of applications like the cryptographic operations and TLS implementations, with safer things. That's definitely why in the Rust ecosystem there's been a huge emphasis on pushing rustls forward; it's a shame to put all of the work into making safe and robust software, only to have a giant OpenSSL or BoringSSL-shaped liability sitting in the same address space as your program. Battle testing alone has proven insufficient over and over and over and over, whereas statically verifying the absence of (at least some kinds of) flaws remains very promising still. In practice I'm sure it's obvious that to make any application secure and/or robust it's going to take multiple layers of defense where ideally there is no single point of failure. But to make progress, you have to... you know, make progress. Programming languages, toolchains, and standard libraries can't stop your operating system from being insecure, but they do have the ability to not put less safe/verified things into your address space. Maybe some day, all of these safe Go/Rust/Swift programs will have much more robust hardware and operating systems to run in that are, themselves, "verified" to have certain security assurances. For that to really be all that meaningful, though, the programs themselves have to actually be "safe". And hopefully, even when standard library code can't be proven to be "safe" using the language's normal mechanisms, we can still prove it using heavier duty tools. But, it certainly isn't happening over night...
- trealira 3y ago> Maybe some day, all of these safe Go/Rust/Swift programs will have much more robust hardware and operating systems to run in that are, themselves, "verified" to have certain security assurances. For that to really be all that meaningful, though, the programs themselves have to actually be "safe". On some older operating systems, the system was safer in some ways. On the Genera operating system for Lisp machines, almost all code ran in a single address space, which was possible because Lisp is garbage collected and doesn't allow arbitrary reads and writes through arbitrary pointers. You could still write code in unsafe languages like C, but those programs did need their own address space, like in traditional OSes. On the Burroughs MCP operating system from 1961, it had a similar mechanism. It was written in the language NEWP, which had certain unsafe constructs marked, and if code used unsafe constructs, it had to be approved by the system administrator to be executed. https://en.wikipedia.org/wiki/Single_address_space_operating_system https://en.wikipedia.org/wiki/Single_address_space_operating... https://en.wikipedia.org/wiki/NEWP https://en.wikipedia.org/wiki/NEWP
- Someone 3y agoThat’s true, but Swift moves out of Swift code way before it has to interact with system libraries. If you use Java, your xml parser, json parser, character encoder, date/time formatter, jdbc library, etc. likely are mostly written in a JVM language. If you use Swift, they were (at least in the first versions) almost entirely written in (Objective-)C, so the amount of unsafe code is larger. Wrapping that in a safe language doesn’t remove all security concerns of that code.
- nvm0n2 3y agoAnd don't forget that Java has its own SSL stack, also written in Java. So you can go a long way with no native code at all.
- viraptor 3y ago> Every language ends up having to deal with either hardware or system libraries There's a big difference between using a C library which does extra processing + maybe uses some syscalls and doing the syscalls directly. The interface is much stricter for the system interface and the memory ownership is better defined. You don't have to think about the library using a different allocator than the app does. So no, I wouldn't say every language needs to do that. Golang definitely didn't https://github.com/golang/go/issues/36435 https://github.com/golang/go/issues/36435
- bhawks 3y agoThe OpenBSD way of one blessed libc as the interface to the operating system is perfectly fine for their project. The Linux kernel is a separate project from the several different libc implementations and the Linux kernel project treats the syscall API as a first class contract. If a language wants to use it directly for some reason that is an option on the table.
- Mathnerd314 3y agoRecently I was looking at how to do streaming I/O in Swift, reading a file line by line (but not the whole file at once). In C on Linux, this is one function, fgets, which does the buffering itself and uses SIMD-accelerated memchr to look for the newline. You call it with a file handle and it's done. In Swift the best advice I found was https://forums.swift.org/t/read-text-file-line-by-line/28852/6 https://forums.swift.org/t/read-text-file-line-by-line/28852...: use the C functions. I experimented with using FileHandle in Swift and manually scanning the return Data value for newlines, and found it added a 6x slowdown. The loop for scanning the newline does not optimize even the array access, it was calling a lookup function for every byte and there was of course no SIMD acceleration. Converting the Data value to a native Swift array did at least eliminate most of the lookup overhead, but from the profile it seems like it still does bounds checking on every access, so it doesn't optimize to SIMD or anything like that. So that's the issue - the Foundation library isn't a portable wrapper over the native system calls, it is a thick wad of gunk that adds significant overhead to even simple programs. There was an effort to make a better API, https://github.com/apple/swift-system/ https://github.com/apple/swift-system/, but it seems to have stalled (or perhaps an improved cross-platform API was never its goal).
- ptrpavlik 3y agoSwift is currently safe in terms of type safety and dealing with pointer, I think that they've done an overall great job there. There's just few architectural decisions where doing things like accessing an array out of it's bounds crashes the app by design. In the server world, one might prefer throwing an exception you can capture instead of bringing the whole process down. What Swift currently isn't is thread safe. They're working on it for Swift 6, but it's still probably years away, and will be a massive breaking change. Vapor has done a great job putting a thread-safe layer on top of this mess, but once you go down to say that mentioned SwiftNio level, you really need careful with concurrent memory access.