[Bug sanitizer/85774] Incorrect stack-use-after-scope caused by missing cleanup of shadow bytes
marxin at gcc dot gnu.org
gcc-bugzilla@gcc.gnu.org
Tue May 15 11:33:00 GMT 2018
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85774
--- Comment #2 from Martin Liška <marxin at gcc dot gnu.org> ---
It's very interesting issue, took me some time to investigate what happens.
So first gimplifier creates a stack vars that have no usage:
Setup ()
{
const struct function D.31062;
struct __lambda0 D.30927;
struct __lambda0 D.35389;
const struct function D.31201;
struct __lambda1 D.31102;
struct __lambda1 D.35390;
{
switch (1) <default: <D.31202>, case 1: <D.30913>, case 2: <D.31088>>
{
<D.30913>:
{
typedef struct __lambda0 __lambda0;
ASAN_MARK (UNPOISON, &D.31062, 32);
ASAN_MARK (UNPOISON, &D.30927, 1);
try
{
std::function<void()>::function<Setup()::<lambda()> > (&D.31062,
D.35389);
try
{
try
{
DoFunc (&D.31062);
}
finally
{
std::function<void()>::~function (&D.31062);
}
}
finally
{
ASAN_MARK (POISON, &D.31062, 32);
}
}
finally
{
ASAN_MARK (POISON, &D.30927, 1);
}
goto <D.31203>;
}
<D.31088>:
{
typedef struct __lambda1 __lambda1;
ASAN_MARK (UNPOISON, &D.31201, 32);
ASAN_MARK (UNPOISON, &D.31102, 1);
try
{
std::function<void()>::function<Setup()::<lambda()> > (&D.31201,
D.35390);
try
{
try
{
DoFunc (&D.31201);
}
finally
{
std::function<void()>::~function (&D.31201);
}
}
finally
{
ASAN_MARK (POISON, &D.31201, 32);
}
}
finally
{
ASAN_MARK (POISON, &D.31102, 1);
}
goto <D.31203>;
}
<D.31202>:
goto <D.31203>;
}
<D.31203>:
}
DoSomething ();
}
The problematic one is e.g. struct __lambda0 D.30927;
It looks as follows after asan0 pass:
;; Function Setup (_Z5Setupv, funcdef_no=1368, decl_uid=30911, cgraph_uid=294,
symbol_order=298)
Setup ()
{
struct __lambda1 D.35390;
struct __lambda1 D.31102;
const struct function D.31201;
struct __lambda0 D.35389;
struct __lambda0 D.30927;
const struct function D.31062;
<bb 2> :
ASAN_MARK (UNPOISON, &D.31062, 32);
ASAN_MARK (UNPOISON, &D.30927, 1);
std::function<void()>::function<Setup()::<lambda()> > (&D.31062, D.35389);
<bb 3> :
DoFunc (&D.31062);
<bb 4> :
std::function<void()>::~function (&D.31062);
ASAN_MARK (POISON, &D.31062, 32);
ASAN_MARK (POISON, &D.30927, 1);
<bb 5> :
DoSomething ();
return;
<bb 6> :
<L4>:
std::function<void()>::~function (&D.31062);
resx 3
<bb 7> :
<L5>:
ASAN_MARK (POISON, &D.31062, 32);
resx 2
<bb 8> :
<L6>:
ASAN_MARK (POISON, &D.30927, 1);
resx 1
}
Then sanopt removes all ASAN_MARK statements for the &D.30927. Then we end up
with following stack partitioning:
Partition 1: size 32 align 16
D.31201 D.31062
Partition 0: size 1 align 8
D.31102 D.30927
And we have trouble as asan_emit_stack_protection is called for the stack
variables that have none usage.
Thus following code in asan.c is never executed:
1520 /* Unpoison shadow memory that corresponds to a variable that is
1521 is subject of use-after-return sanitization. */
1522 if (l > 2)
1523 {
1524 decl = decls[l / 2 - 2];
1525 if (asan_handled_variables != NULL
1526 && asan_handled_variables->contains (decl))
1527 {
1528 HOST_WIDE_INT size = offsets[l - 3] - offsets[l - 2];
1529 if (dump_file && (dump_flags & TDF_DETAILS))
1530 {
1531 debug_tree(decl);
1532 const char *n = (DECL_NAME (decl)
1533 ? IDENTIFIER_POINTER (DECL_NAME
(decl))
1534 : "<unknown>");
1535 fprintf (dump_file, "Unpoisoning shadow stack for
variable: "
1536 "%s (%" PRId64 " B)\n", n, size);
1537 }
1538
1539 last_size += size & ~(ASAN_RED_ZONE_SIZE -
HOST_WIDE_INT_1);
1540 }
1541 }
And thus we don't clean up stack in a function epilogue. One possible solution
would be
to mark all stack variables handled by ASAN (asan_handled_variables) to always
conflict
for partitioning. But I hope there's a nicer way how to do it.
Jakub?
More information about the Gcc-bugs
mailing list