
RustBelt: securing the foundations of the Rust programming language - based2
https://dl.acm.org/citation.cfm?doid=3177123.3158154
======
chrismorgan
My favourite part:

> our verification work resulted in uncovering and fixing a bug in Rust’s
> standard library, demonstrating that our model of Rust is realistic enough
> to be useful.

The bug was [https://github.com/rust-
lang/rust/issues/41622](https://github.com/rust-lang/rust/issues/41622).

~~~
dmix
One thing about formal verification, and a lesser extent automated testing, is
that you often end up spending the majority of the time adapting the program
or language to work with verification rather than actually solving a real-
world problem or fixing a real bug.

His third comment fits this category well. The problem set he describes was
that the code is designed in a way that's ill suited for formal verification.
But he never mentions any other benefits or justification for changing how it
works.

So I'm not sure it's necessarily a 'bug' in a traditional sense that supports
the use of formal verification, except in some self-serving way.

That said, I'm very much in support of formal verification in these types of
languages.

~~~
tom_mellior
> But he never mentions any other benefits or justification for changing how
> it works.

I read "it lets me write a program that has a data race", accompanied by a
program that has a data race, as saying that the bug is that a language
feature meant to provide mutual exclusion does not provide mutual exclusion.
In a language one of whose main selling points is "you always get mutual
exclusion". How is this not a bug?

The benefit of changing this bug is that the guarantees Rusts makes to
programmers are actually guaranteed.

~~~
dmix
I was specifically referring to the 3rd comment on the Github issues page, not
the first comment which mention a race condition which the eventual commit
addressed

> His third comment fits this category well.

And even then I was only talking about the language he used, aka the
justifications and rationale he proposed, which seemed entirely around being
difficult to verify formally, rather than mentioning any real hypothetical
'bug', security flaw, or architectural downside.

~~~
tom_mellior
Fair enough about the particular Github comment, but even there I think you
are wrong.

The architectural downside is being unable to understand a program you have
written: "However, if the type does not have an explicit impl for Send/Sync, I
don't know how to even figure this out -- I would have to chase all types
(including safe ones, and across all abstractions) of all fields recursively
and then check when they are Send/Sync... that's way too error-prone."

This doesn't just talk about his particular proof tool being unable to
understand this. It's about any Rust developer being unable to easily
understand the types used by their programs, using only standard library
constructs. (If anything, this should be _easier_ for tools than for humans.)

------
fovc
Adrian Colyer did a good write up of this paper recently on The Morning Paper
: [https://blog.acolyer.org/2018/01/18/rustbelt-securing-the-
fo...](https://blog.acolyer.org/2018/01/18/rustbelt-securing-the-foundations-
of-the-rust-programming-language/)

~~~
nickpsecurity
From the Adrian Colyer's review, we find the title might be misleading
depending on what ownership model entails for a given developer’s use of the
language:

“We had to make some concessions in our modelling: we do not model…

1\. more relaxed forms of atomic accesses, which Rust uses for efficiency in
libraries like Arc

2\. Rust’s trait objects, which can pose safety issues due to their
interactions with lifetimes

3\. stack unwinding when a panic occurs, which can cause similar issues to
exception safety in C++

4\. automatic destruction, “which has already caused problems for which the
Rust community still does not have a modular solution.”

5\. a few details unrelated to ownership, such as type-polymorphic functions
and “unsized” types.”

Also shows why it’s good to have a semantics in the beginning covering the
whole language like with ML or later SPARK Ada. It forces language developers
to spot and address these corner cases early on. Good news is that, like with
SPARK, one might just subset their use of Rust to exclusively what has been
proven with no use of anything else. Is anything on the list common enough in
most Rust development where it can’t be avoided and must be verified later?
I’ve obviously seen many people mention traits. Arc and automatic destruction
safety not being proven is a bit ironic given they’re presumably there to
boost safety versus raw pointers and manual destruction.

EDIT: Thanks for quick, clarifying replies about these. It's looking like a
subsetting approach can work.

~~~
lambda
You can avoid 1 if you do no multithreading, or if you accept a slower
implementation of Arc.

Embedded code in Rust almost always avoids 1, 2, 3, and 4. Arc is used for
freeing dynamically allocated memory when all references, including across
threads, are dropped, but if you're running without dynamic memory allocation,
you don't need that. Trait objects also require allocation. Embedded targets
also generally don't implement stack unwinding, and stack unwinding can be
disabled on other targets as well; it's really only most useful in larger
applications in which you want some threads to be able to continue running
even if another thread panics. Automatic destruction can be fairly easily
avoided if you're running without an allocator, as it is mostly used for
freeing such allocated memory, as well as a few things like file handles and
the like.

The things covered in 5 might be harder to avoid, but also more likely to be
amenable to being covered by further versions of the RustBelt work.

So yes, using a subset of Rust without these features is quite feasible, and
often done when working in embedded (aka no_std) code.

I don't know SPARK Ada that well, but I believe it has similar constraints of
no dynamic allocation. If you can do without dynamic allocation in Rust, many
of the problematic features wouldn't be used.

~~~
dikaiosune
Nit: IIRC one can receive a trait object through a stack based reference.

fn trait_obj(foo: &Trait)...

~~~
lambda
Sorry, you are right about that. You can have trait objects without an
allocator.

I'm not sure how common a pattern that is. Many people like to avoid trait
objects and just parameterize the function on a type bounded by the trait in
that case. And impl Trait is replacing some of the other cases in which you
had to return an allocated trait object.

Anyhow, you're right, I was wrong that people couldn't be using trait objects
in an embedded context with no allocator, but I also think it's perfectly
reasonable to write in a subset of Rust in which you don't use trait objects
at all.

~~~
Argorak
Taking a trait object by reference is a pretty common pattern.

Box<Trait> is so pervasive because it's currently the easiest way to get type
erasure in return position.

------
Asdfbla
I think I can understand and appreciate many different aspects of Computer
science, but programming language theory truly is a bit too arcane and formal
for me. I still try to follow it somewhat though (like when the Morning Paper
had its nice writeup about RustBelt) and I enjoy learning Rust (which exposes
you to the theory underlying its design more than other languages). Maybe it's
good that the results of formal methods now find more practical application,
so it's easier to get people (who aren't hardcore logicians) interested in the
whole topic.

