Skip to contentSkip to conversation

arm64: kexec_file: Stop using the Image text_offset

PATCH In review Jinjie Ruan 3 participants 3 messages Nov 5 – Nov 9 arm64

lore
Share & tools
lore
#0 lore raw

PATCH v8 1/1 arm64: kexec_file: Stop using the Image text_offset

image_load() adds text_offset to kernel_segment->mem and subtracts it from kernel_segment->memsz. For CMA backed segments this breaks the load path: the payload is copied to page_address(cma), which is the CMA base, while image->start is set to the adjusted address, so kexec jumps into the middle of the kernel. kimage_map_segment() has the same mismatch when a segment has been moved.

The adjusted memsz also breaks the release path: the CMA pages are freed using segment->memsz >> PAGE_SHIFT, which is fewer than were allocated, leaking the tail of the allocation.

Since commit 751c263bb74f ("arm64: remove TEXT_OFFSET randomization") TEXT_OFFSET is always zero. This has been upstream since v5.8, so no LTS kernel carries a non-zero text_offset. Drop the adjustment and reject images with a non-zero value instead of booting them from a wrong address. With the adjustment gone, segments stay at their CMA base and the release size matches the allocation.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mike Rapoport <[email protected]>
Fixes 07d24902977e ("kexec: enable CMA based contiguous allocation")

---
v2:
- Split out from the patch set.
- Link to v7: https://lore.kernel.org/all/[email protected]/

---
 arch/arm64/kernel/kexec_image.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
