Skip to content

Commit

Permalink
Fix memory corruption in vector
Browse files Browse the repository at this point in the history
  • Loading branch information
LunaTheFoxgirl committed Oct 15, 2024
1 parent b48ef47 commit 6260825
Showing 1 changed file with 102 additions and 27 deletions.
129 changes: 102 additions & 27 deletions source/numem/collections/vector.d
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private:

pragma(inline, true)
void copy(T* dst, T* src, size_t length) {
this.freeRange(dst, length);
this.freeRange(dst-memory, length);

static if (__traits(hasCopyConstructor, T)) {

Expand All @@ -110,14 +110,36 @@ private:
}
}

// Gets whether src overlaps with the memory in the vector.
bool doSourceOverlap(T* src, size_t length = 0) {
return src+length >= memory && src < memory+size_;
}

pragma(inline, true)
void move(T* dst, T* src, size_t length) {
this.freeRange(dst, length);
memmove(dst, src, T.sizeof*length);
if (this.doSourceOverlap(src, length)) {
if (src < dst) {
this.fillRangeInit(src, dst-src);
} else if (src > dst) {
auto ptr = dst+length;
auto len = (src-length)-ptr;
this.fillRangeInit(ptr, len);
}
}
}

pragma(inline, true)
void freeRange(T* dst, size_t length) {
void fillRangeInit(T* dst, size_t length) {
foreach(i; 0..length) {

T tmp = T.init;
memcpy(dst + i, &tmp, T.sizeof);
}
}

pragma(inline, true)
void freeRange(size_t offset, size_t length) {
static if (ownsMemory) {

// Heap allocated items require more smarts.
Expand All @@ -127,28 +149,33 @@ private:
// To prevent double-frees this is neccesary.
weak_set!(void*) freed;

foreach_reverse(i; 0..length) {
if (cast(void*)dst[i] in freed)
foreach_reverse(i; offset..offset+length) {
if (cast(void*)memory[i] in freed)
continue;

freed.insert(cast(void*)dst[i]);
freed.insert(cast(void*)memory[i]);

// Basic types don't need destruction,
// so we can skip this step.
static if (!isBasicType!T)
nogc_delete(dst[i]);
dst[i] = null;
nogc_delete(memory[i]);
memory[i] = null;
}

nogc_delete(freed);
} else static if (!isBasicType!T) {

foreach_reverse(i; 0..length)
nogc_delete(dst[i]);
foreach_reverse(i; offset..offset+length)
nogc_delete(memory[i]);
}

}
}

void freeAll() {
this.freeRange(0, size_);
}

public:

/// Gets the type of character stored in the string.
Expand All @@ -159,7 +186,7 @@ public:
~this() {
if (this.memory) {
static if (ownsMemory) {
this.freeRange(this.memory, size_);
this.freeRange(0, size_);
}

// Free the pointer
Expand Down Expand Up @@ -341,12 +368,7 @@ public:
void clear() {

// Delete elements in the array.
static if (ownsMemory && !isBasicType!T) {
foreach_reverse(item; 0..size_) {
nogc_delete(memory[item]);
}
}

this.freeAll();
this.size_ = 0;
}

Expand Down Expand Up @@ -495,6 +517,17 @@ public:
return this;
}

/**
Appends 2 vectors together, creating a new vector.
*/
@trusted
vector!T opBinary(string op: "~", U)(ref auto U items) if (isCompatibleRange!(U, T)) {
vector!T output;
output ~= this;
output ~= items;
return output;
}

/**
Add value to vector
*/
Expand Down Expand Up @@ -601,6 +634,11 @@ public:
NuRangeException.sliceOutOfRange(offset, offset+slice.length)
);

enforce(
!this.doSourceOverlap(cast(T*)values.ptr, values.length),
"Overlapping copies are not allowed!"
);

this.copy(cast(T*)slice.ptr, cast(T*)values.ptr, slice.length);
}

Expand All @@ -615,6 +653,9 @@ public:
if (offset+values.length > size_)
return false;

if (this.doSourceOverlap(cast(T*)values.ptr, values.length))
return false;

this.copy(cast(T*)memory+offset, cast(T*)values.ptr, values.length);
return true;
}
Expand Down Expand Up @@ -696,19 +737,53 @@ unittest {

@("vector: slice overlap")
unittest {
static
struct Test {
@nogc:
static uint counter;
try {
static
struct Test { }

vector!(Test*) tests;
tests ~= nogc_new!Test;
tests ~= nogc_new!Test;
tests ~= nogc_new!Test;

~this() { counter++; }
tests[0..1] = tests[1..2];
} catch(NuException ex) {
ex.free();
return;
}

assert(0, "An exception should've been thrown!");
}

@("vector: insert pointers")
unittest {
static struct Test { int a; }

vector!(Test*) tests;
tests ~= nogc_new!Test;
tests ~= nogc_new!Test;
tests ~= nogc_new!Test;
tests ~= nogc_new!Test(0);
tests ~= nogc_new!Test(1);
tests ~= nogc_new!Test(2);

tests.insert(1, [nogc_new!Test(24), nogc_new!Test(25), nogc_new!Test(26)]);
foreach(i; 0..tests.length) {
foreach(j; 0..tests.length) {
if (i == j) continue;
assert(tests[i] !is tests[j]);
}
}

assert(tests[0].a == 0);
assert(tests[1].a == 24);
assert(tests[2].a == 25);
assert(tests[3].a == 26);
assert(tests[4].a == 1);
assert(tests[5].a == 2);
}

@("vector: append vectors")
unittest {
vector!uint veca = [0, 1, 2, 3, 4];
vector!uint vecb = [0, 1, 2, 3, 4];

tests[0..1] = tests[1..2];
assert(Test.counter == 1);
assert((veca~vecb)[] == [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]);
}

0 comments on commit 6260825

Please sign in to comment.