# Homework 5
- [Homework 5](#homework-5)
- [Programming Language Security](#programming-language-security)
  - [Setup](#setup)
  - [Part 0: Basics setup](#part-0-basics-setup)
  - [Part 1: Unsafe code practices](#part-1-unsafe-code-practices)
  - [Part 2: Securing unsafe code](#part-2-securing-unsafe-code)
  
# Programming Language Security

In this homework, you will learn about how bugs inherent to some programming languages are stopped by newer programming languages and how to transform basic code snippets with potentially unsafe behavior into their safer versions.
You will learn as well about unsafe code practices that are inherent to the C programming language design and the strategies to transform that code to Rust.
A lot of security defenses and research focuses on C, the most popular systems programming language.

In the journey of a security career, you will often find technologies you have not faced before. For this assignment, we do not expect you to have a previous understanding of the code you will use, but at the end of it, you should be able to understand WHY some practices in C language are unsafe and how modern programming languages like Rust do address problems from unsafe programming languages. It is **very important** to read the compiler messages and acquire knowledge about the language safety features. This is not a crash course on Rust, for that, you can check the [Rust book](https://doc.rust-lang.org/book/), or blogs like the one by [Google](https://security.googleblog.com/2023/09/scaling-rust-adoption-through-training.html).

A compiled language enables early detection of errors in programs, even before execution, with respect to the rules that define a valid program in that language. Apart from obvious syntactic errors (e.g., missing closing brackets or semicolons), programs that contain [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) will be characterized by this sort of error. For example, consider a conversion between pointers and integers:

``` c
int main() {
  int a = 0;
  void *b = a;
  return 0;
}
```

When compiling the program, a modern version of a C compiler (e.g., C will flag the error:

> error: incompatible integer to pointer conversion initializing 'void *' with an expression of type 'int' [-Wint-conversion])

This error can be masked with a compiler flag `-Wno-int-conversion`, with the developer explicitly informing the compiler that this type conversion is on purpose. The C programming language can also show warnings for certain unsafe code patterns. However, unlike errors, a compiler will continue with the compilation despite warnings. For the sake of security, larger projects often only allow code which does not raise compiler warnings, as unexpected warnings typically point to bugs.  An example of such a warning is the one from the next code snippet (from the Part 2, Exercise 1 of this homework):

> warning: array index 5 is past the end of the array (that has type 'uint8_t[5]' (aka 'unsigned char[5]')) [-Warray-bounds]

This homework points out some of the undefined behavior patterns in C, and how novel language security practices enable developers to get rid of them. C is not fully unsafe, as it already implements best practices like [static typing](https://en.wikipedia.org/wiki/Type_system#Static_type_checking). There are more advanced concepts in which C and Rust differ, like [threading](https://doc.rust-lang.org/book/ch16-01-threads.html) and [macros](https://doc.rust-lang.org/reference/macros.html). We won't cover all the similarities and differences between languages in this homework, but we encourage you to dive into these concepts if you want to learn more and develop your skills.

## Part 0: Basics setup

We introduce the basic concepts for using the platform to translate C code into Rust, and how both languages differ. For the sake of simplicity, use the dockerfile we provide to create the container for completing the exercise. Run the docker using the following command: `docker run -it -v <host_path>:/hw5 com402/hw5 /bin/bash`. `<host_path>` is the full path to the handout folder. This should directly drop you a shell inside the container. All the necessary dependencies are already installed for you inside the container.

The goals for this part are:

- Launch the container to translate and compile the provided code
- Understand the Rust features inside the translated program
- In Rust, reduce the capabilities of the generated code and understand the compiler messages

### How to translate

In [translation_example](./basics/translation_example) you will find two folders, one with [existing c code](./basics/translation_example/c/) that will be translated to Rust code, and another [with Rust code](./basics/translation_example/rust/) that we will evaluate.
As you may expect, the syntax of those languages is different, but the way to organize the code is similar, and you should be comfortable with it from working with any C-like language like Java or Javascript.

To translate a C code snippet to Rust we will use [c2rust](https://github.com/immunant/c2rust), a tool that produces Rust code with the same functionality as the C code. Despite the code having the same functionality, it doesn't mean it is safe, so you should handle all the `unsafe` and `extern "C"` declarations inside the Rust code and check the unsafe code practice.
We use [compiledb](https://github.com/nickdiego/compiledb) to produce the `compile_commands.json` file, which is used later by c2rust. Each exercise that we will introduce during the homework has a Makefile which target `transpile` will perform this task (overwriting the rust file), invoked within the docker container (in the shell popped by docker) in the next way:

```
cd basics/translation_example && make transpile
```

We provide you a template to write your code in the `solved.rs` file for each exercise (or `solved_X.rs` for `X.rs`), a program version that addresses the issues each exercise points to. Be careful and write your progress over the `solved` files and not the transpiled ones (`code.rs` or similar), because the latter are overwritten if you run `make transpile`.

### Dissecting the original code

On the [original file](./basics/translation_example/c/main.c) you find three functions:

- `foo` consists of an array (`arr`) and an accumulator (`acc`) that stores the sum of the array by iterating through the array.
- `bar` creates a variable (`a`) and passes its reference to the function (`baz`).
- `baz` increments the value of the variable it receives by one.

### Dissecting the translated code

On the [produced file](./basics/translation_example/rust/src/main.rs) you will find [lint statements for the compiler](https://doc.rust-lang.org/reference/attributes.html): `#![allow(dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, non_upper_case_globals, unused_assignments, unused_mut)]`. These statements are produced by default by `c2rust` and tell the compiler to not abort compilation for certain code patterns.

The functions, variable names, and overall functionality remain the same as the original C code.

### Applying the principle of least privilege

The lint annotations in the generated code prevent the generated code from raising any error when the Rust compiler processes them. Indeed, the compiler produces elaborate messages with the concerns of the code being compiled, and often, how to address them. We offer a [less verbose](./basics/translation_example/rust/src/main.rs) code version helped by type inference, note that for this version the variables do not need type annotations, but the function arguments do. Also note that all the named functions from C have a `#[no_mangle]` attribute, which we will further investigate in the next part.

The function, variable names, and overall functionality is the same as in the original C code. We provide a [minimized version](./basics/translation_example/rust/src/main_minimized.rs) with less verbosity on the types. The code doesn't have any IO functionality (e.g., print a message), it only performs a computation.

In this example the code does not print anything, so you should check the compiler messages by running `make rust`. For the next parts you may be able to run the binaries in order to check if the ouput is similar with `./main/c` and `./rust/target/debug/solved` (or the pair of programs specified for every exercise).

#### Mutability

In Rust, the variables are [immutable by default](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html), so a mutable variable must be annotated as such. Note that in `foo`, `arr` is never modified, but `acc` is. Therefore the `mut` qualifier in `arr` can be given up. The `#!allow(unused_mut)` lint flag tells the compiler not to raise a concern when having an unnecessary `mut` qualifier. In fact, if we remove that lint flag, we will see that `arr` will be told to never be modified with the message: `warning: variable does not need to be mutable`.

Can you spot any other variable in the code that should not have a `mut` qualifier?

#### Ownership

In Rust, all data has an owner. By tracking ownership of data objects as the code passes through various functions and blocks, Rust can enforce higher level properties on the data. One such property is that no data object that is dynamically allocated (for e.g., via `malloc`) can be accessed after the corresponding `free`, thereby preventing use-after-free attacks. The owner is represented by a code block (e.g., function), which can transfer the [ownership](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) to other code blocks or directly use a reference. While we will explore these concepts later, note that `baz` does not own the variable it receives as an argument, but `bar` does. There are fundamental limitations to ownership, specially when programs include code in other languages in addition to Rust. For example, Rust cannot judge/track ownership for objects through code included in the program that was written in other languages like C. Raw pointers are the default way that `c2rust` references memory (introduced in the next part), they [do not respect ownership](https://doc.rust-lang.org/std/primitive.pointer.html), but the other data types should do. We will later introduce how to address data ownership.

## Part 1: Unsafe code practices

There are code patterns that cannot be secured because of their [unsafe nature](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html).
Operating systems, for example, might utilize unsafe code to implement context switches which require direct access to CPU registers.
Such code often deals with architectural features and, by necessity, breaks Rust's language-level abstractions.
We will visit them in this part and know how to handle them. For these exercises, you do not have to solve any concrete problem but understand how to deal with them for the next part of this assignment in which you will find some of these patterns and we will ask you to remove all the unsafe code.

The goals for this part are:
- Go through the C code examples for each of the `c` folders in `unsafe_code_practices`, each exercise gives you a hint of what it addresses
- Think about how the unsafe features cause an issue if not handled correctly
- Search the online information for why Rust considers these features as unsafe and try to solve the exercise by yourself (in the `solved.rs` file) comparing your outcome with the existing `solution.rs` file.

### Exercise 1: External code

Calling into functions defined by external code is unsafe. We provide a simple program that calls the `printf` function from `libc`  [code example](./unsafe_code_practices/external_code/c/main.c). When transformed to [Rust code](./unsafe_code_practices/external_code/rust/src/main.rs) the program will still call the same function. All calls to C functions are unsafe. The external functions have to be declared as such with an `extern "C"` tag. In fact, `printf` indicates in its signature that it is a variadic function, so it has an indeterminate number of arguments (denoted by the `_: ...`), [variadic functions cannot be defined in Rust](https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions).

> Both `printf("Hello world");` and `printf("Hello %s", name);` are valid calls to `printf`. The first call has a single argument, whereas the second call has two. In general, since `printf` is a variadic function, it allows calls with an arbitrary number of arguments.

#### How to handle

Calling to an external unsafe library cannot be avoided without rewriting the library in Rust. There are ways to hide the unsafe C behavior through function proxies: these are functions written in Rust that have an unsafe block that calls to the external C code. Nevertheless, the unsafety of the called C code remains.

The template has [Rust code using function proxies](./unsafe_code_practices/external_code/rust/src/solved.rs). Concretely, `printf_proxy` is a Rust function that has an internal unsafe block that calls to `printf`. This code pattern isolates this unsafe behavior to a concrete function.

We can appreciate as well the produced code calls to `::std::process::exit`, which is a [double function proxy](https://github.com/rust-lang/rust/blob/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/process.rs#L2141-L2143) to [`libc`'s exit function](https://github.com/rust-lang/rust/blob/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/sys/unix/os.rs#L700-L702). The function returns the [never type (`!`)](https://doc.rust-lang.org/reference/types/never.html), but this advanced behavior is out of the scope of this homework. There are more builtin proxies in Rust standard libraries that substitute the calls to `libc` functions, for example, printing can be done through the [`println` macro](https://doc.rust-lang.org/std/macro.println.html).

The additional `as_ptr()` called method is used to match the types and directly relates to the next exercise: Raw Pointers.

In each directory, to run `solved.rs`, run:

```bash
make run_solved
```

### Exercise 2: Raw pointers

In C, a pointer can have arbitrary values, meaning that it can point to any location in memory. In Rust, there are [raw pointers and references](https://doc.rust-lang.org/reference/types/pointer.html). The former has a similar use as in C and is always considered unsafe. The latter is a safe way to indirectly reference to data in memory [from the Rust book](https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html):

> A reference is like a pointer in that it’s an address we can follow to access the data stored at that address; that data is owned by some other variable. Unlike a pointer, a reference is guaranteed to point to a valid value of a particular type for the life of that reference.

As you may have noticed from the previous reference, in Rust these pointers will have different types, and easily distinguishable: `*T` for the raw pointers, and `&T` for the references.
The code snippet provided ([raw_pointers](./unsafe_code_practices/raw_pointers/c/main.c)) passes a pointer between functions, which can either be accessed by the function or even modified.

#### How to handle

The most obvious and straightforward way to deal with the lack of safety with raw pointers is to use references instead, as they are the safe counterpoint. Doing so requires to define the ownership of the variables when they are defined (which we will look in depth for the next part). In the [solution we provide](./unsafe_code_practices/raw_pointers/rust/src/solution.rs), we only change the types of the arguments from being raw pointers (`*mut libc::c_int`) to references (`&mut libc::c_int`), by doing so, the dereference of the element is not considered unsafe anymore.

### Exercise 3: Union types

C programming language can define [union types](https://en.wikipedia.org/wiki/Union_type#C/C++), these types hold only one of the possible elements that the union type defines, and which type can vary. By definition, Rust considers the use of union types unsafe. Rust, therefore, offers (enumerations)[https://doc.rust-lang.org/book/ch06-00-enums.html] as an alternative, that are tagged elements. The code snippet provided, ([union_types](./unsafe_code_practices/union_types/c/main.c)), defines an union type that is later referenced within the code. 

#### How to handle

We show the alternative to the [generated code](./unsafe_code_practices/union_types/rust/src/main.rs) using [enumerations](./unsafe_code_practices/union_types/rust/src/solution.rs).
As you can appreciate, the program's enumeration version will use an enumeration field only if the enumeration contains that field, otherwise, it cannot be referenced.

### Exercise 4: Global variables

Global variables in Rust can be either immutable (constants) or mutable. The second kind is [always considered unsafe](https://doc.rust-lang.org/rust-by-example/custom_types/constants.html). We provide a [C code snippet](./unsafe_code_practices/global_variables/c/main.c) that uses global variables and their translation to [Rust code](./unsafe_code_practices/global_variables/rust/src/main.rs).

#### How to handle

As you can observe, `global_string` is declared as a static variable, but as it is never modified, it can be declared as a constant. For mutable global variables, a developer should figure out the data flow, which parts of the code access the data, and accordingly define it in another region (e.g., the heap).

### Exercise 5: Name mangling

Writing code in different source files is a de facto standard in modern coding practices. To reference functions written in a different source file, C relies on a [linker program](https://en.wikipedia.org/wiki/Linker_(computing)) that combines their code and handles function name conflicts like in the next one.

``` sh
$ ld test1.o test2.o -o combined.o
ld: test2.o: in function `foo':
test2.c:(.text+0x0): multiple definition of `foo'; test1.o:test1.c:(.text+0x0): first defined here
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
```

In other programming languages producing binaries (e.g., C++ and Rust), the exported function names are encoded to be unique through name mangling. When translating code from C to Rust, the produced functions will have the same name, therefore two functions with the same name can conflict when being used by the sime file. To not mangle the name, each function has to include the `#[no_mangle]` attribute. We provide a [Rust binary that uses two libraries](./unsafe_code_practices/name_mangling/merge/main.rs), depending on whether the library mangles the names or not. Without mangling, compilation can result in a undetectable name conflict.

#### How to handle

If Rust code is to not be called by external code (e.g., C code), then it can (and should) have name mangling. If there is a Rust function that should be called by C, the most straightforward manner to call to it is by disabling the name mangling, but it will come with the unsafety inconvenience described above. Similarly, global variables (static) names can be mangled, and they can receive the `#[no_mangle]` attribute. By default, [Rust prohibits constants to have this attribute](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#no-mangle-const-items), but the security impact is practically the same as with static variables.

Both `rust_test_mangle1` and `rust_test_mangle2` have a `foo` function in their respective library.
Their mangled names are `_RNvCsevfOVBIAGQy_5test13foo` and `_RNvCsevfOVBIAGQy_5test13foo` respectively.
For the function `foo` in the file `test1.rs`, its non-mangled name is `foo`, and the mangled name is `_RNvCs6FajfjIiOWV_5test13foo` (you can check it by running `nm target/debug/libtest1.rlib`). The mangled name indicates the module the function belongs to, and of course, the function name. If we demangle it (with a tool like [c++filt](https://sourceware.org/binutils/docs/binutils/c_002b_002bfilt.html)) we find the function has the name `test1[4d9e91a51264191f]::foo`.

Can you spot why the hash is included in the mangled name?

The example is partially based on [GitHub the issue describing the unsafety `no_mangle`](https://github.com/rust-lang/rust/issues/28179). If you wish to know more about name mangling, check the respective [RFC](https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html).

## Part 2: Securing unsafe code - Hard

In this set of exercises, pay attention to the compiler output (warnings and errors) when compiling a program. In these examples, you should write the `solved` files. Rust unsafe code is **NOT**  allowed on these files, and for the purpose of forbidding it, we will use a [configuration file](./securing_unsafe_code/cargo/config) that aborts compilation in the case an unsafe feature is used (like the ones shown in the previous part). In this set of exercises you shouldn't use raw pointers, but references instead (otherwise accessing an element through a pointer has to always be unsafe). The generated code by the transpiler will have this features, but as it is not compiled it does not affect your solution. For this set of exercises we provide the templates in the `solved` files, but not the `solution` files.

### Exercise 1: Stack Spatial Safety

We provide a two C code snippets with two type of stack violations:
- [fixed.c](securing_unsafe_code/stack_spatial_safety/c/fixed.c) has a function `foo` that indexes with a fixed index out-of bounds the array `buf1`.
- [variable.c](securing_unsafe_code/stack_spatial_safety/c/variable.c) has a function `foo` that calls to `set_buffer_one` and `zero_buffer`, the second function performs a buffer overflow.

While we will look in two weeks how to exploit these issues to gain control over a program, now we will study how Rust detects and protects against them at compile-time and runtime. A variable in Rust is stored by default in the stack unless told otherwise (e.g., by using the `Box` type). Because all the arrays have a size, the compiler can check whether accesses with immutable indexes will fall out of bounds. Furthermore, at runtime, arrays include an index check with the array length, so the program will panic and abort execution.

To run `solved_fixed.rs`, run:

```bash
make run_solved_fixed
```

To run `solved_variable.rs`, run:

```bash
make run_solved_variable
```

### Exercise 2: Stack Temporal Safety

Returning a function's local variable should return their data, not a reference to it. In this example you have a function `foo` which returns a reference to `buf`, that is data created within the function that loses its validity when the function exits. If you translate this code to Rust and use references instead of pointers you will get the next error message:

> error[E0515]: cannot return reference to local variable `buf`

And the lint will include the next message: `returns a reference to data owned by the current function`. As you may have noticed, the ownership concept we have been referencing in the rest of the homework enters in play. A hint to how to write valid code without any safety issues is included at the beginning of this exercise.

To run `solved.rs`, run:

```bash
make run_solved
```

### Exercise 3: Heap Temporal Safety

There may be no code blocks that hold ownership over data because the data is allocated in the heap. As introduced before, in Rust there are ways to reference data in the heap, like with a `Box` allocator. Rust offers [allocation functions](https://doc.rust-lang.org/alloc/alloc/fn.alloc.html) but they are unsafe, so the developer should use a helper. In the case of the helpers like `Box` or `Rc`, a premature deallocation of a reference is not possible, as they are reference counted and cannot be manually deallocated.

To run `solved.rs`, run:

```bash
make run_solved
```

### Exercise 4: Heap Spatial Safety

This exercise is virtually the same as the Exercise 1, but the allocations are in the stack. Use the knowledge from the previous parts to write valid code. If the functionality is preserved, both programs should panic at runtime.

To run `solved_fixed.rs`, run:

```bash
make run_solved_fixed
```

To run `solved_variable.rs`, run:

```bash
make run_solved_variable
```

### Exercise 5: Type Safety

Several concepts are attributed to the term of [type safety](https://en.wikipedia.org/wiki/Type_safety) such as:

- The value of an element with a type must have only the value that type allows
- Transitioning between elements of different types is a well defined operation

In Rust, an expression that returns a result has to respect the type of the variable is assigned to. While this rule still allows the existence of `NULL` pointers (considered the [billion dollar mistake](https://en.wikipedia.org/wiki/Null_pointer)), the use of pointers is considered unsafe, and references cannot hold `NULL` value (as that would not be a valid reference to an element of any type). Explicit type conversion is needed to transition between elements of arbitrary types through the [`as` operator](https://doc.rust-lang.org/std/keyword.as.html).

We provide a [C code snippet](securing_unsafe_code/type_safety/c/code.c) that does type conversion in `foo` function, and creates a `NULL` pointer in  `bar` function. While the latter doesn't raise any flag and is a matter of the developer to have the right value assigned, the former produces the next warning but produces code (which can be turned in an error and abort compilation):

> warning: incompatible pointer types initializing 'int *' with an expression of type 'char *' [-Wincompatible-pointer-types]

In the [C code snippet](securing_unsafe_code/type_safety/c/code.c) an element of the type `uint32_t` is casted to an element of the type `uint8_t`. In Rust, doing a cast directly will result in an error saying `error[E0308]: mismatched types`, so for converting an element from one type into another, the `as` operator (which you may have observed in all the code produced by `c2rust`) is needed.

For reproducing the functionality of `NULL` pointers, Rust offers the `Option` type in it's standard library, so the `NULL` pointers are the `None` elements of the type, and the legal values are expressed as `Some(value)`.

In this exercise we provided a simple type conversion. We leave to you as an exercise to write a function that returns data of the type `&[u8]` by receiving an element of the type `&[u16]`, or if it is even possible without executing unsafe code.

To run `solved.rs`, run:

```bash
make run_solved
```
