HN Theater @HNTheaterMonth

The best talks and videos of Hacker News.

Hacker News Comments on
Safe and Secure Drivers in High-Level Languages

media.ccc.de · 173 HN points · 7 HN comments
HN Theater has aggregated all Hacker News stories and comments that mention media.ccc.de's video "Safe and Secure Drivers in High-Level Languages".
Watch on media.ccc.de [↗]
media.ccc.de Summary
Drivers are usually written in C for historical reasons, this can be bad if you want your driver to be safe and secure. We show that it i...
HN Theater Rankings

Hacker News Stories and Comments

All the comments and stories posted to Hacker News that reference this video.
Not sure about the characteristics of your workload, but here's a talk where a group wrote device drivers in several high-level languages, and measured their performance: https://media.ccc.de/v/35c3-9670-safe_and_secure_drivers_in_...

They found that the Swift version spent 76% of the time doing reference counting, even slower than Go, which spent 0.5% in the garbage collector.

mbrodersen
All that tells me is that the Swift implementation is different from the one I implemented.
bitcharmer
Yeah, GP has no idea what they are talking about. They replied something similar on another thread without ever providing evidence for their claims. They even claimed their RC implementation in Haskell is faster than hand-crafted C++.

Gave me a chuckle.

mbrodersen
And your reply game me a chuckle :) The code is unfortunately not open source so I can’t provide the evidence. You believe what you want to believe of course. However I am happy to answer any questions on how it is implemented down to the lowest-level nitty gritty details.
On the other hand, Swift changes so frequently that book authors teaching the language can't keep up with Apple to write up-to-date books. By the time a book comes out covering version N, we're a couple months away from version N+1. In this situation I can't trust I will be able to buy a book, learn the language and become comfortable with it before there is a new version with major changes. It's a very moving ground.

As for performance, it would be cool if every single thing wasn't behind an atomic reference counter, making it slower than even garbage-collected Go: https://media.ccc.de/v/35c3-9670-safe_and_secure_drivers_in_... (the relevant part starts at 33:06).

raydev
> Swift changes so frequently that book authors teaching the language can't keep up

This hasn't been true for a couple years.

ralmidani
Regarding language changes, I think this has improved quite a bit. I forget if stability was introduced in 4.x or 5.x, but code written today should be fine even when run through a new major version of the compiler. You can simply ignore new language features you aren't ready for until your codebase is due for a refactor.

Regarding ARC, you don't have to use reference types in Swift, and the community seems to agree that structs are more suitable than classes for most use cases (a SwiftUI app is made of structs that conform to certain protocols, and those structs can be initialized, copied, and "modified" with little overhead).

The cool thing about Swift is that structs can still have methods, computed properties, and custom initializers. So if you're coming from Python or Ruby or Java, or think OOP can help you organize your code, you don't have to throw away everything you've learned in order to be productive and write elegant code (and Swift brings a few new OOP tricks of its own).

wool_gather
> I forget if stability was introduced in 4.x or 5.x, but code written today should be fine...

They've always provided source compatibility modes in new releases of the compiler so you can delay making syntax changes to an existing codebase if you need to.

You're thinking of ABI stability, which is in place as of Swift 5, and doesn't have anything to do with what parent is talking about.

saagarjha
For some time; at some point newer compilers will drop support for older versions.
Interesting, thanks for sharing. It's always nice to see Lua in the wild. This headline immediately reminded me of this[0] talk from CCC 2018 comparing different high level languages for writing userspace network drivers.

[0]: https://media.ccc.de/v/35c3-9670-safe_and_secure_drivers_in_...

It would seem that the Swift compiler is far from smart[1].

[1]: <https://media.ccc.de/v/35c3-9670-safe_and_secure_drivers_in_...

The author of this project presented it last year at CCC, here is the video: https://media.ccc.de/v/35c3-9670-safe_and_secure_drivers_in_...
ksangeelee
Thanks, that was interesting. If anyone is excited enough to try driving peripherals in userspace via hardware registers, I can recommend starting with a Raspberry Pi, since it has several well documented peripherals (UART, SPI, I2C, DMA, and of course lots of GPIO), and the techniques described in this talk are transferable.

