From patchwork Mon Dec 7 00:57:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adder X-Patchwork-Id: 41327 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5DE3A3857005; Mon, 7 Dec 2020 00:58:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5DE3A3857005 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1607302691; bh=VJoWU+PsgbVjE3eLQV+CBeX/ATXrnTF04drhrk0Zke0=; h=Date:Subject:To:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=xPtRX+5i4e2tEJUT4JfmHrv3wbd8p+9DVKBefUSO7I4Z57LQzT4MfsxCsiylbPpNS FpAiJRI2AxXX2KXSXBtsEDEih5fmyE2wZPSDGKGYKKCJXY+crlxUg1FHUrw3p/KAZJ nM2oVfRc35GoY+7I1btYos8fIuy6iB9wEaLZPaws= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-lf1-x143.google.com (mail-lf1-x143.google.com [IPv6:2a00:1450:4864:20::143]) by sourceware.org (Postfix) with ESMTPS id 612503857C78 for ; Mon, 7 Dec 2020 00:58:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 612503857C78 Received: by mail-lf1-x143.google.com with SMTP id u19so15633657lfr.7 for ; Sun, 06 Dec 2020 16:58:09 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=VJoWU+PsgbVjE3eLQV+CBeX/ATXrnTF04drhrk0Zke0=; b=npiGic/fwxmVCDZklrGfz9S3g5MUZTN2VtzkgUOHZ9OcSXkgH+oa0W1A/xV/UxwiZW F03hdTZjHj5xedSqEdMtTM0oIFQ8TE8xwxF1ctfwpLtfzOgfVEAF5d4LlQTc7qlK8ms3 XoOyGmy6kmpAAfR97gZD2WF2S+Hh9iQAMuS6phdQaCxDyfnqFX632jFLNuSYmdUdGXkP nsJ117T9FS01ybTdfn7eNTzIvnCjK/skJKBdXKG4HmCApgMqtTMpUBU7/VtSNvxoCFeY 5Djb8I1GoIiY/kvdm+KCz0CyERbik4eke8SK1IJBZIOW8GbL39eI5la+kWNI8GFGoiIr 3mAg== X-Gm-Message-State: AOAM532Dr+9qDHIjjtSDyKpjs2jTuZsT09i8A5St8Skt6PiLaAFUBhbE EJA62gWyb1LUiKtCnxXLpZogiK1DujEPPYMT2HH0HBE7Qok= X-Google-Smtp-Source: ABdhPJwZi4niebojBohKO56jmXKDCt1MPPNBn060i14VdV5lmHsyLIpkUx6PjmgPwKCvFAwonK7w0nkKqCzr9gDLRCs= X-Received: by 2002:a05:6512:1095:: with SMTP id j21mr6878839lfg.309.1607302687759; Sun, 06 Dec 2020 16:58:07 -0800 (PST) MIME-Version: 1.0 Date: Mon, 7 Dec 2020 02:57:55 +0200 Message-ID: Subject: [PATCH] malloc tcache: Debugger now sees the address of the corrupted chunk. To: libc-alpha@sourceware.org, dj@redhat.com, fweimer@redhat.com X-Spam-Status: No, score=-10.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adder via Libc-alpha From: Adder Reply-To: Adder Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" If the user overwrites the "next" field of the tcache_entry (i.e. the first 4 or 8 bytes at the address returned by malloc) illegally (after having freed the chunk), it is possible (and actually probable, especially on 64-bit platforms) that the resulting pointer cannot be dereferenced. Thus, a crash is caused upon a later allocation of similar size (in our tcache_get function). But at that point, only the "next" field is available to the debugger (it has been copied in tcache->entries[tc_idx] and is being dereferenced). In particular, the address of the corrupted chunk is not available. To make it available and therefore to ease debugging, we attempt to dereference (currently for reading) the "next" field earlier, while the address of the chunk is still available. --- malloc/malloc.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) +{ + tcache_entry *const e_next = REVEAL_PTR (e->next); + __libc_malloc_tcache_dbg_peek (dbg_tcache, dbg_tc_idx, e, e_next); + return e_next; +} + /* Caller must ensure that we know tc_idx is valid and there's available chunks to remove. */ static __always_inline void * @@ -2954,7 +2993,7 @@ tcache_get (size_t tc_idx) tcache_entry *e = tcache->entries[tc_idx]; if (__glibc_unlikely (!aligned_OK (e))) malloc_printerr ("malloc(): unaligned tcache chunk detected"); - tcache->entries[tc_idx] = REVEAL_PTR (e->next); + tcache->entries[tc_idx] = tcache_dbg_get_next (tcache, tc_idx, e); --(tcache->counts[tc_idx]); e->key = NULL; return (void *) e; diff --git a/malloc/malloc.c b/malloc/malloc.c index 5b87bdb0..6e1cae1c 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -2946,6 +2946,45 @@ tcache_put (mchunkptr chunk, size_t tc_idx) ++(tcache->counts[tc_idx]); } +/* Upon free, we overwrite the beginning of the memory block with a tcache_entry. + If the application (illegally) then overwrites the "next" field of our tcache_entry, + a later tcache_get might cause a page fault (SIGSEGV) + when that field is (revealed and) dereferenced. + + But at that point we are not going to see (in the debugger) + the address of the corrupted chunk -- only the (revealed) "next" field. + + Let us dereference the "next" field while we still have the chunk address. + This consumes a few CPU cycles, but the extra information may help debugging. + + The "dbg_" parameters might seem redundant, but they are useful for the debugger + (who might not otherwise be able to obtain their "optimized out" values). + + This function is not inlined and not static in order to prevent optimizations + and ensure all parameters are available. +*/ + +__attribute__ ((noinline)) tcache_entry * +__libc_malloc_tcache_dbg_peek (const tcache_perthread_struct *dbg_tcache, size_t dbg_tc_idx, + const tcache_entry *dbg_e, const tcache_entry *e_next) +{ + /* The caller does not use the result. + Therefore, the REVEAL_PTR computation is only for clarity. + But it might be useful later if we decide to walk for more than one step, + for earlier detection of the write-after-free bug. + (We do return a result in order to help prevent unwanted optimizations.) */ + return e_next ? REVEAL_PTR (e_next->next) : NULL; +} + +static __always_inline tcache_entry * +tcache_dbg_get_next (const tcache_perthread_struct *dbg_tcache, size_t dbg_tc_idx, + const tcache_entry *e)