enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
 
+struct ima_rule_opt_list {
+       size_t count;
+       char *items[];
+};
+
 struct ima_rule_entry {
        struct list_head list;
        int action;
                int type;       /* audit type */
        } lsm[MAX_LSM_RULES];
        char *fsname;
-       char *keyrings; /* Measure keys added to these keyrings */
+       struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
        struct ima_template_desc *template;
 };
 
 static LIST_HEAD(ima_temp_rules);
 static struct list_head *ima_rules = &ima_default_rules;
 
-/* Pre-allocated buffer used for matching keyrings. */
-static char *ima_keyrings;
-static size_t ima_keyrings_len;
-
 static int ima_policy __initdata;
 
 static int __init default_measure_policy_setup(char *str)
 }
 __setup("ima_appraise_tcb", default_appraise_policy_setup);
 
+static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
+{
+       struct ima_rule_opt_list *opt_list;
+       size_t count = 0;
+       char *src_copy;
+       char *cur, *next;
+       size_t i;
+
+       src_copy = match_strdup(src);
+       if (!src_copy)
+               return ERR_PTR(-ENOMEM);
+
+       next = src_copy;
+       while ((cur = strsep(&next, "|"))) {
+               /* Don't accept an empty list item */
+               if (!(*cur)) {
+                       kfree(src_copy);
+                       return ERR_PTR(-EINVAL);
+               }
+               count++;
+       }
+
+       /* Don't accept an empty list */
+       if (!count) {
+               kfree(src_copy);
+               return ERR_PTR(-EINVAL);
+       }
+
+       opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL);
+       if (!opt_list) {
+               kfree(src_copy);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       /*
+        * strsep() has already replaced all instances of '|' with '\0',
+        * leaving a byte sequence of NUL-terminated strings. Reference each
+        * string with the array of items.
+        *
+        * IMPORTANT: Ownership of the allocated buffer is transferred from
+        * src_copy to the first element in the items array. To free the
+        * buffer, kfree() must only be called on the first element of the
+        * array.
+        */
+       for (i = 0, cur = src_copy; i < count; i++) {
+               opt_list->items[i] = cur;
+               cur = strchr(cur, '\0') + 1;
+       }
+       opt_list->count = count;
+
+       return opt_list;
+}
+
+static void ima_free_rule_opt_list(struct ima_rule_opt_list *opt_list)
+{
+       if (!opt_list)
+               return;
+
+       if (opt_list->count) {
+               kfree(opt_list->items[0]);
+               opt_list->count = 0;
+       }
+
+       kfree(opt_list);
+}
+
 static void ima_lsm_free_rule(struct ima_rule_entry *entry)
 {
        int i;
         * the defined_templates list and cannot be freed here
         */
        kfree(entry->fsname);
-       kfree(entry->keyrings);
+       ima_free_rule_opt_list(entry->keyrings);
        ima_lsm_free_rule(entry);
        kfree(entry);
 }
 static bool ima_match_keyring(struct ima_rule_entry *rule,
                              const char *keyring, const struct cred *cred)
 {
-       char *next_keyring, *keyrings_ptr;
        bool matched = false;
+       size_t i;
 
        if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
                return false;
        if (!keyring)
                return false;
 
-       strcpy(ima_keyrings, rule->keyrings);
-
-       /*
-        * "keyrings=" is specified in the policy in the format below:
-        * keyrings=.builtin_trusted_keys|.ima|.evm
-        */
-       keyrings_ptr = ima_keyrings;
-       while ((next_keyring = strsep(&keyrings_ptr, "|")) != NULL) {
-               if (!strcmp(next_keyring, keyring)) {
+       for (i = 0; i < rule->keyrings->count; i++) {
+               if (!strcmp(rule->keyrings->items[i], keyring)) {
                        matched = true;
                        break;
                }
        bool uid_token;
        struct ima_template_desc *template_desc;
        int result = 0;
-       size_t keyrings_len;
 
        ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
                                       AUDIT_INTEGRITY_POLICY_RULE);
                case Opt_keyrings:
                        ima_log_string(ab, "keyrings", args[0].from);
 
-                       keyrings_len = strlen(args[0].from) + 1;
-
-                       if ((entry->keyrings) ||
-                           (keyrings_len < 2)) {
+                       if (entry->keyrings) {
                                result = -EINVAL;
                                break;
                        }
 
-                       if (keyrings_len > ima_keyrings_len) {
-                               char *tmpbuf;
-
-                               tmpbuf = krealloc(ima_keyrings, keyrings_len,
-                                                 GFP_KERNEL);
-                               if (!tmpbuf) {
-                                       result = -ENOMEM;
-                                       break;
-                               }
-
-                               ima_keyrings = tmpbuf;
-                               ima_keyrings_len = keyrings_len;
-                       }
-
-                       entry->keyrings = kstrdup(args[0].from, GFP_KERNEL);
-                       if (!entry->keyrings) {
-                               kfree(ima_keyrings);
-                               ima_keyrings = NULL;
-                               ima_keyrings_len = 0;
-                               result = -ENOMEM;
+                       entry->keyrings = ima_alloc_rule_opt_list(args);
+                       if (IS_ERR(entry->keyrings)) {
+                               result = PTR_ERR(entry->keyrings);
+                               entry->keyrings = NULL;
                                break;
                        }
-                       result = 0;
+
                        entry->flags |= IMA_KEYRINGS;
                        break;
                case Opt_fsuuid:
                seq_printf(m, "func=%d ", func);
 }
 
+static void ima_show_rule_opt_list(struct seq_file *m,
+                                  const struct ima_rule_opt_list *opt_list)
+{
+       size_t i;
+
+       for (i = 0; i < opt_list->count; i++)
+               seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]);
+}
+
 int ima_policy_show(struct seq_file *m, void *v)
 {
        struct ima_rule_entry *entry = v;
        }
 
        if (entry->flags & IMA_KEYRINGS) {
-               if (entry->keyrings != NULL)
-                       snprintf(tbuf, sizeof(tbuf), "%s", entry->keyrings);
-               seq_printf(m, pt(Opt_keyrings), tbuf);
+               seq_puts(m, "keyrings=");
+               ima_show_rule_opt_list(m, entry->keyrings);
                seq_puts(m, " ");
        }