Sat, 12 Oct 2024 19:41:04 +0200
fix invalid reads when removing linked list nodes
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "cx/test.h" #include "cx/properties.h" CX_TEST(test_cx_properties_init) { CxProperties prop; CX_TEST_DO { cxPropertiesInitDefault(&prop); CX_TEST_ASSERT(prop.config.delimiter == '='); CX_TEST_ASSERT(prop.config.comment1 == '#'); CX_TEST_ASSERT(prop.config.comment2 == 0); CX_TEST_ASSERT(prop.config.comment3 == 0); CX_TEST_ASSERT(prop.flags == 0); CX_TEST_ASSERT(prop.text == NULL); CX_TEST_ASSERT(prop.buf == NULL); cxPropertiesDestroy(&prop); } } CX_TEST(test_cx_properties_next) { const char *tests[] = { "name = value\n", "name=value\n", "n=value\n", "name=v\n", "n=v\n", "name = value # comment\n", "#comment\nn=v\n", "# comment1\n# comment2\n\n \n\nname = value\n", " name = value\n", "name = value\n\n" }; const char *keys[] = { "name", "name", "n", "name", "n", "name", "n", "name", "name", "name" }; const char *values[] = { "value", "value", "value", "v", "v", "value", "v", "value", "value", "value" }; CxProperties prop; cxPropertiesInitDefault(&prop); enum cx_properties_status result; cxstring key; cxstring value; CX_TEST_DO { for (int i = 0; i < 10; i++) { cxPropertiesInput(&prop, tests[i], strlen(tests[i])); CX_TEST_ASSERT(prop.text == tests[i]); CX_TEST_ASSERT(prop.text_size == strlen(tests[i])); CX_TEST_ASSERT(prop.text_pos == 0); result = cxPropertiesNext(&prop, &key, &value); cxstring k = cx_str(keys[i]); cxstring v = cx_str(values[i]); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, k)); CX_TEST_ASSERT(0 == cx_strcmp(value, v)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); } } cxPropertiesDestroy(&prop); } CX_TEST(test_cx_properties_next_multi) { const char *keys[] = { "a", "b", "c", "uap", "name", "key1", "key2", "key3" }; const char *values[] = { "a value", "b value", "core", "core", "ucx", "value1", "value2", "value3" }; const char *str = "#\n" "# properties\n" "# contains key/value pairs\n" "#\n" "a = a value\n" "b = b value\n" "c = core\n" "\n# test\n" "uap = core\n" "name = ucx\n" "# no = property\n" "key1 = value1\n" "#key1 = wrong value\n" "#key2 = not value 2\n" "key2 = value2\n" "\n\n\n \n key3=value3\n"; CxProperties prop; cxPropertiesInitDefault(&prop); enum cx_properties_status result; cxstring key; cxstring value; CX_TEST_DO { result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NULL_INPUT); cxPropertiesInput(&prop, str, strlen(str)); for (int i = 0; i < 8; i++) { result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(!cx_strcmp(key, cx_str(keys[i]))); CX_TEST_ASSERT(!cx_strcmp(value, cx_str(values[i]))); } result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); } cxPropertiesDestroy(&prop); } CX_TEST(test_cx_properties_next_part) { CxProperties prop; cxPropertiesInitDefault(&prop); enum cx_properties_status result; cxstring key; cxstring value; const char *str; CX_TEST_DO { str = ""; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); str = " \n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); str = "name"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = " "; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); // call fill twice in a row str = "= "; cxPropertiesFill(&prop, str, strlen(str)); str = "value"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = "\n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("name"))); CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value"))); // second round str = "#comment\n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); str = "#comment\nname2 = "; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = "value2\na = b\n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("name2"))); CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value2"))); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("a"))); CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("b"))); str = "# comment\n#\n#\ntests = "; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = "test1 "; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = "test2 test3 test4\n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("tests"))); CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("test1 test2 test3 test4"))); // test if cxPropertiesNext finds a name/value after a comment str = "# just a comment"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = " in 3"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = " parts\nx = 1\n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("x"))); CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("1"))); // finally we are done result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); } cxPropertiesDestroy(&prop); } CX_TEST(test_ucx_properties_next_long_lines) { CxProperties prop; cxPropertiesInitDefault(&prop); enum cx_properties_status result; cxstring key; cxstring value; size_t key_len = 512; char *long_key = (char*)malloc(key_len); memset(long_key, 'a', 70); memset(long_key + 70, 'b', 242); memset(long_key + 312, 'c', 200); size_t value_len = 2048; char *long_value = (char*)malloc(value_len); memset(long_value, 'x', 1024); memset(long_value+1024, 'y', 1024); CX_TEST_DO { cxPropertiesFill(&prop, long_key, 10); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); cxPropertiesFill(&prop, long_key + 10, 202); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); cxPropertiesFill(&prop, long_key + 212, 200); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); cxPropertiesFill(&prop, long_key + 412, 100); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); const char *str = " = "; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); cxPropertiesFill(&prop, long_value, 512); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); cxPropertiesFill(&prop, long_value + 512, 1024); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); cxPropertiesFill(&prop, long_value + 1536, 512); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); str = "\n#comment\nkey = value\n"; cxPropertiesFill(&prop, str, strlen(str)); result = cxPropertiesNext(&prop, &key, &value); cxstring k = cx_strn(long_key, key_len); cxstring v = cx_strn(long_value, value_len); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, k)); CX_TEST_ASSERT(0 == cx_strcmp(value, v)); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key"))); CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value"))); result = cxPropertiesNext(&prop, &key, &value); CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); CX_TEST_ASSERT(prop.buf != NULL); CX_TEST_ASSERT(prop.buf_capacity > 0); CX_TEST_ASSERT(prop.buf_size == 0); cxPropertiesDestroy(&prop); CX_TEST_ASSERT(prop.buf == NULL); CX_TEST_ASSERT(prop.buf_capacity == 0); CX_TEST_ASSERT(prop.buf_size == 0); } free(long_key); free(long_value); } CxTestSuite *cx_test_suite_properties(void) { CxTestSuite *suite = cx_test_suite_new("properties"); cx_test_register(suite, test_cx_properties_init); cx_test_register(suite, test_cx_properties_next); cx_test_register(suite, test_cx_properties_next_multi); cx_test_register(suite, test_cx_properties_next_part); cx_test_register(suite, test_ucx_properties_next_long_lines); return suite; }