List of Phobos functions that allocate memory?
Adam D. Ruppe
destructionator at gmail.com
Thu Feb 6 14:56:43 PST 2014
On Thursday, 6 February 2014 at 21:38:03 UTC, Dicebot wrote:
> Any application that operates on some external user input will
> be subject to DoS attack vector if it uses Phobos directly.
Hmm, I hadn't considered that. Maybe exceptions could be handled
automatically though due to the facts that there are rarely more
than one in flight at any time and they typically don't live for
long:
1) prohibit escaping of exception objects from catch blocks (we
could just say it is undefined behavior in the spec). The data
pointed to by the throwable object should be normal though, if
you want to keep the exception, you can thus just shallow copy it.
2) Set aside a static (thread local) buffer early on with a size
of like 512 bytes.
3) Make "throw new" call a special function which favors the
static buffer. It can do a simple bump-the-pointer allocation in
the static region or call the regular GC if there isn't enough
room (should be extremely rare).
throw e; works the same way it does now. You can pre-allocate
with some other method if you want.
4) Have the compiler automatically insert a call to
_d_free_exception in a scope(success) block inside every catch
block. It checks the given reference, if it is in the static
buffer, just zero it all out. If all the chain is in there,
zeroing it will free it all. If there's any GC chained
exceptions, zeroing it will orphan them and they'll be freed on
the next sweep. Otherwise ... well do nothing, let the GC clean
up after it.
Proof of concept:
bool isThrowable(const ClassInfo ci) {
if(ci is null) return false;
if(ci is typeid(Throwable)) return true;
return isThrowable(ci.base);
}
byte[512] exceptionHolder = 0;
size_t exceptionHolderPosition = 0;
extern(C)
Object _d_newclass(const ClassInfo ci) {
if(!isThrowable(ci))
return _d_newclass_original(ci);
auto size = ci.init.length;
if(exceptionHolderPosition + size >
exceptionHolder.length)
return _d_newclass_original(ci);
byte[] slice = exceptionHolder[exceptionHolderPosition ..
exceptionHolderPosition + size];
exceptionHolderPosition += size;
slice[] = ci.init[];
import core.stdc.stdio;
printf("Magic allocation to %d\n",
exceptionHolderPosition);
return cast(Object) slice.ptr;
}
extern(C)
void _d_freeexception(Throwable t) {
auto ptr = cast(void*) t;
if(ptr >= exceptionHolder.ptr && ptr <
exceptionHolder.ptr + exceptionHolder.length) {
exceptionHolder[] = 0;
exceptionHolderPosition = 0;
import core.stdc.stdio;
printf("Freeing\n");
}
// else do nothing, the GC will handle it
}
void main() {
import std.stdio;
try {
writefln("%s"); // orphaned argument
} catch(Exception e) {
scope(success) _d_freeexception(e);
writeln(e);
}
}
// copy/paste from druntime as fallback
extern (C) void onOutOfMemoryError();
extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
extern (C) Object _d_newclass_original(const ClassInfo ci)
{
import core.stdc.stdlib;
static import core.memory;
alias BlkAttr = core.memory.GC.BlkAttr;
void* p;
if (ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass)
{
p = malloc(ci.init.length);
if (!p)
onOutOfMemoryError();
}
else
{
// TODO: should this be + 1 to avoid having pointers to
the next block?
BlkAttr attr = BlkAttr.FINALIZE;
// extern(C++) classes don't have a classinfo pointer in
their vtable so the GC can't finalize them
if (ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass)
attr &= ~BlkAttr.FINALIZE;
if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)
attr |= BlkAttr.NO_SCAN;
p = gc_malloc(ci.init.length, attr);
}
// initialize it
(cast(byte*) p)[0 .. ci.init.length] = ci.init[];
debug(PRINTF) printf("initialization done\n");
return cast(Object) p;
}
===
Just compile and run normally, the linker will prefer our
d_newclass to the one in phobos.lib automatically.
And you'll see the throw from writeln went into our static buffer
and was freed at the end.
I toyed with a few other things too:
void main() {
import std.stdio;
try {
try {
writefln("%s"); // orphaned argument
} catch(Exception e) {
scope(success) _d_freeexception(e); // don't forget
these
throw new Exception("LOL", e);
}
} catch(Exception e) {
scope(success) _d_freeexception(e);
writeln(e);
writeln(e.next);
}
}
still works.
Am I missing a fatal flaw here? It seems to work and is kinda
simple to do... exceptions don't really need a huge amount of
dynamic memory.
More information about the Digitalmars-d
mailing list