diff --git a/Documentation/security/tpe.txt b/Documentation/security/tpe.txt
new file mode 100644
index 0000000..226afcc
--- /dev/null
+++ b/Documentation/security/tpe.txt
@@ -0,0 +1,59 @@
+Trusted Path Execution is a Linux Security Module that generally requires
+programs to be run from a trusted path. A trusted path is one that is owned by
+root and is not group/world writable. This prevents an attacker from executing
+their own malicious binaries from an unprivileged user on the system. This
+feature is enabled with CONFIG_SECURITY_TPE. When enabled, a set of sysctls
+are created in /proc/sys/kernel/tpe.
+
+---------------------------------------------------------------------------
+
+Trusted Path Execution introduces the following Kconfig options and sysctls.
+
+CONFIG_SECURITY_TPE (sysctl=kernel.tpe.enabled)
+
+default: n
+
+This option enables Trusted Path Execution. TPE blocks *untrusted*
+users from executing files that meet the following conditions:
+
+* file is not in a root-owned directory
+* file is writable by a user other than root
+
+NOTE: By default root is not restricted by TPE.
+
+CONFIG_SECURITY_TPE_GID (sysctl=kernel.tpe.gid)
+
+default: 0
+
+This option defines a group id that, by default, is the trusted group.
+If a user is not trusted then it has the checks described in
+CONFIG_SECURITY_TPE applied. Otherwise, the user is trusted and the
+checks are not applied. You can disable the trusted gid option by
+setting it to 0. This makes all non-root users untrusted.
+
+CONFIG_SECURITY_TPE_STRICT (sysctl=kernel.tpe.strict)
+
+default: n
+
+This option applies another set of restrictions to all non-root users
+even if they are trusted. This only allows execution under the
+following conditions:
+
+* file is in a directory owned by the user that is not group or
+  world-writable
+* file is in a directory owned by root and writable only by root
+
+CONFIG_SECURITY_TPE_RESTRICT_ROOT (sysctl=kernel.tpe.restrict_root)
+
+default: n
+
+This option applies TPE restrictions to root.
+
+CONFIG_SECURITY_TPE_INVERT_GID (sysctl=kernel.tpe.invert_gid)
+
+default: n
+
+This option reverses the trust logic of the gid option and makes
+kernel.tpe.gid into the untrusted group. This means that all other groups
+become trusted. This sysctl is helpful when you want TPE restrictions
+to be limited to a small subset of users.
diff --git a/MAINTAINERS b/MAINTAINERS
index 9e98464..67e10c8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11574,6 +11574,11 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
 S:	Supported
 F:	security/yama/
 
+TPE SECURITY MODULE
+M:	Matt Brown <matt@nmatt.com>
+S:	Supported
+F:	security/tpe/
+
 SENSABLE PHANTOM
 M:	Jiri Slaby <jirislaby@gmail.com>
 S:	Maintained
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 080f34e..4c165ce 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1946,5 +1946,10 @@ void __init loadpin_add_hooks(void);
 #else
 static inline void loadpin_add_hooks(void) { };
 #endif
+#ifdef CONFIG_SECURITY_TPE
+void __init tpe_add_hooks(void);
+#else
+static inline void tpe_add_hooks(void) { };
+#endif
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/security/Kconfig b/security/Kconfig
index bdcbb92..58c4216 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -195,6 +195,7 @@ source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/loadpin/Kconfig
 source security/yama/Kconfig
+source security/tpe/Kconfig
 
 source security/integrity/Kconfig
 
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..f8b5197 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
 subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin
+subdir-$(CONFIG_SECURITY_TPE)		+= tpe
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/
 obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/
+obj-$(CONFIG_SECURITY_TPE)		+= tpe/
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/security.c b/security/security.c
index 38316bb..36137a3 100644
--- a/security/security.c
+++ b/security/security.c
@@ -70,6 +70,7 @@ int __init security_init(void)
 	capability_add_hooks();
 	yama_add_hooks();
 	loadpin_add_hooks();
