Skip to content

Commit

Permalink
Fix Issue 19164 - malloc may be considered pure when failure results …
Browse files Browse the repository at this point in the history
…in program exit (no need to reset errno)
  • Loading branch information
n8sh committed Nov 17, 2018
1 parent ea4711e commit dcca23e
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 60 deletions.
22 changes: 8 additions & 14 deletions std/container/array.d
Expand Up @@ -251,10 +251,8 @@ private struct RangeT(A)
struct Array(T)
if (!is(Unqual!T == bool))
{
import core.memory : pureMalloc, pureRealloc, pureFree;
private alias malloc = pureMalloc;
private alias realloc = pureRealloc;
private alias free = pureFree;
import core.memory : free = pureFree;
import std.internal.memory : enforceMalloc, enforceRealloc;
import core.stdc.string : memcpy, memmove, memset;

import core.memory : GC;
Expand Down Expand Up @@ -321,8 +319,7 @@ if (!is(Unqual!T == bool))

static if (hasIndirections!T)
{
auto newPayloadPtr = cast(T*) malloc(nbytes);
newPayloadPtr || assert(false, "std.container.Array.length failed to allocate memory.");
auto newPayloadPtr = cast(T*) enforceMalloc(nbytes);
auto newPayload = newPayloadPtr[0 .. newLength];
memcpy(newPayload.ptr, _payload.ptr, startEmplace * T.sizeof);
memset(newPayload.ptr + startEmplace, 0,
Expand All @@ -334,7 +331,7 @@ if (!is(Unqual!T == bool))
}
else
{
_payload = (cast(T*) realloc(_payload.ptr,
_payload = (cast(T*) enforceRealloc(_payload.ptr,
nbytes))[0 .. newLength];
}
_capacity = newLength;
Expand Down Expand Up @@ -368,8 +365,7 @@ if (!is(Unqual!T == bool))
*/
immutable oldLength = length;

auto newPayloadPtr = cast(T*) malloc(sz);
newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory");
auto newPayloadPtr = cast(T*) enforceMalloc(sz);
auto newPayload = newPayloadPtr[0 .. oldLength];

// copy old data over to new array
Expand All @@ -386,8 +382,7 @@ if (!is(Unqual!T == bool))
else
{
// These can't have pointers, so no need to zero unused region
auto newPayloadPtr = cast(T*) realloc(_payload.ptr, sz);
newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory");
auto newPayloadPtr = cast(T*) enforceRealloc(_payload.ptr, sz);
auto newPayload = newPayloadPtr[0 .. length];
_payload = newPayload;
}
Expand Down Expand Up @@ -445,7 +440,7 @@ if (!is(Unqual!T == bool))
bool overflow;
const nbytes = mulu(values.length, T.sizeof, overflow);
if (overflow) assert(0);
auto p = cast(T*) malloc(nbytes);
auto p = cast(T*) enforceMalloc(nbytes);
static if (hasIndirections!T)
{
if (p)
Expand Down Expand Up @@ -567,8 +562,7 @@ if (!is(Unqual!T == bool))
bool overflow;
const sz = mulu(elements, T.sizeof, overflow);
if (overflow) assert(0);
auto p = malloc(sz);
p || assert(false, "std.container.Array.reserve failed to allocate memory");
auto p = enforceMalloc(sz);
static if (hasIndirections!T)
{
GC.addRange(p, sz);
Expand Down
23 changes: 14 additions & 9 deletions std/internal/cstring.d
Expand Up @@ -235,28 +235,33 @@ private To[] trustedRealloc(To)(scope To[] buf, size_t strLength, bool bufIsOnSt
{
pragma(inline, false); // because it's rarely called

import core.exception : onOutOfMemoryError;
import core.memory : pureMalloc, pureRealloc;
import std.internal.memory : enforceMalloc, enforceRealloc;

size_t newlen = buf.length * 3 / 2;

if (bufIsOnStack)
{
if (newlen <= strLength)
newlen = strLength + 1; // +1 for terminating 0
auto ptr = cast(To*) pureMalloc(newlen * To.sizeof);
if (!ptr)
onOutOfMemoryError();
auto ptr = cast(To*) enforceMalloc(newlen * To.sizeof);
ptr[0 .. buf.length] = buf[];
return ptr[0 .. newlen];
}
else
{
if (buf.length >= size_t.max / (2 * To.sizeof))
onOutOfMemoryError();
auto ptr = cast(To*) pureRealloc(buf.ptr, newlen * To.sizeof);
if (!ptr)
onOutOfMemoryError();
{
version (D_Exceptions)
{
import core.exception : onOutOfMemoryError;
onOutOfMemoryError();
}
else
{
assert(0, "Memory allocation failed");
}
}
auto ptr = cast(To*) enforceRealloc(buf.ptr, newlen * To.sizeof);
return ptr[0 .. newlen];
}
}
58 changes: 58 additions & 0 deletions std/internal/memory.d
@@ -0,0 +1,58 @@
module std.internal.memory;

package(std):

version (D_Exceptions)
{
import core.exception : onOutOfMemoryError;
private enum allocationFailed = `onOutOfMemoryError();`;
}
else
{
private enum allocationFailed = `assert(0, "Memory allocation failed");`;
}

// (below comments are non-DDOC, but are written in similar style)

/+
Pure variants of C's memory allocation functions `malloc`, `calloc`, and
`realloc` that achieve purity by aborting the program on failure so
they never visibly change errno.
The functions may terminate the program using `onOutOfMemoryError` or
`assert(0)`. These functions' purity guarantees no longer hold if
the program continues execution after catching AssertError or
OutOfMemoryError.
See_Also: $(REF pureMalloc, core,memory)
+/
void* enforceMalloc()(size_t size) @nogc nothrow pure @safe
{
auto result = fakePureMalloc(size);
if (!result) mixin(allocationFailed);
return result;
}

// ditto
void* enforceCalloc()(size_t nmemb, size_t size) @nogc nothrow pure @safe
{
auto result = fakePureCalloc(nmemb, size);
if (!result) mixin(allocationFailed);
return result;
}

// ditto
void* enforceRealloc()(void* ptr, size_t size) @nogc nothrow pure @system
{
auto result = fakePureRealloc(ptr, size);
if (!result) mixin(allocationFailed);
return result;
}

// Purified for local use only.
extern (C) @nogc nothrow pure private
{
pragma(mangle, "malloc") void* fakePureMalloc(size_t) @safe;
pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size) @safe;
pragma(mangle, "realloc") void* fakePureRealloc(void* ptr, size_t size) @system;
}
14 changes: 4 additions & 10 deletions std/random.d
Expand Up @@ -2914,31 +2914,25 @@ private struct RandomCoverChoices

this(this) pure nothrow @nogc @trusted
{
import core.memory : pureMalloc;
import core.stdc.string : memcpy;
import core.exception : onOutOfMemoryError;
import std.internal.memory : enforceMalloc;

if (!hasPackedBits && buffer !is null)
{
void* nbuffer = pureMalloc(_length);
if (nbuffer is null)
onOutOfMemoryError();
void* nbuffer = enforceMalloc(_length);
buffer = memcpy(nbuffer, buffer, _length);
}
}

this(size_t numChoices) pure nothrow @nogc @trusted
{
import core.memory : pureCalloc;
import core.exception : onOutOfMemoryError;
import std.internal.memory : enforceCalloc;

_length = numChoices;
hasPackedBits = _length <= size_t.sizeof * 8;
if (!hasPackedBits)
{
buffer = pureCalloc(numChoices, 1);
if (buffer is null)
onOutOfMemoryError();
buffer = enforceCalloc(numChoices, 1);
}
}

Expand Down
13 changes: 4 additions & 9 deletions std/typecons.d
Expand Up @@ -6098,7 +6098,6 @@ if (!is(T == class) && !(is(T == interface)))
/// `RefCounted` storage implementation.
struct RefCountedStore
{
import core.memory : pureMalloc;
private struct Impl
{
T _payload;
Expand All @@ -6109,12 +6108,10 @@ if (!is(T == class) && !(is(T == interface)))

private void initialize(A...)(auto ref A args)
{
import core.exception : onOutOfMemoryError;
import std.internal.memory : enforceMalloc;
import std.conv : emplace;

_store = cast(Impl*) pureMalloc(Impl.sizeof);
if (_store is null)
onOutOfMemoryError();
_store = cast(Impl*) enforceMalloc(Impl.sizeof);
static if (hasIndirections!T)
pureGcAddRange(&_store._payload, T.sizeof);
emplace(&_store._payload, args);
Expand All @@ -6123,12 +6120,10 @@ if (!is(T == class) && !(is(T == interface)))

private void move(ref T source)
{
import core.exception : onOutOfMemoryError;
import std.internal.memory : enforceMalloc;
import core.stdc.string : memcpy, memset;

_store = cast(Impl*) pureMalloc(Impl.sizeof);
if (_store is null)
onOutOfMemoryError();
_store = cast(Impl*) enforceMalloc(Impl.sizeof);
static if (hasIndirections!T)
pureGcAddRange(&_store._payload, T.sizeof);

Expand Down
28 changes: 10 additions & 18 deletions std/uni.d
Expand Up @@ -1818,22 +1818,20 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound;

static T[] alloc(T)(size_t size) @trusted
{
import core.memory : pureMalloc;
import std.exception : enforce;
import std.internal.memory : enforceMalloc;

import core.checkedint : mulu;
bool overflow;
size_t nbytes = mulu(size, T.sizeof, overflow);
if (overflow) assert(0);

auto ptr = cast(T*) enforce(pureMalloc(nbytes), "out of memory on C heap");
auto ptr = cast(T*) enforceMalloc(nbytes);
return ptr[0 .. size];
}

static T[] realloc(T)(scope T[] arr, size_t size) @trusted
{
import core.memory : pureRealloc;
import std.exception : enforce;
import std.internal.memory : enforceRealloc;
if (!size)
{
destroy(arr);
Expand All @@ -1845,7 +1843,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound;
size_t nbytes = mulu(size, T.sizeof, overflow);
if (overflow) assert(0);

auto ptr = cast(T*) enforce(pureRealloc(arr.ptr, nbytes), "out of memory on C heap");
auto ptr = cast(T*) enforceRealloc(arr.ptr, nbytes);
return ptr[0 .. size];
}

Expand Down Expand Up @@ -7487,8 +7485,7 @@ public:
{
static if (op == "~")
{
import core.exception : onOutOfMemoryError;
import core.memory : pureRealloc;
import std.internal.memory : enforceRealloc;
if (!isBig)
{
if (slen_ == small_cap)
Expand All @@ -7509,8 +7506,7 @@ public:
cap_ = addu(cap_, grow, overflow);
auto nelems = mulu(3, addu(cap_, 1, overflow), overflow);
if (overflow) assert(0);
ptr_ = cast(ubyte*) pureRealloc(ptr_, nelems);
if (ptr_ is null) onOutOfMemoryError();
ptr_ = cast(ubyte*) enforceRealloc(ptr_, nelems);
}
write24(ptr_, ch, len_++);
return this;
Expand Down Expand Up @@ -7567,17 +7563,15 @@ public:

this(this) @nogc nothrow pure @trusted
{
import core.exception : onOutOfMemoryError;
import core.memory : pureMalloc;
import std.internal.memory : enforceMalloc;
if (isBig)
{// dup it
import core.checkedint : addu, mulu;
bool overflow;
auto raw_cap = mulu(3, addu(cap_, 1, overflow), overflow);
if (overflow) assert(0);

auto p = cast(ubyte*) pureMalloc(raw_cap);
if (p is null) onOutOfMemoryError();
auto p = cast(ubyte*) enforceMalloc(raw_cap);
p[0 .. raw_cap] = ptr_[0 .. raw_cap];
ptr_ = p;
}
Expand Down Expand Up @@ -7619,13 +7613,11 @@ private:

void convertToBig() @nogc nothrow pure @trusted
{
import core.exception : onOutOfMemoryError;
import core.memory : pureMalloc;
import std.internal.memory : enforceMalloc;
static assert(grow.max / 3 - 1 >= grow);
enum nbytes = 3 * (grow + 1);
size_t k = smallLength;
ubyte* p = cast(ubyte*) pureMalloc(nbytes);
if (p is null) onOutOfMemoryError();
ubyte* p = cast(ubyte*) enforceMalloc(nbytes);
for (int i=0; i<k; i++)
write24(p, read24(small_.ptr, i), i);
// now we can overwrite small array data
Expand Down

0 comments on commit dcca23e

Please sign in to comment.