From nobody Tue Apr 7 14:04:30 2026 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6DDC730CDAE for ; Wed, 25 Feb 2026 20:36:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772051819; cv=none; b=oj0uZttz3fuMs3socfVP4lLhbB314BDrLaKDIIKAqPNaWVcHnaIgoV8xjz/JZZiJVPQoKONiC85+qzqbF0X8gMnogD7unul7ED5m8WFcxBDGOne68Ns6fMzwyO8nFQxkpu5zpAYebiG8JdYRld/gTmMH2h3sOtDlrZIpxQY9EmQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772051819; c=relaxed/simple; bh=gNXLf2LKE1DuJc2Y0oQKcfavjlPkhFFcfvH8nC7wdJA=; h=Date:Mime-Version:Message-ID:Subject:From:To:Cc:Content-Type; b=C8fVCyp/ZZiYaM/ca2XdUQhO50HCvsNj89Ohk9JRPvOmrmMu9pD6+svmGUC7mhRKK3upAX8RivbeJG/WSP3f5ac+P9JyVvb8x6NDrxkbmKDsh38ODzDlrTmkHxi0oplpQRsdwxHsZse3S/Q1isTmLIqtnaTtltP4h4+nMMZ2JIQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--elver.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=XYLT2Xj7; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--elver.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="XYLT2Xj7" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-4837f288194so1250245e9.2 for ; Wed, 25 Feb 2026 12:36:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772051816; x=1772656616; darn=vger.kernel.org; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=3O3Me7fgwyHmdf0v5rLq8O8sd5sCO/cE1nxT2vqkXJo=; b=XYLT2Xj7CuRpBsYF9OVkllFHruNDTKHf4wRiAeIfNda2JrfB03hYSJAl4LjwnoRnhE +K8L6rB1ZOxk3QM8yAjPyv0HExVXrxVvWuzSEBMfLLUbdJ/6z5PD+vQjA1OFF003qclX ypnXFqJpoD+qZaiPMGvcjA3jHJQLQPXfK7zQwhrXyAlI+dY1C1i2Mj5fgsuQQtZybYRa X26jGtWgVdH1l/5m4Z1eiJ/YXLaEGDAoH/Px+iM69n47TmMsf56SEahVpk8Kst0F7oik 23U3b+iyM1LIMJcgWQSmaoWu+d/2lbJjmli9UrMayQKUwcp5yqfriPW2PUU+2Q1idiRv +LYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772051816; x=1772656616; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=3O3Me7fgwyHmdf0v5rLq8O8sd5sCO/cE1nxT2vqkXJo=; b=sbXvYyMzicuaC2G/ht2VA5dGWnKLEFiTzl5jLs3oZXZuhSRghBISWpGKs+SNqQ467+ 1RmQs55Uu+JVeJItkj4XrgdBw0uRGlBS2uZtY3MemhQ8Ebb+FNp/kyPNsVDopIAvFhWk Huake31fIhID1K2kE+s/xXxfZEy7EycDuBaeswcuduwl/RgWdE4u8HFgsGVO506KSPwn V9CzNZz96nWMHLaqoNQSUOaVYmp4nXx6zKZ2WmkvecwJ70jMdIw1NrHX8tJxov4jo116 e/XgvPbp75aksWYlAVXd4KvBsNY+JINpVba6frmegZ+wD3ItXZYAevqhGMkZyyHPF2am yQXw== X-Forwarded-Encrypted: i=1; AJvYcCVpKvg+UG7DRIjSdjeKEAVPyGtt2djwZlurR60LFRaGoYL9e6ApggXERp9IZPuGs431XddoeAoFInWI/s8=@vger.kernel.org X-Gm-Message-State: AOJu0YyXqoNeBPF1ME8v+6oehyWAKcg/NJb7V4wqnLecaqBoHcN/E4Dh ZcdykRue7hKJ27GYPghlwTl91uoPb+SQERWHT+bOJeMqr+ui6T1w+DR6abtVWyKCOaLVtvcc65+ +gQ== X-Received: from wmbjp9.prod.google.com ([2002:a05:600c:5589:b0:47f:c96e:8381]) (user=elver job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600d:6409:20b0:483:78c5:d743 with SMTP id 5b1f17b1804b1-483a9637a19mr199333935e9.28.1772051815765; Wed, 25 Feb 2026 12:36:55 -0800 (PST) Date: Wed, 25 Feb 2026 21:36:05 +0100 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Mailer: git-send-email 2.53.0.414.gf7e9f6c205-goog Message-ID: <20260225203639.3159463-1-elver@google.com> Subject: [PATCH] kfence: add kfence.fault parameter From: Marco Elver To: elver@google.com, Andrew Morton Cc: Alexander Potapenko , Dmitry Vyukov , Jonathan Corbet , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com, workflows@vger.kernel.org, linux-mm@kvack.org, Ernesto Martinez Garcia , Kees Cook Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add kfence.fault parameter to control the behavior when a KFENCE error is detected (similar in spirit to kasan.fault=3D). The supported modes for kfence.fault=3D are: - report: print the error report and continue (default). - oops: print the error report and oops. - panic: print the error report and panic. In particular, the 'oops' mode offers a trade-off between no mitigation on report and panicking outright (if panic_on_oops is not set). Signed-off-by: Marco Elver Reviewed-by: Alexander Potapenko --- .../admin-guide/kernel-parameters.txt | 6 +++ Documentation/dev-tools/kfence.rst | 7 +++ mm/kfence/core.c | 23 ++++++--- mm/kfence/kfence.h | 16 +++++- mm/kfence/report.c | 49 +++++++++++++++++-- 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentatio= n/admin-guide/kernel-parameters.txt index cb850e5290c2..05acdea306b2 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2958,6 +2958,12 @@ Kernel parameters Format: Default: CONFIG_KFENCE_DEFERRABLE =20 + kfence.fault=3D [MM,KFENCE] Controls the behavior when a KFENCE + error is detected. + report - print the error report and continue (default). + oops - print the error report and oops. + panic - print the error report and panic. + kfence.sample_interval=3D [MM,KFENCE] KFENCE's sample interval in milliseconds. Format: diff --git a/Documentation/dev-tools/kfence.rst b/Documentation/dev-tools/k= fence.rst index 541899353865..b03d1201ddae 100644 --- a/Documentation/dev-tools/kfence.rst +++ b/Documentation/dev-tools/kfence.rst @@ -81,6 +81,13 @@ tables being allocated. Error reports ~~~~~~~~~~~~~ =20 +The boot parameter ``kfence.fault`` can be used to control the behavior wh= en a +KFENCE error is detected: + +- ``kfence.fault=3Dreport``: Print the error report and continue (default). +- ``kfence.fault=3Doops``: Print the error report and oops. +- ``kfence.fault=3Dpanic``: Print the error report and panic. + A typical out-of-bounds access looks like this:: =20 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D diff --git a/mm/kfence/core.c b/mm/kfence/core.c index b4ea3262c925..a5f7dffa9f6f 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -50,7 +50,7 @@ =20 /* =3D=3D=3D Data =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */ =20 -static bool kfence_enabled __read_mostly; +bool kfence_enabled __read_mostly; static bool disabled_by_warn __read_mostly; =20 unsigned long kfence_sample_interval __read_mostly =3D CONFIG_KFENCE_SAMPL= E_INTERVAL; @@ -335,6 +335,7 @@ metadata_update_state(struct kfence_metadata *meta, enu= m kfence_object_state nex static check_canary_attributes bool check_canary_byte(u8 *addr) { struct kfence_metadata *meta; + enum kfence_fault fault; unsigned long flags; =20 if (likely(*addr =3D=3D KFENCE_CANARY_PATTERN_U8(addr))) @@ -344,8 +345,9 @@ static check_canary_attributes bool check_canary_byte(u= 8 *addr) =20 meta =3D addr_to_metadata((unsigned long)addr); raw_spin_lock_irqsave(&meta->lock, flags); - kfence_report_error((unsigned long)addr, false, NULL, meta, KFENCE_ERROR_= CORRUPTION); + fault =3D kfence_report_error((unsigned long)addr, false, NULL, meta, KFE= NCE_ERROR_CORRUPTION); raw_spin_unlock_irqrestore(&meta->lock, flags); + kfence_handle_fault(fault); =20 return false; } @@ -524,11 +526,14 @@ static void kfence_guarded_free(void *addr, struct kf= ence_metadata *meta, bool z raw_spin_lock_irqsave(&meta->lock, flags); =20 if (!kfence_obj_allocated(meta) || meta->addr !=3D (unsigned long)addr) { + enum kfence_fault fault; + /* Invalid or double-free, bail out. */ atomic_long_inc(&counters[KFENCE_COUNTER_BUGS]); - kfence_report_error((unsigned long)addr, false, NULL, meta, - KFENCE_ERROR_INVALID_FREE); + fault =3D kfence_report_error((unsigned long)addr, false, NULL, meta, + KFENCE_ERROR_INVALID_FREE); raw_spin_unlock_irqrestore(&meta->lock, flags); + kfence_handle_fault(fault); return; } =20 @@ -830,7 +835,8 @@ static void kfence_check_all_canary(void) static int kfence_check_canary_callback(struct notifier_block *nb, unsigned long reason, void *arg) { - kfence_check_all_canary(); + if (READ_ONCE(kfence_enabled)) + kfence_check_all_canary(); return NOTIFY_OK; } =20 @@ -1249,6 +1255,7 @@ bool kfence_handle_page_fault(unsigned long addr, boo= l is_write, struct pt_regs struct kfence_metadata *to_report =3D NULL; unsigned long unprotected_page =3D 0; enum kfence_error_type error_type; + enum kfence_fault fault; unsigned long flags; =20 if (!is_kfence_address((void *)addr)) @@ -1307,12 +1314,14 @@ bool kfence_handle_page_fault(unsigned long addr, b= ool is_write, struct pt_regs if (to_report) { raw_spin_lock_irqsave(&to_report->lock, flags); to_report->unprotected_page =3D unprotected_page; - kfence_report_error(addr, is_write, regs, to_report, error_type); + fault =3D kfence_report_error(addr, is_write, regs, to_report, error_typ= e); raw_spin_unlock_irqrestore(&to_report->lock, flags); } else { /* This may be a UAF or OOB access, but we can't be sure. */ - kfence_report_error(addr, is_write, regs, NULL, KFENCE_ERROR_INVALID); + fault =3D kfence_report_error(addr, is_write, regs, NULL, KFENCE_ERROR_I= NVALID); } =20 + kfence_handle_fault(fault); + return kfence_unprotect(addr); /* Unprotect and let access proceed. */ } diff --git a/mm/kfence/kfence.h b/mm/kfence/kfence.h index f9caea007246..1f618f9b0d12 100644 --- a/mm/kfence/kfence.h +++ b/mm/kfence/kfence.h @@ -16,6 +16,8 @@ =20 #include "../slab.h" /* for struct kmem_cache */ =20 +extern bool kfence_enabled; + /* * Get the canary byte pattern for @addr. Use a pattern that varies based = on the * lower 3 bits of the address, to detect memory corruptions with higher @@ -140,8 +142,18 @@ enum kfence_error_type { KFENCE_ERROR_INVALID_FREE, /* Invalid free. */ }; =20 -void kfence_report_error(unsigned long address, bool is_write, struct pt_r= egs *regs, - const struct kfence_metadata *meta, enum kfence_error_type type); +enum kfence_fault { + KFENCE_FAULT_NONE, + KFENCE_FAULT_REPORT, + KFENCE_FAULT_OOPS, + KFENCE_FAULT_PANIC, +}; + +enum kfence_fault +kfence_report_error(unsigned long address, bool is_write, struct pt_regs *= regs, + const struct kfence_metadata *meta, enum kfence_error_type type); + +void kfence_handle_fault(enum kfence_fault fault); =20 void kfence_print_object(struct seq_file *seq, const struct kfence_metadat= a *meta) __must_hold(&meta->lock); =20 diff --git a/mm/kfence/report.c b/mm/kfence/report.c index 787e87c26926..d548536864b1 100644 --- a/mm/kfence/report.c +++ b/mm/kfence/report.c @@ -7,9 +7,12 @@ =20 #include =20 +#include +#include #include #include #include +#include #include #include #include @@ -29,6 +32,26 @@ #define ARCH_FUNC_PREFIX "" #endif =20 +static enum kfence_fault kfence_fault __ro_after_init =3D KFENCE_FAULT_REP= ORT; + +static int __init early_kfence_fault(char *arg) +{ + if (!arg) + return -EINVAL; + + if (!strcmp(arg, "report")) + kfence_fault =3D KFENCE_FAULT_REPORT; + else if (!strcmp(arg, "oops")) + kfence_fault =3D KFENCE_FAULT_OOPS; + else if (!strcmp(arg, "panic")) + kfence_fault =3D KFENCE_FAULT_PANIC; + else + return -EINVAL; + + return 0; +} +early_param("kfence.fault", early_kfence_fault); + /* Helper function to either print to a seq_file or to console. */ __printf(2, 3) static void seq_con_printf(struct seq_file *seq, const char *fmt, ...) @@ -189,8 +212,9 @@ static const char *get_access_type(bool is_write) return str_write_read(is_write); } =20 -void kfence_report_error(unsigned long address, bool is_write, struct pt_r= egs *regs, - const struct kfence_metadata *meta, enum kfence_error_type type) +enum kfence_fault +kfence_report_error(unsigned long address, bool is_write, struct pt_regs *= regs, + const struct kfence_metadata *meta, enum kfence_error_type type) { unsigned long stack_entries[KFENCE_STACK_DEPTH] =3D { 0 }; const ptrdiff_t object_index =3D meta ? meta - kfence_metadata : -1; @@ -206,7 +230,7 @@ void kfence_report_error(unsigned long address, bool is= _write, struct pt_regs *r =20 /* Require non-NULL meta, except if KFENCE_ERROR_INVALID. */ if (WARN_ON(type !=3D KFENCE_ERROR_INVALID && !meta)) - return; + return KFENCE_FAULT_NONE; =20 /* * Because we may generate reports in printk-unfriendly parts of the @@ -282,6 +306,25 @@ void kfence_report_error(unsigned long address, bool i= s_write, struct pt_regs *r =20 /* We encountered a memory safety error, taint the kernel! */ add_taint(TAINT_BAD_PAGE, LOCKDEP_STILL_OK); + + return kfence_fault; +} + +void kfence_handle_fault(enum kfence_fault fault) +{ + switch (fault) { + case KFENCE_FAULT_NONE: + case KFENCE_FAULT_REPORT: + break; + case KFENCE_FAULT_OOPS: + BUG(); + break; + case KFENCE_FAULT_PANIC: + /* Disable KFENCE to avoid recursion if check_on_panic is set. */ + WRITE_ONCE(kfence_enabled, false); + panic("kfence.fault=3Dpanic set ...\n"); + break; + } } =20 #ifdef CONFIG_PRINTK --=20 2.53.0.414.gf7e9f6c205-goog