diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 27ec49af1bf2..34a293a8fcea 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4388,6 +4388,9 @@
 			the specified number of seconds.  This is to be used if
 			your oopses keep scrolling off the screen.
 
+	pax_softmode=<int>
+			Enables OpenPaX soft mode if set to a non-zero value.
+
 	pcbit=		[HW,ISDN]
 
 	pci=option[,option...]	[PCI,EARLY] various PCI subsystem options.
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index a43897b03ce9..4bae70add098 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -47,6 +47,7 @@
 #include <linux/dax.h>
 #include <linux/uaccess.h>
 #include <linux/rseq.h>
+#include <linux/xattr.h>
 #include <asm/param.h>
 #include <asm/page.h>
 
@@ -816,6 +817,68 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
 	return ret == -ENOENT ? 0 : ret;
 }
 
+#ifdef CONFIG_OPENPAX
+#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS
+static int openpax_parse_xattr_flags(struct file * const file)
+{
+	ssize_t xattr_size, i;
+	unsigned char xattr_value[sizeof("pemrs") - 1];
+
+	xattr_size = pax_getxattr(file, xattr_value, sizeof xattr_value);
+	if (xattr_size < 0 || xattr_size > sizeof xattr_value)
+		return -ENOENT;
+
+	for (i = 0; i < xattr_size; i++)
+		switch (xattr_value[i]) {
+		default:
+			return -EINVAL;
+
+#define parse_flag(option_disable, option_enable, flag)				\
+		case option_disable:						\
+			clear_bit(flag, &current->mm->pax_flags);		\
+			break;							\
+		case option_enable:						\
+			set_bit(flag, &current->mm->pax_flags);			\
+			break;
+
+		parse_flag('p', 'P', PAXF_PAGEEXEC);
+		parse_flag('e', 'E', PAXF_EMUTRAMP);
+		parse_flag('m', 'M', PAXF_MPROTECT);
+		parse_flag('r', 'R', PAXF_RANDMMAP);
+		parse_flag('s', 'S', PAXF_SEGMEXEC);
+#undef parse_flag
+		}
+
+	return 0;
+}
+#endif
+
+static int openpax_set_flags(struct file * const file)
+{
+#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS
+	int error;
+#endif
+	current->mm->pax_flags = 0;
+
+	if (randomize_va_space) {
+		set_bit(PAXF_RANDMMAP, &current->mm->pax_flags);
+	}
+
+	if (!pax_softmode) {
+		set_bit(PAXF_PAGEEXEC, &current->mm->pax_flags);
+		set_bit(PAXF_MPROTECT, &current->mm->pax_flags);
+	}
+
+#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS
+	error = openpax_parse_xattr_flags(file);
+	if (error != -ENOENT)
+		return error;
+#endif
+
+	return 0;
+}
+#endif
+
 static int load_elf_binary(struct linux_binprm *bprm)
 {
 	struct file *interpreter = NULL; /* to shut gcc up */
@@ -1000,10 +1063,28 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
 	   may depend on the personality.  */
 	SET_PERSONALITY2(*elf_ex, &arch_state);
-	if (elf_read_implies_exec(*elf_ex, executable_stack))
-		current->personality |= READ_IMPLIES_EXEC;
 
-	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
+#ifdef CONFIG_OPENPAX
+	retval = openpax_set_flags(bprm->file);
+	if (retval)
+		goto out_free_dentry;
+
+	if (!test_bit(PAXF_EMUTRAMP, &current->mm->pax_flags))
+		executable_stack = EXSTACK_DISABLE_X;
+#endif
+
+	if (elf_read_implies_exec(*elf_ex, executable_stack)) {
+#ifdef CONFIG_OPENPAX
+		if (!test_bit(PAXF_MPROTECT, &current->mm->pax_flags))
+#endif
+			current->personality |= READ_IMPLIES_EXEC;
+	}
+
+	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space
+#ifdef CONFIG_OPENPAX
+	    && test_bit(PAXF_RANDMMAP, &current->mm->pax_flags)
+#endif
+	    )
 		current->flags |= PF_RANDOMIZE;
 
 	setup_new_exec(bprm);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 34a47fb0c57f..cf22281d6fd9 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -436,6 +436,18 @@ __weak void arch_proc_pid_thread_features(struct seq_file *m,
 {
 }
 
+#ifdef CONFIG_OPENPAX
+static inline void task_pax(struct seq_file *m, struct mm_struct *mm)
+{
+	seq_printf(m, "PaX:\t%c%c%c%c%c\n",
+		   test_bit(PAXF_PAGEEXEC, &mm->pax_flags) ? 'P' : 'p',
+		   test_bit(PAXF_EMUTRAMP, &mm->pax_flags) ? 'E' : 'e',
+		   test_bit(PAXF_MPROTECT, &mm->pax_flags) ? 'M' : 'm',
+		   test_bit(PAXF_RANDMMAP, &mm->pax_flags) ? 'R' : 'r',
+		   test_bit(PAXF_SEGMEXEC, &mm->pax_flags) ? 'S' : 's');
+}
+#endif
+
 int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
 			struct pid *pid, struct task_struct *task)
 {
@@ -452,6 +464,9 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
 		task_core_dumping(m, task);
 		task_thp_status(m, mm);
 		task_untag_mask(m, mm);
+#ifdef CONFIG_OPENPAX
+		task_pax(m, mm);
+#endif
 		mmput(mm);
 	}
 	task_sig(m, task);
