Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 29e9ac2

Browse files
authored
Merge pull request #2429 from rainers/gc_freebits
fix Issue 19522 - [GC] GC.query/addrOf/sizeOf fail for freed memory merged-on-behalf-of: Nicholas Wilson <thewilsonator@users.noreply.github.com>
2 parents 8b02a86 + a44c9bf commit 29e9ac2

File tree

2 files changed

+104
-69
lines changed

2 files changed

+104
-69
lines changed

src/core/memory.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ unittest
10761076
assert(GC.addrOf(cast(void*) b) == null);
10771077
// but be careful, a still points to it
10781078
assert(a !is null);
1079-
assert(GC.addrOf(cast(void*) a) !is null);
1079+
assert(GC.addrOf(cast(void*) a) == null); // but not a valid GC pointer
10801080
}
10811081

10821082
/// Deleting interfaces
@@ -1143,7 +1143,7 @@ unittest
11431143
assert(GC.addrOf(b.ptr) == null);
11441144
// but be careful, a still points to it
11451145
assert(a !is null);
1146-
assert(GC.addrOf(a.ptr) !is null);
1146+
assert(GC.addrOf(a.ptr) == null); // but not a valid GC pointer
11471147
}
11481148

11491149
/// Deleting arrays of structs

src/gc/impl/conservative/gc.d

Lines changed: 102 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ class ConservativeGC : GC
412412
if (pool)
413413
{
414414
p = sentinel_sub(p);
415+
if (p != pool.findBase(p))
416+
return 0;
415417
auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
416418

417419
oldb = pool.getBits(biti);
@@ -438,6 +440,8 @@ class ConservativeGC : GC
438440
if (pool)
439441
{
440442
p = sentinel_sub(p);
443+
if (p != pool.findBase(p))
444+
return 0;
441445
auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
442446

443447
oldb = pool.getBits(biti);
@@ -465,6 +469,8 @@ class ConservativeGC : GC
465469
if (pool)
466470
{
467471
p = sentinel_sub(p);
472+
if (p != pool.findBase(p))
473+
return 0;
468474
auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
469475

470476
oldb = pool.getBits(biti);
@@ -854,12 +860,10 @@ class ConservativeGC : GC
854860

855861
sentinel_Invariant(p);
856862
p = sentinel_sub(p);
857-
biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
858-
859-
pool.clrBits(biti, ~BlkAttr.NONE);
860863

861864
if (pool.isLargeObject) // if large alloc
862865
{
866+
biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large;
863867
assert(bin == B_PAGE);
864868
auto lpool = cast(LargeObjectPool*) pool;
865869

@@ -869,15 +873,21 @@ class ConservativeGC : GC
869873
lpool.freePages(pagenum, npages);
870874
}
871875
else
872-
{ // Add to free list
876+
{
877+
biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Small;
878+
if (pool.freebits.test (biti))
879+
return;
880+
// Add to free list
873881
List *list = cast(List*)p;
874882

875883
debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]);
876884

877885
list.next = gcx.bucket[bin];
878886
list.pool = pool;
879887
gcx.bucket[bin] = list;
888+
pool.freebits.set(biti);
880889
}
890+
pool.clrBits(biti, ~BlkAttr.NONE);
881891

882892
gcx.log_free(sentinel_add(p));
883893
}
@@ -1416,6 +1426,9 @@ struct Gcx
14161426
{
14171427
for (auto list = cast(List*)bucket[i]; list; list = list.next)
14181428
{
1429+
auto pool = list.pool;
1430+
auto biti = cast(size_t)(cast(void*)list - pool.baseAddr) >> Pool.ShiftBy.Small;
1431+
assert(pool.freebits.test(biti));
14191432
}
14201433
}
14211434
}
@@ -1546,35 +1559,7 @@ struct Gcx
15461559

