[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