diff --git a/fs/xattr.c b/fs/xattr.c
index f8b643f91a98..943b096ffbdb 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -424,6 +424,22 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
 }
 EXPORT_SYMBOL(__vfs_getxattr);
 
+#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS
+ssize_t
+pax_getxattr(struct file *file, void *value, size_t size)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	ssize_t error;
+
+	error = inode_permission(file_mnt_idmap(file), inode, MAY_EXEC);
+	if (error)
+		return error;
+
+	return __vfs_getxattr(file->f_path.dentry, inode, XATTR_NAME_USER_PAX_FLAGS, value, size);
+}
+EXPORT_SYMBOL(pax_getxattr);
+#endif
+
 ssize_t
 vfs_getxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 	     const char *name, void *value, size_t size)
diff --git a/include/linux/init.h b/include/linux/init.h
index 58cef4c2e59a..3fe1ecc73c3a 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -144,6 +144,9 @@ extern char __initdata boot_command_line[];
 extern char *saved_command_line;
 extern unsigned int saved_command_line_len;
 extern unsigned int reset_devices;
+#ifdef CONFIG_OPENPAX_SOFTMODE
+extern int pax_softmode;
+#endif
 
 /* used by init/main.c */
 void setup_arch(char **);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index af3a0256fa93..a6915152c7f0 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -906,6 +906,9 @@ struct mm_struct {
 		mm_context_t context;
 
 		unsigned long flags; /* Must use atomic bitops to access */
+#ifdef CONFIG_OPENPAX
+		unsigned long pax_flags;
+#endif
 
 #ifdef CONFIG_AIO
 		spinlock_t			ioctx_lock;
diff --git a/include/linux/mman.h b/include/linux/mman.h
index bcb201ab7a41..f01ff1102dbb 100644
--- a/include/linux/mman.h
+++ b/include/linux/mman.h
@@ -188,11 +188,19 @@ static inline bool arch_memory_deny_write_exec_supported(void)
  *
  *	d)	mmap(PROT_READ | PROT_EXEC)
  *		mmap(PROT_READ | PROT_EXEC | PROT_BTI)
+ *
+ * If OpenPaX is enabled, it will be assumed that we want to deny PROT_WRITE | PROT_EXEC
+ * by default, unless the MPROTECT feature bit is disabled on a binary.
  */
 static inline bool map_deny_write_exec(struct vm_area_struct *vma,  unsigned long vm_flags)
 {
-	if (!test_bit(MMF_HAS_MDWE, &current->mm->flags))
+	if (
+#ifdef CONFIG_OPENPAX_MPROTECT
+	    !test_bit(PAXF_MPROTECT, &current->mm->pax_flags) &&
+#endif
+	    !test_bit(MMF_HAS_MDWE, &current->mm->flags)) {
 		return false;
+	}
 
 	if ((vm_flags & VM_EXEC) && (vm_flags & VM_WRITE))
 		return true;
diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index e62ff805cfc9..b5c31fed6311 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -107,4 +107,12 @@ static inline unsigned long mmf_init_flags(unsigned long flags)
 	return flags & MMF_INIT_MASK;
 }
 
+#ifdef CONFIG_OPENPAX
+#define PAXF_PAGEEXEC		1
+#define PAXF_EMUTRAMP		2
+#define PAXF_MPROTECT		3
+#define PAXF_RANDMMAP		4
+#define PAXF_SEGMEXEC		5
+#endif
+
 #endif /* _LINUX_SCHED_COREDUMP_H */
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index d20051865800..e41b92f2b0dd 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -21,6 +21,7 @@
 
 struct inode;
 struct dentry;