arch/arm64/kernel/kexec_image.c +8 -5
index 78b1f956d8d7..7d6baea3d28f 100644
@@ -80,12 +80,17 @@ static void *image_load(struct kimage *image,
 	kbuf.bufsz = kernel_len;
 	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
 	kbuf.memsz = le64_to_cpu(h->image_size);
+	/*
+	 * Pre-v5.8 images carry a non-zero text_offset, which is no longer
+	 * honored; reject them instead of loading them at a wrong address.
+	 */
 	text_offset = le64_to_cpu(h->text_offset);
+	if (text_offset) {
+		pr_err("Unsupported Image text_offset 0x%lx\n", text_offset);
+		return ERR_PTR(-ENOEXEC);
+	}
 	kbuf.buf_align = MIN_KIMG_ALIGN;
 
-	/* Adjust kernel segment with TEXT_OFFSET */
-	kbuf.memsz += text_offset;
-
 	kernel_segment_number = image->nr_segments;
 
 	/*
@@ -117,8 +122,6 @@ static void *image_load(struct kimage *image,
 	}
 
 	kernel_segment = &image->segment[kernel_segment_number];
-	kernel_segment->mem += text_offset;
-	kernel_segment->memsz -= text_offset;
 	image->start = kernel_segment->mem;
 
 	kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
Signature · 1 line
2.34.1
maintainer
#1 lore raw
Quoted 7 lines · Jinjie Ruan

image_load() adds text_offset to kernel_segment->mem and subtracts it from kernel_segment->memsz. For CMA backed segments this breaks the load path: the payload is copied to page_address(cma), which is the CMA base, while image->start is set to the adjusted address, so kexec jumps into the middle of the kernel. kimage_map_segment() has the same mismatch when a segment has been moved.

Quoted 5 lines

The adjusted memsz also breaks the release path: the CMA pages are freed using segment->memsz >> PAGE_SHIFT, which is fewer than were allocated, leaking the tail of the allocation.

Since commit 751c263bb74f ("arm64: remove TEXT_OFFSET randomization")

This commit only removed the randomisation. The one you want mentioned here is cfa7ede20f13 ("arm64: set TEXT_OFFSET to 0x0 in preparation for removing it entirely"). Although this removing never happened (see more below).

Quoted 29 lines

TEXT_OFFSET is always zero. This has been upstream since v5.8, so no LTS kernel carries a non-zero text_offset. Drop the adjustment and reject images with a non-zero value instead of booting them from a wrong address. With the adjustment gone, segments stay at their CMA base and the release size matches the allocation.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mike Rapoport <[email protected]>
Fixes 07d24902977e ("kexec: enable CMA based contiguous allocation")

---
v2:
- Split out from the patch set.
- Link to v7: https://lore.kernel.org/all/[email protected]/

---
 arch/arm64/kernel/kexec_image.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
arch/arm64/kernel/kexec_image.c +0 -0
index 78b1f956d8d7..7d6baea3d28f 100644
@@ -80,12 +80,17 @@ static void *image_load(struct kimage *image,
 	kbuf.bufsz = kernel_len;
 	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;

arch/arm64/kernel/kexec_image.c static void *image_load(struct kimage *image, · line 91

Quoted 10 lines
arch/arm64/kernel/kexec_image.c +8 -0
 	kbuf.memsz = le64_to_cpu(h->image_size);
+	/*
+	 * Pre-v5.8 images carry a non-zero text_offset, which is no longer
+	 * honored; reject them instead of loading them at a wrong address.
+	 */
 	text_offset = le64_to_cpu(h->text_offset);
+	if (text_offset) {
+		pr_err("Unsupported Image text_offset 0x%lx\n", text_offset);
+		return ERR_PTR(-ENOEXEC);
+	}

If it's only CMA getting it wrong, can we not just set image->no_cma = true here and continue as normal?

While text_offset is 0 now, I don't think we concluded it should be deprecated. There was a recent push back on the attempt to document the always 0 behaviour:

https://lore.kernel.org/all/[email protected]/

Signature · 1 line
Catalin
#2 lore raw

Hi Catalin,

Quoted 12 lines · Catalin Marinas
Quoted 11 lines · Jinjie Ruan

image_load() adds text_offset to kernel_segment->mem and subtracts it from kernel_segment->memsz. For CMA backed segments this breaks the load path: the payload is copied to page_address(cma), which is the CMA base, while image->start is set to the adjusted address, so kexec jumps into the middle of the kernel. kimage_map_segment() has the same mismatch when a segment has been moved.

The adjusted memsz also breaks the release path: the CMA pages are freed using segment->memsz >> PAGE_SHIFT, which is fewer than were allocated, leaking the tail of the allocation.

Quoted 6 lines
Quoted 1 line

Since commit 751c263bb74f ("arm64: remove TEXT_OFFSET randomization")

This commit only removed the randomisation. The one you want mentioned here is cfa7ede20f13 ("arm64: set TEXT_OFFSET to 0x0 in preparation for removing it entirely"). Although this removing never happened (see more below).

You are right! we should correct the commit.

Quoted 35 lines
Quoted 35 lines

TEXT_OFFSET is always zero. This has been upstream since v5.8, so no LTS kernel carries a non-zero text_offset. Drop the adjustment and reject images with a non-zero value instead of booting them from a wrong address. With the adjustment gone, segments stay at their CMA base and the release size matches the allocation.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mike Rapoport <[email protected]>
Fixes 07d24902977e ("kexec: enable CMA based contiguous allocation")

---
v2:
- Split out from the patch set.
- Link to v7: https://lore.kernel.org/all/[email protected]/

---
  arch/arm64/kernel/kexec_image.c | 13 ++++++++-----
  1 file changed, 8 insertions(+), 5 deletions(-)
arch/arm64/kernel/kexec_image.c +4 -0
index 78b1f956d8d7..7d6baea3d28f 100644
@@ -80,12 +80,17 @@ static void *image_load(struct kimage *image,
  	kbuf.bufsz = kernel_len;
  	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
  	kbuf.memsz = le64_to_cpu(h->image_size);
+	/*
+	 * Pre-v5.8 images carry a non-zero text_offset, which is no longer
+	 * honored; reject them instead of loading them at a wrong address.
+	 */
Quoted 9 lines

arch/arm64/kernel/kexec_image.c static void *image_load(struct kimage *image, · line 91

Quoted 5 lines
arch/arm64/kernel/kexec_image.c +4 -0
  	text_offset = le64_to_cpu(h->text_offset);
+	if (text_offset) {
+		pr_err("Unsupported Image text_offset 0x%lx\n", text_offset);
+		return ERR_PTR(-ENOEXEC);
+	}

If it's only CMA getting it wrong, can we not just set image->no_cma = true here and continue as normal?

Yes, it also works.

I tested this cma problem on QEMU arm64 with cma=64M and an Image header whose text_offset is 0x80000.
- Without fix , it jump 512K past the CMA copy and never boot;
- With the fix, it loads from normal memory and the second kernel boots.

Quoted 3 lines

While text_offset is 0 now, I don't think we concluded it should be deprecated. There was a recent push back on the attempt to document the always 0 behaviour:

Thank you! I will fix it with your suggestion.

Best regards,
Jinjie

Quoted 3 lines

https://lore.kernel.org/all/[email protected]/