A search for 'raspberry pi mmap' will yield a lot of good starting points.

Dec 30, 2018 · 173 points, 61 comments · submitted by DyslexicAtheist
duneroadrunner
Another option is to use a memory safe subset of C++ [1]. It should be less work to migrate existing C drivers as (reasonable) C code maps directly into the safe C++ subset. And the migration can be done incrementally with corresponding incremental safety benefits.

[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus

shmerl
Why do that, if there are languages that do it by design?
duneroadrunner
Well like I said, if you already have a driver written in C (or C++), translating it to the safe subset of C++ would be less work as most of the code would remain unchanged and the unsafe elements (like pointers, arrays, etc.) map to direct (safe) replacements. And your driver maintainers/authors may already be familiar with C++ (if not big fans of it :) .

While the OP may demonstrate that other languages aren't always that bad in practice, I think the consensus is that Rust and C/C++ are the appropriate languages when maximum efficiency and minimum overhead are desired.

While Rust is a good option, the (safe subset of the) language has an intrinsic shortcoming that doesn't seem to be generally acknowledged. The forthcoming C++ "lifetime checker" (static analyzer) has the same issue [1]. Essentially, if you have a list (or any dynamic container) of references to a given set of existing objects, you cannot (temporarily) insert a reference to a (newly created) object that may not outlive the container itself.

In my view, this is a problem. The workarounds are intrusive, inconvenient and/or involve significant overhead. Which makes it tempting to just resort to unsafe Rust in those cases. (And of course this specific example is representative of a whole class of situations with a similar dilemma.) The safe C++ subset doesn't suffer the same issue. (Because in some sense, C++ is still a more "powerful" language.)

[1] https://github.com/duneroadrunner/misc/blob/master/201/8/Jul...

pjmlp
Only if the team plays along regarding static analysers and compiler warnings as errors.
newnewpdro
Wouldn't you simply enforce this with automation if you were making a serious effort? It's already quite common for github PR's to require myriad CI tests to pass before anything can be merged... those can incorporate static analysis and warnings as errors.
pjmlp
Still needs to have the buy-in from the team.

Try to be that clever guy putting such gates into place without having the team be on the same wave length.

Github is a bubble, there are tons of software projects out there, using a myriad of build infrastructures, or even just doing plain old IDE builds (yes I know, but it is as it is).

newnewpdro
Convincing your team to switch languages is infinitely more difficult than adding infrastructure to enforce good hygiene. So I don't really see what your point is, it's moot.
pjmlp
This whole thread started about enforcing behaviors that are largely ignored by enterprise developers outside HN bubble.

In no point there was a mention of switching languages.

newnewpdro
Ah, this had left an lingering impression in my mind:

"Why do that, if there are languages that do it by design?"

But I see it wasn't your comment, my bad :)

emmericp
Code on GitHub: https://github.com/ixy-languages/ixy-languages/

Discussion about the C version on GitHub in 2017: https://news.ycombinator.com/item?id=16014307

guerby
Great work! Do you have by any chance the C driver latency measurements? It would be nice to have them on the same graph as those for Rust and other languages.
emmericp
Would look the same as rust; I haven't run the full measurement with all the packets. But sampling 1k packets/second yields the same result for C and Rust.

You can't really get a faster speed than Rust here; the only minor thing that could be improved is the worst-case latency by isolating the core that the driver is running on (isolcpus kernel option) to prevent other random interrupts or scheduling weirdness. But that optimization is the same for all languages and should get rid of the (tiny) long tail.

guerby
Thanks!
mpweiher
Great direction! One of the lesser known but really nifty bits of NeXTStep was DriverKit:

http://www.nextcomputers.org/NeXTfiles/Software/OPENSTEP/Dev...

Objective-C was a great match for driver development, devices tended to have a very natural OO flavour to them and naturally sorted into classes.

Putting things in user-space seemed a natural extension, that sadly didn't happen at the time even though Mach had the hooks for it (we never got user-level pagers either, which would have rocked together with garbage collected languages). There certainly didn't seem to be a good reason why I had to reboot the machine and wait for fsck when there was a minor bug in the little driver I was writing to talk to an EISA printer controller that had nothing to do with the basic functioning of the system...

(Why would a printer controller be on an EISA controller, you ask? It directly drove a Canon Laser Copier, so, yes!)

Oh, and not surprised by the abysmal Swift performance. Alas, Apple's marketing of Swift as a "high-performance" language has been very successful despite all the crushing evidence to the contrary.

saagarjha
> Objective-C was a great match for driver development, devices tended to have a very natural OO flavour to them and naturally sorted into classes.

How do you feel about the current state of driver development on macOS, with Objective-C basically being replaced with Embedded C++ with partial reflection?

mpweiher
It was part appeasement of the "never Objective-C" crowd (hello CoreFoundation, hello CocoaJava, hello Swift) and part the exact sentiment discussed here, that you cannot possibly do kernel development in a higher level language.

What I heard (quite some time ago) is that this move is now seen as a mistake.

mrpippy
ObjC was definitely seen as a dead-end at the time: it would either be replaced with Java, or maybe Mac developers would just stick with Carbon and C/C++. Either way, all driver development (on classic Mac, Windows, Unix) was in C, and C++ would be much more familiar than the “weird obsolete square-brackets NeXT language”

I made a post a few years ago discussing the issue:

https://news.ycombinator.com/item?id=10006411

pjmlp
As addition to the talk,

Android Things userspace drivers are done in Java, and since Treble, it is also possible to write Android drivers in Java.

MicroEJ and Windows IoT Core also allow for such capabilities.

Loved the talk.

henrikeh
It is disappointing that Ada seems to be completely shunned from all discussions of safe high-level languages despite having both a track record (several successful safety critical systems) and a unique feature set (multiple compilers, provably correct subset, ranged subtypes).

Is it purely a matter of what is well-branded?

zozbot123
"Multiple compilers" will do little good if the compilers aren't generally available. The Ada problem is all about availability of good implementations. (And no, GCC doesn't cut it. Not in 2018 at least.)
henrikeh
Paying for a compiler isn't really as crazy as it sounds. Companies pay for CI, static analyzers etc. etc. For non-commercial applications it is obviously different.*

