[PATCH v6 3/7] crash_dump: store dm crypt keys in kdump reserved memory

Coiby Xu posted 7 patches 3 weeks, 6 days ago
[PATCH v6 3/7] crash_dump: store dm crypt keys in kdump reserved memory
Posted by Coiby Xu 3 weeks, 6 days ago
When the kdump kernel image and initrd are loaded, the dm crypts keys
will be read from keyring and then stored in kdump reserved memory.

Assume a key won't exceed 256 bytes thus MAX_KEY_SIZE=256 according to
"cryptsetup benchmark".

Signed-off-by: Coiby Xu <coxu@redhat.com>
---
 include/linux/crash_core.h   |   6 +-
 include/linux/kexec.h        |   4 ++
 kernel/crash_dump_dm_crypt.c | 129 +++++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
index 44305336314e..2e6782239034 100644
--- a/include/linux/crash_core.h
+++ b/include/linux/crash_core.h
@@ -34,7 +34,11 @@ static inline void arch_kexec_protect_crashkres(void) { }
 static inline void arch_kexec_unprotect_crashkres(void) { }
 #endif
 
-
+#ifdef CONFIG_CRASH_DM_CRYPT
+int crash_load_dm_crypt_keys(struct kimage *image);
+#else
+static inline int crash_load_dm_crypt_keys(struct kimage *image) {return 0; }
+#endif
 
 #ifndef arch_crash_handle_hotplug_event
 static inline void arch_crash_handle_hotplug_event(struct kimage *image, void *arg) { }
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 0dc66ca2506a..5bda0978bab3 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -396,6 +396,10 @@ struct kimage {
 	void *elf_headers;
 	unsigned long elf_headers_sz;
 	unsigned long elf_load_addr;
+
+	/* dm crypt keys buffer */
+	unsigned long dm_crypt_keys_addr;
+	unsigned long dm_crypt_keys_sz;
 };
 
 /* kexec interface functions */
diff --git a/kernel/crash_dump_dm_crypt.c b/kernel/crash_dump_dm_crypt.c
index 62a3c47d8b3b..ec2ec2967242 100644
--- a/kernel/crash_dump_dm_crypt.c
+++ b/kernel/crash_dump_dm_crypt.c
@@ -1,14 +1,62 @@
 // SPDX-License-Identifier: GPL-2.0-only
+#include <linux/key.h>
+#include <linux/keyctl.h>
 #include <keys/user-type.h>
 #include <linux/crash_dump.h>
 #include <linux/configfs.h>
 #include <linux/module.h>
 
 #define KEY_NUM_MAX 128	/* maximum dm crypt keys */
+#define KEY_SIZE_MAX 256	/* maximum dm crypt key size */
 #define KEY_DESC_MAX_LEN 128	/* maximum dm crypt key description size */
 
 static unsigned int key_count;
 
+struct dm_crypt_key {
+	unsigned int key_size;
+	char key_desc[KEY_DESC_MAX_LEN];
+	u8 data[KEY_SIZE_MAX];
+};
+
+static struct keys_header {
+	unsigned int total_keys;
+	struct dm_crypt_key keys[] __counted_by(total_keys);
+} *keys_header;
+
+static size_t get_keys_header_size(size_t total_keys)
+{
+	return struct_size(keys_header, keys, total_keys);
+}
+
+static int read_key_from_user_keying(struct dm_crypt_key *dm_key)
+{
+	const struct user_key_payload *ukp;
+	struct key *key;
+
+	kexec_dprintk("Requesting key %s", dm_key->key_desc);
+	key = request_key(&key_type_logon, dm_key->key_desc, NULL);
+
+	if (IS_ERR(key)) {
+		pr_warn("No such key %s\n", dm_key->key_desc);
+		return PTR_ERR(key);
+	}
+
+	ukp = user_key_payload_locked(key);
+	if (!ukp)
+		return -EKEYREVOKED;
+
+	if (ukp->datalen > KEY_SIZE_MAX) {
+		pr_err("Key size %u exceeds maximum (%u)\n", ukp->datalen, KEY_SIZE_MAX);
+		return -EINVAL;
+	}
+
+	memcpy(dm_key->data, ukp->data, ukp->datalen);
+	dm_key->key_size = ukp->datalen;
+	kexec_dprintk("Get dm crypt key (size=%u) %s: %8ph\n", dm_key->key_size,
+		      dm_key->key_desc, dm_key->data);
+	return 0;
+}
+
 struct config_key {
 	struct config_item item;
 	const char *description;
@@ -130,6 +178,87 @@ static struct configfs_subsystem config_keys_subsys = {
 	},
 };
 
