May 31, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #11 from Manu <turkeyman@gmail.com> ---
> In fact, the whole project becomes @trusted because of __traits(getMember). Shall we make that not @safe too?

Maybe.
I think what you're demonstrating here is that this should work:

void func() @safe
{
  // safecode...

  ...

  @trusted {
    trusted code
  }

  ...

  // safeCode...
}

That way it's easy to contain the one line of trusted code in a surrounding context without over-elevating @trusted.

--
May 31, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #12 from Dennis <dkorpel@live.nl> ---
(In reply to Manu from comment #10)
> int x = void;
> array[x]; // boom

In @safe code that either accesses the array within bounds or gives a run-time range violation. No memory corruption there.

> Accessing uninitialised int's (as above) is possibly the most accessible form ob > buffer overflow I can imagine.

It's not buffer overflow. It can only _lead_ to buffer overflow in @system or poorly written @trusted code. In @safe code it's merely a logic bug.

If we're going to prevent any language aspect that commonly causes bugs, then @safe should also disallow classic for-loops, unsigned numbers and null-pointers.

The goals and meaning of @safe are currently clear. Let's not change this by subjectively disabling other things that only 'feel' unsafe but really aren't with respect to memory corruption.

--
May 31, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

ag0aep6g <ag0aep6g@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ag0aep6g@gmail.com

--- Comment #13 from ag0aep6g <ag0aep6g@gmail.com> ---
(In reply to Manu from comment #10)
> (In reply to Dennis from comment #7)
> > (In reply to Manu from comment #6)
> > > Accessing uninitialised memory is absolutely a memory safety issue.
> > 
> > Not per se. This compiles, prints a random number, and doesn't corrupt memory.
> > 
> > ```
> > import std;
> > 
> > void main() @safe
> > {
> >     int a = void;
> >     writeln(a);
> > }
> > ```
> 
> Hah, well that's obviously broken too!

For reference, that's issue 18016. Walter has an open PR to resolve it by making the value of `a` "implementation defined".

--
June 01, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #14 from Manu <turkeyman@gmail.com> ---
> If we're going to prevent any language aspect that commonly causes bugs, then @safe should also disallow classic for-loops, unsigned numbers and null-pointers.

You're seriously going to suggest that allowing functional access to uninitialised memory is comparable to a for-loop... with a straight face?


> The goals and meaning of @safe are currently clear. Let's not change this by subjectively disabling other things that only 'feel' unsafe but really aren't with respect to memory corruption.

How can you argue that feeding uninitialised memory into ANY transformation
pipeline is @safe? Or only 'subjectively' broken?
Accessing uninitialised memory doesn't 'feel' unsafe, it's unsafe. No valid
result can appear from any process where the input is fed uninitialised data,
and it's the first and most obvious place any sane security engineer will look
for attack surface.

Anyway, we're done here.

--
June 01, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #15 from Dennis <dkorpel@live.nl> ---
(In reply to Manu from comment #14)
> You're seriously going to suggest that allowing functional access to uninitialised memory is comparable to a for-loop... with a straight face?

Note that we are talking about union member access here, i.e. potential access to garbage memory. Not guaranteed access to garbage memory. The constructs I mentioned are all possible to use right, but common sources of bugs in C.

for (size_t i=length; i >= 0; i--) //endless loop because of unsigned underflow
for (int i=0; i < length; i++) // endless loop if > 2GB array
for (p = str; p; p++) // read past null-byte until segfault
int *a = tryFindPointer(); *a = 3; // possible null-dereference
struct Obj obj; useObj(&obj); // uninitialized object

While these bugs are also possible in D, in @safe code they won't result in memory corruption, only in a segfault or range violation.

> How can you argue that feeding uninitialised memory into ANY transformation pipeline is @safe?

@safe does not mean no bugs!
@safe is not the same as the English word 'safe'!
```
void main() @safe {
    int* a;
    *a = 3;
}
```
This is a bug.
Arguably it should give an error
It may not be 'safe', but it is '@safe' with its current definition.

Please mentally replace the term @safe with something like 'insufficient to
cause memory corruption'.
As mentioned by ag0aep6g, an uninitialized array can allow you to pass the
guard page that prevents stack overflow. Therefor, using uninitialized arrays
on the stack is sufficient to cause memory corruption and should be fixed for
@safe code. Can you show that union member access is sufficient for memory
corruption?

Alternatively, can you give a new, better definition of @safe that includes union member access and excludes the earlier mentioned risky language constructs?

> it's the first and most obvious place any sane security engineer will look for attack surface.

In C, definitely. He will also look for suspicious for-loops and non-trivial
bounds checks. Here's an example of one:
CUTE_PNG_CHECK(s->out - backwards_distance >= s->begin, "Attempted to write
before out buffer (invalid backwards distance).");
https://github.com/RandyGaul/cute_headers/blob/master/cute_png.h#L499

--
June 05, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #16 from Simen Kjaeraas <simen.kjaras@gmail.com> ---
There's code that simply cannot be @trusted under Dennis' @safe regime that would be perfectly safe without void initialization and union member access. For instance, there's C libraries that return opaque handles that may or may not be actual pointers, and that offer no way to determine if such a handle is valid or not, and may summon nasal demons if invalid handles are used.

Also, unions are really a low-level trick that is very rarely used. Disallowing their use in @safe code will impact very little code.

--
June 05, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #17 from Dennis <dkorpel@live.nl> ---
(In reply to Simen Kjaeraas from comment #16)
> There's code that simply cannot be @trusted under Dennis' @safe regime that would be perfectly safe without void initialization and union member access.

If you don't agree with the 'insufficient to corrupt memory' regime, please propose a new one! The spec is pretty informal currently. If you have a better interpretation, I'm eager to hear it. So far I've only heard 'things any sane security engineer is wary of'.

> For instance, there's C libraries that return opaque handles that may or may not be actual pointers, and that offer no way to determine if such a handle is valid or not, and may summon nasal demons if invalid handles are used.

Then store that handle as a void* or make it absolutely inaccessible from the outside. Disabling @safe union access of non-pointers is neither necessary nor sufficient to prevent invalid handles to be passed to your C library. The constraint 'I only want integer members in my struct with @trusted memory sensitive data' is completely arbitrary and only exists in an attempt to validate this issue.

> Also, unions are really a low-level trick that is very rarely used.

How do you know? I use them in @safe code. GFM (which is downloaded 31739 times at the time of writing) uses them for their vectors [1] among other things. Any formerly safe game using GFM vectors needlessly breaks! And the quick fix will be to slap @trusted labels in every function where vector members are accessed. How is that for memory safety?

[1] https://github.com/d-gamedev-team/gfm/blob/5b86c52582de04b8b2277a3b257507083ed31cbf/math/gfm/math/vector.d#L26

--
June 05, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #18 from Manu <turkeyman@gmail.com> ---
(In reply to Dennis from comment #17)
> (In reply to Simen Kjaeraas from comment #16)
> > There's code that simply cannot be @trusted under Dennis' @safe regime that would be perfectly safe without void initialization and union member access.
> 
> If you don't agree with the 'insufficient to corrupt memory' regime, please propose a new one!

Uninitialised memory is ALREADY CORRUPTED. There is no world where access to what can be confirmed to be uninitialised memory is @safe, period.

Any interaction with uninitialised memory will guarantee unintended consequences, which could be anything.

It's very simple; if you're going to use unions, or uninitialised memory, then do so carefully, and @trusted that block of code.


> Then store that handle as a void* or make it absolutely inaccessible from the outside. Disabling @safe union access of non-pointers is neither necessary nor sufficient to prevent invalid handles to be passed to your C library. The constraint 'I only want integer members in my struct with @trusted memory sensitive data' is completely arbitrary and only exists in an attempt to validate this issue.

I don't understand any sentence in this paragraph.


> > Also, unions are really a low-level trick that is very rarely used.
> 
> How do you know? I use them in @safe code. GFM (which is downloaded 31739 times at the time of writing) uses them for their vectors [1] among other things. Any formerly safe game using GFM vectors needlessly breaks! And the quick fix will be to slap @trusted labels in every function where vector members are accessed. How is that for memory safety?

It's an explicit statement that you considered and correctly handled the issues
of interacting with unsafe low-level machinery.
You shouldn't slap it on functions, you should minimise the surface area as
much as possible; perhaps higher-level functions should access the unsafe shit
through small helpers. Maybe use a tagged-union helper, etc.

--
June 05, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #19 from ag0aep6g <ag0aep6g@gmail.com> ---
(In reply to Dennis from comment #17)
> If you don't agree with the 'insufficient to corrupt memory' regime, please propose a new one! The spec is pretty informal currently. If you have a better interpretation, I'm eager to hear it. So far I've only heard 'things any sane security engineer is wary of'.

Per the spec (<https://dlang.org/spec/function.html#function-safety>), @safe functions "are functions that are statically checked to exhibit no possibility of undefined behavior".

I suppose that's equivalent to "doesn't corrupt memory". Memory corruption is always a symptom of undefined behavior, and there is no kind of undefined behavior that doesn't potentially lead to memory corruption (or it wouldn't really be UB).

I prefer focusing on undefined behavior, though. It feels more more clear cut to me. When the spec says that something has UB, then it can't be in @safe. Period.

Otherwise, when the focus is on memory corruption, there's more (perceived) wiggle room. We're tempted to allow anything that doesn't lead to memory corruption in practice, even if has undefined behavior. That quickly gets messy.


(In reply to Manu from comment #18)
> Uninitialised memory is ALREADY CORRUPTED. There is no world where access to what can be confirmed to be uninitialised memory is @safe, period.
> 
> Any interaction with uninitialised memory will guarantee unintended consequences, which could be anything.
> 
> It's very simple; if you're going to use unions, or uninitialised memory, then do so carefully, and @trusted that block of code.

You might want to argue your position in <https://github.com/dlang/dlang.org/pull/2260>. It's an open PR by Walter where he does exactly what you oppose here: he wants to explicitly allow accessing uninitialized memory in @safe code (unless it involves pointers).

--
June 05, 2019
https://issues.dlang.org/show_bug.cgi?id=19916

--- Comment #20 from Manu <turkeyman@gmail.com> ---
> Otherwise, when the focus is on memory corruption, there's more (perceived) wiggle room. We're tempted to allow anything that doesn't lead to memory corruption in practice, even if has undefined behavior. That quickly gets messy.

T x = void; // <- DOES lead to memory corruption; it's effectively an explicit statement to do memory corruption. How could a more explicit violation of @safe exist, no matter how you squint and interpret it?


> (In reply to Manu from comment #18)
> > Uninitialised memory is ALREADY CORRUPTED. There is no world where access to what can be confirmed to be uninitialised memory is @safe, period.
> > 
> > Any interaction with uninitialised memory will guarantee unintended consequences, which could be anything.
> > 
> > It's very simple; if you're going to use unions, or uninitialised memory, then do so carefully, and @trusted that block of code.
> 
> You might want to argue your position in <https://github.com/dlang/dlang.org/pull/2260>. It's an open PR by Walter where he does exactly what you oppose here: he wants to explicitly allow accessing uninitialized memory in @safe code (unless it involves pointers).

That PR doesn't appear to have anything to do with @safe?

--