Re: [PATCH v3] ARM: imx: Fix suspend/resume crash with Clang CFI

From: Yo'av Moshe

Date: Mon Aug 24 2026 - 10:10:28 EST


Sorry it took so long, I finally got it to work using your method!

On 2026-08-17 11:58 PM, Nick Desaulniers wrote:
> Can you share that diff? I would have expected that to work. Perhaps a
> minor mistake in your implementation?

I started with something very simple: using SYM_TYPED_FUNC_START and
copying the 4-byte hash into OCRAM before fncpy(). But that didn't boot
on my hardware (Kobo Clara HD, i.MX6SLL).

I think the issues were:
1. My first attempt wrote the hash to suspend_ocram_base +
sizeof(*pm_info) - 4, which I suspect overwrote the last member of
struct imx6_cpu_pm_info, corrupting the memory controller setup.
2. Moving the hash after pm_info and offsetting fncpy by +4 still failed
— I think because fncpy requires 8-byte aligned source and destination
addresses (FNCPY_ALIGN, with a BUG_ON check).
3. Offsetting by +8 for alignment also failed. I suspect it is because
SYM_TYPED_FUNC_START emits the 4-byte hash after the .align directive,
which shifts the imx6_suspend label out of 8-byte alignment, again
triggering fncpy's source alignment BUG_ON.

The version that finally boots on hardware emits the hash manually with
explicit alignment padding, instead of using SYM_TYPED_FUNC_START:

diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index a671ca4..b500922 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -515,7 +515,7 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
pm_info = suspend_ocram_base;
pm_info->pbase = ocram_pbase;
pm_info->resume_addr = __pa_symbol(v7_cpu_resume);
- pm_info->pm_info_size = sizeof(*pm_info);
+ pm_info->pm_info_size = sizeof(*pm_info) + 8;

/*
* ccm physical address is not used by asm code currently,
@@ -568,10 +568,16 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
mmdc_offset_array[i]);
}

+ /* Reserve 8 bytes between pm_info and the function copy in OCRAM:
+ * 4 bytes padding + 4 bytes kCFI type hash, so that the hash sits
+ * at fncpy_dest - 4 and fncpy_dest remains 8-byte aligned. */
+ *(u32 *)(suspend_ocram_base + sizeof(*pm_info) + 4) =
+ *(((u32 *)&imx6_suspend) - 1);
+
imx6_suspend_in_ocram_fn = fncpy(
- suspend_ocram_base + sizeof(*pm_info),
+ suspend_ocram_base + sizeof(*pm_info) + 8,
&imx6_suspend,
- MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
+ MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info) - 8);

__arm_iomem_set_ro(suspend_ocram_base, MX6Q_SUSPEND_OCRAM_SIZE);

diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S
index 63ccc2d..c06e474 100644
--- a/arch/arm/mach-imx/suspend-imx6.S
+++ b/arch/arm/mach-imx/suspend-imx6.S
@@ -3,6 +3,7 @@
* Copyright 2014 Freescale Semiconductor, Inc.
*/

+#include <linux/cfi_types.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/asm-offsets.h>
@@ -148,6 +149,15 @@

.endm

+#ifdef CONFIG_CFI
+ /*
+ * Emit kCFI type hash before imx6_suspend with padding to preserve
+ * the 8-byte alignment that fncpy requires for the source address.
+ */
+ .align 3
+ .4byte 0
+ __CFI_TYPE(imx6_suspend)
+#endif
ENTRY(imx6_suspend)
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]


> How did you verify this? Can you share the command line invocations and output?
I tested each iteration on postmarketOS by building the kernel with
pmbootstrap (Clang/LLVM, CONFIG_CFI=y), replacing the vmlinuz on the SD
card, and booting the Kobo Clara HD. The earlier attempts all failed to
boot (though they worked fine in QEMU, which doesn't emulate the i.MX6
MMDC hardware I guess?).

I'm not really sure which approach is better now. The version above
preserves CFI on the indirect call, but it's quite involved compared to
the v3 __nocfi wrapper. If you think this is the better way to go, I'm
happy to clean it up and resubmit as v4.

Yo'av