Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lw/safer string functions #841

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 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;

buffer[i] = 0;
size_t i = strlen(str); // NOSONAR

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

void utils_copyValue(void * dst,
Expand Down Expand Up @@ -1155,3 +1146,42 @@ lwm2m_data_type_t utils_depthToDatatype(uri_depth_t depth)

return LWM2M_TYPE_UNDEFINED;
}

size_t utils_strnlen(const char *str, size_t max_size) {
if (str == NULL) {
return 0;
}
const char *pos = memchr(str, '\0', max_size);
if (pos == NULL) {
return 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;
}
31 changes: 31 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 All @@ -47,5 +57,26 @@ lwm2m_server_t *utils_findBootstrapServer(lwm2m_context_t *contextP, void *fromS
#if defined(LWM2M_SERVER_MODE) || defined(LWM2M_BOOTSTRAP_SERVER_MODE)
lwm2m_client_t *utils_findClient(lwm2m_context_t *contextP, void *fromSessionH);
#endif
/** A safer version of `strlen`. Finds the number of chars (bytes) of the given string up to a given max. length.
*
* The behaviour is undefined if the maximum length value is bigger than the actually reserved memory behind the @a str
* buffer.
*
* @param str The null-terminated string to find the length of.
* @param max_size Max size of string.
* @return The size of the string if it is smaller or equal to max_size. Otherwise max_size.
*/
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 */
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(TEST_SOURCES
core_logging_tests.c
core_registration_tests.c
core_uritests.c
core_utils_tests.c
data_senml_cbor_tests.c
data_senml_json_tests.c
data_tlv_json_lwm2m_data_test.c
Expand Down
197 changes: 197 additions & 0 deletions tests/core_utils_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*******************************************************************************
*
* Copyright (c) 2025 GARDENA GmbH
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* The Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Lukas Woodtli, GARDENA GmbH - Please refer to git log
*
*******************************************************************************/
#include "CUnit/Basic.h"
#include "tests.h"
#include "utils.h"

void test_strnlen_null(void) {
size_t len = utils_strnlen(NULL, 10);
CU_ASSERT_EQUAL(len, 0);
}

void test_strnlen_0(void) {
const char *string = "";
size_t len = utils_strnlen(string, 10);
CU_ASSERT_EQUAL(len, 0);
}

void test_strnlen_1(void) {
const char *string = "a";
size_t len = utils_strnlen(string, 10);
CU_ASSERT_EQUAL(len, 1);
}

void test_strnlen_2(void) {
const char *string = "ab";
size_t len = utils_strnlen(string, 10);
CU_ASSERT_EQUAL(len, 2);
}

void test_strnlen_max_len_0(void) {
const char *string = "abc";
size_t len = utils_strnlen(string, 0);
CU_ASSERT_EQUAL(len, 0);
}

void test_strnlen_max_len_1(void) {
const char *string = "abc";
size_t len = utils_strnlen(string, 1);
CU_ASSERT_EQUAL(len, 1);
}

void test_strnlen_max_len_2(void) {
const char *string = "abc";
size_t len = utils_strnlen(string, 2);
CU_ASSERT_EQUAL(len, 2);
}

void test_strnlen_max_len_3(void) {
const char *string = "abc";
size_t len = utils_strnlen(string, 3);
CU_ASSERT_EQUAL(len, 3);
}

void test_strnlen_max_len_4(void) {
const char *string = "abc";
size_t len = utils_strnlen(string, 4);
CU_ASSERT_EQUAL(len, 3);
}

void test_strnlen_max_len_5(void) {
const char *string = "abc";
size_t len = utils_strnlen(string, 5);
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},
{"test_strnlen_1()", test_strnlen_1},
{"test_strnlen_2()", test_strnlen_2},
{"test_strnlen_max_len_0()", test_strnlen_max_len_0},
{"test_strnlen_max_len_1()", test_strnlen_max_len_1},
{"test_strnlen_max_len_2()", test_strnlen_max_len_2},
{"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},
};

CU_ErrorCode create_utils_suit(void) {
CU_pSuite pSuite = NULL;

pSuite = CU_add_suite("Suite_utils", NULL, NULL);
if (NULL == pSuite) {
return CU_get_error();
}

return add_tests(pSuite, table);
}
1 change: 1 addition & 0 deletions tests/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ CU_ErrorCode create_logging_test_suit(void);
#ifdef LWM2M_SERVER_MODE
CU_ErrorCode create_registration_test_suit(void);
#endif
CU_ErrorCode create_utils_suit(void);
#endif /* TESTS_H_ */
3 changes: 3 additions & 0 deletions tests/unittests.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ int main(void) {
goto exit;
#endif

if (CUE_SUCCESS != create_utils_suit())
goto exit;

CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
CU_basic_show_failures(CU_get_failure_list());
Expand Down
Loading