A new Linux vulnerability known as 'Looney Tunables' enables local attackers to gain root privileges by exploiting a buffer overflow weakness in the GNU C Library's ld.so dynamic loader.
C has no memory protection. If you access to the 10th element of a 5 element array, you get to access whatever is in memory there, even if it has nothing to do with that array. Furthermore this doesn’t just allow access to data you shouldn’t be able to access, but also the execution of arbitrary code, as memory doesn’t make a (big) difference between data and code.
C++ provides a few classes to make it easier to avoid those issues, but still allows all of them.
Ruby/Python/Java/… provide memory safety and will throw an exception, but they manually check it at runtime, which makes them slow.
Rust on the other side tries to proof as much as it can at compile time. This makes it fast, but also requires some relearning, as it doesn’t allow pointers without clearly defined ownership (e.g. the classic case of keeping a pointer to the parent element in a tree structure isn’t allowed in Rust).
Adding the safeties of Rust into C would be impossible, as C allows far to much freedom to reliably figure out if a given piece of code is safe (halting problem and all that). Rust purposefully throws that freedom away to make safe code possible.
The short answer is Rust was built with safety in mind. The longer answer is C was built mostly to abstract from assembly without much thought to safety. In C, if you want to use an array, you must manually request a chunk of memory, check to make sure you are writing within the bounds of your array, and free up the memory used by your array when completely done using it. If you do not do those steps correctly, you could write to a null pointer, cause a buffer overflow error, a use-after-free error, or memory leak depending on what step was forgotten or done out of order. In Rust, the compiler keeps track of when variables are used through a borrowing system. With this borrowing system the Rust compiler requests and frees memory safely. It also checks array bounds at run-time without a programmer explicitly needing to code it in. Several high-level languages have alot of these safety features too. C# for example, can make sure objects are not freed until they fall out of scope, but it does this at run-time with a garbage collector where Rust borrower rules are done at compile-time.
That’s actually not true; rather, many modern architectures are designed to allow languages like C to be compiled more easily. Old architectures don’t even have a built-in stack.
The compiler enforces “aliasing XOR mutability”; utilizes “move semantics”; has a “borrowing and ownership” model; and requires the programmer to tag their references with “lifetimes”. Array accesses are checked at runtime if they cannot be guaranteed safe at compile-time. Variables passed by value (moved) cannot be reused. Variables cannot be moved or mutated if any borrow to them exists. You may either have only one mutable borrow, or many immutable borrows, but never both. Therefore you cannot mutate an array while iterating on it, and you cannot have two separate unchecked references to the same array. Every function or type that accepts a borrow must be able to annotate the lifetimes of references to ensure that references are always dropped in the correct order to prevent dangling references. Rust requires developing software with discipline using patterns that satisfy all of these constraints.
What makes rust so resiliant against these types of atacks?
C has no memory protection. If you access to the 10th element of a 5 element array, you get to access whatever is in memory there, even if it has nothing to do with that array. Furthermore this doesn’t just allow access to data you shouldn’t be able to access, but also the execution of arbitrary code, as memory doesn’t make a (big) difference between data and code.
C++ provides a few classes to make it easier to avoid those issues, but still allows all of them.
Ruby/Python/Java/… provide memory safety and will throw an exception, but they manually check it at runtime, which makes them slow.
Rust on the other side tries to proof as much as it can at compile time. This makes it fast, but also requires some relearning, as it doesn’t allow pointers without clearly defined ownership (e.g. the classic case of keeping a pointer to the parent element in a tree structure isn’t allowed in Rust).
Adding the safeties of Rust into C would be impossible, as C allows far to much freedom to reliably figure out if a given piece of code is safe (halting problem and all that). Rust purposefully throws that freedom away to make safe code possible.
The short answer is Rust was built with safety in mind. The longer answer is C was built mostly to abstract from assembly without much thought to safety. In C, if you want to use an array, you must manually request a chunk of memory, check to make sure you are writing within the bounds of your array, and free up the memory used by your array when completely done using it. If you do not do those steps correctly, you could write to a null pointer, cause a buffer overflow error, a use-after-free error, or memory leak depending on what step was forgotten or done out of order. In Rust, the compiler keeps track of when variables are used through a borrowing system. With this borrowing system the Rust compiler requests and frees memory safely. It also checks array bounds at run-time without a programmer explicitly needing to code it in. Several high-level languages have alot of these safety features too. C# for example, can make sure objects are not freed until they fall out of scope, but it does this at run-time with a garbage collector where Rust borrower rules are done at compile-time.
That’s actually not true; rather, many modern architectures are designed to allow languages like C to be compiled more easily. Old architectures don’t even have a built-in stack.
The compiler enforces “aliasing XOR mutability”; utilizes “move semantics”; has a “borrowing and ownership” model; and requires the programmer to tag their references with “lifetimes”. Array accesses are checked at runtime if they cannot be guaranteed safe at compile-time. Variables passed by value (moved) cannot be reused. Variables cannot be moved or mutated if any borrow to them exists. You may either have only one mutable borrow, or many immutable borrows, but never both. Therefore you cannot mutate an array while iterating on it, and you cannot have two separate unchecked references to the same array. Every function or type that accepts a borrow must be able to annotate the lifetimes of references to ensure that references are always dropped in the correct order to prevent dangling references. Rust requires developing software with discipline using patterns that satisfy all of these constraints.