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?