diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index dcb5e5b5eb13..73db3c41df26 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -105,6 +105,8 @@ extern char *lsm_names;
 
 extern void security_add_hooks(struct security_hook_list *hooks, int count,
 				const char *lsm);
+extern int register_loadable_lsm(struct security_hook_list *hooks, int count,
+				 const char *lsm);
 
 #define LSM_FLAG_LEGACY_MAJOR	BIT(0)
 #define LSM_FLAG_EXCLUSIVE	BIT(1)
diff --git a/security/security.c b/security/security.c
index 23b129d482a7..6c64b7afb251 100644
--- a/security/security.c
+++ b/security/security.c
@@ -74,6 +74,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
 };
 
 struct security_hook_heads security_hook_heads __ro_after_init;
+EXPORT_SYMBOL_GPL(security_hook_heads);
 static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
 
 static struct kmem_cache *lsm_file_cache;
@@ -537,6 +538,112 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
 	}
 }
 
+#if defined(CONFIG_STRICT_KERNEL_RWX)
+#define MAX_RO_PAGES 1024 /* Wild guess. Can be minimized by dynamic allocation. */
+static struct page *ro_pages[MAX_RO_PAGES]; /* Pages that are marked read-only. */
+static unsigned int ro_pages_len; /* Number of pages that are marked read-only. */
+
+/* Check whether a page containing given address does not have _PAGE_BIT_RW bit. */
+static bool lsm_test_page_ro(void *addr)
+{
+	unsigned int i;
+	int unused;
+	struct page *page;
+
+	page = (struct page *) lookup_address((unsigned long) addr, &unused);
+	if (!page)
+		return false;
+	if (test_bit(_PAGE_BIT_RW, &(page->flags)))
+		return true;
+	for (i = 0; i < ro_pages_len; i++)
+		if (page == ro_pages[i])
+			return true;
+	if (ro_pages_len == MAX_RO_PAGES)
+		return false;
+	ro_pages[ro_pages_len++] = page;
+	return true;
+}
+
+/* Find pages which do not have _PAGE_BIT_RW bit. */
+static bool check_ro_pages(struct security_hook_list *hooks, int count)
+{
+	int i;
+	struct hlist_head *list = &security_hook_heads.capable;
+
+	if (!copy_to_kernel_nofault(list, list, sizeof(void *)))
+		return true;
+	for (i = 0; i < count; i++) {
+		struct hlist_head *head = hooks[i].head;
+		struct security_hook_list *shp;
+
+		if (!lsm_test_page_ro(&head->first))
+			return false;
+		hlist_for_each_entry(shp, head, list)
+			if (!lsm_test_page_ro(&shp->list.next) ||
+			    !lsm_test_page_ro(&shp->list.pprev))
+				return false;
+	}
+	return true;
+}
+#endif
+
+/**
+ * register_loadable_lsm - Add a dynamically appendable module's hooks to the hook lists.
+ * @hooks: the hooks to add
+ * @count: the number of hooks to add
+ * @lsm: the name of the security module
+ *
+ * Each dynamically appendable LSM has to register its hooks with the infrastructure.
+ *
+ * Assumes that this function is called from module_init() function where
+ * call to this function is already serialized by module_mutex lock.
+ */
+int register_loadable_lsm(struct security_hook_list *hooks, int count,
+			  const char *lsm)
+{
+	int i;
+	char *cp;
+
+	// TODO: Check whether proposed hooks can co-exist with already chained hooks,
+	//       and bail out here if one of hooks cannot co-exist...
+
+#if defined(CONFIG_STRICT_KERNEL_RWX)
+	// Find pages which needs to make temporarily writable.
+	ro_pages_len = 0;
+	if (!check_ro_pages(hooks, count)) {
+		pr_err("Can't make security_hook_heads again writable. Retry with rodata=off kernel command line option added.\n");
+		return -EINVAL;
+	}
+	pr_info("ro_pages_len=%d\n", ro_pages_len);
+#endif
+	// At least "capability" is already included.
+	cp = kasprintf(GFP_KERNEL, "%s,%s", lsm_names, lsm);
+	if (!cp) {
+		pr_err("%s - Cannot get memory.\n", __func__);
+		return -ENOMEM;
+	}
+#if defined(CONFIG_STRICT_KERNEL_RWX)
+	// Make security_hook_heads (and hooks chained) temporarily writable.
+	for (i = 0; i < ro_pages_len; i++)
+		set_bit(_PAGE_BIT_RW, &(ro_pages[i]->flags));
+#endif
+	// Register dynamically appendable module's hooks.
+	for (i = 0; i < count; i++) {
+		hooks[i].lsm = lsm;
+		hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
+	}
+#if defined(CONFIG_STRICT_KERNEL_RWX)
+	// Make security_hook_heads (and hooks chained) again read-only.
+	for (i = 0; i < ro_pages_len; i++)
+		clear_bit(_PAGE_BIT_RW, &(ro_pages[i]->flags));
+#endif
+	// TODO: Wait for reader side before kfree().
+	kfree(lsm_names);
+	lsm_names = cp;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(register_loadable_lsm);
+
 int call_blocking_lsm_notifier(enum lsm_event event, void *data)
 {
 	return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
