Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
RaAl_264636
Level 6
Level 6
50 sign-ins 25 sign-ins 10 solutions authored

Hello,

I encountered a strange problem regarding the heap memory - if I increase the UART transmit buffer size from 256 to 1024 bytes, the heap seems to get corrupted. The first call to malloc() returns NULL. However there's no relationship between the UART component and the heap as far as I know because the UART component doesn't use dynamic memory. I wonder how I can get this debugged.

I'm using two UARTs with redirection of printf() to UART_1 and stderr to UART_2 in the application, but the call to malloc() is done before doing any prints and the requested size is way below heap size. Here are my project settings:

SRAM used: 7601 of 65536 bytes (11,6 %). Stack: 2048 bytes. Heap: 2048 bytes.


I tried to figure out if the Tx buffer somehow is within the heap section, but that doesn't seem to be the case:

extern uint8_t __cy_heap_start;
extern uint8_t __cy_heap_end;
extern uint8_t __cy_stack;
extern uint8_t __cy_stack_limit;
extern volatile uint8 UART_1_txBuffer[UART_1_TX_BUFFER_SIZE];
extern volatile uint8 UART_2_txBuffer[UART_2_TX_BUFFER_SIZE];

int main(void) {
	CyGlobalIntEnable; /* Enable global interrupts. */

	/* Place your initialization/startup code here (e.g. MyInst_Start()) */
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);
	UART_1_Start();
	UART_2_Start();
	//doing some other initialization, etc including the 1st malloc()

	printf("Heap start at %p, heap end at %p\n", &__cy_heap_start, &__cy_heap_end);
	printf("Stack start at %p, stack end at %p\n", &__cy_stack, &__cy_stack_limit);
	printf("UART1 Tx buffer start at %p, end at %p, size %d bytes\n", &UART_1_txBuffer, &UART_1_txBuffer[UART_1_TX_BUFFER_SIZE - 1], UART_1_TX_BUFFER_SIZE);
	printf("UART2 Tx buffer start at %p, end at %p, size %d bytes\n", &UART_2_txBuffer, &UART_2_txBuffer[UART_2_TX_BUFFER_SIZE - 1], UART_2_TX_BUFFER_SIZE);
	while(1);
}

This is with a TX buffer size of 256 bytes which works correctly and gives the following output:

Heap start at 0x1fff8db8, heap end at 0x20007800
Stack start at 0x20008000, stack end at 0x20007800
UART1 Tx buffer start at 0x1fff8b94, end at 0x1fff8c93, size 256 bytes
UART2 Tx buffer start at 0x1fff8cb4, end at 0x1fff8db3, size 256 bytes

The stack size equals the setting from the system design settings exactly and the linker script seems to provide all the unused RAM for the heap: 0x20007800 - 0x1FFF8DB8 = 0xEA48 => 59976 bytes.

If I increase the TX buffer size of UART_2 (stderr) to 1024 bytes, the 1st malloc() with a request for 256 bytes fails. If I left out the malloc() request, the stack/heap information is:

Heap start at 0x1fff8f98, heap end at 0x20007800
Stack start at 0x20008000, stack end at 0x20007800
UART1 Tx buffer start at 0x1fff8a70, end at 0x1fff8b6f, size 256 bytes
UART2 Tx buffer start at 0x1fff8b90, end at 0x1fff8f8f, size 1024 bytes

So, I can't see anything wrong here. There's still no overlap. I can successfully request 76 byte, anything above fails if the TX buffer size is 1024. If I reduce it to the original 256 bytes it works again. I have not checked at which Tx buffer size the original request of 256 bytes fails.

Since there's no obvious relation between the UART buffers which are simply global arrays and the heap memory I'm a bit lost on how to debug and fix this. Ideas are welcome. Currently I'm trying to setup a minimum working example project to track it down and I'll provide it if I can manage to reproduce the issue.


Regards

 

0 Likes
12 Replies
RaAl_264636
Level 6
Level 6
50 sign-ins 25 sign-ins 10 solutions authored

UPDATE:
I set a breakpoint within the _sbrk() function of Cm3Start.c file to check how much memory is requested by malloc(). _sbrk() was called multiple times on the first call to malloc(), where one request to _sbrk() was done with 4kByte(!) when the UART Tx buffer is set to 1024. This was not the case with a buffer size of 256 byte.

