@trusted assumptions about @safe code
ag0aep6g
anonymous at example.com
Wed May 27 06:36:14 UTC 2020
On 27.05.20 02:50, Paul Backus wrote:
> On Tuesday, 26 May 2020 at 22:52:09 UTC, ag0aep6g wrote:
>> But yeah, the @trusted function might rely on the @safe function
>> returning 42. And when it suddenly returns 43, all hell breaks loose.
>> There doesn't need to be any monkey business with unsafe aliasing or
>> such. Just an @safe function returning an unexpected value.
>>
>> I suppose the only ways to catch that kind of thing would be to forbid
>> calling @safe (and other @trusted?) functions from @trusted (and
>> @system?) code, or to mandate that the exact behavior of @safe
>> functions (including their return values) cannot be relied upon for
>> safety. Those would be really, really tough sells.
>
> All that's necessary is to have the @trusted function check that the
> assumption it's relying on is actually true:
>
> @safe int foo() { ... }
>
> @trusted void bar() {
> int fooResult = foo();
> assert(fooResult == 42);
> // proceed accordingly
> }
>
> If the assumption is violated, the program will crash at runtime rather
> than potentially corrupt memory.
I.e., "the exact behavior of @safe functions (including their return
values) cannot be relied upon for safety". I think it's going to be hard
selling that to users. Especially, because there is no such requirement
when calling @system functions.
Say you have this code:
void f() @trusted
{
import core.stdc.string: strlen;
import std.stdio: writeln;
char[5] buf = "foo\0\0";
char last_char = buf.ptr[strlen(buf.ptr) - 1];
writeln(last_char);
}
That's ok, right? `f` doesn't have to verify that `strlen` returns a
value that is in bounds. It's allowed to assume that `strlen` counts
until the first null byte.
Now you realize that you can calculate the length of the string more
safely than C's strlen does, so you change the code to:
size_t my_strlen(ref char[5] buf) @safe
{
foreach (i; 0 .. buf.length) if (buf[i] == '\0') return i;
return buf.length;
}
void f() @trusted
{
import std.stdio: writeln;
char[5] buf = "foo\0\0";
char last_char = buf.ptr[my_strlen(buf) - 1];
writeln(last_char);
}
Nice. Now you're safe even if you forget to put a null-terminator into
the buffer.
But oh no, `my_strlen` is @safe. That means `f` cannot assume that the
returned value is in bounds. It now has to verify that. Somehow, it's
harder to call the @safe function correctly than the @system one.
What user is going to remember those subtleties?
More information about the Digitalmars-d
mailing list