【问题标题】:What is TCACHE?什么是 TCACHE?
【发布时间】:2018-09-13 03:57:02
【问题描述】:

我在linux上玩了很长时间的二进制利用,最近我正在写一些基于ptmalloc的堆利用笔记,所以我回去查看一些过去安全的payload我已经解决的挑战,令人惊讶的是,它们不再起作用了。

比如基本的double free 损坏(不是fastbin)

char *chunk1 = malloc(0xc0);
free(chunk1);
free(chunk1);

我希望看到类似的东西

*** Error in `main': double free or corruption (top): 0x0000000000c85010 ***  

但是没有,什么都没有发生,程序正常退出。

为此我去查看我的机器对应的glibc源代码-Debian GLIBC 2.27-2,发现malloc.c有很大的不同。

void *
__libc_malloc (size_t bytes)
{     
...
#if USE_TCACHE
  /* int_free also calls request2size, be careful to not pad twice.  */
  size_t tbytes;
  checked_request2size (bytes, tbytes);
  size_t tc_idx = csize2tidx (tbytes);

  MAYBE_INIT_TCACHE ();

  DIAG_PUSH_NEEDS_COMMENT;
  if (tc_idx < mp_.tcache_bins
      /*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
      && tcache
      && tcache->entries[tc_idx] != NULL)
    {
      return tcache_get (tc_idx);
    }
  DIAG_POP_NEEDS_COMMENT;
#endif

USE_TCACHE 也出现在许多其他地方。

然后我回到上面的程序,发现chunk1 没有放在small bin 中,而是放在tcache_entry 中。

TCACHE 的目的是什么?我搜索了很多,仍然感到困惑。

【问题讨论】:

  • 快速谷歌搜索提示per thread cache。我猜这个malloc 实现为每个线程保留了一个较小的、不同大小的预分配块池。这样就可以在没有锁的情况下满足许多调用。
  • 欢迎来到漏洞利用的世界。每个漏洞利用都必须针对 malloc() 的特定实现进行定制——不同的实现有不同的漏洞,对一个人有效的不一定适用于其他人。
  • 真正的问题不是为什么您使用的工具不再检测到双重释放吗? (可能是因为 malloc() 是从 tcache 提供的)
  • @Flow 不,实际上我已经弄清楚了 tcache 漏洞利用的工作原理,简而言之,它放弃了自 glibc 2.26 以来的许多安全检查(就像我的问题中的情况一样),许多漏洞利用变得更加强大。

标签: c malloc heap-memory glibc heap-corruption


【解决方案1】:

线程本地缓存 (tcache) 是 glibc 中的一种性能优化。不幸的是,它是以牺牲安全为代价的,并且使一些攻击变得更加容易,正如您后来发现的那样。

来自https://sourceware.org/glibc/wiki/MallocInternals#Thread_Local_Cache_.28tcache.29

虽然这个 malloc 知道多个线程,但这几乎是它的意识范围 - 它知道有多个线程。此 malloc 中没有代码可以针对 NUMA 架构对其进行优化、协调线程局部性、按内核对线程进行排序等。假设内核可以很好地处理这些问题。

每个线程都有一个线程局部变量,用于记住它上次使用的区域。如果当某个线程需要使用该竞技场时,该竞技场正在使用中,则该线程将阻塞以等待该竞技场空闲。如果线程之前从未使用过竞技场,那么它可能会尝试重用未使用的竞技场、创建新竞技场或选择全局列表中的下一个竞技场。

每个线程都有一个每线程缓存(称为 tcache),其中包含一小部分块集合,无需锁定竞技场即可访问这些块。这些块存储为单链表数组,如 fastbins,但链接指向有效负载(用户区域)而不是块头。每个 bin 包含一个大小的块,因此数组按块大小(间接)索引。与 fastbins 不同,tcache 受限于每个 bin 中允许的块数 (tcache_count)。如果 tcache bin 对于给定的请求大小是空的,则不使用下一个更大的块(可能导致内部碎片),而是使用正常的 malloc 例程,即锁定线程的 arena 并从那里工作。

【讨论】:

    猜你喜欢
    • 2020-06-14
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 2022-01-08
    • 2019-06-24
    • 1970-01-01
    • 2017-04-21
    相关资源
    最近更新 更多