[Issue 17566] New: can use void initialization in @safe code to break out of stack
via Digitalmars-d-bugs
digitalmars-d-bugs at puremagic.com
Wed Jun 28 06:05:37 PDT 2017
https://issues.dlang.org/show_bug.cgi?id=17566
Issue ID: 17566
Summary: can use void initialization in @safe code to break out
of stack
Product: D
Version: D2
Hardware: x86_64
OS: Linux
Status: NEW
Keywords: safe
Severity: normal
Priority: P1
Component: dmd
Assignee: nobody at puremagic.com
Reporter: ag0aep6g at gmail.com
This is basically issue 17561 but without `Fiber`s. A fix for this is likely to
also fix issue 17561, so 17561 could be considered a duplicate of this. I'm
leaving it open for now, because it might be solvable by working around the
more general issue somehow.
Related links:
* https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
* https://github.com/dlang/druntime/pull/1698
Memory corrupting code:
----
import core.sys.posix.sys.mman;
import std.conv: text;
enum pageSize = 1024 * 4; // 4 KiB
enum stackSize = 1024 * 1024 * 3; // 3 MiB
void main()
{
/* Allocate memory near the stack. */
ubyte foo;
auto stackTop = &foo + pageSize - cast(size_t) &foo % pageSize;
auto stackBottom = stackTop - stackSize;
auto sz = pageSize;
void* dst = stackBottom - sz;
void* p = mmap(dst, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
-1, 0);
assert(p == dst, "failed to allocate page");
/* Set it up with zeroes. */
auto mem = cast(ubyte[]) p[0 .. sz];
mem[] = 0;
foreach (x; mem) assert(x == 0, text(x)); /* passes */
/* Break out of the stack and wreak havoc. */
wreak_havoc();
/* Look at the mess. */
foreach (x; mem) assert(x == 0, text(x)); /* fails; prints "13" */
}
void wreak_havoc() @safe
{
ubyte[stackSize] x = void;
x[0] = 13;
}
----
Like in issue 17561, the surrounding code is not @safe, but is actually safe
(as far as I can tell). It's the void initialized static array that breaks
safety.
In a 32-bit program it's also possible to get there with `malloc` instead of a
targeted `mmap`:
----
/* WARNING: This fails quickly for me in a 32-bit Ubuntu VM, but it can
potentially consume all memory. */
import core.stdc.stdlib: malloc;
import std.conv: text;
enum pageSize = 1024 * 4; // 4 KiB
enum stackSize = 1024 * 1024 * 3; // 3 MiB
void main()
{
ubyte foo;
auto stackTop = &foo + pageSize - cast(size_t) &foo % pageSize;
auto stackBottom = stackTop - stackSize;
assert(cast(size_t) stackBottom % pageSize == 0);
while (true)
{
/* Allocate memory. */
auto sz = 1024 * 1024; // 1 MiB
auto p = malloc(sz);
assert(p !is null, "malloc failed");
assert(stackBottom > p);
/* See if it's near the stack. */
size_t distance = stackBottom - p;
if (distance <= sz)
{
/* Set it up with zeroes. */
auto mem = cast(ubyte[]) p[0 .. sz];
mem[] = 0;
foreach (x; mem) assert(x == 0, text(x)); /* passes */
/* Break out of the stack and wreak havoc. */
wreak_havoc();
/* Look at the mess. */
foreach (x; mem) assert(x == 0, text(x)); /* fails; prints "13" */
break;
}
}
}
void wreak_havoc() @safe
{
ubyte[stackSize] x = void;
x[0] = 13;
}
----
--
More information about the Digitalmars-d-bugs
mailing list