Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933724AbdGTERf (ORCPT ); Thu, 20 Jul 2017 00:17:35 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34520 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933653AbdGTERd (ORCPT ); Thu, 20 Jul 2017 00:17:33 -0400 DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 27EDA80E7C Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=jpoimboe@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 27EDA80E7C Date: Wed, 19 Jul 2017 23:17:23 -0500 From: Josh Poimboeuf To: Petr Mladek Cc: Joe Lawrence , live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, Jessica Yu , Jiri Kosina , Miroslav Benes , Chris J Arges Subject: Re: [PATCH] livepatch: add (un)patch hooks Message-ID: <20170720041723.35r6qk2fia7xix3t@treble> References: <1499868600-10176-1-git-send-email-joe.lawrence@redhat.com> <1499868600-10176-2-git-send-email-joe.lawrence@redhat.com> <20170717155144.GF32632@pathway.suse.cz> <20170719204952.4fyhtig3rbw7z4w4@treble> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20170719204952.4fyhtig3rbw7z4w4@treble> User-Agent: Mutt/1.6.0.1 (2016-04-01) X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Thu, 20 Jul 2017 04:17:33 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5287 Lines: 161 On Wed, Jul 19, 2017 at 03:49:52PM -0500, Josh Poimboeuf wrote: > > I am sorry for the long mail. But I have really troubles to > > understand and describe what can be done with these hooks > > a safe way. > > > > It might help if you share some real-life examples. > > Agreed, we should share some real world examples. For a few cases, load > hooks were extremely useful. But most of our experience has been with > the kpatch consistency model, so we need to revisit our past findings > and view them through the livepatch lens. > > One crazy -- but potentially very useful -- idea would be if the user > were allowed to run stop_machine() from the load hook. If possible, > that would help prevent a lot of race conditions. To try to give us all a better idea of what's needed, here are some of the patches that Joe and I looked at before which seem to need load hooks. A few of these are ones we actually delivered to customers with kpatch. I've tried to re-analyze them in light of the livepatch CM. First, a few observations: - Load hooks are a power feature. They're live patching in "hard mode". But they're also very powerful. Luckily I think they're only needed in a few cases, probably < 5% of patches. - In general, the hooks seem to be useful for cases like: - global data updates - "patches" to __init and probe functions - patching otherwise unpatchable code (i.e., assembly) In many/most cases, it seems like stop_machine() would be very useful to avoid concurrency issues. - The more examples I look at, the more I'm thinking we will need both pre-patch and post-patch hooks, as well as pre-unpatch and post-unpatch hooks. - The pre-patch and pre-unpatch hooks can be run before the patching/unpatching process begins. - The post-patch and post-unpatch hooks will need to be run from either klp_complete_transition() or klp_module_coming/going(), depending on whether the to-be-patched module is already loaded or is being loaded/unloaded. Here's a simple example: 75ff39ccc1bd ("tcp: make challenge acks less predictable") It involves changing a global sysctl, as well as a patch to the tcp_send_challenge_ack() function. We used load hooks to change the sysctl. In this case, if we're being super paranoid, it might make sense to patch the data *after* patching is complete (i.e., a post-patch hook), so that tcp_send_challenge_ack() could first be changed to read sysctl_tcp_challenge_ack_limit with READ_ONCE. But I think the race is harmless (and such a race already exists in that function with respect to sysctl writes anyway). Another way of dealing with concurrency would be to use stop_machine() in the load hook. Another example: 48900cb6af42 ("virtio-net: drop NETIF_F_FRAGLIST") That changes the net_device features in a driver probe function. I don't know exactly how to patch it, but if it's possible, I'm pretty sure load hooks is the way to do it :-) Another one: 54a20552e1ea ("KVM: x86: work around infinite loop in microcode when #AC is delivered") Again, I have no idea how to do it, but I'd bet that load hooks are involved. This one was interesting: 6f442be2fb22 ("x86_64, traps: Stop using IST for #SS") A livepatch patch for it is below. We had something similar for kpatch. The below patch is completely untested because we don't have kpatch-build tooling support for livepatch hooks yet. Note that the load hook would need to run *after* the patch has been applied and the transition has completed. And also, it would need to run inside stop_machine(). I didn't put that in the patch yet. But it should at least give you an idea. diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 819662746e23..68fe9d5f1c22 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -236,6 +236,7 @@ DO_ERROR(X86_TRAP_NP, SIGBUS, "segment not present", segment_not_present) #ifdef CONFIG_X86_32 DO_ERROR(X86_TRAP_SS, SIGBUS, "stack segment", stack_segment) #endif +DO_ERROR(X86_TRAP_SS, SIGBUS, "stack segment", stack_segment_v2) DO_ERROR(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check) #ifdef CONFIG_X86_64 diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c index cbd82dff7e81..504b01dea937 100644 --- a/fs/proc/cmdline.c +++ b/fs/proc/cmdline.c @@ -27,3 +27,42 @@ static int __init proc_cmdline_init(void) return 0; } fs_initcall(proc_cmdline_init); + + +#include "kpatch-macros.h" +#include +#include +#include +#define trace_stack_segment_v2 stack_segment_v2 + +static void swapgs_load_hook(void) +{ + /* bug doesn't exist on xen */ + if (paravirt_enabled() && strcmp(pv_info.name, "KVM")) + return; + + write_cr0(read_cr0() & ~X86_CR0_WP); + barrier(); + + /* disable IST for #SS */ + set_intr_gate(X86_TRAP_SS, stack_segment_v2); + + barrier(); + write_cr0(read_cr0() | X86_CR0_WP); +} +KLP_LOAD_HOOK(swapgs_load_hook); + +static void swapgs_unload_hook(void) +{ + if (paravirt_enabled() && strcmp(pv_info.name, "KVM")) + return; + + write_cr0(read_cr0() & ~X86_CR0_WP); + barrier(); + + set_intr_gate_ist(X86_TRAP_SS, stack_segment_v2, STACKFAULT_STACK); + + barrier(); + write_cr0(read_cr0() | X86_CR0_WP); +} +KLP_UNLOAD_HOOK(swapgs_unload_hook);