~boringcactus/crowbar-spec

ref: ea5e1204a493ccfa400c87134cba6262137ccbe2 crowbar-spec/safety.rst -rw-r--r-- 2.9 KiB
ea5e1204 — Melody Horn half-assedly define structs 1 year, 7 months ago
                                                                                
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
*************
Memory Safety
*************

In general, Crowbar does its best to ensure that code will not exhibit any of the following memory errors (pulled from `Wikipedia's list of 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.

.. _Wikipedia's list of memory errors: https://en.wikipedia.org/wiki/Memory_safety#Types_of_memory_errors

Access errors
=============

Buffer overflow
---------------

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

    void process(uintsize bufferSize, uint8[bufferSize] buffer) {
        // do some work with buffer, given that we know its size
    }

    int8 main(uintsize argc, (uint8[1024?])[argc] argv) {
        uintsize bufferSize = getBufferSize();
        (uint8[bufferSize])* buffer = malloc(bufferSize);
        process(bufferSize, buffer);
        free(buffer);
    }

Note that ``malloc`` as part of the Crowbar standard library has signature ``(void[size])* malloc(uintsize 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.

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 idk

Page fault
----------

bounds checking, dubious-pointer checking

Use after free
--------------

``free(&x);`` will set ``x = NULL;``
``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 idk

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?