避免固件意外覆盖

发布于 2025-01-06 05:02:56 字数 1108 浏览 1 评论 0原文

首先是一些背景知识。当固件由于某种原因崩溃时(例如堆栈溢出、函数指针损坏......),它可能会跳转到某个地方并开始执行一些代码。这迟早会导致看门狗复位。 MCU 将重置,我们又回到正轨。除非...

当我们有写入闪存的代码(例如引导加载程序)时该怎么办?现在,我们可能会意外地直接跳到闪存写入代码 - 跳过所有检查。在看门狗吠叫之前,您将得到损坏的固件。这正是发生在我身上的事情。

现在有些人可能会说 - 修复导致我们甚至开始编写代码的根本错误。嗯,当你开发时,你会不断地改变代码。即使现在没有这样的错误,明天也可能会有。此外,没有代码是没有错误的——或者至少不是我的。

所以现在我正在做某种交叉检查。我有一个名为“wen”的变量,在常规检查之前将其设置为 0xa5(例如检查以确保目的地有效)。然后在进行实际擦除或写入之前,我检查“wen”是否确实设置为 0xa5。否则,这意味着我们不知何故意外地开始编写代码。成功写入后“wen”被清除。我已经用 C 语言完成了这个工作,效果很好。但理论上仍然有轻微的机会发生损坏,因为从最后检查“wen”到写入 SPMCR 寄存器,几乎没有指令。

现在我想通过将此检查放入汇编中(在写入 SPMCR 和 spm 指令之间)来改进这一点。

__asm__ __volatile__
(   
    "lds __zero_reg__, %0\n\t"
    "out %1, %2\n\t"
    "ldi r25, %3\n\t"
    "add __zero_reg__, r25\n\t"
    "brne spm_fail\n\t"
    "spm\n\t"
    "rjmp spm_done\n\t"
    "spm_fail: clr __zero_reg__\n\t"
    "call __assert\n\t"
    "spm_done:"
    :
    : "i" ((uint16_t)(&wen)),
      "I" (_SFR_IO_ADDR(__SPM_REG)),
      "r" ((uint8_t)(__BOOT_PAGE_ERASE)),
      "M" ((uint8_t)(-ACK)),
      "z" ((uint16_t)(adr))
   : "r25"
);

还没试过代码,明天再试试。你看到什么问题了吗?你会如何解决这样的问题?

First some background. When firmware for whatever reason crashes (e.g. stack overflow, corrupted function pointer...) it may happen, that it jumps somewhere and starts executing some code. This will sooner or later result in watchdog reset. MCU will reset and we are back on track. Unless...

What about when we have code that writes to flash (e.g. bootloader)? Now it can happen that we will accidentally jump directly into the flash write code - skipping all the checks. Before watchdog will bark you will end up with corrupted firmware. This is exactly what was happening to me.

Now some might say - fix the root bug that caused that we even jumped into write code. Well, when you are developing you are constantly changing the code. Even if there is no such bug in there at the moment, there might be tomorrow. Besides, no code is bug free - or at least not mine.

So now I am doing some kind of cross checking. I have a variable named 'wen' which I set to 0xa5 before the usual checks (e.g. check to make sure that destination is valid). Then just before doing the actual erase or write I check if 'wen' is really set to 0xa5. Otherwise this means that we somehow accidentally jumped into the writing code. After successful write 'wen' is cleared. I have done this in C and it worked well. But there is still slight theoretical chance corruption will happen, cause there are few instructions from this final check of 'wen' till write to SPMCR register.

Now I want to improve this by putting this check into assembly, between the write to SPMCR and spm instruction.

__asm__ __volatile__
(   
    "lds __zero_reg__, %0\n\t"
    "out %1, %2\n\t"
    "ldi r25, %3\n\t"
    "add __zero_reg__, r25\n\t"
    "brne spm_fail\n\t"
    "spm\n\t"
    "rjmp spm_done\n\t"
    "spm_fail: clr __zero_reg__\n\t"
    "call __assert\n\t"
    "spm_done:"
    :
    : "i" ((uint16_t)(&wen)),
      "I" (_SFR_IO_ADDR(__SPM_REG)),
      "r" ((uint8_t)(__BOOT_PAGE_ERASE)),
      "M" ((uint8_t)(-ACK)),
      "z" ((uint16_t)(adr))
   : "r25"
);

Haven't tried the code yet, will do that tomorrow. Do you see any problems? How do/would you solve such problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

深者入戏 2025-01-13 05:02:56

我见过的一种技术是确保闪存写入例程之前的字节将触发某种看门狗超时,或重置处理器。这样,就不可能执行导致闪存写入功能的随机数据并“落入”该功能。

在重置之前,您可能需要执行一些 NOP,以确保正确解释指令。

您验证函数是否从一开始就运行的技术看起来不错,假设您在完成写入后清除了 wen 变量。

One technique I've seen is to make sure the bytes immediately before your flash write routines will trigger some sort of watchdog timeout, or reset the processor. That way, it's not possible to execute random data leading up to the flash write function and just "fall into" the function.

You may need to have some NOPs before your reset to ensure the instructions are interpreted correctly.

Your technique of verifying that the function has run from the beginning, looks like a good one, assuming you clear the wen variable once you've done the write.

豆芽 2025-01-13 05:02:56

我不确定为什么您需要具有在引导加载程序中写入闪存的能力。我们的bootloader可以,因为它可以通过串口更新应用程序。因此,我们通过确保加载程序不包含任何写入闪存的代码来消除意外写入的可能性。下载的代码是包含要写入的映像的同一包中的标头。板载映像存储了编程算法的校验和,并在运行之前对其进行验证。

如果您正在编写内部生成的内容,那么我会查看与硬件相关的互锁。仅当您之前已将特定离散输出引脚设置为 ON 时才允许写入。回答“如果IP跳过检查怎么办”的问题?你可以分两部分来做。首先为算法设置一些关键变量。 (例如要写入的地址 - 将其初始化为无效内存,并且仅在写入之前进行的单独调用中正确设置它。然后让写入函数检查您的硬件互锁。在中断中执行启用步骤之一,或者响应计时器,如果您有恶意 IP,则不太可能按正确的顺序命中。

如果您的 IP 确实可以跳转到任何地方,则可能无法阻止无意的写入。确保到达那里的唯一路径还设置成功写入所需的所有其他内容。

Im not sure why you need to have the capability to write to flash in your bootloader. Our bootloader does, because it can update the application program via serial port. So we eliminate the potential for inadvertent writes by ensuring that the loader does not contain any of the code that writes to flash. That code is downloaded is a header in the same package that contains the image to be written. The onboard image has the checksum of the programming algo stored, and verifies it before running it.

If you are writing things that are generated internally, then I would look at hardware related interlocks. Only allow writes if you have previously set a particular discrete output pin to ON. To answer the problem of "what if the IP jumps past the checks"? you can do it in 2 parts. First set some critical variables for the algorithm. (the address to write to for example- keep that initialized to invalid memory, and only set it correctly in a separate call made before the write. Then have the write function check your HW interlock. Do one of the enable steps in an interrupt, or in response to a timer, something that is unlikely to be hit in the correct sequence if you have a rogue IP.

If your IP can really jump anywhere , it may be impossible to prevent an inadvertent write. The best you can hope for is that you ensure the only path to get there also sets up everything else needed for a successful write.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文