Skip to main content
Associate III
June 12, 2023
Solved

Hardfault randomly when I do sprintf

Hello everyone,

I'm developing a firmware for an STM32F767 and I have a problem that happens randomly and I can't figure out why. From what I could analyze looking at the stack I think the problem comes when doing a sprintf and I have the hypothesis that it could be an overflow problem.

According to the fault analyzer, when the error is caught I have this context:

Hard Fault Details: Bus, memory management or usage fault (FORCED)

Bus Fault Details: Precise data access violation (PRECISERR) BFAR: 0x30333531

Register Content During Fault Exception:

  • sp(PSP): 0x20013480
  • r0: 0x30333531
  • r1: 0x0
  • r2: 0x18
  • r3: 0x20040fe0
  • r12: 0x819bc400
  • lr: 0x813dc81
  • pc: 0x813f6fa
  • xpsr: 0x2100000

It is curious since the BFAR and r0 register contains part of the value to be formatted by sprintf (the number is 15300), which further strengthens the hypothesis that there may be an overflow problem somewhere.

MPU and cache is used with the next configuration:

/* Configure the MPU attributes for the frontbuffer to normal memory (800px x 600px * 16bpp) */
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.BaseAddress = 0xC0000000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_2MB;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER1;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
	MPU_InitStruct.SubRegionDisable = 0x00;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
	/* Configure the MPU attributes for the backbuffer to normal memory (800px x 600px * 16bpp) */
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.BaseAddress = 0xC0200000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_2MB;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER1;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
	MPU_InitStruct.SubRegionDisable = 0x00;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
 
	/* Configure the MPU attributes as device for NOR (Strongly Ordered Memory) */
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.BaseAddress = 0x60000000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;
	MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER3;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
	MPU_InitStruct.SubRegionDisable = 0x00;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.BaseAddress = 0x60000000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER4;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
	MPU_InitStruct.SubRegionDisable = 0x00;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.BaseAddress = 0x20000000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER5;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
	MPU_InitStruct.SubRegionDisable = 0x00;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);

I have tried increasing stacks, heaps and stacks of the task where the error occurs. But I am facing the same error.

I am currently using the standard c library which incorporates the latest version Atollic True Studio 9.3.0. Can you suggest what I can do or what I can check to see where the error is coming from?

Thank you in advance

Best regards

This topic has been closed for replies.
waclawek.jan
waclawek.janBest answer
Super User
June 12, 2023

Look at disasm at and before address pc: 0x813f6fa . Which function is it in?

Do you use RTOS? If yes, don't use printf(), it's not reentrant. (I mean, generally, using printf() in microcontrollers is something which should be considered twice and then rejected).

JW

10 replies

KnarfB
Super User
June 12, 2023

Is the sprintf buffer large enough? Can you make it static (to remove it from the stack)?

Besides, your code says twice MPU_REGION_NUMBER1, copy&paste error?

hth

KnarfB

NickjmasAuthor
Associate III
June 12, 2023

Hi KnarfB

thank you for your suggestions. About buffer used, it is static and has enought size to show the data (100 bytes).

About the MPU configuration, you are in true, I have twice MPU_REGION_NUMBER1 and source is wrong. I will fix and I will try again.

Best regards

Bob S
Super User
June 12, 2023

Even though not likely your issue (if your buffer really is large enough), use snprintf() instead of sprintf(). It prevents any possible buffer overrun.

You don't show to sprintf() call that generated the fault. Are you trying to print floats?

waclawek.jan
waclawek.janBest answer
Super User
June 12, 2023

Look at disasm at and before address pc: 0x813f6fa . Which function is it in?

Do you use RTOS? If yes, don't use printf(), it's not reentrant. (I mean, generally, using printf() in microcontrollers is something which should be considered twice and then rejected).

JW

NickjmasAuthor
Associate III
June 13, 2023

Hi wacakwek.jan,

thank you for you comments. The fault fall between Balloc and Bfree functions (see picture)


_legacyfs_online_stmicro_images_0693W00000dDgdHQAS.pngI'm using FreeRTOS with #define configUSE_NEWLIB_REENTRANT 0 because I'm using C standar library (no newlib). Analyzing the Call Stack, what I undertand is the standard C library is using reentrant functions since the most of them have the "_r" ending.

0x0813DC89 - _dtoa_r (Where the fault is triggered)

0x0813F702 - _Balloc

0x0813FD27 - __2db

0x0813DC89 - _dtoa_r

0x081404D3 - __ssprint_r

0x0813A6B5 - __ssvfscanf_r

Do you suggest use any alternative to format float values without using the standar library?

Best regards

Bob S
Super User
June 13, 2023

When printing floats, printf does use malloc as @TDK​ mentioned. And malloc() is not re-entrant unless you take specific steps to make it so (newer versions of CubeMX/IDE make this easier but not foolproof). So malloc() is probably the failure point as @Community member​ said.

Even though you see the functions with trailing _r, if you don't define configUSE_NEWLIB_REENTRANT then FreeRTOS doesn't make space for the reentrant structure in its task blocks (I think - need to verify this). No matter WHICH library you are using (clib or newlib).

[EDIT] And using floating point in multiple tasks has its own set of pitfalls.

TDK
June 12, 2023

sprintf/printf calls malloc under the hood. Likely the malloc call is failing, or returning a bogus address. Source code would be good to see.

"If you feel a post has answered your question, please click ""Accept as Solution""."
TDJ
Senior III
June 22, 2023

I occasionally experience the same HardFault problem using printf() from two or more ISRs of unequal priority.
In such case, one ISR may interrupt another one in the middle of strig rendering (and e.g. memcpy) or sending. There is no way to prevent it from happening other than temporarily disabling other IRQs or using a separate buffer for each ISR and spawning another low-priority process for grabbing and sending strings from each such buffer, but that is not a simple fix you are probably looking for.