+	tpe_add_hooks();
 
 	/*
 	 * Load all the remaining security modules.
diff --git a/security/tpe/Kconfig b/security/tpe/Kconfig
new file mode 100644
index 0000000..a1b8f17
--- /dev/null
+++ b/security/tpe/Kconfig
@@ -0,0 +1,64 @@
+config SECURITY_TPE
+	bool "Trusted Path Execution (TPE)"
+	default n
+	help
+	  If you say Y here, you will be able to choose a gid to add to the
+	  supplementary groups of users you want to mark as "trusted."
+	  Untrusted users will not be able to execute any files that are not in
+	  root-owned directories writable only by root.  If the sysctl option
+	  is enabled, a sysctl option with name "tpe.enabled" is created.
+
+config SECURITY_TPE_GID
+	int
+	default SECURITY_TPE_TRUSTED_GID if (SECURITY_TPE && !SECURITY_TPE_INVERT_GID)
+	default SECURITY_TPE_UNTRUSTED_GID if (SECURITY_TPE && SECURITY_TPE_INVERT_GID)
+
+config SECURITY_TPE_TRUSTED_GID
+	int "GID for TPE-trusted users"
+	depends on SECURITY_TPE && !SECURITY_TPE_INVERT_GID
+	default 0
+	help
+	  Setting this GID determines what group TPE restrictions will be
+	  *disabled* for. If the sysctl option is enabled, a sysctl option
+	  with name "tpe.gid" is created.
+
+config SECURITY_TPE_UNTRUSTED_GID
+	int "GID for TPE-untrusted users"
+	depends on SECURITY_TPE && SECURITY_TPE_INVERT_GID
+	default 0
+	help
+	  Setting this GID determines what group TPE restrictions will be
+	  *enabled* for. If the sysctl option is enabled, a sysctl option
+	  with name "tpe.gid" is created.
+
+config SECURITY_TPE_INVERT_GID
+	bool "Invert GID option"
+	depends on SECURITY_TPE
+	help
+	  If you say Y here, the group you specify in the TPE configuration will
+	  decide what group TPE restrictions will be *enabled* for.  This
+	  option is useful if you want TPE restrictions to be restricted to a
+	  small subset of users. If the sysctl option is enabled, a sysctl option
+	  with name "tpe.invert_gid" is created.
+
+config SECURITY_TPE_STRICT
+	bool "Partially restrict all non-root users"
+	depends on SECURITY_TPE
+	help
+	  If you say Y here, all non-root users will be covered under
+	  a weaker TPE restriction.  This is separate from, and in addition to,
+	  the main TPE options that you have selected elsewhere.  Thus, if a
+	  "trusted" GID is chosen, this restriction applies to even that GID.
+	  Under this restriction, all non-root users will only be allowed to
+	  execute files in directories they own that are not group or
+	  world-writable, or in directories owned by root and writable only by
+	  root.  If the sysctl option is enabled, a sysctl option with name
+	  "tpe.strict" is created.
+
+config SECURITY_TPE_RESTRICT_ROOT
+	bool "Restrict root"
+	depends on SECURITY_TPE
+	help
+	  If you say Y here, root will be marked as untrusted. This will require
+	  root to pass all the same checks as any other untrusted user.
+
diff --git a/security/tpe/Makefile b/security/tpe/Makefile
new file mode 100644
index 0000000..e1bd8ef
--- /dev/null
+++ b/security/tpe/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_TPE) := tpe_lsm.o
+
+tpe-y := tpe_lsm.o
diff --git a/security/tpe/tpe_lsm.c b/security/tpe/tpe_lsm.c
new file mode 100644
index 0000000..fda811a
--- /dev/null
+++ b/security/tpe/tpe_lsm.c
@@ -0,0 +1,218 @@
+/*
+ * Trusted Path Execution Security Module
+ *
+ * Copyright (C) 2017 Matt Brown
+ * Copyright (C) 2001-2014 Bradley Spengler, Open Source Security, Inc.
+ * http://www.grsecurity.net spender@grsecurity.net
+ * Copyright (C) 2011 Corey Henderson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/uidgid.h>
+#include <linux/ratelimit.h>
+#include <linux/limits.h>
+#include <linux/cred.h>
+#include <linux/slab.h>
+#include <linux/lsm_hooks.h>
+#include <linux/sysctl.h>
+#include <linux/binfmts.h>
+#include <linux/string_helpers.h>
+#include <linux/dcache.h>
+#include <uapi/asm-generic/mman-common.h>
+
+#define global_root(x) uid_eq((x), GLOBAL_ROOT_UID)
+#define global_nonroot(x) (!uid_eq((x), GLOBAL_ROOT_UID))
+#define global_root_gid(x) (gid_eq((x), GLOBAL_ROOT_GID))
+#define global_nonroot_gid(x) (!gid_eq((x), GLOBAL_ROOT_GID))
+
+static int tpe_enabled __read_mostly = IS_ENABLED(CONFIG_SECURITY_TPE);
+static kgid_t tpe_gid __read_mostly = KGIDT_INIT(CONFIG_SECURITY_TPE_GID);
+static int tpe_invert_gid __read_mostly =
+	IS_ENABLED(CONFIG_SECURITY_TPE_INVERT_GID);
+static int tpe_strict __read_mostly = IS_ENABLED(CONFIG_SECURITY_TPE_STRICT);
+static int tpe_restrict_root __read_mostly =
+	IS_ENABLED(CONFIG_SECURITY_TPE_RESTRICT_ROOT);
+
+int print_tpe_error(struct file *file, char *reason1, char *reason2,
+	char *method)
+{
+	char *filepath;
+
+	filepath = kstrdup_quotable_file(file, GFP_KERNEL);
+
+	if (!filepath)
+		return -ENOMEM;
+
+	pr_warn_ratelimited("TPE: Denied %s of %s Reason: %s%s%s\n", method,
+		(IS_ERR(filepath) ? "failed fetching file path" : filepath),
+		reason1, reason2 ? " and " : "", reason2 ?: "");
+	kfree(filepath);
+	return -EPERM;
+}
+
+static int tpe_check(struct file *file, char *method)
+{
+	struct inode *inode;
+	struct inode *file_inode;
+	struct dentry *dir;
+	const struct cred *cred = current_cred();
+	char *reason1 = NULL;
+	char *reason2 = NULL;
+
+	dir = dget_parent(file->f_path.dentry);
+	inode = d_backing_inode(dir);
+	file_inode = d_backing_inode(file->f_path.dentry);
+
+	if (!tpe_enabled)
+		return 0;
+
+	/* never restrict root unless restrict_root sysctl is 1*/
+	if (global_root(cred->uid) && !tpe_restrict_root)
+		return 0;
+
+	if (!tpe_strict)
+		goto general_tpe_check;
+
+	/* TPE_STRICT: restrictions enforced even if the gid is trusted */
+	if (global_nonroot(inode->i_uid) && !uid_eq(inode->i_uid, cred->uid))
+		reason1 = "directory not owned by user";
+	else if (inode->i_mode & 0002)
+		reason1 = "file in world-writable directory";
+	else if ((inode->i_mode & 0020) && global_nonroot_gid(inode->i_gid))
+		reason1 = "file in group-writable directory";
+	else if (file_inode->i_mode & 0002)
+		reason1 = "file is world-writable";
+
+	if (reason1)
+		goto end;
+
+general_tpe_check:
+	/* determine if group is trusted */
+	if (global_root_gid(tpe_gid))
+		goto next_check;
+	if (!tpe_invert_gid && !in_group_p(tpe_gid))
+		reason2 = "not in trusted group";
+	else if (tpe_invert_gid && in_group_p(tpe_gid))
+		reason2 = "in untrusted group";
+	else
+		return 0;
+
+next_check:
+	/* main TPE checks */
+	if (global_nonroot(inode->i_uid))
+		reason1 = "file in non-root-owned directory";
+	else if (inode->i_mode & 0002)
+		reason1 = "file in world-writable directory";
+	else if ((inode->i_mode & 0020) && global_nonroot_gid(inode->i_gid))
+		reason1 = "file in group-writable directory";
+	else if (file_inode->i_mode & 0002)
+		reason1 = "file is world-writable";
+
+end:
+	dput(dir);
+	if (reason1)
+		return print_tpe_error(file, reason1, reason2, method);
+	else
+		return 0;
+}
+
+int tpe_mmap_file(struct file *file, unsigned long reqprot,
+	unsigned long prot, unsigned long flags)
+{
+	if (!file || !(prot & PROT_EXEC))
+		return 0;
+
+	return tpe_check(file, "mmap");
+}
+
+int tpe_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+	unsigned long prot)
+{
+	if (!vma->vm_file)
+		return 0;
+	return tpe_check(vma->vm_file, "mprotect");
+}
+
+static int tpe_bprm_set_creds(struct linux_binprm *bprm)
+{
+	if (!bprm->file)
+		return 0;
+	return tpe_check(bprm->file, "exec");
+
+}
+
+static struct security_hook_list tpe_hooks[] = {
+	LSM_HOOK_INIT(mmap_file, tpe_mmap_file),
+	LSM_HOOK_INIT(file_mprotect, tpe_file_mprotect),
+	LSM_HOOK_INIT(bprm_set_creds, tpe_bprm_set_creds),
+};
+
+#ifdef CONFIG_SYSCTL
+struct ctl_path tpe_sysctl_path[] = {
+	{ .procname = "kernel", },
+	{ .procname = "tpe", },
+	{ }
+};
+
+static struct ctl_table tpe_sysctl_table[] = {
+	{
+		.procname	= "enabled",
+		.data		= &tpe_enabled,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_dointvec,
+	},
+	{
+		.procname	= "gid",
+		.data		= &tpe_gid,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_dointvec,
+	},
+	{
+		.procname	= "invert_gid",
+		.data		= &tpe_invert_gid,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_dointvec,
+	},
+	{
+		.procname	= "strict",
+		.data		= &tpe_strict,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_dointvec,
+	},
+	{
+		.procname	= "restrict_root",
+		.data		= &tpe_restrict_root,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_dointvec,
+	},
+	{ }
+};
+static void __init tpe_init_sysctl(void)
+{
+	if (!register_sysctl_paths(tpe_sysctl_path, tpe_sysctl_table))
+		panic("TPE: sysctl registration failed.\n");
+}
+#else
+static inline void tpe_init_sysctl(void) { }
+#endif /* CONFIG_SYSCTL */
+
+void __init tpe_add_hooks(void)
+{
+	pr_info("TPE: securing systems like it's 1998\n");
+	security_add_hooks(tpe_hooks, ARRAY_SIZE(tpe_hooks), "tpe");
+	tpe_init_sysctl();
+}