Yet, I'm genuinely curious, why isn't GCC up to snuff?

* Edit: Which of course limits general adoption.

pjmlp
Somehow it feels strange how many devs expect to be paid for their work, while blantly refusing to pay for the work of others.
agumonkey
Ada also suffered from bad roots and bad timing. We're off a decade of simple dynamic languages. Ada seems like an old and immense ruin. Maybe there will be a julia/rust equivalent for Ada.
henrikeh
And it is very true -- Ada never truly gained any foothold in anything but situations where it truly delivered upon a requirement.

If anything, I can take solace in the fact that it is very much alive despite the exaggerated rumors of its death. I'd encourage everyone to try it out and steal all the ideas.

Santosh83
> Our drivers in Rust, C#, go, and Swift are completely finished, tuned for performance, evaluated, and benchmarked. And all of them except for Swift are about 80-90% as fast as our user space C driver and 6-10 times faster than the kernel C driver.

Interesting. What is the reason for higher performance of user space C driver (and the other user space drivers for that matter) when compared to the kernel C driver? Will this hold for all driver types or is this a rather uncommon property of this particular kind of driver?

emmericp
Check out my talk last year, that should answer that: https://media.ccc.de/v/34c3-9159-demystifying_network_cards
usaphp
> A main driver of performance for network drivers is sending/receiving packets in batches from/to the NIC. Ixy can already achieve a high performance with relatively low batch sizes of 32-64 because it is a full user space driver. Other user space packet processing frameworks like netmap that rely on a kernel driver need larger batch sizes of 512 and above to amortize the larger overhead of communicating with the driver in the kernel.
mpweiher
Interesting!