+struct file;
 
 static inline bool is_posix_acl_xattr(const char *name)
 {
@@ -71,6 +72,9 @@ struct xattr {
 	size_t value_len;
 };
 
+#ifdef CONFIG_OPENPAX_XATTR_PAX_FLAGS
+ssize_t pax_getxattr(struct file *, void *, size_t);
+#endif
 ssize_t __vfs_getxattr(struct dentry *, struct inode *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct mnt_idmap *, struct dentry *, const char *,
 		     void *, size_t);
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 9463db2dfa9d..d4264c8df0fb 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -81,5 +81,10 @@
 #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
 #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
 
+/* User namespace */
+#define XATTR_PAX_PREFIX "pax."
+#define XATTR_PAX_FLAGS_SUFFIX "flags"
+#define XATTR_NAME_USER_PAX_FLAGS XATTR_USER_PREFIX XATTR_PAX_PREFIX XATTR_PAX_FLAGS_SUFFIX
+#define XATTR_NAME_PAX_FLAGS XATTR_PAX_PREFIX XATTR_PAX_FLAGS_SUFFIX
 
 #endif /* _UAPI_LINUX_XATTR_H */
diff --git a/init/main.c b/init/main.c
index 206acdde51f5..49478af95719 100644
--- a/init/main.c
+++ b/init/main.c
@@ -188,6 +188,17 @@ static int __init set_reset_devices(char *str)
 
 __setup("reset_devices", set_reset_devices);
 
+#ifdef CONFIG_OPENPAX_SOFTMODE
+int pax_softmode;
+
+static int __init setup_pax_softmode(char *str)
+{
+	get_option(&str, &pax_softmode);
+	return 1;
+}
+__setup("pax_softmode=", setup_pax_softmode);
+#endif
+
 static const char *argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 const char *envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
 static const char *panic_later, *panic_param;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index e0b917328cf9..45651e1b00a2 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1615,6 +1615,18 @@ int proc_do_static_key(struct ctl_table *table, int write,
 	return ret;
 }
 
+#ifdef CONFIG_OPENPAX_SOFTMODE
+static struct ctl_table pax_table[] = {
+	{
+		.procname       = "softmode",
+		.data           = &pax_softmode,
+		.maxlen         = sizeof(int),
+		.mode           = 0600,
+		.proc_handler   = proc_dointvec,
+	},
+};
+#endif
+
 static struct ctl_table kern_table[] = {
 	{
 		.procname	= "panic",
@@ -2245,6 +2257,9 @@ int __init sysctl_init_bases(void)
 {
 	register_sysctl_init("kernel", kern_table);
 	register_sysctl_init("vm", vm_table);
+#ifdef CONFIG_OPENPAX_SOFTMODE
+	register_sysctl_init("kernel/pax", pax_table);
+#endif
 
 	return 0;
 }
diff --git a/security/Kconfig b/security/Kconfig
index 412e76f1575d..34e6641dcb56 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -246,6 +246,7 @@ config LSM
 	  If unsure, leave this as the default.
 
 source "security/Kconfig.hardening"
+source "security/Kconfig.openpax"
 
 endmenu
 
diff --git a/security/Kconfig.openpax b/security/Kconfig.openpax
new file mode 100644
index 000000000000..7a07386e116a
--- /dev/null
+++ b/security/Kconfig.openpax
@@ -0,0 +1,58 @@
+#
+# OpenPaX configuration
+#
+
+menu "OpenPaX options"
+
+config OPENPAX
+	bool "Enable OpenPaX features"
+	default y
+	help
+	  This configuration setting enables OpenPaX features.
+	  OpenPaX adds memory safety-related defenses to the kernel which
+	  reduce the risks posed by exploitable memory safety bugs.
+
+config OPENPAX_SOFTMODE
+	bool "Support PaX soft mode"
+	default y
+	help
+	  Enabling this option will allow you to configure OpenPaX
+	  features to run in soft mode.  In this mode, OpenPaX features
+	  will be disabled by default, only running on applications
+	  which explicitly enable them.
+	
+	  Soft mode can be enabled via the kernel.pax.softmode sysctl,
+	  or the pax_softmode=1 kernel command-line option.
+
+config OPENPAX_XATTR_PAX_FLAGS
+	bool "Use filesystem extended attributes to modify OpenPaX features"
+	depends on OPENPAX
+	default y
+	help
+	  Enabling this option will allow you to control whether
+	  OpenPaX features are enabled on a per-executable basis via
+	  xattr attributes.
+	
+	  For compatibility with the original PaX patch, the feature
+	  flags are read from the user.pax.flags extended attribute.
+	
+	  If you disable this feature, then all applications will run
+	  with OpenPaX enabled by default.
+
+config OPENPAX_MPROTECT
+	bool "Enforce W^X for memory mappings"
+	depends on OPENPAX
+	default y
+	help
+	  Enabling this option prevents programs from making pages
+	  executable when they are also writable.  In addition, it
+	  also denies transition of writable mappings to executable
+	  mappings.
+	
+	  This feature is known to break programs which depend on
+	  just-in-time (JIT) compilation.  It is advisable to enable
+	  this feature system-wide, but mark programs which have
+	  JIT compilation appropriately so the W^X enforcement is
+	  disabled for them.
+
+endmenu