15471560
pool = findPool(p);
15481561
if (pool)
1549-
{
1550-
size_t offset = cast(size_t)(p - pool.baseAddr);
1551-
size_t pn = offset / PAGESIZE;
1552-
Bins bin = cast(Bins)pool.pagetable[pn];
1553-
1554-
// Adjust bit to be at start of allocated memory block
1555-
if (bin < B_NUMSMALL)
1556-
{
1557-
return pool.baseAddr + baseOffset(offset, bin);
1558-
}
1559-
else if (bin == B_PAGE)
1560-
{
1561-
return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1562-
}
1563-
else if (bin == B_PAGEPLUS)
1564-
{
1565-
size_t pageOffset = pool.bPageOffsets[pn];
1566-
offset -= pageOffset * PAGESIZE;
1567-
pn -= pageOffset;
1568-
1569-
return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1570-
}
1571-
else
1572-
{
1573-
// we are in a B_FREE page
1574-
assert(bin == B_FREE);
1575-
return null;
1576-
}
1577-
}
1562+
return pool.findBase(p);
15781563
return null;
15791564
}
15801565

@@ -1735,8 +1720,11 @@ struct Gcx
17351720
// Return next item from free list
17361721
bucket[bin] = (cast(List*)p).next;
17371722
auto pool = (cast(List*)p).pool;
1723+
auto biti = (p - pool.baseAddr) >> pool.shiftBy;
1724+
assert(pool.freebits.test(biti));
1725+
pool.freebits.clear(biti);
17381726
if (bits)
1739-
pool.setBits((p - pool.baseAddr) >> pool.shiftBy, bits);
1727+
pool.setBits(biti, bits);
17401728
//debug(PRINTF) printf("\tmalloc => %p\n", p);
17411729
debug (MEMSTOMP) memset(p, 0xF0, alloc_size);
17421730
return p;
@@ -2141,38 +2129,15 @@ struct Gcx
21412129
// collection step 1: prepare freebits and mark bits
21422130
void prepare() nothrow
21432131
{
2144-
size_t n;
2145-
Pool* pool;
2146-
2147-
for (n = 0; n < npools; n++)
2148-
{
2149-
pool = pooltable[n];
2150-
pool.mark.zero();
2151-
if (!pool.isLargeObject) pool.freebits.zero();
2152-
}
2153-
2154-
debug(COLLECT_PRINTF) printf("Set bits\n");
2155-
2156-
// Mark each free entry, so it doesn't get scanned
2157-
for (n = 0; n < B_PAGE; n++)
2158-
{
2159-
for (List *list = bucket[n]; list; list = list.next)
2160-
{
2161-
pool = list.pool;
2162-
assert(pool);
2163-
pool.freebits.set(cast(size_t)(cast(void*)list - pool.baseAddr) / 16);
2164-
}
2165-
}
2132+
debug(COLLECT_PRINTF) printf("preparing mark.\n");
21662133

2167-
debug(COLLECT_PRINTF) printf("Marked free entries.\n");
2168-
2169-
for (n = 0; n < npools; n++)
2134+
for (size_t n = 0; n < npools; n++)
21702135
{
2171-
pool = pooltable[n];
2172-
if (!pool.isLargeObject)
2173-
{
2136+
Pool* pool = pooltable[n];
2137+
if (pool.isLargeObject)
2138+
pool.mark.zero();
2139+
else
21742140
pool.mark.copy(&pool.freebits);
2175-
}
21762141
}
21772142
}
21782143