I checked my project settings and the only deviation from the default settings was that I disabled usage of newlib-nano. To be honest, I don't remember why I initially disabled newlib-nano when I created the project. EDIT: now I know (again) why I used newlib instead of newlib-nano: I had to do a printf() with an uint64_t variable, which isn't supported on newlib-nano. However, enabling newlib-nano seems to work even with the increased buffer, but I need to check this first in detail. I also was able to reproduce it with a minimum project.

Using the regular newlib seems to request much bigger memory chunks, but I can't see why it's affected by the size of the Tx buffer. So I hope someone can clarify what might happen here.

Regards

0 Likes
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

@RaAl_264636 ,

I'm going to make some experiential guesses here.   These are from my personal experiences in similar results.

Overall, unintentional corruption of SRAM memory occurs when the memory array being controlled is written out-of-bounds.  Ie.  A memory write to the array occurs before the start of the array or, more likely, a write occurs after the maximum memory address for the array.

I noticed you are using the printf() function.   It is a function to simplify your code.   However, printf() as well as sprintf() and fprintf() are considered "unsafe" functions.

Why unsafe?   This is because these functions assume you did your "homework" that you allocated enough heap memory (heap array) at design-time.   

Functions printf(), sprintf() and fprintf() use heap to temporarily store the results of the filled-in formatted string before pushing it out to the appropriate stdout device.   However, look at this extreme case: 

 

byte strg[] = {"`01234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|SDFGHJKL:""ZXCVBNM<>?"};
...
printf ("%s %s %s", strg, strg, strg);

 

strg has about 200 bytes.   The printf() result string will require at least 600 bytes of heap memory.   The printf() has no known CHECKS for detecting overrunning the heap memory allocated.   Therefore, without proper consideration in your design, you can overrun the heap (array) memory.

Sadly, this self-checking can be complicated by the fact that other functions (such as math and floating functions) can use the heap as needed.  Therefore, it is difficult to tell exactly how much heap is really used in a running program.

One way to avoid this potential printf(), sprintf() and fprintf() heap issue is to use snprint() or fnprintf() functions instead.   These functions use memory allocated different from the heap.   I pointer to this memory is passed to the function along with this memory's size.   The snprint() and fnprintf() does a bounds check as it is expanding the resulting formatted string.  It WILL NOT exceed the memory size allowed.  Hence:  They are "Memory safe".

{
byte tstr[700];  // allocate a temporary string memory for snprintf()
byte strg[] = {"`01234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|SDFGHJKL:""ZXCVBNM<>?"};

...
snprintf(tstr , strlen(tstr), "%s %s %s", strg, strg, strg);  // bounds checked.
OUTPUT_PutString(tstr);    // physical dump of the result string 
...
}

I use these functions over the non-memory testing "brothers" all the time.  The exception is when I know the output result strings are guaranteed to be small memory use.

I realize the printf() is convenient.

There should be a way to replace the printf() function without having to changing all your printf()s in your code.

Len
"Engineering is an Art. The Art of Compromise."
RaAl_264636
Level 6
Level 6
50 sign-ins 25 sign-ins 10 solutions authored

Hello @Len_CONSULTRON 

thank you for pointing me to the memory-safe versions. I'll check if I can switch over to them. Is it guaranteed that those function never use malloc(), etc.?

However, regarding the initial problem I doubt that it's an issue with the used printf() or heap in general etc because the effect raises if the UART buffer is increased. The UART doesn't use the heap. My assumption is that the chunk size requested by the malloc() function depends on the address boundary or something similar.

Regards

0 Likes

@RaAl_264636 ,

The memory-safe versions don't use malloc() to allocate the string result memory.  You statically allocate this memory at design-time like I did with the tstr[] buffer.

Assuming you're using a well-behaved UART buffer management scheme, you are correct that the UART is probably not the one corrupting memory.   However, using the non-safe printf() might be at fault for overwriting memory it doesn't own which can include the UART buffer.  Therefore when you expanded the UART buffer, the heap overwrites may have collided with the UART buffer causing the unexpected corruption.

Len
"Engineering is an Art. The Art of Compromise."
RaAl_264636
Level 6
Level 6
50 sign-ins 25 sign-ins 10 solutions authored

Hello @Len_CONSULTRON 

well, I didn't use any Tx buffer management, I left buffer handling to the UART component. If I remember correctly I managed to create a minimum working / non-working example where the error occurs by only changing the Tx buffer size, resulting in requesting a small chunk by malloc() on lower buffer size and 4kB size on higher buffer size.
I'll check for that example and provide it here for investigation.

