Skip to content

Commit

Permalink
utils: provide safer strncpy function
Browse files Browse the repository at this point in the history
The function checks for source and destination lengths provided.
  • Loading branch information
LukasWoodtli committed Jan 8, 2025
1 parent d9d46db commit b4304de
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 11 deletions.
40 changes: 29 additions & 11 deletions core/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -940,18 +940,9 @@ int utils_stringCopy(char * buffer,
size_t length,
const char * str)
{
size_t i;

for (i = 0 ; i < length && str[i] != 0 ; i++)
{
buffer[i] = str[i];
}

if (i == length) return -1;
size_t i = strlen(str); // NOSONAR

buffer[i] = 0;

return (int)i;
return (int)utils_strncpy(buffer, length, str, i);
}

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

return pos - str;
}

size_t utils_strncpy(char *dest, const size_t dest_size, const char *src, const size_t src_size) {
if (src == NULL || dest == NULL) {
return 0;
}

size_t bytes_to_write = dest_size;
size_t actual_src_len = utils_strnlen(src, src_size);

if (actual_src_len < bytes_to_write) {
bytes_to_write = actual_src_len;
}
memmove(dest, src, bytes_to_write);
if (bytes_to_write < dest_size) {
dest[bytes_to_write] = '\0';
}

// Do this always. Just to be sure!
if (dest_size > 0) {
dest[dest_size - 1] = '\0';
}

if (dest_size > 0 && bytes_to_write == dest_size) {
return bytes_to_write - 1; // '\0' written to last position
}
return bytes_to_write;
}
21 changes: 21 additions & 0 deletions core/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ lwm2m_media_type_t utils_convertMediaType(coap_content_type_t type);
uint8_t utils_getResponseFormat(uint8_t accept_num, const uint16_t *accept, int numData, const lwm2m_data_t *dataP,
bool singleResource, lwm2m_media_type_t *format);
int utils_isAltPathValid(const char *altPath);
/** Copy a string.
*
* Use utils_strncpy() if possible!
* This is less safe than utils_strncpy().
*
* @param buffer The destination buffer
* @param length The max number of bytes to be written to in the destination buffer
* @param str The source string (needs to be NULL-terminated)
* @return The number of bytes written
*/
int utils_stringCopy(char *buffer, size_t length, const char *str);
size_t utils_intToText(int64_t data, uint8_t *string, size_t length);
size_t utils_uintToText(uint64_t data, uint8_t *string, size_t length);
Expand Down Expand Up @@ -57,4 +67,15 @@ lwm2m_client_t *utils_findClient(lwm2m_context_t *contextP, void *fromSessionH);
*/
size_t utils_strnlen(const char *str, size_t max_size);

/** A safer version of `strncpy`. Copies at most src_size bytes to dest, but checks for available space.
*
* If dest is not NULL and dest_size is not 0, then the destination buffer is always terminated with '\0'.
*
* @param dest The char buffer where to copy the string.
* @param dest_size The size of the destination buffer.
* @param src The source string.
* @param src_size The size of the source string buffer. The effective source string can be shorter.
*/
size_t utils_strncpy(char *dest, const size_t dest_size, const char *src, const size_t src_size);

#endif /* WAKAAMA_UTILS_H */
93 changes: 93 additions & 0 deletions tests/core_utils_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,92 @@ void test_strnlen_max_len_5(void) {
CU_ASSERT_EQUAL(len, 3);
}

void test_strncpy_null(void) {
char dest[] = {'a', 'b', 'c'};
size_t len = utils_strncpy(dest, sizeof(dest), NULL, 1);
CU_ASSERT_EQUAL(len, 0);
CU_ASSERT_NSTRING_EQUAL(dest, "abc", sizeof(dest));

len = utils_strncpy(NULL, 99, "xyz", 1);
CU_ASSERT_EQUAL(len, 0);
len = utils_strncpy(NULL, 5, NULL, 3);
CU_ASSERT_EQUAL(len, 0);
}

void test_strncpy_dest_0_src_0(void) {
char dst[] = {'a', 'b', 'c'};
const char *src = "xyz";
const size_t len = utils_strncpy(dst, 0, src, 0);
CU_ASSERT_EQUAL(len, 0);
CU_ASSERT_NSTRING_EQUAL(dst, "abc", sizeof(dst));
}

