Jump to page: 1 2
Thread overview
[Issue 1164] Wrong order of memory deallocation
Jul 04, 2014
Pieter Penninckx
Dec 25, 2016
Pieter Penninckx
Dec 26, 2016
Pieter Penninckx
Dec 26, 2016
safety0ff.bugz
Dec 26, 2016
safety0ff.bugz
Dec 26, 2016
safety0ff.bugz
Dec 27, 2016
Pieter Penninckx
Dec 27, 2016
safety0ff.bugz
[Issue 1164] Unordered GC finalization leading to memory bugs
May 15, 2018
Dmitry Olshansky
May 21, 2019
Mathias LANG
July 04, 2014
https://issues.dlang.org/show_bug.cgi?id=1164

Pieter Penninckx <pieter.penninckx@scarlet.be> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |pieter.penninckx@scarlet.be
            Version|D1                          |2.040

--- Comment #5 from Pieter Penninckx <pieter.penninckx@scarlet.be> ---
Reproduced with DMD version 2.065.


If the destructor cannot reference sub objects, this implies that also an invariant cannot reference sub objects, because an invariant is called just before the destructor. Example below triggers segfault with DMD version 2.065.


class B
{
    double a, b, c, d, e, f, g, h;
    bool fun() const { return true;    }
}

class A
{
    B b;
    invariant() {
        assert(b !is null);
        if (b.fun()) // <- Segfault here, but b is not null.
        { assert(true);    }
    }
    this() { b = new B(); }
    ~this()    {}
}

int main() {
    A a = new A();
    return 0;
}

--
June 09, 2015
https://issues.dlang.org/show_bug.cgi?id=1164

Andrei Alexandrescu <andrei@erdani.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Version|2.040                       |D2

--
December 22, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

Andrei Alexandrescu <andrei@erdani.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrei@erdani.com

--- Comment #6 from Andrei Alexandrescu <andrei@erdani.com> ---
Couldn't reproduce on dmd 2.072.1. Any better code sample?

--
December 25, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #7 from Pieter Penninckx <pieter.penninckx@scarlet.be> ---
The following code still segfaults with DMD 2.072.1-0.

class X
{
    double a, b, c, d, e, f, g, h;
    X sibling;
    bool fun() const { return true;    }
    invariant() {
        if(sibling !is null) {
            if (sibling.fun())
            { assert(true);    }
        }
    }
    this() {sibling = new X(this); }
    this(X sibling_) {sibling = sibling_;}
    ~this() {}
}

int main() {
    X x = new X();
    return 0;
}


Garbage collector + destructor (or finalizer) is a difficult combination. See for instance this comment (https://github.com/WebAssembly/design/issues/238#issuecomment-116877193) that strongly opposes introducing a destructor in Javascript because this combination can lead to object resurrection (objects made reachable again in a destructor call).

--
December 25, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #8 from Andrei Alexandrescu <andrei@erdani.com> ---
Cool, was able to repro. Fortunately we have a couple of cards in our sleeve. What we could do is:

* Mark
* For each collectable object:
  * Call dtor
  * (NEW) Obliterate with .init
* Sweep

That way objects used during destruction will at least find objects in a deterministic state.

--
December 26, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #9 from Pieter Penninckx <pieter.penninckx@scarlet.be> ---
Just to be sure I understand you correctly.

Am I right that the garbage collector currently works as follows:

* Mark (= mark all reachable objects as reachable)
* For each collectable (= non-reachable) object:
  * Call dtor
  * release memory of the collectable object

Am I right that the steps you are thinking about can also be formulated as follows:

* Mark (= mark all reachable objects as reachable)
* For each collectable (= non-reachable) object:
  * Call dtor
  * Obliterate with .init
* For each collectable object:
  * release memory of the collectable object

--
December 26, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #10 from Andrei Alexandrescu <andrei@erdani.com> ---
(In reply to Pieter Penninckx from comment #9)
> Just to be sure I understand you correctly.
> 
> Am I right that the garbage collector currently works as follows:
> 
> * Mark (= mark all reachable objects as reachable)
> * For each collectable (= non-reachable) object:
>   * Call dtor
>   * release memory of the collectable object

I don't know exactly. I am pretty certain, however, that freed objects are currently not overwritten with .init.

--
December 26, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

safety0ff.bugz <safety0ff.bugz@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |safety0ff.bugz@gmail.com

--- Comment #11 from safety0ff.bugz <safety0ff.bugz@gmail.com> ---
(In reply to Pieter Penninckx from comment #7)
> 
> int main() {
> 	X x = new X();
> 	return 0;
> }

Your invariant gets called infinitely:
x.sibling.sibling == x
sibling's invariant() executes before and after sibling.fun() executes.
The invariant has the line: if (sibling.fun())

--
December 26, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #12 from safety0ff.bugz <safety0ff.bugz@gmail.com> ---
(In reply to Pieter Penninckx from comment #7)
>
> 	invariant() {
> 		if(sibling !is null) {
> 			if (sibling.fun())
> 			{ assert(true);	}
> 		}
> 	}
>

Also:
https://dlang.org/spec/contracts.html#Invariants "The code in the invariant may
not call any public non-static members of the class or struct, either directly
or indirectly. Doing so will result in a stack overflow, as the invariant will
wind up being called in an infinitely recursive manner."

--
December 26, 2016
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #13 from safety0ff.bugz <safety0ff.bugz@gmail.com> ---
(In reply to Pieter Penninckx from comment #9)
> 
> Am I right that the garbage collector currently works as follows:


It currently works as follows:

* Mark
* For each unmarked object:
*   Finalize it if necessary
*   If it can be released without overwriting it do so
* For each unmarked unreleased object:
*   release memory of the object

However, you shouldn't rely on this: http://dlang.org/spec/class.html#destructors

If you recompile druntime with the MEMSTOMP option, the GC and it will write arbitrary data to finalized memory.

Therefore it follows that referencing GC managed objects from invariants of other GC managed objects is unadvised.

I think a case could be made for being able to control insertion of invariant calls in destructors.

--
« First   ‹ Prev
1 2