Currently, the mechanism to map and unmap segments to the kimage
structure is not available to the subsystems outside of kexec. This
functionality is needed when IMA is allocating the memory segments
during kexec 'load' operation.
Implement kimage_map_segment() which takes a kimage pointer, an address,
and a size. Ensure that the entire segment is being mapped by comparing
the given address and size to each segment in the kimage's segment array.
Collect the source pages that correspond to the given address range,
allocate an array of pointers to these pages, and map them to a
contiguous range of virtual addresses. If the mapping operation is
successful, the function returns the start of this range. Otherwise, it
frees the page pointer array and returns NULL.
Implement kimage_unmap_segment() that takes a pointer to a segment buffer
and unmaps it using vunmap().
Implement function ima_kexec_post_load(), to be called by IMA after kexec
loads the new Kernel image. ima_kexec_post_load() would map the IMA
buffer allocated during kexec 'load' to a segment in the loaded image.
Finally, move for_each_kimage_entry() macro from kexec_core.c to kexec.h.
Signed-off-by: Tushar Sugandhi<tusharsu@xxxxxxxxxxxxxxxxxxx>
---
include/linux/ima.h | 3 ++
include/linux/kexec.h | 13 ++++++
kernel/kexec_core.c | 73 ++++++++++++++++++++++++++++--
security/integrity/ima/ima_kexec.c | 32 +++++++++++++
4 files changed, 116 insertions(+), 5 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 86b57757c7b1..006db20f852d 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -49,6 +49,9 @@ static inline void ima_appraise_parse_cmdline(void) {}
#ifdef CONFIG_IMA_KEXEC
extern void ima_add_kexec_buffer(struct kimage *image);
+extern void ima_kexec_post_load(struct kimage *image);
+#else
+static inline void ima_kexec_post_load(struct kimage *image) {}
#endif
#else
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 22b5cd24f581..e00b8101b53b 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -490,6 +490,15 @@ static inline int
arch_kexec_post_alloc_pages(void *vaddr, unsigned int pages, g
static inline void arch_kexec_pre_free_pages(void *vaddr, unsigned
int pages) { }
#endif
+#define for_each_kimage_entry(image, ptr, entry) \
+ for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
+ ptr = (entry & IND_INDIRECTION) ? \
+ boot_phys_to_virt((entry & PAGE_MASK)) : ptr + 1)
+
+extern void *kimage_map_segment(struct kimage *image,
+ unsigned long addr, unsigned long size);
+extern void kimage_unmap_segment(void *buffer);
+
#else /* !CONFIG_KEXEC_CORE */
struct pt_regs;
struct task_struct;
@@ -497,6 +506,10 @@ static inline void __crash_kexec(struct pt_regs
*regs) { }
static inline void crash_kexec(struct pt_regs *regs) { }
static inline int kexec_should_crash(struct task_struct *p) { return
0; }
static inline int kexec_crash_loaded(void) { return 0; }
+static inline void *kimage_map_segment(struct kimage *image,
+ unsigned long addr, unsigned long size)
+{ return NULL; }
+static inline void kimage_unmap_segment(void *buffer) { }
#define kexec_in_progress false
#endif /* CONFIG_KEXEC_CORE */
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 3d578c6fefee..e01156f3c404 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -594,11 +594,6 @@ void kimage_terminate(struct kimage *image)
*image->entry = IND_DONE;
}
-#define for_each_kimage_entry(image, ptr, entry) \
- for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
- ptr = (entry & IND_INDIRECTION) ? \
- boot_phys_to_virt((entry & PAGE_MASK)) : ptr + 1)
-
static void kimage_free_entry(kimage_entry_t entry)
{
struct page *page;
@@ -921,6 +916,74 @@ int kimage_load_segment(struct kimage *image,
return result;
}
+void *kimage_map_segment(struct kimage *image,
+ unsigned long addr, unsigned long size)
+{
+ unsigned long eaddr = addr + size;
+ unsigned long src_page_addr, dest_page_addr;
+ struct page **src_pages;
+ int i, npages;
+ kimage_entry_t *ptr, entry;
+ void *vaddr = NULL;
+
+ /*
+ * Make sure that we are mapping a whole segment.
+ */
+ for (i = 0; i < image->nr_segments; i++) {
+ if (addr == image->segment[i].mem &&
+ size == image->segment[i].memsz) {
+ break;
+ }
+ }