Skip to content

Commit b3dc648

Browse files
committed
utils: provide safer strncpy function
The function checks for source and destination lengths provided.
1 parent ed0164f commit b3dc648

File tree

3 files changed

+143
-11
lines changed

3 files changed

+143
-11
lines changed

core/utils.c

+29-11
Original file line numberDiff line numberDiff line change
@@ -940,18 +940,9 @@ int utils_stringCopy(char * buffer,
940940
size_t length,
941941
const char * str)
942942
{
943-
size_t i;
944-
945-
for (i = 0 ; i < length && str[i] != 0 ; i++)
946-
{
947-
buffer[i] = str[i];
948-
}
949-
950-
if (i == length) return -1;
943+
size_t i = strlen(str); // NOSONAR
951944

952-
buffer[i] = 0;
953-
954-
return (int)i;
945+
return (int)utils_strncpy(buffer, length, str, i);
955946
}
956947

957948
void utils_copyValue(void * dst,
@@ -1167,3 +1158,30 @@ size_t utils_strnlen(const char *str, size_t max_size) {
11671158

11681159
return pos - str;
11691160
}
1161+
1162+
size_t utils_strncpy(char *dest, const size_t dest_size, const char *src, const size_t src_size) {
1163+
if (src == NULL || dest == NULL) {
1164+
return 0;
1165+
}
1166+
1167+
size_t bytes_to_write = dest_size;
1168+
size_t actual_src_len = utils_strnlen(src, src_size);
1169+
1170+
if (actual_src_len < bytes_to_write) {
1171+
bytes_to_write = actual_src_len;
1172+
}
1173+
memmove(dest, src, bytes_to_write);
1174+
if (bytes_to_write < dest_size) {
1175+
dest[bytes_to_write] = '\0';
1176+
}
1177+
1178+
// Do this always. Just to be sure!
1179+
if (dest_size > 0) {
1180+
dest[dest_size - 1] = '\0';
1181+
}
1182+
1183+
if (dest_size > 0 && bytes_to_write == dest_size) {
1184+
return bytes_to_write - 1; // '\0' written to last position
1185+
}
1186+
return bytes_to_write;
1187+
}

core/utils.h

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ lwm2m_media_type_t utils_convertMediaType(coap_content_type_t type);
2626
uint8_t utils_getResponseFormat(uint8_t accept_num, const uint16_t *accept, int numData, const lwm2m_data_t *dataP,
2727
bool singleResource, lwm2m_media_type_t *format);
2828
int utils_isAltPathValid(const char *altPath);
29+
/** Copy a string.
30+
*
31+
* Use utils_strncpy() if possible!
32+
* This is less safe than utils_strncpy().
33+
*
34+
* @param buffer The destination buffer
35+
* @param length The max number of bytes to be written to in the destination buffer
36+
* @param str The source string (needs to be NULL-terminated)
37+
* @return The number of bytes written
38+
*/
2939
int utils_stringCopy(char *buffer, size_t length, const char *str);
3040
size_t utils_intToText(int64_t data, uint8_t *string, size_t length);
3141
size_t utils_uintToText(uint64_t data, uint8_t *string, size_t length);
@@ -58,4 +68,15 @@ lwm2m_client_t *utils_findClient(lwm2m_context_t *contextP, void *fromSessionH);
5868
*/
5969
size_t utils_strnlen(const char *str, size_t max_size);
6070

71+
/** A safer version of `strncpy`. Copies at most src_size bytes to dest, but checks for available space.
72+
*
73+
* If dest is not NULL and dest_size is not 0, then the destination buffer is always terminated with '\0'.
74+
*
75+
* @param dest The char buffer where to copy the string.
76+
* @param dest_size The size of the destination buffer.
77+
* @param src The source string.
78+
* @param src_size The size of the source string buffer. The effective source string can be shorter.
79+
*/
80+
size_t utils_strncpy(char *dest, const size_t dest_size, const char *src, const size_t src_size);
81+
6182
#endif /* WAKAAMA_UTILS_H */

tests/core_utils_tests.c

+93
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,92 @@ void test_strnlen_max_len_5(void) {
7878
CU_ASSERT_EQUAL(len, 3);
7979
}
8080

81+
void test_strncpy_null(void) {
82+
char dest[] = {'a', 'b', 'c'};
83+
size_t len = utils_strncpy(dest, sizeof(dest), NULL, 1);
84+
CU_ASSERT_EQUAL(len, 0);
85+
CU_ASSERT_NSTRING_EQUAL(dest, "abc", sizeof(dest));
86+
87+
len = utils_strncpy(NULL, 99, "xyz", 1);
88+
CU_ASSERT_EQUAL(len, 0);
89+
len = utils_strncpy(NULL, 5, NULL, 3);
90+
CU_ASSERT_EQUAL(len, 0);
91+
}
92+
93+
void test_strncpy_dest_0_src_0(void) {
94+
char dst[] = {'a', 'b', 'c'};
95+
const char *src = "xyz";
96+
const size_t len = utils_strncpy(dst, 0, src, 0);
97+
CU_ASSERT_EQUAL(len, 0);
98+
CU_ASSERT_NSTRING_EQUAL(dst, "abc", sizeof(dst));
99+
}
100+
101+
void test_strncpy_dest_1_src_0(void) {
102+
char dst[] = {'a', 'b', 'c'};
103+
const char *src = "xyz";
104+
const size_t len = utils_strncpy(dst, 1, src, 0);
105+
CU_ASSERT_EQUAL(dst[0], '\0');
106+
CU_ASSERT_EQUAL(len, 0);
107+
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 0);
108+
}
109+
110+
void test_strncpy_dest_sizeof_src_0(void) {
111+
char dst[] = {'a', 'b', 'c'};
112+
const char *src = "xyz";
113+
const size_t len = utils_strncpy(dst, sizeof(dst), src, 0);
114+
CU_ASSERT_EQUAL(dst[0], '\0');
115+
CU_ASSERT_EQUAL(len, 0);
116+
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 0);
117+
}
118+
119+
void test_strncpy_dest_sizeof_src_1(void) {
120+
char dst[] = {'a', 'b', 'c'};
121+
const char *src = "xyz";
122+
const size_t len = utils_strncpy(dst, sizeof(dst), src, 1);
123+
CU_ASSERT_NSTRING_EQUAL(dst, "x", 1);
124+
CU_ASSERT_EQUAL(dst[1], '\0');
125+
CU_ASSERT_EQUAL(len, 1);
126+
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 1);
127+
}
128+
129+
void test_strncpy_dest_sizeof_src_2(void) {
130+
char dst[] = {'a', 'b', 'c'};
131+
const char *src = "xyz";
132+
const size_t len = utils_strncpy(dst, sizeof(dst), src, 2);
133+
CU_ASSERT_NSTRING_EQUAL(dst, "xy", 2);
134+
CU_ASSERT_EQUAL(dst[2], '\0');
135+
CU_ASSERT_EQUAL(len, 2);
136+
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 2);
137+
}
138+
139+
void test_strncpy_dest_sizeof_src_3(void) {
140+
char dst[] = {'a', 'b', 'c'};
141+
const char *src = "xyz";
142+
const size_t len = utils_strncpy(dst, sizeof(dst), src, 3);
143+
// only 2 characters and NULL has space in dst
144+
CU_ASSERT_NSTRING_EQUAL(dst, "xy", 2);
145+
CU_ASSERT_EQUAL(dst[2], '\0');
146+
CU_ASSERT_EQUAL(len, 2);
147+
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 2);
148+
}
149+
150+
void test_strncpy_dest_sizeof_src_4(void) {
151+
char dst[] = {'a', 'b', 'c'};
152+
char src[10];
153+
memset(src, '\0', sizeof(src));
154+
src[0] = 'u';
155+
src[1] = 'v';
156+
src[2] = 'w';
157+
const size_t src_len = sizeof(src);
158+
CU_ASSERT_EQUAL(src_len, 10);
159+
const size_t len = utils_strncpy(dst, sizeof(dst), src, src_len);
160+
// only 2 characters and NULL has space in dst
161+
CU_ASSERT_NSTRING_EQUAL(dst, "uv", 2);
162+
CU_ASSERT_EQUAL(dst[2], '\0');
163+
CU_ASSERT_EQUAL(len, 2);
164+
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 2);
165+
}
166+
81167
static struct TestTable table[] = {
82168
{"test_strnlen_null()", test_strnlen_null},
83169
{"test_strnlen_0()", test_strnlen_0},
@@ -89,6 +175,13 @@ static struct TestTable table[] = {
89175
{"test_strnlen_max_len_3()", test_strnlen_max_len_3},
90176
{"test_strnlen_max_len_4()", test_strnlen_max_len_4},
91177
{"test_strnlen_max_len_5()", test_strnlen_max_len_5},
178+
{"test_strncpy_null()", test_strncpy_null},
179+
{"test_strncpy_dest_0_src_0()", test_strncpy_dest_0_src_0},
180+
{"test_strncpy_dest_1_src_0()", test_strncpy_dest_1_src_0},
181+
{"test_strncpy_dest_sizeof_src_0()", test_strncpy_dest_sizeof_src_0},
182+
{"test_strncpy_dest_sizeof_src_1()", test_strncpy_dest_sizeof_src_1},
183+
{"test_strncpy_dest_sizeof_src_3()", test_strncpy_dest_sizeof_src_3},
184+
{"test_strncpy_dest_sizeof_src_4()", test_strncpy_dest_sizeof_src_4},
92185
{NULL, NULL},
93186
};
94187

0 commit comments

Comments
 (0)