04-Apr-2013 23:16, Ali Çehreli пишет:
 > All you need is one example where it would remove the wrong file,

$ dmd deneme.d -ofdeneme -I~/deneme/d -O -inline -m32
$ ./deneme

import std.stdio;
import std.string;
import std.array;

void main()
{
     auto myFile = "some.tmp";
     scope(exit) writeln(format("removing %s", myFile));

     writeln("myFile.ptr ", myFile.ptr);

     void manipulateElement(E)(ref E e)
     {
         size_t local;
         // Playing with pointers (BUG HERE)
         *(&local + 10) = 4;
         *(&local - 1) = 100;
         writeln(&local - 1);
         writeln("myFile ", &myFile);
         writeln("e ", e.ptr);
     }

     void manipulateFileRange(R)(R range)
     {
         for (size_t i = 0; i != range.length; ++i) {
             writeln("&i ", &i);
             writeln("i ", i);
             manipulateElement(range[i]);
         }
     }

     manipulateFileRange([ myFile ]);
}

Note that RangeError below is caused by a bug in the program.

Obviously regardless of whether or not RangeError happened you still corrupted memory. From there to the point of eventual abort/recovery anything potentially could happen. Data loss or corruption can happen no matter what and even way before assert triggers.

Once that
happens, we cannot say anything about the state of the program. It may
be 99% correct but it is still in an invalid state.

Here is the output of the program (arrow and comment are added manually
by me):

myFile.ptr 806C0C4
&i FFFCE5DC
i 0
FFFCE5DC
myFile FFFCE608
e 806C0C4
&i FFFCE5DC
i 101
removing some  <-- WRONG FILE! (not "some.tmp")
core.exception.RangeError@deneme(125887): Range violation

The neat thing about your example is that it doesn't matter if you choose to unwind or abort right away, or even use or not use asserts! The program was compromised and could destroy data and wreck havoc in any one of possible ways that an OS allows it.

Think again - what you look at is a failed or successful exploit of a program (like buffer overflow overwriting some internal pointers say a ret address). The assert aborting on something fishy won't help you an inch here:

a) successful exploit will blow its way past any and all high-level safeguards once it gets in control. The only things that are true obstacles to it are anti-stack corruption, some heap protection, ASLR and related techniques. These operate on "the same" lower level.

b) even unsuccessful exploit corrupts things a level deeper then the language guarantees or constructs operate. In the end simply returning from a call could cause segfault (overwritten return address).

What I want to underline here is regardless of assertion policy you get anything you can imagine by corrupting memory in a certain way.

And assertion failure may or may not indicate corruption but regardless it happens too late to make any kind of judgment based on that and in particular what to do next. Claiming that you protect from memory corruption via assert that calls abort by default is as silly as it gets.

Bottom line what I'd suggest is
a) allow Errors to propagate the usual way as Exceptions with the notion that these are generally fatal and can be used in nothrow. b) add more options regarding protection against memory corruption aside from @safe-D c) add a hook to runtime that allows people to get "abort on Error thrown" behavior.

--
Dmitry Olshansky

Reply via email to