July 28

On 7/28/23 11:15 AM, IchorDev wrote:

>

On Friday, 28 July 2023 at 11:15:31 UTC, Steven Schveighoffer wrote:

>

All __gshared does is give you storage that is accessible from all threads,

"All __gshared does is give you [a live bomb, ready to go off at any moment]"
!!

It seems like it's not __gshared at all, but misusing malloc with GC pointers.

>

On Friday, 28 July 2023 at 14:10:16 UTC, Kagamin wrote:

>

Your error is using allocating the object with malloc. Since gc doesn't see your AA, the AA is freed and you get UAF.

Friend, I think you nailed it. After adding this I haven't been able to reproduce the segfault again:

this(long n){
     (){
         cache = new typeof(cache);
         assert(cache);
         import core.memory;
         core.memory.GC.addRoot(cast(void*)cache);
     }();
     // ...

It did always happen at random, so perhaps I haven't spent enough time testing it yet, but I've gone far longer without it segfaulting than ever before.

This is the wrong approach, it's the allocating call that should add the root (actually a range).

For instance, the mutex is not added, that might be collected. Or if you add more GC-pointing things into the class, that could be a problem.

What I'd do is:

T alloc(T, A...)(auto ref A args){
    enum classSize = __traits(classInstanceSize, T);
    void* mem = core.stdc.stdlib.malloc(classSize);
    assert(mem !is null, "Out of memory");
    core.memory.GC.addRange(mem[0 .. classSize]);
    scope(failure) {
       core.memory.GC.removeRange(mem[0 .. classSize]);
       core.stdc.stdlib.free(mem);
    }
    T inst = cast(T)mem;
    inst.emplace(__traits(parameters));
    return inst;
}

And of course, a dealloc that removes the range should also be added.

-Steve

August 10

On Friday, 28 July 2023 at 21:22:01 UTC, Steven Schveighoffer wrote:

>
T alloc(T, A...)(auto ref A args){
    enum classSize = __traits(classInstanceSize, T);
    void* mem = core.stdc.stdlib.malloc(classSize);
    assert(mem !is null, "Out of memory");
    core.memory.GC.addRange(mem[0 .. classSize]);
    scope(failure) {
       core.memory.GC.removeRange(mem[0 .. classSize]);
       core.stdc.stdlib.free(mem);
    }
    T inst = cast(T)mem;
    inst.emplace(__traits(parameters));
    return inst;
}

And of course, a dealloc that removes the range should also be added.

-Steve

This also works unless I do a foreach over the AA's keys & values (foreach(k,v; aa)), in which case it still segfaults at random. Deconstructing the foreach into a for works though, for whatever reason.

August 10

On 8/10/23 1:59 AM, IchorDev wrote:

>

This also works unless I do a foreach over the AA's keys & values (foreach(k,v; aa)), in which case it still segfaults at random. Deconstructing the foreach into a for works though, for whatever reason.

That shouldn't matter.

The key thing is, if you are going to use malloc, you need to register the memory with the GC. The GC doesn't scan memory it doesn't know about.

But my main point was the class itself shouldn't be registering itself -- it did not make the decision to malloc. You should register where you allocate.

I also highly recommend using emplace to handle all the sticky issues with lifetime/construction.

-Steve

August 12

On Thursday, 10 August 2023 at 15:20:28 UTC, Steven Schveighoffer wrote:

>

That shouldn't matter.

Well, it does here. The AA is mutated during the loop, so perhaps this is an optimisation quirk where it works with for but segfaults in foreach? I've pretty thoroughly abused the for version and I haven't gotten it to segfault yet.

>

I also highly recommend using emplace to handle all the sticky issues with lifetime/construction.

Have not run into the aforementioned sticky issues yet, but I can't even find emplace's docs anywhere now. I recall it being incompatible with classes that have @nogc/nothrow constructors though, which made it pretty useless to me, and it wouldn't work with BetterC, which was a requirement for the allocation wrapper I was writing at the time.

August 15

On 8/12/23 5:55 AM, IchorDev wrote:

>

On Thursday, 10 August 2023 at 15:20:28 UTC, Steven Schveighoffer wrote:

>

That shouldn't matter.

Well, it does here. The AA is mutated during the loop, so perhaps this is an optimisation quirk where it works with for but segfaults in foreach? I've pretty thoroughly abused the for version and I haven't gotten it to segfault yet.

oh yeah. That is not allowed. Any mutation of the AA during iteration can invalidate existing foreach or ranges over the AA.

Basically, a rehash can cause the buckets to jumble up, and in that case, the "current index" can be changed to point at a null bucket.

More here: https://dlang.org/spec/statement.html#foreach_restrictions

In fact, that statement is way too broad. Invalidation of iteration should be based on the type's requirements.

We really should put a note in the AA spec page.

> >

I also highly recommend using emplace to handle all the sticky issues with lifetime/construction.

Have not run into the aforementioned sticky issues yet, but I can't even find emplace's docs anywhere now.

https://dlang.org/phobos/core_lifetime.html#.emplace

>

I recall it being incompatible with classes that have @nogc/nothrow constructors though, which made it pretty useless to me, and it wouldn't work with BetterC, which was a requirement for the allocation wrapper I was writing at the time.

It probably won't work with betterC, but that's probably just because of linker errors.

Any attribute requirements would be inferred based on the attributes of your constructor, because emplace is a template.

-Steve

August 16

On Tuesday, 15 August 2023 at 17:36:13 UTC, Steven Schveighoffer wrote:

>

On 8/12/23 5:55 AM, IchorDev wrote:

>

On Thursday, 10 August 2023 at 15:20:28 UTC, Steven Schveighoffer wrote:

>

That shouldn't matter.

Well, it does here. The AA is mutated during the loop, so perhaps this is an optimisation quirk where it works with for but segfaults in foreach? I've pretty thoroughly abused the for version and I haven't gotten it to segfault yet.

oh yeah. That is not allowed. Any mutation of the AA during iteration can invalidate existing foreach or ranges over the AA.

And yet this apparently doesn’t apply to an equivalent for somehow. Perhaps foreach shouldn’t have this arbitrary problem in the first place…

>

In fact, that statement is way too broad. Invalidation of iteration should be based on the type's requirements.

We really should put a note in the AA spec page.

Probably yeah.

>

https://dlang.org/phobos/core_lifetime.html#.emplace

Any attribute requirements would be inferred based on the attributes of your constructor, because emplace is a template.

-Steve

Well the docs don’t say that at all, and the code is an unreadable mess. I dunno how anymore is meant to figure that out?

1 2
Next ›   Last »