Unsafe Operations
The core language semantics ensure that no low-level memory safety issues can occur in a program. In particular, the following issues are prevented by the core language semantics:
- buffer overflows
- null pointer accesses
- use-after-free
- invalid memory address access
- low-level race conditions
In some cases, the core semantics may be too restrictive, or it may benecessary
to call into code that is implemented in a foreign language that doesn't
offer the same safety guarantees. In these situations, particular functions
or statements can be annotated with @unsafe in order to relax the language
semantics locally.
The following operations are possible inside code marked with @usafe:
- calling
@unsafefunctions - pointer arithmetic
- casting between pointer types
- casting between arbitrarily attributed types
Attributing Foreign Language Code
When declaring external functions, such as those exported by a C library, it
is recommended to put an @trusted annotation on functions that have a
memory-safe interface. Such functions will then be treated as safe, whereas
by default, a function declared like this will be implicitly @unsafe.
A memory-safe function interface must comply with the following rules:
- references passed into the function must not be escaped
- the function does not perform pointer arithmetic before accessing referenced memory
- no C-style variadic function arguments
- the function must be fully thread-safe
- returned references must have an infinite lifetime
These are some examples from the C standard library:
printfmust be left@unsafe, because it uses C-style variadic function arguments and internal pointer arithmeticstrlenmust be left@unsafe, because it uses internal pointer arithmeticrandcan be marked as@trustedmalloccan be marked as@trustedfreemust be left@usafe
For functions that may not be marked as @safe due to internal pointer
arithmetic, it is recommended to use a small wrapper function that has
safe semantics where possible:
extern(C) function memcpy(dest: void*, src: const(void)*, count: size_t);
function safe_memcpy(dst: ubyte[], src: const(ubyte)[])
{
assert (dst.length == src.length);
@unsafe memcpy(dst.ptr, src.ptr, dst.length);
}