Received: by 2002:a05:6a10:5bc5:0:0:0:0 with SMTP id os5csp888694pxb; Wed, 27 Oct 2021 14:31:15 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzo1N32sroxSlWqvS/vsnjDSZmwKuS7VtD3lGbhHjT5Pp/5xn7uIHQn4Ga1ZokUHRX5ip1T X-Received: by 2002:aa7:c6c1:: with SMTP id b1mr493209eds.11.1635370275123; Wed, 27 Oct 2021 14:31:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1635370275; cv=none; d=google.com; s=arc-20160816; b=i42RhD5YDqiLga09t+CPvKHsa0pYbozH+yNBnSUWJ2BOUiJCoOP4jaPmuI/NmbuhXf 5H25ISRKaKAkP1dQ3rMhii7QpWP4r6S6aYxQODbwZ1IIGmGA5y5el8BFYsNSRGmg5KrW QaZ4IklpDDdIzn+SzdUaXZjYHrs8E9IsPV9UOX6C3Ug/mDgfOv4rAsHVqkvAeIG01YEu tztXVQqLMrwVfBgnfmYCGXeWrcN1OXLNPW1L44Uhv63w4vUgyT3I1agynikMEaZNfg0R qPQaptqdNUWgcRPmwS/gproXZK1WSFE2JL7VZGoKCeB7FWhgbjfrp4EtnEDk1A4QS6k2 LujA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=SPqzlmEweI7r1mGXnYdr5XuSqmGXW0SkyZAuxP8LyMQ=; b=Uumzzo4LXn3TaddbWCSi+kr99tDk3C9gQF7A14Y3VJkB4FytmluUTfeu1bjOqhSaZa IeC/t2flEpym0vG8n7umFzmLmiLkcpdk8bkQBw78yFHrTcWQAiRsx5t+zKOI7aFZ7LQS 014MPTNavqzZmGmWEqsMJ5qGVpOUs0BLc0OwACfFl6e/SArpGQmuLMMjkvp/OcA9Hs0F Cb7QJQqgI5AMegxukSO5jVr5Syv6bubbmNzGWj1jIDYa9YHgAY0B8Fr2fFgsMQE9kyPC D+q5gZdEOuTxjuaEsws6BLNkv9TBvI7w4HnLvRxGrpeyt/glUE6bPlCppchl8A3Q4vhl 6p0A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=TxzOqOdA; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id nd7si1645269ejc.595.2021.10.27.14.30.48; Wed, 27 Oct 2021 14:31:15 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=TxzOqOdA; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238429AbhJ0RN4 (ORCPT + 97 others); Wed, 27 Oct 2021 13:13:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53490 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239755AbhJ0RNz (ORCPT ); Wed, 27 Oct 2021 13:13:55 -0400 Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F6B2C061745 for ; Wed, 27 Oct 2021 10:11:30 -0700 (PDT) Received: by mail-pj1-x102a.google.com with SMTP id t5-20020a17090a4e4500b001a0a284fcc2so5671532pjl.2 for ; Wed, 27 Oct 2021 10:11:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=SPqzlmEweI7r1mGXnYdr5XuSqmGXW0SkyZAuxP8LyMQ=; b=TxzOqOdAm06wISQQDG9AUQtMp6CPyx+4EqB3sWwvACs6OPGjSg0kvIFotrs3f5Hr/g 36y0FvkG5ulwsPM+LeYfBGcFyNbRXJWvHyVi0cqnMkMIdWKizVF1QhDWJMEjCdnBnbJu Qbar4HPvzJ08E92cM3uqXKFxfn33qSa2sqNMo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=SPqzlmEweI7r1mGXnYdr5XuSqmGXW0SkyZAuxP8LyMQ=; b=Bf5KYcO0t3lUrGh6Or4N9cDC0cn8VoFdK6cNTtFja2t/VAMc0thwmV6ua3U8i41zYK yrEZw+uNp2oN8G2AtxEk029j1fk07x4fJQs/NVa6y9VrvKHmQuby2snj628f0OSE5LUx JekjW+y+6wVr6dR4Lj7Z5HocrF1XlONKJvG2U5gWeLzv27nAYq1RIl7gI66XTkJWRCAD 9cld1abJa0HU1lCIFq31/r27YBY4cT3YfuFyL7mNgEBdC1eOidej4NxqZwlD50Qo8IId PQOxGApV01JfsBiUmlGg43ZOAj2c5Ar+liYBUzoZJsbIxw+OIIeYvQ0cpYinnCrjomIQ Wyng== X-Gm-Message-State: AOAM530faIcNL5yIVQbnZIBfmkCxJuJSDg817m8cMBJfOZx6nnDhyPWq 1c5PHoeP6Fre4ITfYnRsGZOztVNnlL59/A== X-Received: by 2002:a17:90b:17d2:: with SMTP id me18mr6983821pjb.98.1635354689817; Wed, 27 Oct 2021 10:11:29 -0700 (PDT) Received: from www.outflux.net (smtp.outflux.net. [198.145.64.163]) by smtp.gmail.com with ESMTPSA id lb5sm315185pjb.11.2021.10.27.10.11.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Oct 2021 10:11:29 -0700 (PDT) Date: Wed, 27 Oct 2021 10:11:28 -0700 From: Kees Cook To: Peter Zijlstra Cc: Ard Biesheuvel , Mark Rutland , Sami Tolvanen , X86 ML , Josh Poimboeuf , Nathan Chancellor , Nick Desaulniers , Sedat Dilek , Steven Rostedt , linux-hardening@vger.kernel.org, Linux Kernel Mailing List , llvm@lists.linux.dev Subject: Re: [PATCH v5 00/15] x86: Add support for Clang CFI Message-ID: <202110270939.F5C79CC@keescook> References: <20211013181658.1020262-1-samitolvanen@google.com> <20211026201622.GG174703@worktop.programming.kicks-ass.net> <20211027120515.GC54628@C02TD0UTHF1T.local> <20211027124852.GK174703@worktop.programming.kicks-ass.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, Oct 27, 2021 at 03:04:55PM +0200, Peter Zijlstra wrote: > On Wed, Oct 27, 2021 at 02:48:52PM +0200, Peter Zijlstra wrote: > > On Wed, Oct 27, 2021 at 02:22:27PM +0200, Ard Biesheuvel wrote: > > > On Wed, 27 Oct 2021 at 14:05, Mark Rutland wrote: > > > > > > > Should not this jump-table thingy get converted to an actual function > > > > > address somewhere around arch_static_call_transform() ? This also seems > > > > > relevant for arm64 (which already has CLANG_CFI supported) given: > > > > > > > > > > https://lkml.kernel.org/r/20211025122102.46089-3-frederic@kernel.org > > > > > > > > Ugh, yeah, we'll need to do the function_nocfi() dance somewhere... > > > > > > > > > > Sadly, that only works on symbol names, so we cannot use it to strip > > > CFI-ness from void *func arguments passed into the static call API, > > > unfortunately. > > > > Right, and while mostly static_call_update() is used, whcih is a macro > > and could possibly be used to wrap this, we very much rely on > > __static_call_update() also working without that wrapper and then we're > > up a creek without no paddles. > > Specifically, we support code like: > > struct foo { > void (*func1)(args1); > void (*func2)(args2); > } > > struct foo global_foo; And global_foo is intentionally non-const? > > ... > > DEFINE_STATIC_CALL_NULL(func1, *global_foo.func1); > > ... > > __init foo_init() > { > // whatever code that fills out foo > > static_call_update(func1, global_foo.func1); > } > > ... > > hot_function() > { > ... > static_cal(func1)(args1); > ... > } > > cold_function() > { > ... > global_foo->func1(args1); > ... > } If global_foo is non-const, then the static call stuff is just an obfuscated indirect call. The attack CFI attempts to block is having a controlled write flaw turn into controlled execution. For example, in the above, an attacker would use a flaw that could aim a write to global_foo->func1, and then get the kernel to take an execution path that executes global_foo->func1 (i.e. through cold_function). If static_call_update() accepts an arbitrary function argument, then it needs to perform the same validation that is currently being injected at indirect call sites to avoid having static calls weaken CFI. If things are left alone, static calls will just insert a direct call to the jump table, and things "work" (with an extra jump), but nothing is actually protected (it just requires the attacker locate a more narrow set of kernel call flows that includes static_call_update). Any attacker write to global_foo->func1, followed by a static_call_update(), will create a direct call (with no CFI checking) to the attacker's destination. (It's likely harder to find the needed execution path to induce a static_call_update(), but that shouldn't stop us from trying to protect it.) Getting a "jump table to actual function" primitive only avoids the added jump -- all the CFI checking remains bypassed. If static call function address storage isn't const, for CFI to work as expected the update() routine will need to do the same checking that is done at indirect call sites when extracting the "real" function for writing into a direct call. (i.e. it will need to be function prototype aware, be able to find the real matching jump table and size, and verify the given function is actually in the table. Just dereferencing the jump table to find the address isn't a protection: the attacker just has to write to func1 with a pointer to an attacker-constructed jump table.) I think it's not unreasonable to solve this in phases, though. Given that static calls work with CFI as-is (albeit with an extra jump), and that static calls do offer some hardening of existing indirect call targets (by narrowing the execution path needed to actually bring the func1 value into the kernel execution flow), I don't see either feature blocking the other. Adding proper CFI function prototype checking to static calls seems like a security improvement with or without CFI, and a minor performance improvement under CFI. To avoid all of this, though, it'd be better if static calls only switched between one of a per-site const list of possible functions, which would make it a much tighter hand-rolled CFI system itself. :) (i.e. it would operate from a specific and short list of expected functions rather than the "best effort" approach of matching function prototypes as done by Clang CFI.) -- Kees Cook