@@ -4081,6 +4081,20 @@
pirq= [SMP,APIC] Manual mp-table setup
See Documentation/x86/i386/IO-APIC.rst.
+ memremap.pks_fault_mode= [X86] Control the behavior of page map
+ protection violations. Violations may not be an actual
+ use of the memory but simply an attempt to map it in an
+ incompatible way.
+ (depends on CONFIG_DEVMAP_ACCESS_PROTECTION
+
+ Format: { relaxed | strict }
+
+ relaxed - Print a warning, disable the protection and
+ continue execution.
+ strict - Stop kernel execution via BUG_ON or fault
+
+ default: relaxed
+
plip= [PPT,NET] Parallel port network link
Format: { parport<nr> | timid | 0 }
See also Documentation/admin-guide/parport.rst.
@@ -9,6 +9,7 @@
#include <linux/debugfs.h> /* debugfs_create_u32() */
#include <linux/mm_types.h> /* mm_struct, vma, etc... */
#include <linux/pkeys.h> /* PKEY_* */
+#include <linux/mm.h> /* fault callback */
#include <uapi/asm-generic/mman-common.h>
#include <asm/cpufeature.h> /* boot_cpu_has, ... */
@@ -241,7 +242,12 @@ int handle_abandoned_pks_value(struct pt_regs *regs)
return (ept_regs->thread_pkrs != old);
}
-static const pks_key_callback pks_key_callbacks[PKS_KEY_NR_CONSUMERS] = { 0 };
+static const pks_key_callback pks_key_callbacks[PKS_KEY_NR_CONSUMERS] = {
+ [PKS_KEY_DEFAULT] = NULL,
+#ifdef CONFIG_DEVMAP_ACCESS_PROTECTION
+ [PKS_KEY_PGMAP_PROTECTION] = pgmap_pks_fault_callback,
+#endif
+};
bool handle_pks_key_callback(unsigned long address, bool write, u16 key)
{
@@ -1216,6 +1216,7 @@ static inline bool devmap_protected(struct page *page)
return false;
}
+void __pgmap_protection_flag_invalid(struct dev_pagemap *pgmap);
void __pgmap_mk_readwrite(struct dev_pagemap *pgmap);
void __pgmap_mk_noaccess(struct dev_pagemap *pgmap);
@@ -1232,6 +1233,27 @@ static inline bool pgmap_check_pgmap_prot(struct page *page)
return true;
}
+/*
+ * pgmap_protection_flag_invalid - Check and flag an invalid use of a pgmap
+ * protected page
+ *
+ * There are code paths which are known to not be compatible with pgmap
+ * protections. pgmap_protection_flag_invalid() is provided as a 'relief
+ * valve' to be used in those functions which are known to be incompatible.
+ *
+ * Thus an invalid code path can be flag more precisely what code contains the
+ * bug vs just flagging a fault. Like the fault handler code this abandons the
+ * use of the PKS key and optionally allows the calling code path to continue
+ * based on the configuration of the memremap.pks_fault_mode command line
+ * (and/or sysfs) option.
+ */
+static inline void pgmap_protection_flag_invalid(struct page *page)
+{
+ if (!pgmap_check_pgmap_prot(page))
+ return;
+ __pgmap_protection_flag_invalid(page->pgmap);
+}
+
static inline void pgmap_mk_readwrite(struct page *page)
{
if (!pgmap_check_pgmap_prot(page))
@@ -1247,10 +1269,14 @@ static inline void pgmap_mk_noaccess(struct page *page)
bool pgmap_protection_enabled(void);
+bool pgmap_pks_fault_callback(unsigned long address, bool write);
+
#else
static inline void __pgmap_mk_readwrite(struct dev_pagemap *pgmap) { }
static inline void __pgmap_mk_noaccess(struct dev_pagemap *pgmap) { }
+
+static inline void pgmap_protection_flag_invalid(struct page *page) { }
static inline void pgmap_mk_readwrite(struct page *page) { }
static inline void pgmap_mk_noaccess(struct page *page) { }
static inline bool pgmap_protection_enabled(void)
@@ -95,6 +95,91 @@ static void devmap_protection_disable(void)
static_branch_dec(&dev_pgmap_protection_static_key);
}
+/*
+ * Ignore the checkpatch warning because the typedef allows
+ * param_check_pks_fault_modes to automatically check the passed value.
+ */
+typedef enum {
+ PKS_MODE_STRICT = 0,
+ PKS_MODE_RELAXED = 1,
+} pks_fault_modes;
+
+pks_fault_modes pks_fault_mode = PKS_MODE_RELAXED;
+
+static int param_set_pks_fault_mode(const char *val, const struct kernel_param *kp)
+{
+ int ret = -EINVAL;
+
+ if (!sysfs_streq(val, "relaxed")) {
+ pks_fault_mode = PKS_MODE_RELAXED;
+ ret = 0;
+ } else if (!sysfs_streq(val, "strict")) {
+ pks_fault_mode = PKS_MODE_STRICT;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int param_get_pks_fault_mode(char *buffer, const struct kernel_param *kp)
+{
+ int ret = 0;
+
+ switch (pks_fault_mode) {
+ case PKS_MODE_STRICT:
+ ret = sysfs_emit(buffer, "strict\n");
+ break;
+ case PKS_MODE_RELAXED:
+ ret = sysfs_emit(buffer, "relaxed\n");
+ break;
+ default:
+ ret = sysfs_emit(buffer, "<unknown>\n");
+ break;
+ }
+
+ return ret;
+}
+
+static const struct kernel_param_ops param_ops_pks_fault_modes = {
+ .set = param_set_pks_fault_mode,
+ .get = param_get_pks_fault_mode,
+};
+
+#define param_check_pks_fault_modes(name, p) \
+ __param_check(name, p, pks_fault_modes)
+module_param(pks_fault_mode, pks_fault_modes, 0644);
+
+static void pgmap_abandon_protection(void)
+{
+ static bool protections_abandoned = false;
+
+ if (!protections_abandoned) {
+ protections_abandoned = true;
+ pks_abandon_protections(PKS_KEY_PGMAP_PROTECTION);
+ }
+}
+
+void __pgmap_protection_flag_invalid(struct dev_pagemap *pgmap)
+{
+ BUG_ON(pks_fault_mode == PKS_MODE_STRICT);
+
+ WARN_ONCE(1, "Page map protection disabled");
+ pgmap_abandon_protection();
+}
+EXPORT_SYMBOL_GPL(__pgmap_protection_flag_invalid);
+
+bool pgmap_pks_fault_callback(unsigned long address, bool write)
+{
+ /* In strict mode just let the fault handler oops */
+ if (pks_fault_mode == PKS_MODE_STRICT)
+ return false;
+
+ WARN_ONCE(1, "Page map protection disabled");
+ pgmap_abandon_protection();
+ return true;
+}
+EXPORT_SYMBOL_GPL(pgmap_pks_fault_callback);
+
void __pgmap_mk_readwrite(struct dev_pagemap *pgmap)
{
if (!current->pgmap_prot_count++)