Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

arch/arm: Fix crash when using memcpy/memset as RAMFUNCS #16019

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

alexcekay
Copy link

Summary

Add no_builtin for memcpy/memset to the startup code of boards
with CONFIG_ARCH_RAMFUNCS, because certain compilers call memcpy/memset
instead of the explicit for loop. This will cause a crash if memcpy/memset
are mapped to RAM because the function that copies them to RAM is called later,
resulting in undefined code being executed.

Impact

  • Using memcpy/memset as RAMFUNCS is possible

Testing

  • The problem was observed with the following compiler: arm-none-eabi-gcc (15:13.2.rel1-2) 13.2.1 20231009
    • This is the default arm-none-eabi-gcc that is currently used on Ubuntu Noble
  • Was tested with a STM32F765

Testing logs before

Before the patch there was a call to memset in the startup:

0  0x000006c4 in memset (s=0x20021b00 <g_intstackalloc>, c=c@entry=0, n=91844) at /home/alex/[...]/nuttx/libs/libc/string/lib_memset.c:73
#1  0x0800a804 in __start () at /home/alex/[...]/nuttx/arch/arm/src/stm32f7/stm32_start.c:194
#2  0x08000306 in ?? ()

As can be seen the address of memset it is in non initialized storage.

Testing logs after

After the patch there is no more call to memset/memcpy:

#0  __start () at /home/alex/[...]/nuttx/arch/arm/src/stm32f7/stm32_start.c:196
#1  0x08000306 in ?? ()
   0x0800a834 <+80>:    bl      0x814039c <stm32_boardinitialize()>                                                                                                                                         
   0x0800a838 <+84>:    bl      0x800a884 <up_enable_icache>                                                                                                                                                
   0x0800a83c <+88>:    bl      0x800a8ac <up_enable_dcache>                                                                                                                                                
   0x0800a840 <+92>:    bl      0x800b894 <arm_earlyserialinit>                                                                                                                                             
   0x0800a844 <+96>:    bl      0x800d290 <nx_start>                                                                                                                                                        
=> 0x0800a848 <+100>:   str.w   r1, [r3], #4                                                                                                                                                                
   0x0800a84c <+104>:   b.n     0x800a7f0 <__start+12>
   0x0800a84e <+106>:   ldr.w   r0, [r2], #4
   0x0800a852 <+110>:   str.w   r0, [r3], #4
   0x0800a856 <+114>:   b.n     0x800a7fa <__start+22>
   0x0800a858 <+116>:   ldr.w   r0, [r2], #4
   0x0800a85c <+120>:   str.w   r0, [r3], #4
   0x0800a860 <+124>:   b.n     0x800a804 <__start+32>

This way a successful boot is possible.

Add no_builtin for memcpy/memset to the startup code of boards
with CONFIG_ARCH_RAMFUNCS, because certain compilers call memcpy/memset
instead of the explicit for loop. This will cause a crash if memcpy/memset
are mapped to RAM because the function that copies them to RAM is called later,
resulting in undefined code being executed.

Signed-off-by: Alexander Lerach <alexander@auterion.com>
@github-actions github-actions bot added Arch: arm Issues related to ARM (32-bit) architecture Size: S The size of the change in this PR is small labels Mar 19, 2025
@nuttxpr
Copy link

nuttxpr commented Mar 19, 2025

[Experimental Bot, please feedback here]

Yes, this PR meets the NuttX requirements. The summary clearly explains the problem, the impacted code, and the solution. The impact section correctly identifies the primary impact (enabling memcpy/memset as RAMFUNCS). The testing section provides sufficient detail, including host/target information, the compiler version where the issue was observed, and clear before/after logs demonstrating the fix. The concise explanation of the observed behavior (calling memset before it's copied to RAM) is particularly helpful.

Copy link
Contributor

@pussuw pussuw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this should be set globally / via compiler flag?

If someone defines a for loop explicitly, I'm pretty sure the user WANTS TO EXPLICITLY run the for loop, instead of memcpy. This applies to just like these cases where the lib functions cannot be used.

@pussuw
Copy link
Contributor

pussuw commented Mar 19, 2025

I'm wondering if this should be set globally / via compiler flag?

If someone defines a for loop explicitly, I'm pretty sure the user WANTS TO EXPLICITLY run the for loop, instead of memcpy. This applies to just like these cases where the lib functions cannot be used.

Well then again, initialization lists become less effective, so let's use your way instead.

Copy link
Member

@raiden00pl raiden00pl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make it in a more elegant way? for example by defining a new decorator in nuttx/compiler.h like:

#ifdef CONFIG_ARCH_RAMFUNCS
#  define os_entry_point_decorator no_builtin("memcpy") no_builtin("memset")
#else
#  define os_entry_point_decorator
#endif

the name os_entry_point_decorator might be better, but I just want to show the idea

@alexcekay
Copy link
Author

Hi @raiden00pl,

interesting idea! Yes, this will also work, and I also considered wrapping it. I chose this variant because it is more explicit and follows a style similar to other uses of no_builtin, like in libs/libc/string.

If you prefer the wrapped variant, I can, of course, change it.

@raiden00pl
Copy link
Member

@alexcekay If no one has anything against the decorator, I think that's what we should do. This approach is nicer (my subjective opinion, so it doesn't matter much), but more important allows for easier maintenance in the future if something needs to be changed.

@github-actions github-actions bot added the Area: OS Components OS Components issues label Mar 24, 2025
Copy link
Contributor

@jerpelea jerpelea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d0f034c

please add a commit message explaining why you rename the decorator

Copy link
Contributor

@jerpelea jerpelea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3db5caf

please add a commit message and explain why you add a decorator

Use a decorator that contains the required no_builtin instead of adding
them explicitly to the startup code. This way it will be easier to
maintain them in the future when changes to the no_builtin used are required.

Signed-off-by: Alexander Lerach <alexander@auterion.com>
Rename the entry point decorator used for the startup code to a less explicit name.
This way the style is more consistent with other decorators.

Signed-off-by: Alexander Lerach <alexander@auterion.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Arch: arm Issues related to ARM (32-bit) architecture Area: OS Components OS Components issues Size: S The size of the change in this PR is small
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants