1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2024-12-15 14:27:50 +02:00

Revert minimum wrong example (#1483)

Reverted as a result of discussion in #1475 

@mgeisler
This commit is contained in:
Enes Aydın 2023-11-19 13:40:57 +03:00 committed by GitHub
parent 6624c5874f
commit 34f0cfb770
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 0 additions and 99 deletions

View File

@ -21,7 +21,6 @@
- [Hello World!](hello-world.md)
- [Small Example](hello-world/small-example.md)
- [Why Rust?](why-rust.md)
- [An Example in C](why-rust/an-example-in-c.md)
- [Compile Time Guarantees](why-rust/compile-time.md)
- [Runtime Guarantees](why-rust/runtime.md)
- [Modern Features](why-rust/modern.md)

View File

@ -28,11 +28,3 @@ terms.
The [Interoperability with C++](android/interoperability/cpp.md) section uses an
image from [CXX](https://cxx.rs/). Please see the `third_party/cxx/` directory
for details, including the license terms.
## An Example in C
The [Why Rust? - An Example in C](why-rust/an-example-in-c.md) section has been
taken from the presentation slides of [Colin Finck's Master
Thesis](https://colinfinck.de/Master_Thesis_Colin_Finck.pdf). It has been
relicensed under the terms of the Apache 2.0 license for this course by the
author.

View File

@ -1,90 +0,0 @@
# An Example in C
Let's consider the following "minimum wrong example" program in C:
```c,editable
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(int argc, char* argv[]) {
char *buf, *filename;
FILE *fp;
size_t bytes, len;
struct stat st;
switch (argc) {
case 1:
printf("Too few arguments!\n");
return 1;
case 2:
filename = argv[argc];
stat(filename, &st);
len = st.st_size;
buf = (char*)malloc(len);
if (!buf)
printf("malloc failed!\n", len);
return 1;
fp = fopen(filename, "rb");
bytes = fread(buf, 1, len, fp);
if (bytes = st.st_size)
printf("%s", buf);
else
printf("fread failed!\n");
case 3:
printf("Too many arguments!\n");
return 1;
}
return 0;
}
```
How many bugs do you spot?
<details>
Despite just 29 lines of code, this C example contains serious bugs in at least 11:
1. Assignment `=` instead of equality comparison `==` (line 28)
2. Excess argument to `printf` (line 23)
3. File descriptor leak (after line 26)
4. Forgotten braces in multi-line `if` (line 22)
5. Forgotten `break` in a `switch` statement (line 32)
6. Forgotten NUL-termination of the `buf` string, leading to a buffer overflow (line 29)
7. Memory leak by not freeing the `malloc`-allocated buffer (line 21)
8. Out-of-bounds access (line 17)
9. Unchecked cases in the `switch` statement (line 11)
10. Unchecked return values of `stat` and `fopen` (lines 18 and 26)
_Shouldn't these bugs be obvious even for a C compiler?_
No, surprisingly this code compiles warning-free at the default warning level, even in the latest GCC version (13.2 as of writing).
_Isn't this a highly unrealistic example?_
Absolutely not, these kind of bugs have lead to serious security vulnerabilities in the past. Some examples:
* Assignment `=` instead of equality comparison `==`: [The Linux Backdoor Attempt of 2003](https://freedom-to-tinker.com/2013/10/09/the-linux-backdoor-attempt-of-2003)
* Forgotten braces in multi-line `if`: [The Apple goto fail vulnerability](https://dwheeler.com/essays/apple-goto-fail.html)
* Forgotten `break` in a `switch` statement: [The break that broke sudo](https://www.lufsec.com/anatomy-of-a-security-hole-the-break-that-broke-sudo/)
_How is Rust any better here?_
Safe Rust makes all of these bugs impossible:
1. Assignments inside an `if` clause are not supported.
2. Format strings are checked at compile-time.
3. Resources are freed at the end of scope via the `Drop` trait.
4. All `if` clauses require braces.
5. `match` (as the Rust equivalent to `switch`) does not fall-through, hence you can't accidentally forget a `break`.
6. Buffer slices carry their size and don't rely on a NUL terminator.
7. Heap-allocated memory is freed via the `Drop` trait when the corresponding `Box` leaves the scope.
8. Out-of-bounds accesses cause a panic or can be checked via the `get` method of a slice.
9. `match` mandates that all cases are handled.
10. Fallible Rust functions return `Result` values that need to be unwrapped and thereby checked for success.
Additionally, the compiler emits a warning if you miss to check the return value of a function marked with `#[must_use]`.
</details>