+static int build_keys_header(void)
+{
+	struct config_item *item = NULL;
+	struct config_key *key;
+	int i, r;
+
+	if (keys_header != NULL)
+		kvfree(keys_header);
+
+	keys_header = kzalloc(get_keys_header_size(key_count), GFP_KERNEL);
+	if (!keys_header)
+		return -ENOMEM;
+
+	keys_header->total_keys = key_count;
+
+	i = 0;
+	list_for_each_entry(item, &config_keys_subsys.su_group.cg_children,
+			    ci_entry) {
+		if (item->ci_type != &config_key_type)
+			continue;
+
+		key = to_config_key(item);
+
+		strscpy(keys_header->keys[i].key_desc, key->description,
+			KEY_DESC_MAX_LEN);
+		r = read_key_from_user_keying(&keys_header->keys[i]);
+		if (r != 0) {
+			kexec_dprintk("Failed to read key %s\n",
+				      keys_header->keys[i].key_desc);
+			return r;
+		}
+		i++;
+		kexec_dprintk("Found key: %s\n", item->ci_name);
+	}
+
+	return 0;
+}
+
+int crash_load_dm_crypt_keys(struct kimage *image)
+{
+	struct kexec_buf kbuf = {
+		.image = image,
+		.buf_min = 0,
+		.buf_max = ULONG_MAX,
+		.top_down = false,
+		.random = true,
+	};
+	int r;
+
+
+	if (key_count <= 0) {
+		kexec_dprintk("No dm-crypt keys\n");
+		return -EINVAL;
+	}
+
+	image->dm_crypt_keys_addr = 0;
+	r = build_keys_header();
+	if (r)
+		return r;
+
+	kbuf.buffer = keys_header;
+	kbuf.bufsz = get_keys_header_size(key_count);
+
+	kbuf.memsz = kbuf.bufsz;
+	kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
+	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+	r = kexec_add_buffer(&kbuf);
+	if (r) {
+		kvfree((void *)kbuf.buffer);
+		return r;
+	}
+	image->dm_crypt_keys_addr = kbuf.mem;
+	image->dm_crypt_keys_sz = kbuf.bufsz;
+	kexec_dprintk(
+		"Loaded dm crypt keys to kexec_buffer bufsz=0x%lx memsz=0x%lx\n",
+		kbuf.bufsz, kbuf.bufsz);
+
+	return r;
+}
+
+
 static int __init configfs_dmcrypt_keys_init(void)
 {
 	int ret;
-- 
2.47.0
Re: [PATCH v6 3/7] crash_dump: store dm crypt keys in kdump reserved memory
Posted by kernel test robot 3 weeks, 5 days ago
Hi Coiby,

kernel test robot noticed the following build errors:

[auto build test ERROR on e42b1a9a2557aa94fee47f078633677198386a52]

url:    https://github.com/intel-lab-lkp/linux/commits/Coiby-Xu/kexec_file-allow-to-place-kexec_buf-randomly/20241029-135449
base:   e42b1a9a2557aa94fee47f078633677198386a52
patch link:    https://lore.kernel.org/r/20241029055223.210039-4-coxu%40redhat.com
patch subject: [PATCH v6 3/7] crash_dump: store dm crypt keys in kdump reserved memory
config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20241029/202410292237.HA9vMqbC-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241029/202410292237.HA9vMqbC-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410292237.HA9vMqbC-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   kernel/crash_dump_dm_crypt.c: In function 'crash_load_dm_crypt_keys':
>> kernel/crash_dump_dm_crypt.c:221:16: error: variable 'kbuf' has initializer but incomplete type
     221 |         struct kexec_buf kbuf = {
         |                ^~~~~~~~~
>> kernel/crash_dump_dm_crypt.c:222:18: error: 'struct kexec_buf' has no member named 'image'
     222 |                 .image = image,
         |                  ^~~~~
>> kernel/crash_dump_dm_crypt.c:222:26: warning: excess elements in struct initializer
     222 |                 .image = image,
         |                          ^~~~~
   kernel/crash_dump_dm_crypt.c:222:26: note: (near initialization for 'kbuf')
>> kernel/crash_dump_dm_crypt.c:223:18: error: 'struct kexec_buf' has no member named 'buf_min'
     223 |                 .buf_min = 0,
         |                  ^~~~~~~
   kernel/crash_dump_dm_crypt.c:223:28: warning: excess elements in struct initializer
     223 |                 .buf_min = 0,
         |                            ^
   kernel/crash_dump_dm_crypt.c:223:28: note: (near initialization for 'kbuf')
>> kernel/crash_dump_dm_crypt.c:224:18: error: 'struct kexec_buf' has no member named 'buf_max'
     224 |                 .buf_max = ULONG_MAX,
         |                  ^~~~~~~
   In file included from include/linux/limits.h:7,
                    from include/linux/thread_info.h:12,
                    from include/asm-generic/preempt.h:5,
                    from ./arch/loongarch/include/generated/asm/preempt.h:1,
                    from include/linux/preempt.h:79,
                    from include/linux/rcupdate.h:27,
                    from include/linux/rbtree.h:24,
                    from include/linux/key.h:15,
                    from kernel/crash_dump_dm_crypt.c:2:
>> include/vdso/limits.h:13:25: warning: excess elements in struct initializer
      13 | #define ULONG_MAX       (~0UL)
         |                         ^
   kernel/crash_dump_dm_crypt.c:224:28: note: in expansion of macro 'ULONG_MAX'
     224 |                 .buf_max = ULONG_MAX,
         |                            ^~~~~~~~~
   include/vdso/limits.h:13:25: note: (near initialization for 'kbuf')
      13 | #define ULONG_MAX       (~0UL)
         |                         ^
   kernel/crash_dump_dm_crypt.c:224:28: note: in expansion of macro 'ULONG_MAX'
     224 |                 .buf_max = ULONG_MAX,
         |                            ^~~~~~~~~
>> kernel/crash_dump_dm_crypt.c:225:18: error: 'struct kexec_buf' has no member named 'top_down'
     225 |                 .top_down = false,
         |                  ^~~~~~~~
   kernel/crash_dump_dm_crypt.c:225:29: warning: excess elements in struct initializer
     225 |                 .top_down = false,
         |                             ^~~~~
   kernel/crash_dump_dm_crypt.c:225:29: note: (near initialization for 'kbuf')
>> kernel/crash_dump_dm_crypt.c:226:18: error: 'struct kexec_buf' has no member named 'random'
     226 |                 .random = true,
         |                  ^~~~~~
   kernel/crash_dump_dm_crypt.c:226:27: warning: excess elements in struct initializer
     226 |                 .random = true,
         |                           ^~~~
   kernel/crash_dump_dm_crypt.c:226:27: note: (near initialization for 'kbuf')
>> kernel/crash_dump_dm_crypt.c:221:26: error: storage size of 'kbuf' isn't known
     221 |         struct kexec_buf kbuf = {
         |                          ^~~~
>> kernel/crash_dump_dm_crypt.c:246:20: error: 'KEXEC_BUF_MEM_UNKNOWN' undeclared (first use in this function)
     246 |         kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
         |                    ^~~~~~~~~~~~~~~~~~~~~
   kernel/crash_dump_dm_crypt.c:246:20: note: each undeclared identifier is reported only once for each function it appears in
>> kernel/crash_dump_dm_crypt.c:247:13: error: implicit declaration of function 'kexec_add_buffer' [-Wimplicit-function-declaration]
     247 |         r = kexec_add_buffer(&kbuf);
         |             ^~~~~~~~~~~~~~~~
>> kernel/crash_dump_dm_crypt.c:221:26: warning: unused variable 'kbuf' [-Wunused-variable]
     221 |         struct kexec_buf kbuf = {
         |                          ^~~~


vim +/kbuf +221 kernel/crash_dump_dm_crypt.c

   218	
   219	int crash_load_dm_crypt_keys(struct kimage *image)
   220	{
 > 221		struct kexec_buf kbuf = {
 > 222			.image = image,
 > 223			.buf_min = 0,
 > 224			.buf_max = ULONG_MAX,
 > 225			.top_down = false,
 > 226			.random = true,
   227		};
   228		int r;
   229	
   230	
   231		if (key_count <= 0) {
   232			kexec_dprintk("No dm-crypt keys\n");
   233			return -EINVAL;
   234		}
   235	
   236		image->dm_crypt_keys_addr = 0;
   237		r = build_keys_header();
   238		if (r)
   239			return r;
   240	
   241		kbuf.buffer = keys_header;
   242		kbuf.bufsz = get_keys_header_size(key_count);
   243	
   244		kbuf.memsz = kbuf.bufsz;
   245		kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
 > 246		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
 > 247		r = kexec_add_buffer(&kbuf);
   248		if (r) {
   249			kvfree((void *)kbuf.buffer);
   250			return r;
   251		}
   252		image->dm_crypt_keys_addr = kbuf.mem;
   253		image->dm_crypt_keys_sz = kbuf.bufsz;
   254		kexec_dprintk(
   255			"Loaded dm crypt keys to kexec_buffer bufsz=0x%lx memsz=0x%lx\n",
   256			kbuf.bufsz, kbuf.bufsz);
   257	
   258		return r;
   259	}
   260	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v6 3/7] crash_dump: store dm crypt keys in kdump reserved memory
Posted by Coiby Xu 3 weeks, 3 days ago
On Tue, Oct 29, 2024 at 10:41:50PM +0800, kernel test robot wrote:
>Hi Coiby,
>
>kernel test robot noticed the following build errors:
>
>[auto build test ERROR on e42b1a9a2557aa94fee47f078633677198386a52]
>
>url:    https://github.com/intel-lab-lkp/linux/commits/Coiby-Xu/kexec_file-allow-to-place-kexec_buf-randomly/20241029-135449
>base:   e42b1a9a2557aa94fee47f078633677198386a52
>patch link:    https://lore.kernel.org/r/20241029055223.210039-4-coxu%40redhat.com
>patch subject: [PATCH v6 3/7] crash_dump: store dm crypt keys in kdump reserved memory
>config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20241029/202410292237.HA9vMqbC-lkp@intel.com/config)
>compiler: loongarch64-linux-gcc (GCC) 14.1.0
>reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241029/202410292237.HA9vMqbC-lkp@intel.com/reproduce)
>
>If you fix the issue in a separate patch/commit (i.e. not just a new version of
>the same patch/commit), kindly add following tags
>| Reported-by: kernel test robot <lkp@intel.com>
>| Closes: https://lore.kernel.org/oe-kbuild-all/202410292237.HA9vMqbC-lkp@intel.com/
>
>All error/warnings (new ones prefixed by >>):
>
>   kernel/crash_dump_dm_crypt.c: In function 'crash_load_dm_crypt_keys':
>>> kernel/crash_dump_dm_crypt.c:221:16: error: variable 'kbuf' has initializer but incomplete type
>     221 |         struct kexec_buf kbuf = {
>         |                ^~~~~~~~~
>>> kernel/crash_dump_dm_crypt.c:222:18: error: 'struct kexec_buf' has no member named 'image'
>     222 |                 .image = image,
>         |                  ^~~~~
>>> kernel/crash_dump_dm_crypt.c:222:26: warning: excess elements in struct initializer
>     222 |                 .image = image,
>         |                          ^~~~~
>   kernel/crash_dump_dm_crypt.c:222:26: note: (near initialization for 'kbuf')
>>> kernel/crash_dump_dm_crypt.c:223:18: error: 'struct kexec_buf' has no member named 'buf_min'
>     223 |                 .buf_min = 0,
>         |                  ^~~~~~~
>   kernel/crash_dump_dm_crypt.c:223:28: warning: excess elements in struct initializer
>     223 |                 .buf_min = 0,
>         |                            ^
>   kernel/crash_dump_dm_crypt.c:223:28: note: (near initialization for 'kbuf')
>>> kernel/crash_dump_dm_crypt.c:224:18: error: 'struct kexec_buf' has no member named 'buf_max'
>     224 |                 .buf_max = ULONG_MAX,
>         |                  ^~~~~~~
>   In file included from include/linux/limits.h:7,
>                    from include/linux/thread_info.h:12,
>                    from include/asm-generic/preempt.h:5,
>                    from ./arch/loongarch/include/generated/asm/preempt.h:1,
>                    from include/linux/preempt.h:79,
>                    from include/linux/rcupdate.h:27,
>                    from include/linux/rbtree.h:24,
>                    from include/linux/key.h:15,
>                    from kernel/crash_dump_dm_crypt.c:2:
>>> include/vdso/limits.h:13:25: warning: excess elements in struct initializer
>      13 | #define ULONG_MAX       (~0UL)
>         |                         ^
>   kernel/crash_dump_dm_crypt.c:224:28: note: in expansion of macro 'ULONG_MAX'
>     224 |                 .buf_max = ULONG_MAX,
>         |                            ^~~~~~~~~
>   include/vdso/limits.h:13:25: note: (near initialization for 'kbuf')
>      13 | #define ULONG_MAX       (~0UL)
>         |                         ^
>   kernel/crash_dump_dm_crypt.c:224:28: note: in expansion of macro 'ULONG_MAX'
>     224 |                 .buf_max = ULONG_MAX,
>         |                            ^~~~~~~~~
>>> kernel/crash_dump_dm_crypt.c:225:18: error: 'struct kexec_buf' has no member named 'top_down'
>     225 |                 .top_down = false,
>         |                  ^~~~~~~~
>   kernel/crash_dump_dm_crypt.c:225:29: warning: excess elements in struct initializer
>     225 |                 .top_down = false,
>         |                             ^~~~~
>   kernel/crash_dump_dm_crypt.c:225:29: note: (near initialization for 'kbuf')
>>> kernel/crash_dump_dm_crypt.c:226:18: error: 'struct kexec_buf' has no member named 'random'
>     226 |                 .random = true,
>         |                  ^~~~~~
>   kernel/crash_dump_dm_crypt.c:226:27: warning: excess elements in struct initializer
>     226 |                 .random = true,
>         |                           ^~~~
>   kernel/crash_dump_dm_crypt.c:226:27: note: (near initialization for 'kbuf')
>>> kernel/crash_dump_dm_crypt.c:221:26: error: storage size of 'kbuf' isn't known
>     221 |         struct kexec_buf kbuf = {
>         |                          ^~~~
>>> kernel/crash_dump_dm_crypt.c:246:20: error: 'KEXEC_BUF_MEM_UNKNOWN' undeclared (first use in this function)
>     246 |         kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
>         |                    ^~~~~~~~~~~~~~~~~~~~~
>   kernel/crash_dump_dm_crypt.c:246:20: note: each undeclared identifier is reported only once for each function it appears in
>>> kernel/crash_dump_dm_crypt.c:247:13: error: implicit declaration of function 'kexec_add_buffer' [-Wimplicit-function-declaration]
>     247 |         r = kexec_add_buffer(&kbuf);
>         |             ^~~~~~~~~~~~~~~~
>>> kernel/crash_dump_dm_crypt.c:221:26: warning: unused variable 'kbuf' [-Wunused-variable]
>     221 |         struct kexec_buf kbuf = {
>         |                          ^~~~
>
>
>vim +/kbuf +221 kernel/crash_dump_dm_crypt.c
>
>   218	
>   219	int crash_load_dm_crypt_keys(struct kimage *image)
>   220	{
> > 221		struct kexec_buf kbuf = {
> > 222			.image = image,
> > 223			.buf_min = 0,
> > 224			.buf_max = ULONG_MAX,
> > 225			.top_down = false,
> > 226			.random = true,
>   227		};
>   228		int r;
>   229	
>   230	
>   231		if (key_count <= 0) {
>   232			kexec_dprintk("No dm-crypt keys\n");
>   233			return -EINVAL;
>   234		}
>   235	
>   236		image->dm_crypt_keys_addr = 0;
>   237		r = build_keys_header();
>   238		if (r)
>   239			return r;
>   240	
>   241		kbuf.buffer = keys_header;
>   242		kbuf.bufsz = get_keys_header_size(key_count);
>   243	
>   244		kbuf.memsz = kbuf.bufsz;
>   245		kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
> > 246		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
> > 247		r = kexec_add_buffer(&kbuf);
>   248		if (r) {
>   249			kvfree((void *)kbuf.buffer);
>   250			return r;
>   251		}
>   252		image->dm_crypt_keys_addr = kbuf.mem;
>   253		image->dm_crypt_keys_sz = kbuf.bufsz;
>   254		kexec_dprintk(
>   255			"Loaded dm crypt keys to kexec_buffer bufsz=0x%lx memsz=0x%lx\n",
>   256			kbuf.bufsz, kbuf.bufsz);
>   257	
>   258		return r;
>   259	}
>   260	
>
>-- 
>0-DAY CI Kernel Test Service
>https://github.com/intel/lkp-tests/wiki

Thanks for reporting this issue. I'll make CRASH_DM_CRYPT depend on
KEXEC_FILE to fix it in new version of patch set!

>_______________________________________________
>kexec mailing list
>kexec@lists.infradead.org
>http://lists.infradead.org/mailman/listinfo/kexec
>

-- 
Best regards,
Coiby