As our devices have gotten faster, the user-space/kernel boundary is becoming more and more of an issue. I was shocked when my supposedly super-fast MacBook Pro SSD (2+GB/s) was only giving me around 250MB/s.

Turned out mkfile(8), which I was using without thinking much about it, is only using 512 byte buffers...

https://blog.metaobject.com/2017/02/mkfile8-is-severely-sysc...

zozbot123
The main thing affecting performance at the userspace/kernel boundary these days is Spectre and Meltdown mitigations, FWIW...
mpweiher
Yeah, when I ran the same tests a little later and with the Spectre/Meltdown patches (+APFS, which also didn't help), the mkfile I/O rate had a further precipitous drop to ~80MB/s.
vlovich123
No, userspace/kernel transitions are always and will always be slow. Every time it happens you've got to do a context switch which is super-expensive + cache unfriendly. You also pay a penalty keeping the kernel mapped at all times in terms of more pressure on the TLB but due to Spectre and Meltdown mitigations the kernel has actually been unmapped hurting the performance of switching into kernel further although this will be undone eventually.
emmericp
Previous discussions about our Rust and Go drivers:

https://news.ycombinator.com/item?id=18405515

https://news.ycombinator.com/item?id=18399389

heyjudy
What would make device drivers safer:

- Microkernel OSes - one driver failing is okay, because it runs in mostly in user-space. The big gotcha in microkernel design is transactions that touch multiple components. Some sort of across-driver transaction API is needed (start, commit, rollback) in order to undo changes across several userspace subsystems.

- A standard language (like the talk suggests) and shipped as portable bytecode to run on a VM or compile to native, so that drivers are portable and runnable without knowing the architecture.

- Devices themselves containing OS-signed drivers rather than each OS having a kitchen-sink installation of all drivers. Each bus would have an interrogation call to fetch the driver.

fulafel
Is there a plan for letting applications using the normal networking APIs see this, or is it currently just for "process raw ethernet packets in userspace" kind of apps?

(The latter is a great thing to have built, of course, just thinking aloud about how this might replace existing drivers)

emmericp
Yeah, I'd love to port a Go TCP stack like https://github.com/google/netstack to use our driver to build a microkernel-style service offering network connectivity. Running taps (https://datatracker.ietf.org/wg/taps/about/) on top of that would be ideal for a modern setup. But that's a lot of work...
pjmlp
Swift loosing heavily due to reference counting GC instead of a tracing GC.

Now I have a talk to point out to those that always trump that referencing counting GC are so much better than tracing ones.

zozbot123
Rust also uses refcounting, not tracing GC. The problem with Swift is its use of obligate GC for things that don't need it in other languages (C/C++, Rust).
pjmlp
Rust doesn't do refcounting, it makes use of an affine type system, which is something completly different.

If you make use of the refcounting library types in Rust, from the std::rc and std::sync crates, the performace impact would be quite similar.

zozbot123
Sure, but you don't need refcounting for cases that are covered by Rust's affine types, even in C/C++. You can use the patterns described in the C++ Core Guidelines, and end up with something quite rusty, only without fully-automated checking.

(Besides, I think std::rc has better performance than the refcounts found in Swift and C++, because it's used in cases that don't need atomic update, and yes this is statically checked too.)

steveklabnik
You also only need to bump the counts on Rc<T>/Arc<T> for each owner, not for every reference, which reduces refcount traffic.
mmirate3
> only without fully-automated checking

The fully-automated checking is precisely what allows one to write unmanaged code without tearing one's hair out.

pjmlp
> You can use the patterns described in the C++ Core Guidelines, and end up with something quite rusty, only without fully-automated checking.

Also known as the mythical "only other programmers commit errors, I am always correct".

asdkhadsj
Yea, I'm not sure what point was being made there. Rust isn't about being faster than C, it's about not letting you make mistakes you can make in C/etc.

If you could somehow write perfect code in a timely manner, you'd have no need for Rust. You'd likely also be a unicorn.

tom_mellior
Their notion of "better" may differ from yours. Do you actually know people who claim that reference counting is faster than a marking GC?

Reference counting can be better in terms of ease of implementation, cross-language interoperability, and by reclaiming memory immediately when the last reference to it disappears.

pjmlp
Reference counting is only better in terms of ease of implementation.

Hence why it is usually one of the earliest chapters in any CS book about GC algorithms.

Reclaiming memory immediatly only works in simple data structures. Naive reference counting impletations have similar stop-the-world effects when releasing relatively big data structures, which can even lead to stack overflows, if the destructor calls happen to be nested.

In case of Objective-C and Swift, Objective-C tracing GC project failed due to the underlying C semantics, thus they took the 2nd best approach of enforcing Cocoa retain/release patterns via the compiler, which only applies to a small set of Objective-C data types.

Swift naturally had to support the same memory management model, as means to keep compatibility with the Objective-C runtime.

zozbot123
If you don't need to "reclaim memory immediately", you can often use arenas, a.k.a. regions - freeing an arena does not incur a "stop the world" pause. Similarly, most "big data structures" have little use for reference counting in their internals (albeit concurrent data structures may indeed use refcounting internally, and more obviously it comes up when implementing general graph structures). Overall, outside of the use of obligate GC as in Swift, I suspect that nested destructor calls are unlikely to be a significant problem in practice.
pjmlp
Regions are a feature also available in languages with tracing GC, while offering better productivity in overall.
MaxBarraclough
> Reference counting is only better in terms of ease of implementation.

That sounds unduly broad. You really think the Linux kernel would be better off using a GC?

vlovich123
Eh. In practice reference counting solves 90% of the problem and keeps memory usage low at all times. It's part of why Java programs are so hard to tune for performance & end up eating so much RAM. If you don't believe, compare Android & iOS where even though Android has enormous financial & competitive pressure to improve the performance they still end up requiring 2x the amount of RAM that iOS does which is partially driven by the choice of Java.
pjmlp
People keep referring to Java to talk bad about tracing GCs.

The fact is that Java isn't the only game in town, and all GC enabled system programming languages do offer multiple ways to manage memory.

Value types, traced GC memory references, global memory allocation, stack values, RAII, untraced memory references in unsafe code blocks.

Not every OOP language is Java, nor every GC is Java's GC.

Additionally, not every Java GC is like OpenJDK GC's, there are plenty to chose from, including soft real time ones for embedded deployments.

As for Android, it is a fork still catching up with what Java toolchains like PTC/Aonix are capable of, all because Google decided it could do better while screwing Sun in the process.

zozbot123
> and all GC enabled system programming languages do offer multiple ways to manage memory.

Since "GC-enabled system programming languages" is an oxymoron, a claim about what such languages may or may not include is just not very useful. But it's definitely the case that properly combining, e.g. "traced GC memory references" and RAII including deterministic deallocation for resources is still a matter of ongoing research, e.g. https://arxiv.org/abs/1803.02796 That may or may not pan out in the future, as may other things such as pluggable, lightweight GC for a subset of memory objects, etc., but let's stop putting lipstick on the pig that is obligate tracing GC.

pjmlp
An oxymoron only in the minds of anti-tracing GC hate crowd.

- Mesa/Cedar at Xerox PARC

- Algol 68 at UK Navy computing center

- Modula-2+ at Olivetti DEC

- Modula-3 at Olivetti DEC/Compaq/HP and University of Washington

- Oberon, Oberon-2, Active Oberon, Oberon-07 at ETHZ

- Oberon-07 at Astrobe

- Component Pascal at Oberon microsystems AG

- Sing#, Dafny and System C# (M#) at Microsoft Research

- Java when running AOT compiled on bare metal embedded systems like PTC Perc and Aicas Jamaica

- D by Digital Mars

- Go at Google (Fuchsia) and MIT (Biscuit)

Lets stop pretending reference counting is the best of all GC algorithms, in spite the fact that is quite basic and does not scale in modern multi-core NUMA architectures.

vlovich123
No one is saying that reference counting is the best. What I am saying is that reference counting tends to offer a good set of advantages (predictable memory performance, no hogging of memory, no pauses) for a minimal cost (more frequent GC, more overhead to store reference counts).

The comment about "does not scale in multi-core NUMA" only applies if you have objects that are shared between threads because otherwise there's no atomics going on. For example, Rust has a generic ref-count mechanism that automatically uses atomic operations for ref-counts when an object might be shared between threads but otherwise does simple arithmetic. Non-atomic refcounts are most likely also going to be faster than any other global GC algorithm. Other languages require explicit differences but are still able to offer the same thing.

The fact of the matter is that the majority of objects do not require expensive GC of any kind & can live on the stack or have explicit ownership guarantees. Choosing defaults where everything might be shared is not a good default for systems languages as it pessimizes memory usage & CPU performance to a drastic degree.

That being said, GC does have its place in all manner of applications and has other advantages like making developers more productive which isn't a bad thing but these are domain-specific decisions. There are plenty of techniques - reference counting, memory pools, static memory allocation, various GC algorithms, etc, etc. Each has tradeoffs & every single GC system I've encountered means variable latency/stop-the-world and greedy memory usage (optimized for the 1 application). That's valid in some domains but certainly isn't desirable. If there were an awesome GC system like you claim that could perform that well it would have been deployed already to inemurable applications like all Java vendors, Javascript VMs, C#, etc, etc. It's an extremely complex problem.

Most of your links are niche commercial systems or even pure academic research systems. They're not proof of anything other than GC being possible to implement for various languages/machines which isn't a claim that's been disputed at all.

> Go at Google (Fuchsia)

AFAIK Fuchsia does not use Go for any systems-level portions. Those are written in C/C++/Rust last time I checked (with Rust being the official default going forward). Do you have any links to the contrary?

drasah
You have to visit Maktabtk site to get all about theoretical framework in research plan you need to use in scientific research https://www.maktabtk.com/

https://www.maktabtk.com/service/7/%D8%A5%D8%B9%D8%AF%D8%A7%...

volkisch
Finally a talk that isn't women in tech or tranny agenda.
saagarjha
Personally, I'd call this writing "Safer and More Secure Drivers in High-Level Languages", because there are still unsafe operations going on (for DMA, etc.): https://github.com/ixy-languages/ixy.swift/search?q=Unsafe

While it's great that you get improved safety (and often nicer and easier to reason about code) by using something other than C, you can still have memory safety issues from cavalier or incorrect usage of unsafe APIs, since they undermine the guarantees the language provides with regards to correctness.

Also, unrelated: does anyone actually have the slides (you know, the presentation file with text in it, rather than the mp4 that CCC is offering me) for this presentation? It's really annoying to scrub through a video to find stuff on a slow internet connection :(

emmericp
I've added them to the git repo: https://github.com/ixy-languages/ixy-languages/blob/master/s...
saagarjha
Thanks!
edwintorok
Direct link for download (GitHub won't render the entire PDF by default): https://github.com/ixy-languages/ixy-languages/raw/master/sl...
zozbot123
The talk mentions making DMA operations safe using IOMMU. (Unfortunately, IOMMU is a hardware feature and not always supported.)
voltagex_
The slides may not be up yet, but you could demux just the video stream out of https://mirrors.dotsrc.org/cdn.media.ccc.de//congress/2018/s..., or give me a yell in the next 30 minutes or so and I can do it. Don't have a nice script to de-dupe images though.
qznc
Usually they would be linked on this page: https://fahrplan.events.ccc.de/congress/2018/Fahrplan/events...
pjmlp
Quite true, however the unsafety code coverage gets reduced quite significantly.

On the last Linux Kernel Summit, according to Google, 68% of Linux kernel security exploits are caused by C memory corruption issues due to lack of bounds checking.

HN Theater is an independent project and is not operated by Y Combinator or any of the video hosting platforms linked to on this site.
~ yaj@
;laksdfhjdhksalkfj more things
yahnd.com ~ Privacy Policy ~
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.