~~~
pornel
I find Rust's RFC/design discussions fascinating. They're a mix of serious
Programming Language Theory and practice. The same feature in Rust is seen by
some as "algebraic data types" and _" oh cool, I can add data to enums"_ by
others.

Some feature proposals starting with "wouldn't it be nice to have…" end up
designed with input from people who literally have a PhD in this feature (and
are bikesheded until people without PhDs can use the feature).

------
bennofs
Does this prove that the current borrowck rules are sufficient to catch all
possible mistakes? For example, does it catch stuff like
[https://github.com/rust-lang/rust/issues/31287](https://github.com/rust-
lang/rust/issues/31287) or [https://github.com/rust-
lang/rust/issues/38899](https://github.com/rust-lang/rust/issues/38899)?

~~~
lambda
What the paper demonstrates is that if you have a correct borrow checker, and
you have the ability to create types using `unsafe` which can extend the
semantics of the language to cover things that the borrow checker cannot
cover, you can write proofs that demonstrate that the language is still sound
given the addition of those types.

This helps demonstrate that one of the fundamental design goals of Rust, have
a simple but limited system of borrowing built into the language, and allow
more complex forms of managing reference types safely built on top of it as
library features using `unsafe`, is a fundamentally sound design. In other
words, you don't have to treat the language and all libraries using `unsafe`
as one large system that you need to prove soundness of, but you can do
modular proofs of the core language, and each library that adds a new type of
memory and reference management, independently.

Those bugs you reference are simply bugs in Rust's borrow checker. The
language used for the RustBelt paper, LambdaRust, is a language that is much
simpler than Rust itself, making the borrow checking much easier but the
language not as convenient to use (it is not intended to be used at all, just
to act as a model of Rust).

The things you reference are simply bugs, but because of the fairly expressive
syntax that Rust offers actually getting the borrow checker right can be be
difficult in some cases.

I don't think that there are any theoretical concerns about the ability to
write a correct borrow checker, just practical issues with the current
implementation not correctly handling certain cases.

~~~
bennofs
> I don't think that there are any theoretical concerns about the ability to
> write a correct borrow checker, just practical issues with the current
> implementation not correctly handling certain cases.

Is it really that trivial? When looking through [https://github.com/rust-
lang/rust/blob/master/src/librustc_b...](https://github.com/rust-
lang/rust/blob/master/src/librustc_borrowck/borrowck/README.md), it is not at
all clear that these rules cover everything to me. There are some complex side
conditions, isn't it easy to miss something there?

~~~
lambda
There is a large amount of room between "trivial" and "serious concerns that
it will be impossible without fundamentally altering the language." All I was
saying is that there aren't such serious concerns about the borrow checker;
there are some known holes, but most people think it's just a matter of doing
the work to fix those holes (which could be a lot of work, and involve
significant refactoring), not that needs to be an entire change in the
fundamental way the borrow checker works, or that borrow checking itself is
theoretically unsound, or that some features of the language are fundamentally
incompatible with sound borrow checking.

The RustBelt work addressed one of the big open questions; is it possible to
treat `unsafe` code in a modular fashion, or does all analysis of unsafe code
have to consider all possible interactions with all other modules which use
`unsafe` code as well?

That was in many ways quite a big question about the design of Rust; can you
prove or very each module which uses `unsafe` independently?

The borrow checker, on the other hand, is an entirely local analysis. It can
get quite complex, especially as you allow for more fine-grained borrow
checking to make it convenient to use compound objects, and introduce non-
lexical lifetimes which help reduce the number of restrictions on what you can
do, but since it's entirely local, it's a much more tractable problem.

I think that it would be good to eventually formalize the full Rust borrow
checker, and of course it's always good to fix these soundness bugs, but there
are some more important open questions right now like fully specifying Rust's
memory model ([https://github.com/nikomatsakis/rust-memory-
model](https://github.com/nikomatsakis/rust-memory-model)) so it's possible to
know what kinds of aliasing you can actually do in `unsafe` code, and which
will be safe even under future versions of the compiler.

The RustBelt work so far is just a nice foundation getting Rust to be more
formally analyzed and specified. There's a lot of work left to be done.

------
p0nce
Every post in this thread slightly criticizing Rust is greyed. This is an
unfortunate part of being an overhyped language: no one is allowed to emit
discordant opinions.

~~~
Blaisorblade0
Rust can be criticized
([https://news.ycombinator.com/item?id=16305437](https://news.ycombinator.com/item?id=16305437)),
but flaws will be spotted. I’ll concede nonsense on other topics might have
more leeway.

------
vectorEQ
adding security features to a language itself seems a bit silly. it just adds
complexity and bloat to the code (which has been and still is mostly
unverified..) which gives people a sense of security that's most likely false
(as people imagine hyped things to be bigger than they actually are). I'd say
a good coder could write more seucre and sound code in C, though someone who
isn't conscious of the nature of the language would write 'safer' code in rust
or so. In the end it's better to learn to do things without these training
wheels >.> all this hand holding is just making people weak like anything else
in life.

~~~
gliptic
There's nothing false about the sense of security. In fact, after I started
using Rust, my C/C++ has become more disciplined as well. The idea that memory
safety is "training wheels" is silly.

------
ktpsns
By the way, this is a project funded by the European Union. It got 2 Million
EUR by the European Research Council
([https://cordis.europa.eu/project/rcn/200802_en.html](https://cordis.europa.eu/project/rcn/200802_en.html)).
It is only one of the many projects which give back a lot to the open source
community. Thank you, EU!

~~~
sifoo
I'm not quite as enthusiastic about their choice of funding target. There are
plenty of other worthy languages and tools out there that could use that money
better in my mind. It's the C++/Java thing all over again, but stepped up
several notches; to the point where it's creeping me out since I can't even
mention C online without being attacked by the Rust Brigade; and they just
don't give up, it's like one of those shitty zombie movies where you have
squirting body fluids and jaws chopping at you from every angle. I don't think
they need any more money; more like therapy, chill pills and rooms with soft
walls/locked doors for a while.

~~~
justinpombrio
This funding was for verification work, which was applied to Rust. Rust is a
relatively new language, and hasn't been the focus of much research yet. C and
C++, in contrast, has already been the subject of _tons_ of research and
tooling. For example, Wikipedia lists 28 static analysis tools for C, and none
for Rust:

[https://en.wikipedia.org/wiki/List_of_tools_for_static_code_...](https://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis#C,_C++)

Is there a particular project for a particular language you think is promising
and just needs some funding? Or does it just irk you when other people are
happy?

EDIT: I'm also not sure why you assume that none of this EU funding is going
to your language(s) of choice; have you checked?

~~~
ktpsns
From my knowledge, the ERC funds fundamental science. In my personal area
(theoretical physics), people tend to move from Fortran to C++ nowadays. As an
effect, mostly readability decreases (without linear algebra libraries) or the
compiler doesn't help anymore with compile-time bound checks (so people use
run-time bound checks with assertions instead). Not even to mention the years
of wasted time for people finding bugs in their parallel codes. And all codes
with the "HPC" badge are parallel nowadays. I think supporting a modern
ecosystem is a good decision if people move away from C++ towards Rust or
Julia (another ERC... [http://opendreamkit.org/](http://opendreamkit.org/)).

(Note: Please don't feed the trolls. Nobody has time for language rants)

~~~
sifoo
How is questioning the choice of project to fund feeding trolls? I was under
the impression that diversity was mostly considered a good thing, that has to
go for perspectives as well. I just don't get why Rust desperately needs a lot
more money than any other tool that's equally useful, they were already pretty
well funded by other organizations.

~~~
Blaisorblade0
This is _one_ research grant on Rust among tons of grants on other projects.
And it funds _research_. When somebody has equally valid research project on
C, rest assured it’ll be funded as well, as it has already happened (EDIT)
with research on abstract interpretation, separation logic and so on.

------
crb002
Do this at the LLVM level and you get way more languages for free.

~~~
pcwalton
You can't prove safety properties of something that isn't safe. LLVM IR sure
isn't.

~~~
haberman
This raises an interesting question of whether Rust would make sense as a code
generation target for other languages or runtimes. It is conceivable that you
could compile and run untrusted Rust (with no unsafe blocks and restricted
imports) the same way web browsers run untrusted JavaScript? It could be the
only GC-less IR that you can run with a reasonable defense against memory
corruption.

Looks like people have had this idea before: [https://internals.rust-
lang.org/t/safe-rust-sandboxing-untru...](https://internals.rust-
lang.org/t/safe-rust-sandboxing-untrusted-modules-at-api-level/1505/9)

On the other hand, maybe this role is better served by WebAssembly.

~~~
zzzcpan
I'm not sure I understand your idea, how can code generation benefit from
memory safe abstractions of target languages? The whole point of code
generation or rather compilation is to fit one representation into something
else, doesn't matter what it is, but it has to satisfy its constraints no
matter what, even if you have to use a giant global array to emulate pointers
via indexes. In other words, the goal is to work around any constraints of the
target language to fit yours, but not to design your language to fit any
constraints of the target.

Safety guarantees can only work if you carefully choose and rely on
appropriate abstractions for your problem.

------
carapace
> Rust is a new systems programming language that promises to overcome the
> seemingly fundamental tradeoff between high-level safety guarantees and low-
> level control over resource management. Unfortunately, none of Rust’s safety
> claims have been formally proven, and there is good reason to question
> whether they actually hold.

Aw, c'mon!?

Rust is Sudoku for super-smarties.

\- - - -

I'm going to "double-down" on that: Rust-lang is a toy, a beautiful bauble for
minds addicted to a certain kind of complexity. Rust isn't about producing
production code, Rust is about producing Rust. Scratching that itch.

My point is that I feel like the time and energy spent on Rust won't ever be
justified in terms of productive bug-free software, when compared against some
other simpler language (Hopgood's Noether comes to mind) or approach.

~~~
quotheth
I work for a company with a massive rust codebase. Rust is very much about
building production code.

What is 'good production code' ?

* Few errors * Readable, well documented * Testable, has tests, has testing tools like quickcheck, fuzzing, etc * Meets performance constraints

Rust hits those better than any language I've used. The downside is, oh gosh,
you'll have to actually learn a programming language that isn't just another
variation of the ones you learned in school.

~~~
carapace
> I work for a company with a massive rust codebase. Rust is very much about
> building production code.

Okay, thank you, I feel a little better hearing that.

------
nukeop
I have recently performed a relatively simple development by using programming
languages on which I had low-to-to-no experience: Perl (low), Ruby (no), Rust
(no) and Go (no). Note that I am quite adaptable on the programming language
front and that this small experiment was precisely meant to showcase these
adaptability skills. Rust was, by far, the most difficult-to-learn, difficult-
to-research, counter-intuitive, unfriendly, constrained, unappealing, etc. of
all of them. Warnings and errors appeared systematically and, despite their
verbosity, were rarely helpful.

The most ironic part is that so many restrictions and problems are likely to
provoke people to rely on whatever option happens to work, which might not be
the best/safest one. Being so concerned about making sure that the generated
code is extremely safe no matter what by sacrificing flexibility and user
friendliness is far from ideal. Restrictions and prohibitions have always to
be seen as an in-the-worst-case-scenario resource, not as a primary solution;
much less when dealing with something as complex as programming, a very
powerful tool supposed to be managed by knowledgeable individuals. The higher
the freedom, the better the results delivered by a sensible/knowledgeable
person. Unless Rust changes a lot, I don't see it going anywhere. It might get
some support from theoretical/academical/inside-whatever-bubble circles, but
seriously doubt that developers with real-world experience can like or even
accept most of what this language represents.

~~~
nercury
You have successfuly uncovered that Rust is a fundamentally different
programming language, instead of a variation of the same. I feel that having
learned Rust, any new language similar to Rust would have way lower learning
curve. It is quite similar to the jump required from imperative to a funtional
language. We may be missing a good name for a paradigm the Rust falls into.

~~~
nickpsecurity
Achieving Rust's safety in C took separation logic with the best tool probably
being VCC from Microsoft. I encourage anyone that thinks the borrow checker is
unnecessarily hard or alternative languages would be easy to try doing an
existing Rust library or app in VCC with separation logic. Also, if
applicable, ensure it passes soubd, static analysis to be free of any defects
Rust's design would stop in that library that C doesnt.

Then you'll appreciate the work languages like Cyclone and then Rust save you.
There's other models published and in development that might make the next one
a bit easier. Yet, separation logic is the proper comparison to borrow checker
and most programmers didnt try to learn it.