@@ -2488,7 +2453,7 @@ struct Gcx
24882453
* Returns true if the addr lies within a marked block.
24892454
*
24902455
* Warning! This should only be called while the world is stopped inside
2491-
* the fullcollect function.
2456+
* the fullcollect function after all live objects have been marked, but before sweeping.
24922457
*/
24932458
int isMarked(void *addr) scope nothrow
24942459
{
@@ -2504,6 +2469,8 @@ struct Gcx
25042469
if (bins < B_PAGE)
25052470
{
25062471
biti = baseOffset(offset, bins) >> pool.ShiftBy.Small;
2472+
// doesn't need to check freebits because no pointer must exist
2473+
// to a block that was free before starting the collection
25072474
}
25082475
else if (bins == B_PAGE)
25092476
{
@@ -2719,6 +2686,7 @@ struct Pool
27192686
if (!isLargeObject)
27202687
{
27212688
freebits.alloc(nbits);
2689+
freebits.setRange(0, nbits);
27222690
}
27232691

27242692
noscan.alloc(nbits);
@@ -2949,6 +2917,38 @@ struct Pool
29492917
return (size + PAGESIZE - 1) / PAGESIZE;
29502918
}
29512919

2920+
void* findBase(void* p) nothrow @nogc
2921+
{
2922+
size_t offset = cast(size_t)(p - baseAddr);
2923+
size_t pn = offset / PAGESIZE;
2924+
Bins bin = cast(Bins)pagetable[pn];
2925+
2926+
// Adjust bit to be at start of allocated memory block
2927+
if (bin < B_NUMSMALL)
2928+
{
2929+
auto baseOff = baseOffset(offset, bin);
2930+
const biti = baseOff >> Pool.ShiftBy.Small;
2931+
if (freebits.test (biti))
2932+
return null;
2933+
return baseAddr + baseOff;
2934+
}
2935+
if (bin == B_PAGE)
2936+
{
2937+
return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
2938+
}
2939+
if (bin == B_PAGEPLUS)
2940+
{
2941+
size_t pageOffset = bPageOffsets[pn];
2942+
offset -= pageOffset * PAGESIZE;
2943+
pn -= pageOffset;
2944+
2945+
return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
2946+
}
2947+
// we are in a B_FREE page
2948+
assert(bin == B_FREE);
2949+
return null;
2950+
}
2951+
29522952
size_t slGetSize(void* p) nothrow @nogc
29532953
{
29542954
if (isLargeObject)
@@ -3098,7 +3098,8 @@ struct LargeObjectPool
30983098
return 0;
30993099
size_t pagenum = pagenumOf(p);
31003100
Bins bin = cast(Bins)pagetable[pagenum];
3101-
assert(bin == B_PAGE);
3101+
if (bin != B_PAGE)
3102+
return 0;
31023103
return bPageOffsets[pagenum];
31033104
}
31043105

@@ -3194,6 +3195,9 @@ struct SmallObjectPool
31943195
assert(bin < B_PAGE);
31953196
if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer
31963197
return 0;
3198+
const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small;
3199+
if (freebits.test (biti))
3200+
return 0;
31973201
return binsize[bin];
31983202
}
31993203

@@ -3207,10 +3211,15 @@ struct SmallObjectPool
32073211
if (bin >= B_PAGE)
32083212
return info;
32093213

3210-
info.base = cast(void*)baseOffset(cast(size_t)p, bin);
3214+
auto base = cast(void*)baseOffset(cast(size_t)p, bin);
3215+
const biti = cast(size_t)(base - baseAddr) >> ShiftBy.Small;
3216+
if (freebits.test (biti))
3217+
return info;
3218+
3219+
info.base = base;
32113220
info.size = binsize[bin];
32123221
offset = info.base - baseAddr;
3213-
info.attr = getBits(cast(size_t)(offset >> ShiftBy.Small));
3222+
info.attr = getBits(biti);
32143223

32153224
return info;
32163225
}
@@ -3548,3 +3557,29 @@ version (D_LP64) unittest
35483557
}
35493558
}
35503559
}
3560+
3561+
// https://issues.dlang.org/show_bug.cgi?id=19522
3562+
unittest
3563+
{
3564+
import core.memory;
3565+
3566+
void test(void* p)
3567+
{
3568+
assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
3569+
assert(GC.setAttr(p + 4, BlkAttr.NO_SCAN) == 0); // interior pointer should fail
3570+
assert(GC.clrAttr(p + 4, BlkAttr.NO_SCAN) == 0); // interior pointer should fail
3571+
GC.free(p);
3572+
assert(GC.query(p).base == null);
3573+
assert(GC.query(p).size == 0);
3574+
assert(GC.addrOf(p) == null);
3575+
assert(GC.sizeOf(p) == 0); // fails
3576+
assert(GC.getAttr(p) == 0);
3577+
assert(GC.setAttr(p, BlkAttr.NO_SCAN) == 0);
3578+
assert(GC.clrAttr(p, BlkAttr.NO_SCAN) == 0);
3579+
}
3580+
void* large = GC.malloc(10000, BlkAttr.NO_SCAN);
3581+
test(large);
3582+
3583+
void* small = GC.malloc(100, BlkAttr.NO_SCAN);
3584+
test(small);
3585+
}

0 commit comments

Comments
 (0)