aboutsummaryrefslogtreecommitdiff
path: root/safety.md
blob: 29188065c5c352539c497bb677628b6acc266265 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
---
title: Memory Safety
---

Each item in Wikipedia's [list of types of memory errors](https://en.wikipedia.org/wiki/Memory_safety#Types_of_memory_errors) and what Crowbar does to prevent them.

In general, Crowbar does its best to ensure that code will not exhibit any of the following memory errors.
However, sometimes the compiler knows less than the programmer, and so code that looks dangerous is actually fine.
Crowbar allows programmers to suspend the memory safety checks with the `fragile` keyword.

# Access errors

## Buffer overflow

Crowbar addresses buffer overflow with bounds checking.
In C, the type `char *` can point to a single character, a null-terminated string of unknown length, a buffer of fixed size, or nothing at all.
In Crowbar, the type `char *` can only point to either a single character or nothing at all.
If a buffer is declared as `char[50] name;` then it has type `char[50]`, and can be implicitly converted to `(char[50])*`, a pointer-to-50-chars.
If memory is dynamically allocated, it works as follows:

```crowbar
void process(size_t bufferSize, char[bufferSize] buffer) {
    // do some work with buffer, given that we know its size
}

int main(int argc, (char[1024?])[argc] argv) {
    size_t bufferSize = getBufferSize();
    (char[bufferSize])* buffer = malloc(bufferSize);
    process(bufferSize, buffer);
    free(buffer);
}
```

Note that `malloc` as part of the Crowbar standard library has signature `(void[size])* malloc(size_t size);` and so no cast is needed above.
In C, `buffer` in `main` would have type pointer-to-VLA-of-char, but `buffer` in `process` would have type VLA-of-char, and this conversion would emit a compiler warning.
However, in Crowbar, a `(T[N])*` is always implicitly convertible to `T[N]`, so no warning exists.
(This is translated into C by dereferencing `buffer` in `main`.)

Note as well that the type of `argv` is complicated.
This is because the elements of `argv` have unconstrained size.
TODO figure out if that's the right way to handle that

## Buffer over-read

bounds checking again

## Race condition

uhhhhh 🤷‍♀️

## Page fault

bounds checking, dubious-pointer checking

## Use after free

`free(x);` not followed by `x = NULL;` is a compiler error.
`owned` and `borrowed` keywords 

# Uninitialized variables

forbid them in syntax

## Null pointer dereference

dubious-pointer checking

## Wild pointers

dubious-pointer checking

# Memory leak

## Stack exhaustion

uhhhhhh 🤷‍♀️

## Heap exhaustion

that counts as error handling, just the `malloc`-shaped kind

## Double free

this is just use-after-free but the use is calling free on it

## Invalid free

don't do that

## Mismatched free

how does that even happen

## Unwanted aliasing

uhhh don't do that?