void test_strncpy_dest_1_src_0(void) {
char dst[] = {'a', 'b', 'c'};
const char *src = "xyz";
const size_t len = utils_strncpy(dst, 1, src, 0);
CU_ASSERT_EQUAL(dst[0], '\0');
CU_ASSERT_EQUAL(len, 0);
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 0);
}

void test_strncpy_dest_sizeof_src_0(void) {
char dst[] = {'a', 'b', 'c'};
const char *src = "xyz";
const size_t len = utils_strncpy(dst, sizeof(dst), src, 0);
CU_ASSERT_EQUAL(dst[0], '\0');
CU_ASSERT_EQUAL(len, 0);
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 0);
}

void test_strncpy_dest_sizeof_src_1(void) {
char dst[] = {'a', 'b', 'c'};
const char *src = "xyz";
const size_t len = utils_strncpy(dst, sizeof(dst), src, 1);
CU_ASSERT_NSTRING_EQUAL(dst, "x", 1);
CU_ASSERT_EQUAL(dst[1], '\0');
CU_ASSERT_EQUAL(len, 1);
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 1);
}

void test_strncpy_dest_sizeof_src_2(void) {
char dst[] = {'a', 'b', 'c'};
const char *src = "xyz";
const size_t len = utils_strncpy(dst, sizeof(dst), src, 2);
CU_ASSERT_NSTRING_EQUAL(dst, "xy", 2);
CU_ASSERT_EQUAL(dst[2], '\0');
CU_ASSERT_EQUAL(len, 2);
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 2);
}

void test_strncpy_dest_sizeof_src_3(void) {
char dst[] = {'a', 'b', 'c'};
const char *src = "xyz";
const size_t len = utils_strncpy(dst, sizeof(dst), src, 3);
// only 2 characters and NULL has space in dst
CU_ASSERT_NSTRING_EQUAL(dst, "xy", 2);
CU_ASSERT_EQUAL(dst[2], '\0');
CU_ASSERT_EQUAL(len, 2);
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 2);
}

void test_strncpy_dest_sizeof_src_4(void) {
char dst[] = {'a', 'b', 'c'};
char src[10];
memset(src, '\0', sizeof(src));
src[0] = 'u';
src[1] = 'v';
src[2] = 'w';
const size_t src_len = sizeof(src);
CU_ASSERT_EQUAL(src_len, 10);
const size_t len = utils_strncpy(dst, sizeof(dst), src, src_len);
// only 2 characters and NULL has space in dst
CU_ASSERT_NSTRING_EQUAL(dst, "uv", 2);
CU_ASSERT_EQUAL(dst[2], '\0');
CU_ASSERT_EQUAL(len, 2);
CU_ASSERT_EQUAL(utils_strnlen(dst, sizeof(dst)), 2);
}

static struct TestTable table[] = {
{"test_strnlen_null()", test_strnlen_null},
{"test_strnlen_0()", test_strnlen_0},
Expand All @@ -89,6 +175,13 @@ static struct TestTable table[] = {
{"test_strnlen_max_len_3()", test_strnlen_max_len_3},
{"test_strnlen_max_len_4()", test_strnlen_max_len_4},
{"test_strnlen_max_len_5()", test_strnlen_max_len_5},
{"test_strncpy_null()", test_strncpy_null},
{"test_strncpy_dest_0_src_0()", test_strncpy_dest_0_src_0},
{"test_strncpy_dest_1_src_0()", test_strncpy_dest_1_src_0},
{"test_strncpy_dest_sizeof_src_0()", test_strncpy_dest_sizeof_src_0},
{"test_strncpy_dest_sizeof_src_1()", test_strncpy_dest_sizeof_src_1},
{"test_strncpy_dest_sizeof_src_3()", test_strncpy_dest_sizeof_src_3},
{"test_strncpy_dest_sizeof_src_4()", test_strncpy_dest_sizeof_src_4},
{NULL, NULL},
};

Expand Down

0 comments on commit b4304de

Please sign in to comment.