From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from rock.gnat.com (rock.gnat.com [205.232.38.15]) by sourceware.org (Postfix) with ESMTPS id 377DD38582BA for ; Fri, 8 Jul 2022 17:00:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 377DD38582BA Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 03CD0116897; Fri, 8 Jul 2022 13:00:05 -0400 (EDT) X-Virus-Scanned: Debian amavisd-new at gnat.com Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id gS16z3cU4XBs; Fri, 8 Jul 2022 13:00:04 -0400 (EDT) Received: from free.home (tron.gnat.com [IPv6:2620:20:4000:0:46a8:42ff:fe0e:e294]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by rock.gnat.com (Postfix) with ESMTPS id A15C11168A2; Fri, 8 Jul 2022 13:00:04 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 268Gxui61213927 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 8 Jul 2022 13:59:57 -0300 From: Alexandre Oliva To: Richard Biener Cc: GCC Patches Subject: Re: [PATCH] Control flow redundancy hardening Organization: Free thinker, does not speak for AdaCore References: Errors-To: aoliva@lxoliva.fsfla.org Date: Fri, 08 Jul 2022 13:59:56 -0300 In-Reply-To: (Richard Biener's message of "Fri, 8 Jul 2022 09:06:33 +0200") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-Scanned-By: MIMEDefang 2.84 X-Spam-Status: No, score=-6.3 required=5.0 tests=BAYES_00, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 Jul 2022 17:00:07 -0000 On Jul 8, 2022, Richard Biener wrote: > I'm possibly missing the importance of 'redundancy' in -fharden-control-flow I took "Control Flow Redundancy" as a term of the art and never questioned it. I think the "redundancy" has to do with the fact that control flow is generally affected by tests and conditionals, and the checks that an expected path was seemingly taken is redundant with those. > but how can you, from a set of visited blocks local to a function, > determine whether the control flow through the function is "expected" Hmm, maybe the definition should be in the negated form: what the check catches is *unexpected* execution flows, e.g. when a block that shouldn't have been reached (because none of its predecessors was) somehow was. This unexpected circumstance indicates some kind of fault or attack, which is what IIUC this check is about. Whether the fault was that the hardware took a wrong turn because it was power deprived, or some software exploit returned to an artifact at the end of a function to get it to serve an alternate purpose, the check at the end of the function would catch the unexpected execution of a block that couldn't be reached under normal circumstances, and flag the error before further damage occurs. > Can you elaborate on what kind of "derailed" control flow this catches > (example?) and what cases it does not? As in the comments for the pass: for each visited block, check that at least one predecessor and at least one successor were also visited. > I'm also curious as of how this compares to hardware > mitigations like x86 indirect branch tracking and shadow stack I'm not expert in the field, but my understanding is that these are complementary. Indirect branch tracking constrains the set of available artifacts one might indirectly branch to, but if you reach one of them, you'd be no wiser that something fishy was going on without checking that you got there from some of the predecessor blocks. (we don't really check precisely that, nor do we check at that precise time, but we check at the end of the function that at least one of the predecessor blocks was run.) Constraining the available indirect branch targets helps avoid bypassing the code that sets the bit corresponding to that block, which might enable an attacker to use an artifact without detection., if there's no subsequent block that would be inexplicably reached. Shadow stacks avoid corruption of return addresses, so you're less likely to reach an unexpected block by means of buffer overruns that corrupt the stack and overwrite the return address. Other means to land in the middle of a function, such as corrupting memory or logical units through power deprivation remain, and this pass helps guard against those too. > and how this relates to the LLVM control flow hardening (ISTR such > thing exists). I've never heard of it. I've just tried to learn about it, but I couldn't find anything pertinent. Are you by any chance thinking of https://clang.llvm.org/docs/ControlFlowIntegrity.html ? This appears to be entirely unrelated: the control flow nodes it's concerned with are functions/methods/subprograms in a program, rather than basic blocks within a function. Thanks a lot for these questions. They're going to help me be better prepared for a presentation about various hardening features (*) that I've submitted and am preparing for the upcoming Cauldron. (*) https://docs.adacore.com/live/wave/gnat_rm/html/gnat_rm/gnat_rm/security_hardening_features.html -- Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/ Free Software Activist GNU Toolchain Engineer Disinformation flourishes because many people care deeply about injustice but very few check the facts. Ask me about