Daily Heap #6


Overview
この文章から,malloc()関数とfree()関数の挙動を深く検討する.glibc 2.23、glibc 2.29、glibc 2.34の3つのバージョンのため、glibcのバージョンによって詳細な動作の違いがあります.
上記のバージョンを選択する基準は、最も基本的なglibc 2.23であり、tcacheといくつかの保護技術を追加したglibc 2.29であり、最新リリースUbuntu 21.10で使用されているglibc 2.34が作成されます.malloc() in glibc 2.23
まず,malloc()のコールプロセスを簡単にまとめた.
  • libc malloc()呼び出し
  • malloc hookが空かどうか
  • NULL以外の場合は、malloc hookの値を指定して関数ポインタとして実行
  • NULLの場合、int malloc()呼び出し
  • に移動します.
  • int malloc()呼び出し
  • __libc_malloc()関数の実装コードは次のとおりです.
    void *
    __libc_malloc (size_t bytes)
    {
      mstate ar_ptr;
      void *victim;
    
      void *(*hook) (size_t, const void *)
        = atomic_forced_read (__malloc_hook);
      if (__builtin_expect (hook != NULL, 0))
        return (*hook)(bytes, RETURN_ADDRESS (0));
    
      arena_get (ar_ptr, bytes);
    
      victim = _int_malloc (ar_ptr, bytes);
      /* Retry with another arena only if we were able to find a usable arena
         before.  */
      if (!victim && ar_ptr != NULL)
        {    
          LIBC_PROBE (memory_malloc_retry, 1, bytes);
          ar_ptr = arena_get_retry (ar_ptr, bytes);
          victim = _int_malloc (ar_ptr, bytes);
        }    
    
      if (ar_ptr != NULL)
        (void) mutex_unlock (&ar_ptr->mutex);
    
      assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
              ar_ptr == arena_for_chunk (mem2chunk (victim)));
      return victim;
    }
    「Hook Overwrite Exploit」は、Hookに関連する関数を処理する機会があるため、ここでは詳細に説明しません.
    内部コードを表示すると、2つの変数が宣言されていることがわかります.mstate ar_ptrの場合は、ブロックを割り当てる「arena」のptr値を指定するmalloc_state構造を参照してください.void *victimの変数は、ブロック割当てが完了した後に返される「mem」領域のアドレスを格納し、後で説明するコードで主な動作目標となる項目を関数を使用して「被害者」と命名するために使用される.
    Hook
    変数の宣言が完了したら、hook値がNULLであるかどうかを確認します.デフォルトではNULL値に初期化されているため、NULLでない場合はHook Overwriteで説明を続けます.
    arena_getarena_get()の動作はarenaです.cファイルで定義します.内容は次のとおりです.
    #define arena_get(ptr, size) do {                                        \
        ptr = thread_arena;                                                  \
        arena_lock (ptr, size);                                              \
    } while (0)
    受信したパラメータによってarena_lock()マクロを再呼び出し,デッドロック防止プロセスを推定した._int_malloc()arenaに対する事前準備が完了した後、_int_malloc()関数を呼び出して実際のデータブロックを割り当てる.実装コード量が大きいため,ランダム分割解析を行った.
    Variable Declaration
    3318 static void *
    3319 _int_malloc (mstate av, size_t bytes)
    3320 {
    3321   INTERNAL_SIZE_T nb;               /* normalized request size */
    3322   unsigned int idx;                 /* associated bin index */
    3323   mbinptr bin;                      /* associated bin */
    3324 
    3325   mchunkptr victim;                 /* inspected/selected chunk */
    3326   INTERNAL_SIZE_T size;             /* its size */
    3327   int victim_index;                 /* its bin index */
    3328 
    3329   mchunkptr remainder;              /* remainder from a split */
    3330   unsigned long remainder_size;     /* its size */
    3331 
    3332   unsigned int block;               /* bit map traverser */
    3333   unsigned int bit;                 /* bit map traverser */
    3334   unsigned int map;                 /* current word of binmap */
    3335 
    3336   mchunkptr fwd;                    /* misc temp for linking */
    3337   mchunkptr bck;                    /* misc temp for linking */
    3338 
    3339   const char *errstr = NULL;
    まず、「mbinptr」および「mchunkptr」はいずれもtypedef struct malloc_chunk*と宣言されていることに注意してください.typedef struct malloc_chunk* mchunkptr; typedef struct malloc_chunk *mbinptr;「INTERNAL SIZE T」はsize tデータ型と同様にx 86で4、x 86-64で8バイトである.
    リクエストのサイズが有効範囲内であるかどうか、および使用可能なarenaが存在するかどうかを決定する構文があります.
    3341   /*
    3342      Convert request size to internal form by adding SIZE_SZ bytes
    3343      overhead plus possibly more to obtain necessary alignment and/or
    3344      to obtain a size of at least MINSIZE, the smallest allocatable
    3345      size. Also, checked_request2size traps (returning 0) request sizes
    3346      that are so large that they wrap around zero when padded and
    3347      aligned.
    3348    */
    3349 
    3350   checked_request2size (bytes, nb);
    3351 
    3352   /* There are no usable arenas.  Fall back to sysmalloc to get a chunk from
    3353      mmap.  */
    3354   if (__glibc_unlikely (av == NULL))
    3355     {
    3356       void *p = sysmalloc (nb, av);
    3357       if (p != NULL)
    3358         alloc_perturb (p, bytes);
    3359       return p;
    3360     }
    checked_request2size()defineによりマクロとして定義され、コードは以下の通りである.
    #define checked_request2size(req, sz)                             \
      if (REQUEST_OUT_OF_RANGE (req)) {                                           \
          __set_errno (ENOMEM);                                                   \
          return 0;                                                               \
        }                                                                         \
      (sz) = request2size (req);
    要求された寸法が有効範囲内であれば、整列のために加工された寸法を返します.
    使用可能なarenaがあるかどうかを決定する構文について、パラメータとして渡されるav値がNULLである場合、sysmalloc()mmap()からblockを取得するように呼び出される.このコースで使用している__glibc_unlikely()については、リンクでKernelで効率を向上させるための使用可能な関数の詳細を参照してください.
    Reference
  • https://m.blog.naver.com/eleexpert/140123898205