I’ve been using an IDE making calls to the compiler without me having to configure much, but from the options I can see it seems my project is set to use gnu99 for the C Language Standard and gnu++11 for the C++ Language Standard, while using gcc version 4.2.1.
The project is running on embedded devices for IoT and it got to the point I needed to simulate some parts of it and my lazy go to is onlinegdb. There I noticed that I am probably building strings unsafely and I’m worried the project may be compromised and in need of an urgent update. I am pretty sure I designed the string sizes to never be overflowed with the data I’m passing to them, but that’s only if I implemented it correctly and that’s what I’m afraid I didn’t do.
Here’s a runnable example:
#include <stdio.h>
#include <string.h>
int main()
{
// example 1 works as expected initially - I think it is unsafe
char testString1[6];
sprintf(testString1, "123");
sprintf(testString1, "%s456", testString1);
printf("%sn", testString1); // 123456
// example 2 doesn't work as I expected - I think it is intrinsically safe though
char testString2[6];
snprintf(testString2, 6, "123");
snprintf(testString2, 6, "%s456", testString2);
printf("%sn", testString2); // 456
// example 3 what I found works and I think is safe
char testString3[6];
snprintf(testString3, 6, "123");
snprintf(testString3 + strlen(testString3), 6, "456");
printf("%sn", testString3); // 123456
return 0;
}
And here’s the output:
main.c: In function ‘main’:
main.c:9:29: warning: ‘456’ directive writing 3 bytes into a region of size between 1 and 6 [-Wformat-overflow=]
9 | sprintf(testString1, "%s456", testString1);
| ^~~
main.c:9:5: note: ‘sprintf’ output between 4 and 9 bytes into a destination of size 6
9 | sprintf(testString1, "%s456", testString1);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.c:15:33: warning: ‘456’ directive output may be truncated writing 3 bytes into a region of size between 1 and 6 [-Wformat-truncation=]
15 | snprintf(testString2, 6, "%s456", testString2);
| ^~~
main.c:15:5: note: ‘snprintf’ output between 4 and 9 bytes into a destination of size 6
15 | snprintf(testString2, 6, "%s456", testString2);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123456
456
123456
Example 1 is how I have been building my strings up to this moment. Unfortunately my IDE/compiler didn’t warn me of this, but onlinegdb did, probably since it uses a different compiler or language standard.
Example 2 is my first try to make this safer after noticing the warnings on onlinegdb. I replaced sprintf with snprintf, gave it a size limit and kept filling it with the same pattern as on example 1. This did not work, filling it just with the new data. In fact, if I remove the additional “456” characters on example 2, testString2 ends up being printed blank.
Example 3 is how I found it to work great and I think it is safe.
I would like to ask:
- Is example 1 safe? I think it isn’t due to warnings on example 1 and 2.
- Why doesn’t example 2 work as I expected?
- Is example 3 really safe? Can I start building strings like this safely?