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?
|