Regards

0 Likes

@RaAl_264636,

If you left the UART buffer management to the UART component code, it's probably not the issue.  

As I indicated, printf() is not memory bounds checking.  Therefore it is considered unsafe by proper C coding conventions.   printf() is a very old function which preceded C coding SIGs.  It is normally considered to be a function to be used with much caution.

Here are a couple of links for your research:

https://www.invicti.com/blog/web-security/format-string-vulnerabilities/#:~:text=However%2C%20if%20u....

https://sternumiot.com/iot-blog/sprintf-and-snprintf-c-functions-usage-examples-and-security-best-pr....

 

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
lock attach
Attachments are accessible only for community members.

Hello @Len_CONSULTRON 


If you left the UART buffer management to the UART component code, it's probably not the issue.  

That's what I'm not sure about. It shouldn't / couldn't be the issue because it's not using dynamic memory. Attached is a minimum example of the issue. It can be run on a CY8CKIT-059. It's not exactly what I'm doing in my application (there's no snprintf() usage currently, only printf()), but it reproduces the issue which seems to be forced by use of printf(), figured out after some further investigation.

  • The example has no redirection of printf() to UART, therefore it uses snprintf() to fill some buffers and uses the components output function for strings.
  • Line #64 & 65 of the main function only pulls in printf(), but doesn't use it. Those lines can be disabled by the #if 0 statement.
  • If the printf() is pulled in and the Tx buffer size of the 2nd UART is 256 bytes, the application works
  • If the printf() is pulled in and the Tx buffer size of the 2nd UART is 1024 bytes, the application fails
  • If the printf() is NOT pulled in and the Tx buffer size of the 2nd UART is still 1024 bytes, the application also still works
  • There is a breakpoint in the _sbrk() function of cm3start.c, so it can be seen how much bytes are requested. To me it looks like that GCC is calculating the needed amount in advance and requesting it at once instead of each call of (sn)printf() and/or malloc() - there are fewer calls to _sbrk() than to the functions using it under the hood.

 


As I indicated, printf() is not memory bounds checking.  Therefore it is considered unsafe by proper C coding conventions.   printf() is a very old function which preceded C coding SIGs.  It is normally considered to be a function to be used with much caution.

Here are a couple of links for your research:


Thank you for those links, I'll read them and check if I can replace printf() with a safer version. Maybe this will also disable the heap issue 🤔

 

Regards

0 Likes

You might also want to go under the "system" section in PSOC Creator for the CYDWR page and increase the Heap size there, along with the stack size.  I had some issues like you have, and was overflowing both stack and heap. I ended up making the heap about 8k (0x2000, and the stack about the same size (overkill).

 

0 Likes
RaAl_264636
Level 6
Level 6
50 sign-ins 25 sign-ins 10 solutions authored

Hello @wmaxfield 

well, increasing the HEAP is not the solution I think, it's just curing the symptom but not the issue. Why should increasing a static memory need to increase the HEAP to work properly?
There seems to be an issue with the compiler/linker.

@Ekta I don't agree that increasing the HEAP is the solution, at least as long there's no explanation what happens.

Regards

0 Likes

@RaAl_264636 ,

As I said in an earlier post:   Ditch printf() and use the safer snprintf().

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
  I understand your frustration.

  However, the library that Cypress used has a heap requirement.

  If the project uses any of the library functions that use the heap,
then the heap size is important.

  A lot of times the heap is used for temporary working ram, think
malloc() and free()

  Having written code since 1968, I have lived both sides of the
argument, both as a library user and a library writer.

  You are not wrong in your thinking. The ideal is to not use libraries
that have heap requirements.

  BUT.... In order to get projects out the door, you use what you are
given and live with the ugly warts.

  So, at my hourly rate, it is better for the customer that I increase
heap size and a few seconds later have a working project.

  Is is ideal?  No.  Is it cheaper? Yes.

  Today cheaper is the bottom line, if it works......and refuting this
argument is very valid, except I like to get paid and the customer only
wants working projects quickly.

later!
0 Likes

Hello @wmaxfield 

I can live with increasing the heap as long as I know WHY I'm increasing it. This is not the case for the issue reported. I'm curious why increasing a static buffer makes dynamic allocation behave different.
Currently I'm digging through newlib sourcecode for another reason, but maybe I can figure out what happens. I'll also switch the test project to the newest GCC/newlib combo and check if the problem is still there.

Regards

0 Likes