diff options
Diffstat (limited to 'lib/util/tests')
31 files changed, 7065 insertions, 0 deletions
diff --git a/lib/util/tests/README b/lib/util/tests/README new file mode 100644 index 0000000..c1337d5 --- /dev/null +++ b/lib/util/tests/README @@ -0,0 +1,22 @@ +tfork tests +=========== + +To run the tfork torture testsuite under valgrind with the helgrind or drd +thread checkers, run valgrind with the --suppress option passing a suppressions +file. + +For helgrind: + +$ valgrind \ + --trace-children=yes \ + --tool=helgrind \ + --suppressions=lib/util/tests/tfork-helgrind.supp \ + ./bin/smbtorture ncalrpc:localhost local.tfork.tfork_threads + +For drd: + +$ valgrind \ + --trace-children=yes \ + --tool=drd \ + --suppressions=lib/util/tests/tfork-drd.supp \ + ./bin/smbtorture ncalrpc:localhost local.tfork.tfork_threads diff --git a/lib/util/tests/anonymous_shared.c b/lib/util/tests/anonymous_shared.c new file mode 100644 index 0000000..512a53f --- /dev/null +++ b/lib/util/tests/anonymous_shared.c @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + + anonymous_shared testing + + Copyright (C) Stefan Metzmacher 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool test_anonymous_shared_simple(struct torture_context *tctx) +{ + void *ptr; + size_t len; + + torture_comment(tctx, "anonymous_shared_free(NULL)\n"); + anonymous_shared_free(NULL); + + len = 500; + torture_comment(tctx, "anonymous_shared_allocate(%llu)\n", + (unsigned long long)len); + ptr = anonymous_shared_allocate(len); + torture_assert(tctx, ptr, "valid pointer"); + memset(ptr, 0xfe, len); + torture_comment(tctx, "anonymous_shared_free(ptr)\n"); + anonymous_shared_free(ptr); + + len = 50000; + torture_comment(tctx, "anonymous_shared_allocate(%llu)\n", + (unsigned long long)len); + ptr = anonymous_shared_allocate(len); + torture_assert(tctx, ptr, "valid pointer"); + memset(ptr, 0xfe, len); + torture_comment(tctx, "anonymous_shared_free(ptr)\n"); + anonymous_shared_free(ptr); + + memset(&len, 0xFF, sizeof(len)); + torture_comment(tctx, "anonymous_shared_allocate(%llu)\n", + (unsigned long long)len); + ptr = anonymous_shared_allocate(len); + torture_assert(tctx, ptr == NULL, "null pointer"); + + return true; +} + +/* local.anonymous_shared test suite creation */ +struct torture_suite *torture_local_util_anonymous_shared(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "anonymous_shared"); + + torture_suite_add_simple_test(suite, "simple", + test_anonymous_shared_simple); + + return suite; +} diff --git a/lib/util/tests/asn1_tests.c b/lib/util/tests/asn1_tests.c new file mode 100644 index 0000000..ab5262c --- /dev/null +++ b/lib/util/tests/asn1_tests.c @@ -0,0 +1,383 @@ +/* + Unix SMB/CIFS implementation. + + util_asn1 testing + + Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009 + Copyright (C) Volker Lendecke 2004 + Copyright (C) Andrew Bartlett 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "../asn1.h" + +struct oid_data { + const char *oid; /* String OID */ + const char *bin_oid; /* Binary OID represented as string */ +}; + +/* Data for successful OIDs conversions */ +static const struct oid_data oid_data_ok[] = { + { + .oid = "2.5.4.0", + .bin_oid = "550400" + }, + { + .oid = "2.5.4.1", + .bin_oid = "550401" + }, + { + .oid = "2.5.4.130", + .bin_oid = "55048102" + }, + { + .oid = "2.5.130.4", + .bin_oid = "55810204" + }, + { + .oid = "2.5.4.16387", + .bin_oid = "5504818003" + }, + { + .oid = "2.5.16387.4", + .bin_oid = "5581800304" + }, + { + .oid = "2.5.2097155.4", + .bin_oid = "558180800304" + }, + { + .oid = "2.5.4.130.16387.2097155.268435459", + .bin_oid = "55048102818003818080038180808003" + }, +}; + +/* Data for successful OIDs conversions */ +static const char *oid_data_err[] = { + "", /* empty OID */ + ".2.5.4.130", /* first sub-identifier is empty */ + "2.5.4.130.", /* last sub-identifier is empty */ + "2..5.4.130", /* second sub-identifier is empty */ + "2.5..4.130", /* third sub-identifier is empty */ + "2.abc.4.130", /* invalid sub-identifier */ + "2.5abc.4.130", /* invalid sub-identifier (alpha-numeric)*/ +}; + +/* Data for successful Partial OIDs conversions */ +static const struct oid_data partial_oid_data_ok[] = { + { + .oid = "2.5.4.130:0x81", + .bin_oid = "5504810281" + }, + { + .oid = "2.5.4.16387:0x8180", + .bin_oid = "55048180038180" + }, + { + .oid = "2.5.4.16387:0x81", + .bin_oid = "550481800381" + }, + { + .oid = "2.5.2097155.4:0x818080", + .bin_oid = "558180800304818080" + }, + { + .oid = "2.5.2097155.4:0x8180", + .bin_oid = "5581808003048180" + }, + { + .oid = "2.5.2097155.4:0x81", + .bin_oid = "55818080030481" + }, +}; + +static const struct { + DATA_BLOB blob; + int value; +} integer_tests[] = { + { + .blob = { discard_const_p(uint8_t, "\x02\x01\x00"), 3}, + .value = 0 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x01\x7f"), 3}, + .value = 127 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x02\x00\x80"), 4}, + .value = 128 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x02\x01\x00"), 4}, + .value = 256 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x01\x80"), 3}, + .value = -128 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x02\xff\x7f"), 4}, + .value = -129 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x01\xff"), 3}, + .value = -1 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x02\xff\x01"), 4}, + .value = -255 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x02\x00\xff"), 4}, + .value = 255 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x04\x80\x00\x00\x00"), 6}, + .value = 0x80000000 + }, + { + .blob = { discard_const_p(uint8_t, "\x02\x04\x7f\xff\xff\xff"), 6}, + .value = 0x7fffffff + } +}; + +/* Testing ber_write_OID_String() function */ +static bool test_ber_write_OID_String(struct torture_context *tctx) +{ + int i; + char *hex_str; + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + const struct oid_data *data = oid_data_ok; + + mem_ctx = talloc_new(tctx); + + /* check for valid OIDs */ + for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) { + torture_assert(tctx, ber_write_OID_String(mem_ctx, &blob, data[i].oid), + "ber_write_OID_String failed"); + + hex_str = hex_encode_talloc(mem_ctx, blob.data, blob.length); + torture_assert(tctx, hex_str, "No memory!"); + + torture_assert(tctx, strequal(data[i].bin_oid, hex_str), + talloc_asprintf(mem_ctx, + "Failed: oid=%s, bin_oid:%s", + data[i].oid, data[i].bin_oid)); + } + + /* check for invalid OIDs */ + for (i = 0; i < ARRAY_SIZE(oid_data_err); i++) { + torture_assert(tctx, + !ber_write_OID_String(mem_ctx, &blob, oid_data_err[i]), + talloc_asprintf(mem_ctx, + "Should fail for [%s] -> %s", + oid_data_err[i], + hex_encode_talloc(mem_ctx, blob.data, blob.length))); + } + + talloc_free(mem_ctx); + + return true; +} + +/* Testing ber_read_OID_String() function */ +static bool test_ber_read_OID_String(struct torture_context *tctx) +{ + int i; + char *oid; + DATA_BLOB oid_blob; + TALLOC_CTX *mem_ctx; + const struct oid_data *data = oid_data_ok; + + mem_ctx = talloc_new(tctx); + + for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) { + oid_blob = strhex_to_data_blob(mem_ctx, data[i].bin_oid); + + torture_assert(tctx, ber_read_OID_String(mem_ctx, oid_blob, &oid), + "ber_read_OID_String failed"); + + torture_assert(tctx, strequal(data[i].oid, oid), + talloc_asprintf(mem_ctx, + "Failed: oid=%s, bin_oid:%s", + data[i].oid, data[i].bin_oid)); + } + + talloc_free(mem_ctx); + + return true; +} + +/* Testing ber_write_partial_OID_String() function */ +static bool test_ber_write_partial_OID_String(struct torture_context *tctx) +{ + int i; + char *hex_str; + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + const struct oid_data *data = oid_data_ok; + + mem_ctx = talloc_new(tctx); + + /* ber_write_partial_OID_String() should work with not partial OIDs also */ + for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) { + torture_assert(tctx, ber_write_partial_OID_String(mem_ctx, &blob, data[i].oid), + "ber_write_partial_OID_String failed"); + + hex_str = hex_encode_talloc(mem_ctx, blob.data, blob.length); + torture_assert(tctx, hex_str, "No memory!"); + + torture_assert(tctx, strequal(data[i].bin_oid, hex_str), + talloc_asprintf(mem_ctx, + "Failed: oid=%s, bin_oid:%s", + data[i].oid, data[i].bin_oid)); + } + + /* ber_write_partial_OID_String() test with partial OIDs */ + data = partial_oid_data_ok; + for (i = 0; i < ARRAY_SIZE(partial_oid_data_ok); i++) { + torture_assert(tctx, ber_write_partial_OID_String(mem_ctx, &blob, data[i].oid), + "ber_write_partial_OID_String failed"); + + hex_str = hex_encode_talloc(mem_ctx, blob.data, blob.length); + torture_assert(tctx, hex_str, "No memory!"); + + torture_assert(tctx, strequal(data[i].bin_oid, hex_str), + talloc_asprintf(mem_ctx, + "Failed: oid=%s, bin_oid:%s", + data[i].oid, data[i].bin_oid)); + } + + talloc_free(mem_ctx); + + return true; +} + +/* Testing ber_read_partial_OID_String() function */ +static bool test_ber_read_partial_OID_String(struct torture_context *tctx) +{ + int i; + char *oid; + DATA_BLOB oid_blob; + TALLOC_CTX *mem_ctx; + const struct oid_data *data = oid_data_ok; + + mem_ctx = talloc_new(tctx); + + /* ber_read_partial_OID_String() should work with not partial OIDs also */ + for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) { + oid_blob = strhex_to_data_blob(mem_ctx, data[i].bin_oid); + + torture_assert(tctx, ber_read_partial_OID_String(mem_ctx, oid_blob, &oid), + "ber_read_partial_OID_String failed"); + + torture_assert(tctx, strequal(data[i].oid, oid), + talloc_asprintf(mem_ctx, + "Failed: oid=%s, bin_oid:%s", + data[i].oid, data[i].bin_oid)); + } + + /* ber_read_partial_OID_String() test with partial OIDs */ + data = partial_oid_data_ok; + for (i = 0; i < ARRAY_SIZE(partial_oid_data_ok); i++) { + oid_blob = strhex_to_data_blob(mem_ctx, data[i].bin_oid); + + torture_assert(tctx, ber_read_partial_OID_String(mem_ctx, oid_blob, &oid), + "ber_read_partial_OID_String failed"); + + torture_assert(tctx, strequal(data[i].oid, oid), + talloc_asprintf(mem_ctx, + "Failed: oid=%s, bin_oid:%s", + data[i].oid, data[i].bin_oid)); + } + + talloc_free(mem_ctx); + + return true; +} + +/* + * Testing asn1_read_Integer and asn1_write_Integer functions, + * inspired by Love Hornquist Astrand + */ + +static bool test_asn1_Integer(struct torture_context *tctx) +{ + int i; + TALLOC_CTX *mem_ctx; + bool ret = false; + + mem_ctx = talloc_new(tctx); + + for (i = 0; i < ARRAY_SIZE(integer_tests); i++) { + ASN1_DATA *data; + DATA_BLOB blob; + int val; + + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); + if (!data) { + goto err; + } + + if (!asn1_write_Integer(data, integer_tests[i].value)) goto err; + + if (!asn1_blob(data, &blob)) { + goto err; + } + + torture_assert_data_blob_equal(tctx, blob, integer_tests[i].blob, "asn1_write_Integer gave incorrect result"); + + if (!asn1_load(data, blob)) goto err; + torture_assert(tctx, asn1_read_Integer(data, &val), "asn1_write_Integer output could not be read by asn1_read_Integer()"); + + torture_assert_int_equal(tctx, val, integer_tests[i].value, + "readback of asn1_write_Integer output by asn1_read_Integer() failed"); + } + + ret = true; + + err: + + talloc_free(mem_ctx); + return ret; +} + + +/* LOCAL-ASN1 test suite creation */ +struct torture_suite *torture_local_util_asn1(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "asn1"); + + torture_suite_add_simple_test(suite, "ber_write_OID_String", + test_ber_write_OID_String); + + torture_suite_add_simple_test(suite, "ber_read_OID_String", + test_ber_read_OID_String); + + torture_suite_add_simple_test(suite, "ber_write_partial_OID_String", + test_ber_write_partial_OID_String); + + torture_suite_add_simple_test(suite, "ber_read_partial_OID_String", + test_ber_read_partial_OID_String); + + torture_suite_add_simple_test(suite, "asn1_Integer", + test_asn1_Integer); + + return suite; +} diff --git a/lib/util/tests/binsearch.c b/lib/util/tests/binsearch.c new file mode 100644 index 0000000..b3ecda1 --- /dev/null +++ b/lib/util/tests/binsearch.c @@ -0,0 +1,173 @@ +/* + Unix SMB/CIFS implementation. + + Tests for binsearch.h macros. + + Copyright Catalyst IT 2016. + + Written by Douglas Bagnall <douglas.bagnall@catalyst.net.nz> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util/binsearch.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static int int_cmp(int a, int b) +{ + return a - b; +} + +static int int_cmp_p(int a, int *b) +{ + return a - *b; +} + +static bool test_binsearch_v(struct torture_context *tctx) +{ + int array[] = { -11, -7, 0, 1, 723, 1000000}; + int misses[] = { -121, 17, -10, 10, -1, -723, 1000002}; + int i; + int *result = NULL; + + for (i = 0; i < ARRAY_SIZE(misses); i++) { + BINARY_ARRAY_SEARCH_V(array, ARRAY_SIZE(array), + misses[i], int_cmp, result); + torture_comment(tctx, "looking for misses[%d] == %d\n", i, misses[i]); + torture_assert(tctx, result == NULL, "failed to miss"); + } + + for (i = 0; i < ARRAY_SIZE(array); i++) { + BINARY_ARRAY_SEARCH_V(array, ARRAY_SIZE(array), + array[i], int_cmp, result); + torture_comment(tctx, "looking for array[%d] == %d, %p; got %p\n", + i, array[i], &array[i], result); + torture_assert(tctx, result == &array[i], + "failed to find element"); + } + return true; +} + +static bool test_binsearch_gte(struct torture_context *tctx) +{ + int array[] = { -11, -7, -7, -7, -1, 0, 0, 1, 723, 723, 723, + 724, 724, 10000}; + size_t a_len = ARRAY_SIZE(array); + int targets[] = { -121, -8, -7, -6, 17, -10, 10, -1, 723, + 724, 725, 10002, 10000, 0, -11, 1, 11}; + int i, j, target; + int *result = NULL, *next = NULL; + + for (i = 0; i < ARRAY_SIZE(targets); i++) { + target = targets[i]; + torture_comment(tctx, "looking for targets[%d] %d\n", + i, target); + + BINARY_ARRAY_SEARCH_GTE(array, a_len, target, + int_cmp_p, result, next); + + if (result == NULL) { + /* we think there is no exact match */ + for (j = 0; j < a_len; j++) { + if (target == array[j]) { + torture_comment(tctx, + "failed to find %d\n", + targets[i]); + torture_fail(tctx, + "result is wrongly NULL"); + } + } + if (next != NULL) { + torture_assert(tctx, (next >= array && + next < array + a_len), + "next is out of bounds"); + + torture_assert(tctx, *next > target, + "next <= target"); + if (target <= array[0]) { + torture_assert(tctx, next == array, + "search before start failed"); + } + if (next != array) { + torture_assert(tctx, next[-1] < target, + "next[-1] >= target"); + } + } + else { + torture_assert(tctx, array[a_len - 1] < target, + "next was not found\n"); + } + } else { + /* we think we found an exact match */ + torture_assert(tctx, *result == target, + "result has wrong value"); + + torture_assert(tctx, (result >= array && + result < array + a_len), + "result is out of bounds!"); + + torture_assert(tctx, next == NULL, + "next should be NULL on exact match\n"); + if (result != array) { + torture_assert(tctx, result[-1] != target, + "didn't find first target\n"); + } + } + if (target >= array[a_len - 1]) { + torture_assert(tctx, next == NULL, + "next is not NULL at array end\n"); + } + } + + /* try again, with result and next the same pointer */ + for (i = 0; i < ARRAY_SIZE(targets); i++) { + target = targets[i]; + torture_comment(tctx, "looking for targets[%d] %d\n", + i, target); + + BINARY_ARRAY_SEARCH_GTE(array, a_len, target, + int_cmp_p, result, result); + + if (result == NULL) { + /* we think the target is greater than all elements */ + torture_assert(tctx, array[a_len - 1] < target, + "element >= target not found\n"); + } else { + /* we think an element is >= target */ + torture_assert(tctx, *result >= target, + "result has wrong value"); + + torture_assert(tctx, (result >= array && + result < array + a_len), + "result is out of bounds!"); + + if (result != array) { + torture_assert(tctx, result[-1] < target, + "didn't find first target\n"); + } + } + } + + return true; +} + +struct torture_suite *torture_local_util_binsearch(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "binsearch"); + torture_suite_add_simple_test(suite, "binsearch_v", test_binsearch_v); + torture_suite_add_simple_test(suite, "binsearch_gte", test_binsearch_gte); + return suite; +} diff --git a/lib/util/tests/data_blob.c b/lib/util/tests/data_blob.c new file mode 100644 index 0000000..e1e8129 --- /dev/null +++ b/lib/util/tests/data_blob.c @@ -0,0 +1,172 @@ +/* + Unix SMB/CIFS implementation. + + data blob testing + + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool test_string(struct torture_context *tctx) +{ + DATA_BLOB blob = data_blob_string_const("bla"); + + torture_assert_int_equal(tctx, blob.length, 3, "blob length"); + torture_assert_str_equal(tctx, (char *)blob.data, "bla", "blob data"); + + return true; +} + +static bool test_string_null(struct torture_context *tctx) +{ + DATA_BLOB blob = data_blob_string_const_null("bla"); + + torture_assert_int_equal(tctx, blob.length, 4, "blob length"); + torture_assert_str_equal(tctx, (char *)blob.data, "bla", "blob data"); + + return true; +} + +static bool test_zero(struct torture_context *tctx) +{ + int i; + DATA_BLOB z = data_blob_talloc_zero(tctx, 4); + torture_assert_int_equal(tctx, z.length, 4, "length"); + for (i = 0; i < z.length; i++) + torture_assert_int_equal(tctx, z.data[i], 0, "contents"); + data_blob_free(&z); + return true; +} + + +static bool test_clear(struct torture_context *tctx) +{ + int i; + DATA_BLOB z = data_blob("lalala", 6); + torture_assert_int_equal(tctx, z.length, 6, "length"); + data_blob_clear(&z); + for (i = 0; i < z.length; i++) + torture_assert_int_equal(tctx, z.data[i], 0, "contents"); + data_blob_free(&z); + return true; +} + +static bool test_cmp(struct torture_context *tctx) +{ + DATA_BLOB a = data_blob_string_const("bla"); + DATA_BLOB b = data_blob_string_const("blae"); + torture_assert(tctx, data_blob_cmp(&a, &b) != 0, "cmp different"); + torture_assert(tctx, data_blob_cmp(&a, &a) == 0, "cmp self"); + return true; +} + +static bool test_equal_const_time(struct torture_context *tctx) +{ + const char *test_string = "foobarfoo"; + + DATA_BLOB null = data_blob_const(NULL, 0); + DATA_BLOB foobar = data_blob_const(test_string, 6); + DATA_BLOB bar = data_blob_const(test_string + 3, 3); + + /* These data blobs both contain 'foo', but at different addresses. */ + DATA_BLOB foo_same = data_blob_const(test_string, 3); + DATA_BLOB foo_other = data_blob_const(test_string + 6, 3); + + /* Test all equality combinations behave as expected. */ + torture_assert(tctx, data_blob_equal_const_time(&null, &null), "null == null"); + torture_assert(tctx, !data_blob_equal_const_time(&null, &foobar), "null != 'foobar'"); + torture_assert(tctx, !data_blob_equal_const_time(&null, &bar), "null != 'bar'"); + torture_assert(tctx, !data_blob_equal_const_time(&null, &foo_same), "null != 'foo'"); + torture_assert(tctx, !data_blob_equal_const_time(&null, &foo_other), "null != 'foo'"); + + torture_assert(tctx, !data_blob_equal_const_time(&foobar, &null), "'foobar' != null"); + torture_assert(tctx, data_blob_equal_const_time(&foobar, &foobar), "'foobar' == 'foobar'"); + torture_assert(tctx, !data_blob_equal_const_time(&foobar, &bar), "'foobar' != 'bar'"); + torture_assert(tctx, !data_blob_equal_const_time(&foobar, &foo_same), "'foobar' != 'foo'"); + torture_assert(tctx, !data_blob_equal_const_time(&foobar, &foo_other), "'foobar' != 'foo'"); + + torture_assert(tctx, !data_blob_equal_const_time(&foo_same, &null), "'foo' != null"); + torture_assert(tctx, !data_blob_equal_const_time(&foo_same, &foobar), "'foo' != 'foobar'"); + torture_assert(tctx, !data_blob_equal_const_time(&foo_same, &bar), "'foo' != 'bar'"); + torture_assert(tctx, data_blob_equal_const_time(&foo_same, &foo_same), "'foo' == 'foo'"); + torture_assert(tctx, data_blob_equal_const_time(&foo_same, &foo_other), "'foo' == 'foo'"); + + torture_assert(tctx, !data_blob_equal_const_time(&foo_other, &null), "'foo' != null"); + torture_assert(tctx, !data_blob_equal_const_time(&foo_other, &foobar), "'foo' != 'foobar'"); + torture_assert(tctx, !data_blob_equal_const_time(&foo_other, &bar), "'foo' != 'bar'"); + torture_assert(tctx, data_blob_equal_const_time(&foo_other, &foo_same), "'foo' == 'foo'"); + torture_assert(tctx, data_blob_equal_const_time(&foo_other, &foo_other), "'foo' == 'foo'"); + + torture_assert(tctx, !data_blob_equal_const_time(&bar, &null), "'bar' != null"); + torture_assert(tctx, !data_blob_equal_const_time(&bar, &foobar), "'bar' != 'foobar'"); + torture_assert(tctx, data_blob_equal_const_time(&bar, &bar), "'bar' == 'bar'"); + torture_assert(tctx, !data_blob_equal_const_time(&bar, &foo_same), "'bar' != 'foo'"); + torture_assert(tctx, !data_blob_equal_const_time(&bar, &foo_other), "'bar' != 'foo'"); + + return true; +} + +static bool test_hex_string(struct torture_context *tctx) +{ + DATA_BLOB a = data_blob_string_const("\xC\xA\xF\xE"); + torture_assert_str_equal(tctx, data_blob_hex_string_lower(tctx, &a), "0c0a0f0e", "hex string"); + torture_assert_str_equal(tctx, data_blob_hex_string_upper(tctx, &a), "0C0A0F0E", "hex string"); + return true; +} + +static bool test_append_NULL_0(struct torture_context *tctx) +{ + DATA_BLOB z = data_blob_talloc_zero(tctx, 0); + torture_assert_int_equal(tctx, z.length, 0, "length"); + torture_assert(tctx, z.data == NULL, "data"); + torture_assert(tctx, data_blob_append(NULL, &z, NULL, 0), "append NULL,0"); + torture_assert(tctx, data_blob_append(NULL, &z, "", 0), "append '',0"); + torture_assert_int_equal(tctx, z.length, 0, "length"); + torture_assert(tctx, z.data == NULL, "data"); + return true; +} + +static bool test_append_empty_0(struct torture_context *tctx) +{ + DATA_BLOB e = data_blob_talloc(tctx, "", 0); + torture_assert_int_equal(tctx, e.length, 0, "length"); + torture_assert(tctx, e.data != NULL, "data"); + torture_assert(tctx, data_blob_append(NULL, &e, NULL, 0), "append NULL,0"); + torture_assert(tctx, data_blob_append(NULL, &e, "", 0), "append '',0"); + torture_assert_int_equal(tctx, e.length, 0, "length"); + torture_assert(tctx, e.data != NULL, "data"); + return true; +} + +struct torture_suite *torture_local_util_data_blob(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "datablob"); + + torture_suite_add_simple_test(suite, "string", test_string); + torture_suite_add_simple_test(suite, "string_null", test_string_null); + torture_suite_add_simple_test(suite, "zero", test_zero);; + torture_suite_add_simple_test(suite, "clear", test_clear); + torture_suite_add_simple_test(suite, "cmp", test_cmp); + torture_suite_add_simple_test(suite, "equal_const_time", test_equal_const_time); + torture_suite_add_simple_test(suite, "hex string", test_hex_string); + torture_suite_add_simple_test(suite, "append_NULL_0", test_append_NULL_0); + torture_suite_add_simple_test(suite, "append_empty_0", test_append_empty_0); + + return suite; +} diff --git a/lib/util/tests/dlinklist.c b/lib/util/tests/dlinklist.c new file mode 100644 index 0000000..50adab3 --- /dev/null +++ b/lib/util/tests/dlinklist.c @@ -0,0 +1,131 @@ +/* + Unix SMB/CIFS implementation. + + local testing of DLIST_*() macros + + Copyright (C) Andrew Tridgell 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "lib/util/dlinklist.h" + +struct listel { + struct listel *next, *prev; +}; + +static bool torture_local_dlinklist_simple(struct torture_context *tctx) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct listel *l1 = NULL, *l2 = NULL, *el, *el2; + int i; + + torture_comment(tctx, "add 5 elements at front\n"); + for (i=0; i<5; i++) { + el = talloc(mem_ctx, struct listel); + DLIST_ADD(l1, el); + } + + torture_comment(tctx, "add 5 elements at end\n"); + for (i=0; i<5; i++) { + el = talloc(mem_ctx, struct listel); + DLIST_ADD_END(l1, el); + } + + torture_comment(tctx, "delete 3 from front\n"); + for (i=0; i < 3; i++) { + el = l1; + DLIST_REMOVE(l1, l1); + DLIST_ADD(l2, el); + } + + torture_comment(tctx, "delete 3 from back\n"); + for (i=0; i < 3; i++) { + el = DLIST_TAIL(l1); + DLIST_REMOVE(l1, el); + DLIST_ADD_END(l2, el); + } + + torture_comment(tctx, "count forward\n"); + for (i=0,el=l1; el; el=el->next) i++; + torture_assert_int_equal(tctx, i, 4, "should have 4 elements"); + + torture_comment(tctx, "count backwards\n"); + for (i=0,el=DLIST_TAIL(l1); el; el=DLIST_PREV(el)) i++; + torture_assert_int_equal(tctx, i, 4, "should have 4 elements"); + + torture_comment(tctx, "check DLIST_HEAD\n"); + el = DLIST_TAIL(l1); + DLIST_HEAD(el, el2); + torture_assert(tctx, el2 == l1, "should find head"); + + torture_comment(tctx, "check DLIST_ADD_AFTER\n"); + el = talloc(mem_ctx, struct listel); + el2 = talloc(mem_ctx, struct listel); + DLIST_ADD_AFTER(l1, el, l1); + DLIST_ADD_AFTER(l1, el2, el); + torture_assert(tctx, l1->next == el, "2nd in list"); + torture_assert(tctx, el->next == el2, "3rd in list"); + + torture_comment(tctx, "check DLIST_PROMOTE\n"); + DLIST_PROMOTE(l1, el2); + torture_assert(tctx, el2==l1, "1st in list"); + torture_assert(tctx, el2->next->next == el, "3rd in list"); + + torture_comment(tctx, "check DLIST_DEMOTE\n"); + DLIST_DEMOTE(l1, el); + torture_assert(tctx, el->next == NULL, "last in list"); + torture_assert(tctx, el2->prev == el, "backlink from head"); + + torture_comment(tctx, "count forward\n"); + for (i=0,el=l1; el; el=el->next) i++; + torture_assert_int_equal(tctx, i, 6, "should have 6 elements"); + + torture_comment(tctx, "count backwards\n"); + for (i=0,el=DLIST_TAIL(l1); el; el=DLIST_PREV(el)) i++; + torture_assert_int_equal(tctx, i, 6, "should have 6 elements"); + + torture_comment(tctx, "check DLIST_CONCATENATE\n"); + DLIST_CONCATENATE(l1, l2); + torture_comment(tctx, "count forward\n"); + for (i=0,el=l1; el; el=el->next) i++; + torture_assert_int_equal(tctx, i, 12, "should have 12 elements"); + + torture_comment(tctx, "count backwards\n"); + for (i=0,el=DLIST_TAIL(l1); el; el=DLIST_PREV(el)) i++; + torture_assert_int_equal(tctx, i, 12, "should have 12 elements"); + + torture_comment(tctx, "free forwards\n"); + for (el=l1; el; el=el2) { + el2 = el->next; + DLIST_REMOVE(l1, el); + talloc_free(el); + } + + torture_assert(tctx, l1 == NULL, "list empty"); + torture_assert_int_equal(tctx, talloc_total_blocks(mem_ctx), 1, "1 block"); + + talloc_free(mem_ctx); + return true; +} + +struct torture_suite *torture_local_dlinklist(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "dlinklist"); + torture_suite_add_simple_test(suite, "dlinklist", torture_local_dlinklist_simple); + return suite; +} diff --git a/lib/util/tests/file.c b/lib/util/tests/file.c new file mode 100644 index 0000000..3501c7e --- /dev/null +++ b/lib/util/tests/file.c @@ -0,0 +1,291 @@ +/* + Unix SMB/CIFS implementation. + + util_file testing + + Copyright (C) Jelmer Vernooij 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +#define TEST_FILENAME "utilfile.test" +#define TEST_LINE1 "This is list line 1..." +#define TEST_LINE2 ".. and this is line 2" +#define TEST_LINE3 "and end of the file" + +#define TEST_DATA TEST_LINE1 "\n" TEST_LINE2 "\n" TEST_LINE3 + +static bool test_file_load_save(struct torture_context *tctx) +{ + size_t len; + char *data; + TALLOC_CTX *mem_ctx = tctx; + + torture_assert(tctx, file_save(TEST_FILENAME, TEST_DATA, strlen(TEST_DATA)), + "saving file"); + + torture_assert_file_contains_text(tctx, TEST_FILENAME, TEST_DATA, + "file contents"); + + data = file_load(TEST_FILENAME, &len, 0, mem_ctx); + torture_assert(tctx, data, "loading file"); + + torture_assert_int_equal(tctx, len, strlen(TEST_DATA), "Length"); + + torture_assert_mem_equal(tctx, data, TEST_DATA, len, "Contents"); + + data = file_load(TEST_FILENAME, &len, 5, mem_ctx); + + torture_assert_int_equal(tctx, len, 5, "Length"); + + torture_assert_mem_equal(tctx, data, TEST_DATA, len, "Contents"); + + unlink(TEST_FILENAME); + return true; +} + +#define TEST_DATA_WITH_NEWLINE TEST_DATA "\n" +#define TEST_DATA_NO_NEWLINE TEST_DATA +#define TEST_DATA_EMPTY "" +#define TEST_DATA_BLANKS_ONLY "\n\n\n\n\n" +#define TEST_DATA_WITH_TRAILING_BLANKS TEST_DATA TEST_DATA_BLANKS_ONLY + +static bool test_file_lines_load(struct torture_context *tctx) +{ + char **lines; + int numlines; + TALLOC_CTX *mem_ctx = tctx; + + /* + * Last line has trailing whitespace + */ + + torture_assert(tctx, + file_save(TEST_FILENAME, + TEST_DATA_WITH_NEWLINE, + strlen(TEST_DATA_WITH_NEWLINE)), + "saving file"); + + lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx); + + torture_assert_int_equal(tctx, numlines, 3, "Lines"); + + torture_assert_mem_equal(tctx, + lines[0], + TEST_LINE1, + strlen(TEST_LINE1), + "Line 1"); + + torture_assert_mem_equal(tctx, + lines[1], + TEST_LINE2, + strlen(TEST_LINE2), + "Line 2"); + + torture_assert_mem_equal(tctx, + lines[2], + TEST_LINE3, + strlen(TEST_LINE3), + "Line 3"); + + unlink(TEST_FILENAME); + + /* + * Last line has NO trailing whitespace + */ + + torture_assert(tctx, + file_save(TEST_FILENAME, + TEST_DATA_NO_NEWLINE, + strlen(TEST_DATA_NO_NEWLINE)), + "saving file"); + + lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx); + + torture_assert_int_equal(tctx, numlines, 3, "Lines"); + + torture_assert_mem_equal(tctx, + lines[0], + TEST_LINE1, + strlen(TEST_LINE1), + "Line 1"); + + torture_assert_mem_equal(tctx, + lines[1], + TEST_LINE2, + strlen(TEST_LINE2), + "Line 2"); + + torture_assert_mem_equal(tctx, + lines[2], + TEST_LINE3, + strlen(TEST_LINE3), + "Line 3"); + + unlink(TEST_FILENAME); + + /* + * Empty file + */ + + torture_assert(tctx, + file_save(TEST_FILENAME, + TEST_DATA_EMPTY, + strlen(TEST_DATA_EMPTY)), + "saving file"); + + (void)file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx); + + torture_assert_int_equal(tctx, numlines, 0, "Lines"); + + unlink(TEST_FILENAME); + + /* + * Just blank lines + */ + + torture_assert(tctx, + file_save(TEST_FILENAME, + TEST_DATA_BLANKS_ONLY, + strlen(TEST_DATA_BLANKS_ONLY)), + "saving file"); + + lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx); + + torture_assert_int_equal(tctx, numlines, 0, "Lines"); + + unlink(TEST_FILENAME); + + /* + * Several trailing blank lines + */ + + torture_assert(tctx, + file_save(TEST_FILENAME, + TEST_DATA_WITH_TRAILING_BLANKS, + strlen(TEST_DATA_WITH_TRAILING_BLANKS)), + "saving file"); + + lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx); + + torture_assert_int_equal(tctx, numlines, 3, "Lines"); + + torture_assert_mem_equal(tctx, + lines[0], + TEST_LINE1, + strlen(TEST_LINE1), + "Line 1"); + + torture_assert_mem_equal(tctx, + lines[1], + TEST_LINE2, + strlen(TEST_LINE2), + "Line 2"); + + torture_assert_mem_equal(tctx, + lines[2], + TEST_LINE3, + strlen(TEST_LINE3), + "Line 3"); + + unlink(TEST_FILENAME); + + return true; +} + +static bool test_afdgets(struct torture_context *tctx) +{ + int fd; + char *line; + TALLOC_CTX *mem_ctx = tctx; + bool ret = false; + + torture_assert(tctx, file_save(TEST_FILENAME, (const void *)TEST_DATA, + strlen(TEST_DATA)), + "saving file"); + + fd = open(TEST_FILENAME, O_RDONLY); + + torture_assert(tctx, fd != -1, "opening file"); + + line = afdgets(fd, mem_ctx, 8); + torture_assert_goto(tctx, strcmp(line, TEST_LINE1) == 0, ret, done, + "line 1 mismatch"); + + line = afdgets(fd, mem_ctx, 8); + torture_assert_goto(tctx, strcmp(line, TEST_LINE2) == 0, ret, done, + "line 2 mismatch"); + + line = afdgets(fd, mem_ctx, 8); + torture_assert_goto(tctx, strcmp(line, TEST_LINE3) == 0, ret, done, + "line 3 mismatch"); + ret = true; +done: + close(fd); + + unlink(TEST_FILENAME); + return ret; +} + +static bool test_file_lines_parse(struct torture_context *tctx) +{ + char **lines; + int numlines; + TALLOC_CTX *mem_ctx = tctx; + char *buf; + size_t size; + + torture_assert(tctx, file_save(TEST_FILENAME, + (const void *)TEST_DATA, + strlen(TEST_DATA)), + "saving file"); + + buf = file_load(TEST_FILENAME, &size, 0, mem_ctx); + torture_assert(tctx, buf, "failed to load file"); + unlink(TEST_FILENAME); + + lines = file_lines_parse(buf, + size, + &numlines, + mem_ctx); + torture_assert(tctx, lines, "failed to parse lines"); + + TALLOC_FREE(lines); + TALLOC_FREE(buf); + return true; +} + +struct torture_suite *torture_local_util_file(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "file"); + + torture_suite_add_simple_test(suite, "file_load_save", + test_file_load_save); + + torture_suite_add_simple_test(suite, + "file_lines_load", + test_file_lines_load); + + torture_suite_add_simple_test(suite, "afdgets", test_afdgets); + + torture_suite_add_simple_test(suite, "file_lines_parse", + test_file_lines_parse); + + return suite; +} diff --git a/lib/util/tests/genrand.c b/lib/util/tests/genrand.c new file mode 100644 index 0000000..3987c33 --- /dev/null +++ b/lib/util/tests/genrand.c @@ -0,0 +1,61 @@ +/* + Unix SMB/CIFS implementation. + + local testing of random data routines. + + Copyright (C) Jelmer Vernooij <jelmer@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool test_check_password_quality(struct torture_context *tctx) +{ + torture_assert(tctx, !check_password_quality(""), "empty password"); + torture_assert(tctx, !check_password_quality("a"), "one char password"); + torture_assert(tctx, !check_password_quality("aaaaaaaaaaaa"), "same char password"); + torture_assert(tctx, !check_password_quality("BLA"), "multiple upcases password"); + torture_assert(tctx, !check_password_quality("123"), "digits only"); + torture_assert(tctx, !check_password_quality("matthiéu"), "not enough high symbols"); + torture_assert(tctx, !check_password_quality("abcdééà çè"), "only lower case"); + torture_assert(tctx, !check_password_quality("abcdééà çè+"), "only lower and symbols"); + torture_assert(tctx, check_password_quality("abcdééà çè+ढ"), "valid"); + torture_assert(tctx, check_password_quality("ç+ढ"), "valid"); + torture_assert(tctx, check_password_quality("A2e"), "valid"); + torture_assert(tctx, check_password_quality("BA2eLi443"), "valid"); + return true; +} + +static bool test_generate_random_str(struct torture_context *tctx) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + char *r = generate_random_str(mem_ctx, 10); + torture_assert_int_equal(tctx, strlen(r), 10, "right length generated"); + r = generate_random_str(mem_ctx, 5); + torture_assert_int_equal(tctx, strlen(r), 5, "right length generated"); + + TALLOC_FREE(mem_ctx); + return true; +} + +struct torture_suite *torture_local_genrand(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "genrand"); + torture_suite_add_simple_test(suite, "check_password_quality", test_check_password_quality); + torture_suite_add_simple_test(suite, "generate_random_str", test_generate_random_str); + return suite; +} diff --git a/lib/util/tests/genrandperf.c b/lib/util/tests/genrandperf.c new file mode 100644 index 0000000..32d19ab --- /dev/null +++ b/lib/util/tests/genrandperf.c @@ -0,0 +1,39 @@ +/* + Unix SMB/CIFS implementation. + local testing of random data routines. + Copyright (C) Volker Lendecke <vl@samba.org> 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "lib/util/genrand.h" + +int main(int argc, const char *argv[]) +{ + int i, num; + uint64_t val; + + if (argc != 2) { + fprintf(stderr, "genrandperf <num>\n"); + exit(1); + } + num = atoi(argv[1]); + + for(i=0; i<num; i++) { + generate_random_buffer((uint8_t *)&val, sizeof(val)); + } + printf("%"PRIu64"\n", val); + return 0; +} diff --git a/lib/util/tests/idtree.c b/lib/util/tests/idtree.c new file mode 100644 index 0000000..f4f7b11 --- /dev/null +++ b/lib/util/tests/idtree.c @@ -0,0 +1,122 @@ +/* + Unix SMB/CIFS implementation. + + local testing of idtree routines. + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool torture_local_idtree_simple(struct torture_context *tctx) +{ + struct idr_context *idr; + int i, ret; + int *ids; + int *present; + extern int torture_numops; + int n = torture_numops; + TALLOC_CTX *mem_ctx = tctx; + + idr = idr_init(mem_ctx); + + ids = talloc_zero_array(mem_ctx, int, n); + present = talloc_zero_array(mem_ctx, int, n); + + for (i=0;i<n;i++) { + ids[i] = -1; + } + + for (i=0;i<n;i++) { + int ii = random() % n; + void *p = idr_find(idr, ids[ii]); + if (present[ii]) { + if (p != &ids[ii]) { + torture_fail(tctx, talloc_asprintf(tctx, + "wrong ptr at %d - %p should be %p", + ii, p, &ids[ii])); + } + if (random() % 7 == 0) { + if (idr_remove(idr, ids[ii]) != 0) { + torture_fail(tctx, talloc_asprintf(tctx, + "remove failed at %d (id=%d)", + i, ids[ii])); + } + present[ii] = 0; + ids[ii] = -1; + } + } else { + if (p != NULL) { + torture_fail(tctx, + talloc_asprintf(tctx, + "non-present at %d gave %p (would be %d)", + ii, p, + (int)((((char *)p) - (char *)(&ids[0])) / sizeof(int)))); + } + if (random() % 5) { + ids[ii] = idr_get_new(idr, &ids[ii], n); + if (ids[ii] < 0) { + torture_fail(tctx, talloc_asprintf(tctx, + "alloc failure at %d (ret=%d)", + ii, ids[ii])); + } else { + present[ii] = 1; + } + } + } + } + + torture_comment(tctx, "done %d random ops\n", i); + + for (i=0;i<n;i++) { + if (present[i]) { + if (idr_remove(idr, ids[i]) != 0) { + torture_fail(tctx, talloc_asprintf(tctx, + "delete failed on cleanup at %d (id=%d)", + i, ids[i])); + } + } + } + + /* now test some limits */ + for (i=0;i<25000;i++) { + ret = idr_get_new_above(idr, &ids[0], random() % 25000, 0x10000-3); + torture_assert(tctx, ret != -1, "idr_get_new_above failed"); + } + + ret = idr_get_new_above(idr, &ids[0], 0x10000-2, 0x10000); + torture_assert_int_equal(tctx, ret, 0x10000-2, "idr_get_new_above failed"); + ret = idr_get_new_above(idr, &ids[0], 0x10000-1, 0x10000); + torture_assert_int_equal(tctx, ret, 0x10000-1, "idr_get_new_above failed"); + ret = idr_get_new_above(idr, &ids[0], 0x10000, 0x10000); + torture_assert_int_equal(tctx, ret, 0x10000, "idr_get_new_above failed"); + ret = idr_get_new_above(idr, &ids[0], 0x10000+1, 0x10000); + torture_assert_int_equal(tctx, ret, -1, "idr_get_new_above succeeded above limit"); + ret = idr_get_new_above(idr, &ids[0], 0x10000+2, 0x10000); + torture_assert_int_equal(tctx, ret, -1, "idr_get_new_above succeeded above limit"); + + torture_comment(tctx, "cleaned up\n"); + return true; +} + +struct torture_suite *torture_local_idtree(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "idtree"); + torture_suite_add_simple_test(suite, "idtree", torture_local_idtree_simple); + return suite; +} diff --git a/lib/util/tests/rfc1738.c b/lib/util/tests/rfc1738.c new file mode 100644 index 0000000..6e96037 --- /dev/null +++ b/lib/util/tests/rfc1738.c @@ -0,0 +1,411 @@ +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <stdint.h> +#include <cmocka.h> +#include "lib/replace/replace.h" + +#include <errno.h> +#include <unistd.h> +#include <talloc.h> +#include <ctype.h> +#include <string.h> +#include "lib/util/samba_util.h" + +/* These flags say what can be asserted about a relationship between a string + and its supposedly escaped equivalent. + + The first part of the flag name indicates the direction of transformation; + tyhe second part is the expected result. For example, ESCAPE_EQ means the + escape is expected to succeed and result is expected to be equal to the + given answer. ESCAPE_EQ_CASECMP is only equal when compared + case-insensitively. UNESCAPE_ERR means unescaping the escaped string should + result in an error. +*/ +#define UNESCAPE_ERR 1 +#define ESCAPE_ERR 2 +#define ESCAPE_EQ 4 +#define UNESCAPE_EQ 8 +#define ESCAPE_NE 16 +#define UNESCAPE_NE 32 +#define ESCAPE_EQ_CASECMP 64 + +struct rfc1738_test { + const char *escaped; /* original for unescape; result for escape */ + const char *unescaped; /* result in unescape; original for escape */ + uint32_t flags; /* see above */ + int unesc_len; /* end - start will be this */ + int unesc_strlen; /* strlen() will say this */ + int esc_len; /* escaped string length */ +}; + +/* unreserved = ALPHA DIGIT - . _ ~ */ + +char spectrum[255 + 1]; +char spectrum_escaped[255 * 3 + 1]; + +struct rfc1738_test examples[] = { + +#define SIMPLE1 "this_is_a_simple-string._With_no_escapes~" /* maps to self */ + { + SIMPLE1, + SIMPLE1, + ESCAPE_EQ | UNESCAPE_EQ, /* round trip should work */ + sizeof(SIMPLE1) - 1, + sizeof(SIMPLE1) - 1, + sizeof(SIMPLE1) - 1, + }, +#define SIMPLE2 "no escapes, but\n non-printables \xc5\x8d\x99" +#define SIMPLE2_ESC "no%20escapes%2C%20but%0A%20non-printables%20%C5%8D%99" + { + SIMPLE2_ESC, + SIMPLE2, + ESCAPE_EQ | UNESCAPE_EQ, + sizeof(SIMPLE2) - 1, + sizeof(SIMPLE2) - 1, + sizeof(SIMPLE2_ESC) - 1, + }, +#define SIMPLE3 "this @#$^&*()_+{}:;" +#define SIMPLE3_ESC "this%20%40%23%24%5E%26%2A%28%29_%2B%7B%7D%3A%3B" + { + SIMPLE3_ESC, + SIMPLE3, + ESCAPE_EQ | UNESCAPE_EQ, + sizeof(SIMPLE3) - 1, + sizeof(SIMPLE3) - 1, + sizeof(SIMPLE3_ESC) - 1, + }, + +#define ESCAPE1 "%/\x06this string has expected escapes" +#define ESCAPE1_ESC "%25%2F%06this%20string%20has%20expected%20escapes" +#define ESCAPE1_ESC_ESC "%2525%252F%2506this%2520string%2520has%2520expected"\ + "%2520escapes" + { + ESCAPE1_ESC, + ESCAPE1, + ESCAPE_EQ | UNESCAPE_EQ, + sizeof(ESCAPE1) - 1, + sizeof(ESCAPE1) - 1, + sizeof(ESCAPE1_ESC) - 1, + }, + { + ESCAPE1_ESC_ESC, /*re-escaping */ + ESCAPE1_ESC, + ESCAPE_EQ | UNESCAPE_EQ, + sizeof(ESCAPE1_ESC) - 1, + sizeof(ESCAPE1_ESC) - 1, + sizeof(ESCAPE1_ESC_ESC) - 1, + }, +#define ESCAPE2 "%25%2f%06-this-string-has-expected-lowercase-escapes-%ab" +#define ESCAPE2_UNESC "%/\x06-this-string-has-expected-lowercase-escapes-\xab" + { + ESCAPE2, + ESCAPE2_UNESC, + ESCAPE_EQ_CASECMP | UNESCAPE_EQ, /* escape won't match case */ + sizeof(ESCAPE2_UNESC) - 1, + sizeof(ESCAPE2_UNESC) - 1, + sizeof(ESCAPE2) - 1, + }, +#define ESCAPE3 "%25%2f%06 %32 %44 %6a%AA THIS string h%61s random escapes %ab" +#define ESCAPE3_UNESC "%/\x06 2 D j\xAA THIS string has random escapes \xab" + { + ESCAPE3, + ESCAPE3_UNESC, + ESCAPE_NE | UNESCAPE_EQ, /* escape will have escaped spaces */ + sizeof(ESCAPE3_UNESC) - 1, + sizeof(ESCAPE3_UNESC) - 1, + sizeof(ESCAPE3) - 1, + }, +#define ESCAPE4 "%25%25%25" /* */ +#define ESCAPE4_UNESC "%%%" /* */ +#define ESCAPE4_ESC "%2525%2525%2525" + { + ESCAPE4, + ESCAPE4_UNESC, + ESCAPE_EQ | UNESCAPE_EQ, + sizeof(ESCAPE4_UNESC) - 1, + sizeof(ESCAPE4_UNESC) - 1, + sizeof(ESCAPE4) - 1, + }, + { + ESCAPE4_ESC, + ESCAPE4, + ESCAPE_EQ | UNESCAPE_EQ, + sizeof(ESCAPE4) - 1, + sizeof(ESCAPE4) - 1, + sizeof(ESCAPE4_ESC) - 1, + }, +#define BAD1 "trailing percent is bad %" +#define BAD1_ESC "trailing%20percent%20is%20bad%20%25" + { + BAD1_ESC, + BAD1, + UNESCAPE_EQ |ESCAPE_EQ, + sizeof(BAD1) - 1, + sizeof(BAD1) - 1, + sizeof(BAD1_ESC) - 1, + }, + { + BAD1, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD1) - 1, + }, +#define BAD2 "trailing percent is bad %1" +#define BAD3 "bad characters %1 " + { + BAD2, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD2) - 1, + }, + { + BAD3, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD3) - 1, + }, +#define BAD4 "bad characters %1 " + { + BAD4, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD4) - 1, + }, +#define BAD5 "bad characters %1- " + { + BAD5, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD5) - 1, + }, +#define BAD6 "bad characters %1G " + { + BAD6, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD6) - 1, + }, +#define BAD7 "bad characters %%1 " + { + BAD7, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD7) - 1, + }, +#define BAD8 "bad characters %sb " + { + BAD8, + NULL, + UNESCAPE_ERR, + 0, + 0, + sizeof(BAD8) - 1, + }, +#define BAD_SSCANF "sscanf would be happy with this\n" +#define BAD_SSCANF_ESC "sscanf would be happy with this% a" + { + BAD_SSCANF_ESC, + BAD_SSCANF, + ESCAPE_NE | UNESCAPE_ERR, + sizeof(BAD_SSCANF) - 1, + sizeof(BAD_SSCANF) - 1, + sizeof(BAD_SSCANF_ESC) - 1, + }, + /* now try some with zeros in. escaping can't see past zeros, and the result is truncated */ +#define ZERO "%00" +#define ZERO_UNESC "\0" + { + ESCAPE4 ZERO ESCAPE4, + ESCAPE4_UNESC ZERO_UNESC ESCAPE4_UNESC, + ESCAPE_NE | UNESCAPE_EQ, + sizeof(ESCAPE4_UNESC ZERO_UNESC ESCAPE4_UNESC) - 1, + sizeof(ESCAPE4_UNESC) - 1, + sizeof(ESCAPE4 ZERO ESCAPE4) - 1, + }, + { + ZERO ESCAPE4, + ZERO_UNESC ESCAPE4_UNESC, + ESCAPE_NE | UNESCAPE_EQ, + sizeof(ZERO_UNESC ESCAPE4_UNESC) - 1, + 0, + sizeof(ZERO ESCAPE4) - 1, + }, + { + ZERO, + ZERO_UNESC, + ESCAPE_NE | UNESCAPE_EQ, + sizeof(ZERO_UNESC) - 1, + 0, + sizeof(ZERO) - 1, + }, + { + spectrum_escaped, + spectrum, + ESCAPE_EQ | UNESCAPE_EQ, + 255, + 255, + 255 * 3, + }, +}; + +static struct rfc1738_test * dup_test(struct rfc1738_test *src) +{ + struct rfc1738_test *dest = malloc(sizeof(*dest)); + char *esc = NULL, *unesc = NULL; + if (dest == NULL) { + return NULL; + } + *dest = *src; + if (src->esc_len) { + esc = malloc(src->esc_len + 1); + if (esc == NULL) { + free(dest); + return NULL; + } + memcpy(esc, src->escaped, src->esc_len + 1); + dest->escaped = esc; + } + + if (src->unesc_len) { + unesc = malloc(src->unesc_len + 1); + if (unesc == NULL) { + free(esc); + free(dest); + return NULL; + } + memcpy(unesc, src->unescaped, src->unesc_len + 1); + dest->unescaped = unesc; + } + + return dest; +} + +static void free_test(struct rfc1738_test *t) +{ + free(discard_const_p(char, t->escaped)); + free(discard_const_p(char, t->unescaped)); + free(t); +} + + +static void test_unescape(void **state) +{ + uint i; + char *s, *e; + struct rfc1738_test *test, *orig; + for (i = 0; i < ARRAY_SIZE(examples); i++) { + orig = &examples[i]; + if ((orig->flags & (UNESCAPE_ERR | + UNESCAPE_EQ | + UNESCAPE_NE)) == 0) { + continue; + } + test = dup_test(&examples[i]); + s = discard_const_p(char, test->escaped); + e = rfc1738_unescape(s); + if (test->flags & UNESCAPE_ERR) { + assert_null(e); + free_test(test); + continue; + } + assert_non_null(e); + assert_int_equal(e - s, test->unesc_len); + + if (test->flags & UNESCAPE_EQ) { + assert_memory_equal(s, + orig->unescaped, + orig->unesc_len); + assert_int_equal(strlen(s), + orig->unesc_strlen); + } else { + assert_memory_not_equal(s, + orig->unescaped, + orig->unesc_len); + assert_int_equal(strlen(s), + orig->unesc_strlen); + } + free_test(test); + } +} + +static void test_escape(void **state) +{ + uint i; + char *s, *e; + struct rfc1738_test *test, *orig; + for (i = 0; i < ARRAY_SIZE(examples); i++) { + orig = &examples[i]; + if ((orig->flags & (ESCAPE_EQ | + ESCAPE_EQ_CASECMP | + ESCAPE_NE)) == 0) { + continue; + } + test = dup_test(&examples[i]); + s = discard_const_p(char, test->unescaped); + e = rfc1738_escape_part(NULL, s); + if (test->flags & ESCAPE_EQ) { + assert_memory_equal(e, test->escaped, + test->esc_len + 1); + } else if (test->flags & ESCAPE_EQ_CASECMP) { + int cmp = strcasecmp(e, test->escaped); + assert_int_equal(cmp, 0); + assert_string_not_equal(e, test->escaped); + } else { + assert_string_not_equal(e, test->escaped); + } + free_test(test); + } +} + + +static void gen_spectrum(void) +{ + int i, j = 0; + const char *lut = "0123456789ABCDEF"; + for (i = 1; i < 256; i++) { + spectrum[i - 1] = i; + if (isalnum(i) || + i == '-' || + i == '.' || + i == '_' || + i == '-' || + i == '~') { + spectrum_escaped[j] = i; + j++; + } else { + spectrum_escaped[j] = '%'; + spectrum_escaped[j + 1] = lut[i >> 4]; + spectrum_escaped[j + 2] = lut[i & 15]; + j += 3; + } + } + spectrum[i - 1] = '\0'; + spectrum_escaped[j] = '\0'; +} + +int main(int argc, const char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_escape), + cmocka_unit_test(test_unescape), + }; + + gen_spectrum(); + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/lib/util/tests/str.c b/lib/util/tests/str.c new file mode 100644 index 0000000..41a2836 --- /dev/null +++ b/lib/util/tests/str.c @@ -0,0 +1,180 @@ +/* + Unix SMB/CIFS implementation. + + util_str testing + + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool test_string_sub_simple(struct torture_context *tctx) +{ + char tmp[100]; + strlcpy(tmp, "foobar", sizeof(tmp)); + string_sub(tmp, "foo", "bar", sizeof(tmp)); + torture_assert_str_equal(tctx, tmp, "barbar", "invalid sub"); + return true; +} + +static bool test_string_sub_multiple(struct torture_context *tctx) +{ + char tmp[100]; + strlcpy(tmp, "fooblafoo", sizeof(tmp)); + string_sub(tmp, "foo", "bar", sizeof(tmp)); + torture_assert_str_equal(tctx, tmp, "barblabar", "invalid sub"); + return true; +} + +static bool test_string_sub_longer(struct torture_context *tctx) +{ + char tmp[100]; + strlcpy(tmp, "foobla", sizeof(tmp)); + string_sub(tmp, "foo", "blie", sizeof(tmp)); + torture_assert_str_equal(tctx, tmp, "bliebla", "invalid sub"); + return true; +} + +static bool test_string_sub_shorter(struct torture_context *tctx) +{ + char tmp[100]; + strlcpy(tmp, "foobla", sizeof(tmp)); + string_sub(tmp, "foo", "bl", sizeof(tmp)); + torture_assert_str_equal(tctx, tmp, "blbla", "invalid sub"); + return true; +} + +static bool test_string_sub_special_char(struct torture_context *tctx) +{ + char tmp[100]; + strlcpy(tmp, "foobla", sizeof(tmp)); + string_sub(tmp, "foo", "%b;l", sizeof(tmp)); + torture_assert_str_equal(tctx, tmp, "_b_lbla", "invalid sub"); + return true; +} + +static bool test_talloc_string_sub_simple(struct torture_context *tctx) +{ + char *t; + + t = talloc_string_sub(tctx, "foobla", "foo", "bl"); + + torture_assert_str_equal(tctx, t, "blbla", "invalid sub"); + + return true; +} + +static bool test_talloc_string_sub_multiple(struct torture_context *tctx) +{ + char *t; + + t = talloc_string_sub(tctx, "fooblafoo", "foo", "aapnootmies"); + + torture_assert_str_equal(tctx, t, "aapnootmiesblaaapnootmies", + "invalid sub"); + + return true; +} + +/* + * with these next three tests, the failure is that the pattern looks like + * "+++" because the \x.. bytes encode a zero byte in UTF-8. If we are not + * careful with these strings we will see crashes instead of failures. + */ + +static bool test_talloc_string_sub_tricky_utf8_4(struct torture_context *tctx) +{ + const char string[] = "++++--\xD8\xBB"; + const char pattern[] = "+++\xF0\x80\x80\x80++"; + const char replace[] = "..."; + + char *t = talloc_string_sub(tctx, string, pattern, replace); + torture_assert_str_equal(tctx, t, string, + "should reject 4 byte NUL char"); + talloc_free(t); + return true; +} + +static bool test_talloc_string_sub_tricky_utf8_3(struct torture_context *tctx) +{ + const char string[] = "++++--\xD8\xBB"; + const char pattern[] = "+++\xE0\x80\x80++"; + const char replace[] = "..."; + + char *t = talloc_string_sub(tctx, string, pattern, replace); + torture_assert_str_equal(tctx, t, string, + "should reject 3 byte NUL char"); + talloc_free(t); + return true; +} + +static bool test_talloc_string_sub_tricky_utf8_2(struct torture_context *tctx) +{ + const char string[] = "++++--\xD8\xBB"; + const char pattern[] = "+++\xC0\x80++"; + const char replace[] = "..."; + + char *t = talloc_string_sub(tctx, string, pattern, replace); + torture_assert_str_equal(tctx, t, string, + "should reject 2 byte NUL char"); + talloc_free(t); + return true; +} + + + + +struct torture_suite *torture_local_util_str(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "str"); + + torture_suite_add_simple_test(suite, "string_sub_simple", + test_string_sub_simple); + + torture_suite_add_simple_test(suite, "string_sub_multiple", + test_string_sub_multiple); + + torture_suite_add_simple_test(suite, "string_sub_shorter", + test_string_sub_shorter); + + torture_suite_add_simple_test(suite, "string_sub_longer", + test_string_sub_longer); + + torture_suite_add_simple_test(suite, "string_sub_special_chars", + test_string_sub_special_char); + + torture_suite_add_simple_test(suite, "talloc_string_sub_simple", + test_talloc_string_sub_simple); + + torture_suite_add_simple_test(suite, "string_sub_talloc_multiple", + test_talloc_string_sub_multiple); + + torture_suite_add_simple_test(suite, + "test_talloc_string_sub_tricky_utf8_4", + test_talloc_string_sub_tricky_utf8_4); + + torture_suite_add_simple_test(suite, + "test_talloc_string_sub_tricky_utf8_3", + test_talloc_string_sub_tricky_utf8_3); + + torture_suite_add_simple_test(suite, + "test_talloc_string_sub_tricky_utf8_2", + test_talloc_string_sub_tricky_utf8_2); + + return suite; +} diff --git a/lib/util/tests/strlist.c b/lib/util/tests/strlist.c new file mode 100644 index 0000000..a9306b8 --- /dev/null +++ b/lib/util/tests/strlist.c @@ -0,0 +1,558 @@ +/* + Unix SMB/CIFS implementation. + + util_strlist testing + + Copyright (C) Jelmer Vernooij 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "param/param.h" + +struct test_list_element { + const char *list_as_string; + const char *separators; + const char *list[5]; +}; + +const struct test_list_element test_lists_strings[] = { + { + .list_as_string = "", + .list = { NULL } + }, + { + .list_as_string = "foo", + .list = { "foo", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo", "bar", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo bar", NULL }, + .separators = ";" + }, + { + .list_as_string = "\"foo bar\"", + .list = { "\"foo", "bar\"", NULL } + }, + { + .list_as_string = "\"foo bar\",comma\ttab", + .list = { "\"foo", "bar\"", "comma", "tab", NULL } + }, + { + .list_as_string = "\"foo bar\",comma;semicolon", + .list = { "\"foo bar\",comma", "semicolon", NULL }, + .separators = ";" + } +}; + +const struct test_list_element test_lists_shell_strings[] = { + { + .list_as_string = "", + .list = { NULL } + }, + { + .list_as_string = "foo", + .list = { "foo", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo", "bar", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo bar", NULL }, + .separators = ";" + }, + { + .list_as_string = "\"foo bar\"", + .list = { "foo bar", NULL } + }, + { + .list_as_string = "foo bar \"bla \"", + .list = { "foo", "bar", "bla ", NULL } + }, + { + .list_as_string = "foo \"\" bla", + .list = { "foo", "", "bla", NULL }, + }, + { + .list_as_string = "bla \"\"\"\" blie", + .list = { "bla", "", "", "blie", NULL }, + } +}; + +static bool test_lists_shell(struct torture_context *tctx, const void *data) +{ + const struct test_list_element *element = data; + + char **ret1, **ret2, *tmp; + bool match = true; + TALLOC_CTX *mem_ctx = tctx; + + ret1 = str_list_make_shell(mem_ctx, element->list_as_string, element->separators); + + torture_assert(tctx, ret1, "str_list_make_shell() must not return NULL"); + tmp = str_list_join_shell(mem_ctx, discard_const_p(const char *, ret1), + element->separators ? *element->separators : ' '); + ret2 = str_list_make_shell(mem_ctx, tmp, element->separators); + + if ((ret1 == NULL || ret2 == NULL) && ret2 != ret1) { + match = false; + } else { + int j; + for (j = 0; ret1[j] && ret2[j]; j++) { + if (strcmp(ret1[j], ret2[j]) != 0) { + match = false; + break; + } + } + + if (ret1[j] || ret2[j]) + match = false; + } + + torture_assert(tctx, match, talloc_asprintf(tctx, + "str_list_{make,join}_shell: Error double parsing, first run:\n%s\nSecond run: \n%s", element->list_as_string, tmp)); + torture_assert(tctx, str_list_equal((const char * const *) ret1, + element->list), + talloc_asprintf(tctx, + "str_list_make_shell(%s) failed to create correct list", + element->list_as_string)); + + return true; +} + +static bool test_list_make(struct torture_context *tctx, const void *data) +{ + const struct test_list_element *element = data; + + char **result; + result = str_list_make(tctx, element->list_as_string, element->separators); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + torture_assert(tctx, str_list_equal((const char * const *) result, + element->list), + talloc_asprintf(tctx, + "str_list_make(%s) failed to create correct list", + element->list_as_string)); + return true; +} + +static bool test_list_copy(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { "foo", "bar", NULL }; + const char *empty_list[] = { NULL }; + const char **null_list = NULL; + char **l; + + l = str_list_copy(tctx, list); + result = discard_const_p(const char *, l); + torture_assert_int_equal(tctx, str_list_length(result), 2, "list length"); + torture_assert_str_equal(tctx, result[0], "foo", "element 0"); + torture_assert_str_equal(tctx, result[1], "bar", "element 1"); + torture_assert_str_equal(tctx, result[2], NULL, "element 2"); + + l = str_list_copy(tctx, empty_list); + result = discard_const_p(const char *, l); + torture_assert_int_equal(tctx, str_list_length(result), 0, "list length"); + torture_assert_str_equal(tctx, result[0], NULL, "element 0"); + + l = str_list_copy(tctx, null_list); + result = discard_const_p(const char *, l); + torture_assert(tctx, result == NULL, "result NULL"); + + return true; +} + +static bool test_list_make_empty(struct torture_context *tctx) +{ + char **result; + + result = str_list_make_empty(tctx); + torture_assert(tctx, result, "str_list_make_empty() must not return NULL"); + torture_assert(tctx, result[0] == NULL, "first element in str_list_make_empty() result must be NULL"); + + result = str_list_make(tctx, NULL, NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + torture_assert(tctx, result[0] == NULL, "first element in str_list_make(ctx, NULL, NULL) result must be NULL"); + + result = str_list_make(tctx, "", NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + torture_assert(tctx, result[0] == NULL, "first element in str_list_make(ctx, "", NULL) result must be NULL"); + + return true; +} + +static bool test_list_make_single(struct torture_context *tctx) +{ + char **result; + + result = str_list_make_single(tctx, "foo"); + + torture_assert(tctx, result, "str_list_make_single() must not return NULL"); + torture_assert_str_equal(tctx, result[0], "foo", "element 0"); + torture_assert(tctx, result[1] == NULL, "second element in result must be NULL"); + + return true; +} + +static bool test_list_copy_const(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + result = str_list_copy_const(tctx, list); + torture_assert(tctx, result, "str_list_copy() must not return NULL"); + torture_assert(tctx, str_list_equal(result, list), + "str_list_copy() failed"); + + return true; +} + +static bool test_list_length(struct torture_context *tctx) +{ + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + const char *list2[] = { + NULL + }; + torture_assert_int_equal(tctx, str_list_length(list), 4, + "str_list_length() failed"); + + torture_assert_int_equal(tctx, str_list_length(list2), 0, + "str_list_length() failed"); + + torture_assert_int_equal(tctx, str_list_length(NULL), 0, + "str_list_length() failed"); + + return true; +} + +static bool test_list_add(struct torture_context *tctx) +{ + const char **result, **result2; + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + char **l; + + l = str_list_make(tctx, "element_0, element_1, element_2", NULL); + result = discard_const_p(const char *, l); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + result2 = str_list_add(result, "element_3"); + torture_assert(tctx, result2, "str_list_add() must not return NULL"); + torture_assert(tctx, str_list_equal(result2, list), + "str_list_add() failed"); + + return true; +} + +static bool test_list_add_const(struct torture_context *tctx) +{ + const char **result, **result2; + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + char **l; + + l = str_list_make(tctx, "element_0, element_1, element_2", NULL); + result = discard_const_p(const char *, l); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + result2 = str_list_add_const(result, "element_3"); + torture_assert(tctx, result2, "str_list_add_const() must not return NULL"); + torture_assert(tctx, str_list_equal(result2, list), + "str_list_add() failed"); + + return true; +} + +static bool test_list_remove(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { + "element_0", + "element_1", + "element_3", + NULL + }; + char **l; + + l = str_list_make(tctx, "element_0, element_1, element_2, element_3", NULL); + result = discard_const_p(const char *, l); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + str_list_remove(result, "element_2"); + torture_assert(tctx, str_list_equal(result, list), + "str_list_remove() failed"); + + return true; +} + +static bool test_list_check(struct torture_context *tctx) +{ + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + torture_assert(tctx, str_list_check(list, "element_1"), + "str_list_check() failed"); + + return true; +} + +static bool test_list_check_ci(struct torture_context *tctx) +{ + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + torture_assert(tctx, str_list_check_ci(list, "ELEMENT_1"), + "str_list_check_ci() failed"); + + return true; +} + +static bool test_list_unique(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + const char *list_dup[] = { + "element_0", + "element_1", + "element_2", + "element_0", + "element_2", + "element_1", + "element_1", + "element_2", + NULL + }; + char **l; + + l = str_list_copy(tctx, list_dup); + result = discard_const_p(const char *, l); + /* We must copy the list, as str_list_unique does a talloc_realloc() on it's parameter */ + result = str_list_unique(result); + torture_assert(tctx, result, "str_list_unique() must not return NULL"); + + torture_assert(tctx, str_list_equal(list, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_unique_2(struct torture_context *tctx) +{ + int i; + int count, num_dups; + const char **result; + char **l1 = str_list_make_empty(tctx); + char **l2 = str_list_make_empty(tctx); + const char **list = discard_const_p(const char *, l1); + const char **list_dup = discard_const_p(const char *, l2); + char **l; + + count = lpcfg_parm_int(tctx->lp_ctx, NULL, "list_unique", "count", 9); + num_dups = lpcfg_parm_int(tctx->lp_ctx, NULL, "list_unique", "dups", 7); + torture_comment(tctx, "test_list_unique_2() with %d elements and %d dups\n", count, num_dups); + + for (i = 0; i < count; i++) { + list = str_list_add_const(list, (const char *)talloc_asprintf(tctx, "element_%03d", i)); + } + + for (i = 0; i < num_dups; i++) { + list_dup = str_list_append(list_dup, list); + } + + l = str_list_copy(tctx, list_dup); + result = discard_const_p(const char *, l); + /* We must copy the list, as str_list_unique does a talloc_realloc() on it's parameter */ + result = str_list_unique(result); + torture_assert(tctx, result, "str_list_unique() must not return NULL"); + + torture_assert(tctx, str_list_equal(list, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_append(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + const char *list2[] = { + "element_3", + "element_4", + "element_5", + NULL + }; + const char *list_combined[] = { + "element_0", + "element_1", + "element_2", + "element_3", + "element_4", + "element_5", + NULL + }; + char **l; + l = str_list_copy(tctx, list); + result = discard_const_p(const char *, l); + torture_assert(tctx, result, "str_list_copy() must not return NULL"); + result = str_list_append(result, list2); + torture_assert(tctx, result, "str_list_append() must not return NULL"); + torture_assert(tctx, str_list_equal(list_combined, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_append_const(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + const char *list2[] = { + "element_3", + "element_4", + "element_5", + NULL + }; + const char *list_combined[] = { + "element_0", + "element_1", + "element_2", + "element_3", + "element_4", + "element_5", + NULL + }; + char **l; + l = str_list_copy(tctx, list); + result = discard_const_p(const char *, l); + torture_assert(tctx, result, "str_list_copy() must not return NULL"); + result = str_list_append_const(result, list2); + torture_assert(tctx, result, "str_list_append_const() must not return NULL"); + torture_assert(tctx, str_list_equal(list_combined, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_add_printf_NULL(struct torture_context *tctx) +{ + char **list = NULL; + str_list_add_printf(&list, "x=%d", 1); + torture_assert(tctx, list==NULL, "str_list_add_printf must keep NULL"); + return true; +} + +static bool test_list_add_printf(struct torture_context *tctx) +{ + const char *list2[] = { "foo", "bar=baz", NULL }; + char **list = str_list_make_empty(tctx); + str_list_add_printf(&list, "foo"); + str_list_add_printf(&list, "bar=%s", "baz"); + torture_assert( + tctx, + str_list_equal((const char * const *)list, list2), + "str_list_add_printf failed"); + TALLOC_FREE(list); + return true; +} + +struct torture_suite *torture_local_util_strlist(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "strlist"); + size_t i; + + for (i = 0; i < ARRAY_SIZE(test_lists_shell_strings); i++) { + char *name; + name = talloc_asprintf(suite, "lists_shell(%s)", + test_lists_shell_strings[i].list_as_string); + torture_suite_add_simple_tcase_const(suite, name, + test_lists_shell, &test_lists_shell_strings[i]); + } + + for (i = 0; i < ARRAY_SIZE(test_lists_strings); i++) { + char *name; + name = talloc_asprintf(suite, "list_make(%s)", + test_lists_strings[i].list_as_string); + torture_suite_add_simple_tcase_const(suite, name, + test_list_make, &test_lists_strings[i]); + } + + torture_suite_add_simple_test(suite, "list_copy", test_list_copy); + torture_suite_add_simple_test(suite, "make_empty", test_list_make_empty); + torture_suite_add_simple_test(suite, "make_single", test_list_make_single); + torture_suite_add_simple_test(suite, "list_copy_const", test_list_copy_const); + torture_suite_add_simple_test(suite, "list_length", test_list_length); + torture_suite_add_simple_test(suite, "list_add", test_list_add); + torture_suite_add_simple_test(suite, "list_add_const", test_list_add_const); + torture_suite_add_simple_test(suite, "list_remove", test_list_remove); + torture_suite_add_simple_test(suite, "list_check", test_list_check); + torture_suite_add_simple_test(suite, "list_check_ci", test_list_check_ci); + torture_suite_add_simple_test(suite, "list_unique", test_list_unique); + torture_suite_add_simple_test(suite, "list_unique_2", test_list_unique_2); + torture_suite_add_simple_test(suite, "list_append", test_list_append); + torture_suite_add_simple_test(suite, "list_append_const", test_list_append_const); + torture_suite_add_simple_test( + suite, "list_add_printf_NULL", test_list_add_printf_NULL); + torture_suite_add_simple_test( + suite, "list_add_printf", test_list_add_printf); + return suite; +} diff --git a/lib/util/tests/strv.c b/lib/util/tests/strv.c new file mode 100644 index 0000000..c79ed5c --- /dev/null +++ b/lib/util/tests/strv.c @@ -0,0 +1,201 @@ +/* + * Tests for strv + * + * Copyright Martin Schwenke <martin@meltin.net> 2016 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <talloc.h> + +#include "replace.h" + +#include "libcli/util/ntstatus.h" +#include "torture/torture.h" +#include "lib/util/data_blob.h" +#include "torture/local/proto.h" + +#include "lib/util/strv.h" + +static bool test_strv_empty(struct torture_context *tctx) +{ + /* NULL strv contains 0 entries */ + torture_assert_int_equal(tctx, + strv_count(NULL), + 0, + "strv_count() on NULL failed"); + + /* NULL strv has no next entry */ + torture_assert(tctx, + strv_next(NULL, NULL) == NULL, + "strv_next() on NULL failed"); + + return true; +} + +static bool test_strv_single(struct torture_context *tctx) +{ + const char *data = "foo"; + char *strv = NULL; + char *t; + int ret; + + /* Add an item */ + ret = strv_add(tctx, &strv, data); + torture_assert(tctx, ret == 0, "strv_add() failed"); + + /* Is there 1 item? */ + torture_assert_int_equal(tctx, + strv_count(strv), 1, + "strv_count() failed"); + + /* Is the expected item the first one? */ + t = strv_next(strv, NULL); + torture_assert(tctx, + strcmp(t, data) == 0, + "strv_next() failed"); + + /* Can the expected item be found? */ + t = strv_find(strv, data); + torture_assert(tctx, + strcmp(t, data) == 0, + "strv_next() failed"); + + /* Delete it */ + strv_delete(&strv, t); + + /* Should have no items */ + torture_assert_int_equal(tctx, + strv_count(strv), 0, + "strv_count() failed"); + return true; +} + +static bool test_strv_multi(struct torture_context *tctx) +{ + const char *data[] = { "foo", "bar", "", "samba", "x"}; + char *strv = NULL; + char *t; + int i, ret; + const int num = ARRAY_SIZE(data); + + /* Add items */ + for (i = 0; i < num; i++) { + ret = strv_add(tctx, &strv, data[i]); + torture_assert(tctx, ret == 0, "strv_add() failed"); + } + + torture_assert_int_equal(tctx, + strv_count(strv), num, + "strv_count() failed"); + + /* Check that strv_next() finds the expected values */ + t = NULL; + for (i = 0; i < num; i++) { + t = strv_next(strv, t); + torture_assert(tctx, + strcmp(t, data[i]) == 0, + "strv_next() failed"); + } + + + /* Check that strv_next() finds the expected values */ + t = NULL; + for (i = 0; i < num; i++) { + t = strv_next(strv, t); + torture_assert(tctx, + strcmp(t, data[i]) == 0, + "strv_next() failed"); + } + + /* Find each item, delete it, check count */ + for (i = 0; i < num; i++) { + t = strv_find(strv, data[i]); + torture_assert(tctx, + strcmp(t, data[i]) == 0, + "strv_next() failed"); + strv_delete(&strv, t); + torture_assert_int_equal(tctx, + strv_count(strv), num - i - 1, + "strv_delete() failed"); + } + + /* Add items */ + for (i = 0; i < num; i++) { + ret = strv_add(tctx, &strv, data[i]); + torture_assert(tctx, ret == 0, "strv_add() failed"); + } + + torture_assert_int_equal(tctx, + strv_count(strv), num, + "strv_count() failed"); + + /* Find items in reverse, delete, check count */ + for (i = num - 1; i >= 0; i--) { + t = strv_find(strv, data[i]); + torture_assert(tctx, + strcmp(t, data[i]) == 0, + "strv_next() failed"); + strv_delete(&strv, t); + torture_assert_int_equal(tctx, + strv_count(strv), i, + "strv_delete() failed"); + } + + return true; +} + +/* Similar to above but only add/check first 2 chars of each string */ +static bool test_strv_addn(struct torture_context *tctx) +{ + const char *data[] = { "foo", "bar", "samba" }; + char *strv = NULL; + char *t; + int i, ret; + const int num = ARRAY_SIZE(data); + + /* Add first 2 chars of each item */ + for (i = 0; i < num; i++) { + ret = strv_addn(tctx, &strv, data[i], 2); + torture_assert(tctx, ret == 0, "strv_add() failed"); + } + + torture_assert_int_equal(tctx, + strv_count(strv), num, + "strv_count() failed"); + + /* Check that strv_next() finds the expected values */ + t = NULL; + for (i = 0; i < num; i++) { + t = strv_next(strv, t); + torture_assert(tctx, + strncmp(t, data[i], 2) == 0, + "strv_next() failed"); + } + + return true; +} + +struct torture_suite *torture_local_util_strv(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "strv"); + + torture_suite_add_simple_test(suite, "strv_empty", test_strv_empty); + torture_suite_add_simple_test(suite, "strv_single", test_strv_single); + torture_suite_add_simple_test(suite, "strv_multi", test_strv_multi); + torture_suite_add_simple_test(suite, "strv_addn", test_strv_addn); + + return suite; +} diff --git a/lib/util/tests/strv_util.c b/lib/util/tests/strv_util.c new file mode 100644 index 0000000..b1496c7 --- /dev/null +++ b/lib/util/tests/strv_util.c @@ -0,0 +1,150 @@ +/* + * Tests for strv_util + * + * Copyright Martin Schwenke <martin@meltin.net> 2016 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <talloc.h> + +#include "replace.h" + +#include "libcli/util/ntstatus.h" +#include "torture/torture.h" +#include "lib/util/data_blob.h" +#include "torture/local/proto.h" + +#include "lib/util/strv.h" +#include "lib/util/strv_util.h" + +static bool test_strv_split_none(struct torture_context *tctx) +{ + char *strv = NULL; + int ret; + + /* NULL has 0 entries */ + ret = strv_split(tctx, &strv, NULL, " "); + torture_assert(tctx, ret == 0, "strv_split() on NULL failed"); + torture_assert_int_equal(tctx, + strv_count(strv), + 0, + "strv_split() on NULL failed"); + TALLOC_FREE(strv); + + /* Empty string has 0 entries */ + ret = strv_split(tctx, &strv, "", " "); + torture_assert(tctx, ret == 0, "strv_split() on NULL failed"); + torture_assert_int_equal(tctx, + strv_count(strv), + 0, + "strv_split() on \"\" failed"); + TALLOC_FREE(strv); + + /* String containing only separators has 0 entries */ + ret = strv_split(tctx, &strv, "abcabcabc", "cba "); + torture_assert(tctx, ret == 0, "strv_split() on NULL failed"); + torture_assert_int_equal(tctx, + strv_count(strv), + 0, + "strv_split() on seps-only failed"); + TALLOC_FREE(strv); + + return true; +} + +struct test_str_split_data { + const char *in; + const char *sep; + const char *out[10]; /* Hardcoded maximum! */ +}; + +static bool test_strv_split_some(struct torture_context *tctx) +{ + const struct test_str_split_data data[] = { + { + /* Single string */ + .in = "foo", + .sep = " \t", + .out = { "foo" } + }, + { + /* Single string, single leading separator */ + .in = " foo", + .sep = " \t", + .out = { "foo" } + }, + { + /* Single string, single trailing separator */ + .in = " foo", + .sep = " \t", + .out = { "foo" } + }, + { + /* Single string, lots of separators */ + .in = " \t foo\t ", + .sep = " \t", + .out = { "foo" } + }, + { + /* Multiple strings, many separators */ + .in = " \t foo bar\t\tx\t samba\t ", + .sep = " \t", + .out = { "foo", "bar", "x", "samba" } + }, + }; + const char *t; + char *strv = NULL; + size_t j; + + for (j = 0; j < ARRAY_SIZE(data); j++) { + size_t i, num; + int ret; + const struct test_str_split_data *d = &data[j]; + + num = 0; + while (num < ARRAY_SIZE(d->out) && d->out[num] != NULL) { + num++; + } + ret = strv_split(tctx, &strv, d->in, d->sep); + torture_assert(tctx, ret == 0, "strv_split() on NULL failed"); + torture_assert_int_equal(tctx, + strv_count(strv), + num, + "strv_split() failed"); + t = NULL; + for (i = 0; i < num; i++) { + t = strv_next(strv, t); + torture_assert(tctx, + strcmp(t, d->out[i]) == 0, + "strv_split() failed"); + } + TALLOC_FREE(strv); + } + return true; +} + +struct torture_suite *torture_local_util_strv_util(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = + torture_suite_create(mem_ctx, "strv_util"); + + torture_suite_add_simple_test(suite, + "strv_split_none", + test_strv_split_none); + torture_suite_add_simple_test(suite, + "strv_split_some", + test_strv_split_some); + return suite; +} diff --git a/lib/util/tests/test_bytearray.c b/lib/util/tests/test_bytearray.c new file mode 100644 index 0000000..fcf63d8 --- /dev/null +++ b/lib/util/tests/test_bytearray.c @@ -0,0 +1,435 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include "lib/util/bytearray.h" + +static void torture_pull_le_u8(void **state) +{ + uint8_t data[2] = {0}; + uint8_t result; + + (void)state; + + result = PULL_LE_U8(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + result = PULL_LE_U8(data, 0); + assert_int_equal(result, 42); + + + data[0] = 0xf; + result = PULL_LE_U8(data, 0); + assert_int_equal(result, 0xf); + + data[0] = 0xff; + result = PULL_LE_U8(data, 0); + assert_int_equal(result, 0xff); + + data[1] = 0x2a; + result = PULL_LE_U8(data, 1); + assert_int_equal(result, 42); +} + +static void torture_pull_le_u16(void **state) +{ + uint8_t data[2] = {0, 0}; + uint16_t result; + + (void)state; + + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + data[1] = 0x00; + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 42); + + data[0] = 0xff; + data[1] = 0x00; + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0x00; + data[1] = 0xff; + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0xff; + data[1] = 0xff; + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0xffff); +} + +static void torture_pull_le_u32(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint32_t result; + + (void)state; + + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 42); + + data[0] = 0xff; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0x00; + data[1] = 0xff; + data[2] = 0x00; + data[3] = 0x00; + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0xff; + data[3] = 0x00; + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xff0000); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0xff; + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xff000000); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xffffffff); +} + +static void torture_push_le_u8(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {42, 42, 42, 42}; + + (void)state; + + PUSH_LE_U8(data, 0, 42); + PUSH_LE_U8(data, 1, 42); + PUSH_LE_U8(data, 2, 42); + PUSH_LE_U8(data, 3, 42); + assert_memory_equal(data, data2, sizeof(data)); +} + +static void torture_push_le_u16(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {0xa6, 0x7f, 0x2a, 0x00}; + uint16_t result; + + (void)state; + + PUSH_LE_U16(data, 0, 32678); + PUSH_LE_U16(data, 2, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = PULL_LE_U16(data, 2); + assert_int_equal(result, 42); + + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 32678); +} + +static void torture_push_le_u32(void **state) +{ + uint8_t data[8] = {0}; + uint8_t data2[8] = {0xa6, 0x7f, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00}; + uint32_t result; + + (void)state; + + PUSH_LE_U32(data, 0, 32678); + PUSH_LE_U32(data, 4, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = PULL_LE_U32(data, 4); + assert_int_equal(result, 42); + + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 32678); + + PUSH_LE_U32(data, 0, 0xfffefffe); + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xfffefffe); +} + +static void torture_push_le_u64(void **state) +{ + uint8_t data[16] = {0}; + uint64_t result; + + (void)state; + + PUSH_LE_U64(data, 0, 32678); + + result = PULL_LE_U64(data, 0); + assert_int_equal(result, 32678); + + PUSH_LE_U64(data, 0, 0xfffefffefffefffeUL); + + result = PULL_LE_U64(data, 0); + assert_int_equal(result, 0xfffefffefffefffeUL); +} + +/****************** BIG ENDIAN ********************/ + +static void torture_pull_be_u8(void **state) +{ + uint8_t data[2] = {0}; + uint8_t result; + + (void)state; + + result = PULL_BE_U8(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + result = PULL_BE_U8(data, 0); + assert_int_equal(result, 42); + + + data[0] = 0xf; + result = PULL_BE_U8(data, 0); + assert_int_equal(result, 0xf); + + data[0] = 0xff; + result = PULL_BE_U8(data, 0); + assert_int_equal(result, 0xff); + + data[1] = 0x2a; + result = PULL_BE_U8(data, 1); + assert_int_equal(result, 42); +} + +static void torture_pull_be_u16(void **state) +{ + uint8_t data[2] = {0, 0}; + uint16_t result; + + (void)state; + + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x00; + data[1] = 0x2a; + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 42); + + data[0] = 0x00; + data[1] = 0xff; + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0xff; + data[1] = 0x00; + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0xff; + data[1] = 0xff; + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 0xffff); +} + +static void torture_pull_be_u32(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint32_t result; + + (void)state; + + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x2a; + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 42); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0xff; + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0xff; + data[3] = 0x00; + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0x00; + data[1] = 0xff; + data[2] = 0x00; + data[3] = 0x00; + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xff0000); + + data[0] = 0xff; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xff000000); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xffffffff); +} + +static void torture_push_be_u8(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {42, 42, 42, 42}; + + (void)state; + + PUSH_BE_U8(data, 0, 42); + PUSH_BE_U8(data, 1, 42); + PUSH_BE_U8(data, 2, 42); + PUSH_BE_U8(data, 3, 42); + assert_memory_equal(data, data2, sizeof(data)); +} + +static void torture_push_be_u16(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {0x7f, 0xa6, 0x00, 0x2a}; + uint16_t result; + + (void)state; + + PUSH_BE_U16(data, 0, 32678); + PUSH_BE_U16(data, 2, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = PULL_BE_U16(data, 2); + assert_int_equal(result, 42); + + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 32678); +} + +static void torture_push_be_u32(void **state) +{ + uint8_t data[8] = {0}; + uint8_t data2[8] = {0x00, 0x00, 0x7f, 0xa6, 0x00, 0x00, 0x00, 0x2a}; + uint32_t result; + + (void)state; + + PUSH_BE_U32(data, 0, 32678); + PUSH_BE_U32(data, 4, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = PULL_BE_U32(data, 4); + assert_int_equal(result, 42); + + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 32678); + + PUSH_BE_U32(data, 0, 0xfffefffe); + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xfffefffe); +} + +static void torture_push_be_u64(void **state) +{ + uint8_t data[16] = {0}; + uint64_t result; + + (void)state; + + PUSH_BE_U64(data, 0, 32678); + + result = PULL_BE_U64(data, 0); + assert_int_equal(result, 32678); + + PUSH_LE_U64(data, 8, 0xfffefffe); + + result = PULL_LE_U64(data, 8); + assert_int_equal(result, 0xfffefffe); +} + +int main(int argc, char *argv[]) +{ + int rc; + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_pull_le_u8), + cmocka_unit_test(torture_pull_le_u16), + cmocka_unit_test(torture_pull_le_u32), + + cmocka_unit_test(torture_push_le_u8), + cmocka_unit_test(torture_push_le_u16), + cmocka_unit_test(torture_push_le_u32), + cmocka_unit_test(torture_push_le_u64), + + /* BIG ENDIAN */ + cmocka_unit_test(torture_pull_be_u8), + cmocka_unit_test(torture_pull_be_u16), + cmocka_unit_test(torture_pull_be_u32), + + cmocka_unit_test(torture_push_be_u8), + cmocka_unit_test(torture_push_be_u16), + cmocka_unit_test(torture_push_be_u32), + cmocka_unit_test(torture_push_be_u64), + }; + + if (argc == 2) { + cmocka_set_test_filter(argv[1]); + } + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/lib/util/tests/test_byteorder.c b/lib/util/tests/test_byteorder.c new file mode 100644 index 0000000..9faa038 --- /dev/null +++ b/lib/util/tests/test_byteorder.c @@ -0,0 +1,435 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include "lib/util/byteorder.h" + +static void torture_pull_le_u8(void **state) +{ + uint8_t data[2] = {0}; + uint8_t result; + + (void)state; + + result = CVAL(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + result = CVAL(data, 0); + assert_int_equal(result, 42); + + + data[0] = 0xf; + result = CVAL(data, 0); + assert_int_equal(result, 0xf); + + data[0] = 0xff; + result = CVAL(data, 0); + assert_int_equal(result, 0xff); + + data[1] = 0x2a; + result = CVAL(data, 1); + assert_int_equal(result, 42); +} + +static void torture_pull_le_u16(void **state) +{ + uint8_t data[2] = {0, 0}; + uint16_t result; + + (void)state; + + result = SVAL(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + data[1] = 0x00; + result = SVAL(data, 0); + assert_int_equal(result, 42); + + data[0] = 0xff; + data[1] = 0x00; + result = SVAL(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0x00; + data[1] = 0xff; + result = SVAL(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0xff; + data[1] = 0xff; + result = SVAL(data, 0); + assert_int_equal(result, 0xffff); +} + +static void torture_pull_le_u32(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint32_t result; + + (void)state; + + result = IVAL(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + result = IVAL(data, 0); + assert_int_equal(result, 42); + + data[0] = 0xff; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + result = IVAL(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0x00; + data[1] = 0xff; + data[2] = 0x00; + data[3] = 0x00; + result = IVAL(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0xff; + data[3] = 0x00; + result = IVAL(data, 0); + assert_int_equal(result, 0xff0000); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0xff; + result = IVAL(data, 0); + assert_int_equal(result, 0xff000000); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + result = IVAL(data, 0); + assert_int_equal(result, 0xffffffff); +} + +static void torture_push_le_u8(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {42, 42, 42, 42}; + + (void)state; + + SCVAL(data, 0, 42); + SCVAL(data, 1, 42); + SCVAL(data, 2, 42); + SCVAL(data, 3, 42); + assert_memory_equal(data, data2, sizeof(data)); +} + +static void torture_push_le_u16(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {0xa6, 0x7f, 0x2a, 0x00}; + uint16_t result; + + (void)state; + + SSVALX(data, 0, 32678); + SSVALX(data, 2, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = SVAL(data, 2); + assert_int_equal(result, 42); + + result = SVAL(data, 0); + assert_int_equal(result, 32678); +} + +static void torture_push_le_u32(void **state) +{ + uint8_t data[8] = {0}; + uint8_t data2[8] = {0xa6, 0x7f, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00}; + uint32_t result; + + (void)state; + + SIVALX(data, 0, 32678); + SIVALX(data, 4, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = IVAL(data, 4); + assert_int_equal(result, 42); + + result = IVAL(data, 0); + assert_int_equal(result, 32678); + + SIVALX(data, 0, 0xfffefffe); + result = IVAL(data, 0); + assert_int_equal(result, 0xfffefffe); +} + +static void torture_push_le_u64(void **state) +{ + uint8_t data[16] = {0}; + uint64_t result; + + (void)state; + + SBVAL(data, 0, 32678); + + result = BVAL(data, 0); + assert_int_equal(result, 32678); + + SBVAL(data, 0, 0xfffefffefffefffeUL); + + result = BVAL(data, 0); + assert_int_equal(result, 0xfffefffefffefffeUL); +} + +/****************** BIG ENDIAN ********************/ + +static void torture_pull_be_u8(void **state) +{ + uint8_t data[2] = {0}; + uint8_t result; + + (void)state; + + result = CVAL(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x2a; + result = CVAL(data, 0); + assert_int_equal(result, 42); + + + data[0] = 0xf; + result = CVAL(data, 0); + assert_int_equal(result, 0xf); + + data[0] = 0xff; + result = CVAL(data, 0); + assert_int_equal(result, 0xff); + + data[1] = 0x2a; + result = CVAL(data, 1); + assert_int_equal(result, 42); +} + +static void torture_pull_be_u16(void **state) +{ + uint8_t data[2] = {0, 0}; + uint16_t result; + + (void)state; + + result = RSVAL(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x00; + data[1] = 0x2a; + result = RSVAL(data, 0); + assert_int_equal(result, 42); + + data[0] = 0x00; + data[1] = 0xff; + result = RSVAL(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0xff; + data[1] = 0x00; + result = RSVAL(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0xff; + data[1] = 0xff; + result = RSVAL(data, 0); + assert_int_equal(result, 0xffff); +} + +static void torture_pull_be_u32(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint32_t result; + + (void)state; + + result = RIVAL(data, 0); + assert_int_equal(result, 0); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x2a; + result = RIVAL(data, 0); + assert_int_equal(result, 42); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0xff; + result = RIVAL(data, 0); + assert_int_equal(result, 0x00ff); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0xff; + data[3] = 0x00; + result = RIVAL(data, 0); + assert_int_equal(result, 0xff00); + + data[0] = 0x00; + data[1] = 0xff; + data[2] = 0x00; + data[3] = 0x00; + result = RIVAL(data, 0); + assert_int_equal(result, 0xff0000); + + data[0] = 0xff; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + result = RIVAL(data, 0); + assert_int_equal(result, 0xff000000); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + result = RIVAL(data, 0); + assert_int_equal(result, 0xffffffff); +} + +static void torture_push_be_u8(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {42, 42, 42, 42}; + + (void)state; + + SCVAL(data, 0, 42); + SCVAL(data, 1, 42); + SCVAL(data, 2, 42); + SCVAL(data, 3, 42); + assert_memory_equal(data, data2, sizeof(data)); +} + +static void torture_push_be_u16(void **state) +{ + uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data2[4] = {0x7f, 0xa6, 0x00, 0x2a}; + uint16_t result; + + (void)state; + + RSSVALS(data, 0, 32678); + RSSVALS(data, 2, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = RSVAL(data, 2); + assert_int_equal(result, 42); + + result = RSVAL(data, 0); + assert_int_equal(result, 32678); +} + +static void torture_push_be_u32(void **state) +{ + uint8_t data[8] = {0}; + uint8_t data2[8] = {0x00, 0x00, 0x7f, 0xa6, 0x00, 0x00, 0x00, 0x2a}; + uint32_t result; + + (void)state; + + RSIVALS(data, 0, 32678); + RSIVALS(data, 4, 42); + assert_memory_equal(data, data2, sizeof(data)); + + result = RIVAL(data, 4); + assert_int_equal(result, 42); + + result = RIVAL(data, 0); + assert_int_equal(result, 32678); + + RSIVALS(data, 0, 0xfffefffe); + result = RIVAL(data, 0); + assert_int_equal(result, 0xfffefffe); +} + +static void torture_push_be_u64(void **state) +{ + uint8_t data[16] = {0}; + uint64_t result; + + (void)state; + + RSBVALS(data, 0, 32678); + + result = RBVAL(data, 0); + assert_int_equal(result, 32678); + + SBVAL(data, 8, 0xfffefffe); + + result = BVAL(data, 8); + assert_int_equal(result, 0xfffefffe); +} + +int main(int argc, char *argv[]) +{ + int rc; + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_pull_le_u8), + cmocka_unit_test(torture_pull_le_u16), + cmocka_unit_test(torture_pull_le_u32), + + cmocka_unit_test(torture_push_le_u8), + cmocka_unit_test(torture_push_le_u16), + cmocka_unit_test(torture_push_le_u32), + cmocka_unit_test(torture_push_le_u64), + + /* BIG ENDIAN */ + cmocka_unit_test(torture_pull_be_u8), + cmocka_unit_test(torture_pull_be_u16), + cmocka_unit_test(torture_pull_be_u32), + + cmocka_unit_test(torture_push_be_u8), + cmocka_unit_test(torture_push_be_u16), + cmocka_unit_test(torture_push_be_u32), + cmocka_unit_test(torture_push_be_u64), + }; + + if (argc == 2) { + cmocka_set_test_filter(argv[1]); + } + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/lib/util/tests/test_byteorder_verify.c b/lib/util/tests/test_byteorder_verify.c new file mode 100644 index 0000000..a18ddad --- /dev/null +++ b/lib/util/tests/test_byteorder_verify.c @@ -0,0 +1,273 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include "lib/util/bytearray.h" +#include "lib/util/byteorder.h" + +static void torture_le_u8(void **state) +{ + uint8_t data[2] = {0}; + uint8_t result; + + (void)state; + + /* Test CVAL and SCVAL */ + PUSH_LE_U8(data, 0, 23); + PUSH_LE_U8(data, 1, 42); + + result = CVAL(data, 0); + assert_int_equal(result, 23); + + result = CVAL(data, 1); + assert_int_equal(result, 42); + + /* Test CVAL_NC and PVAL */ + PUSH_LE_U8(data, 0, 23); + PUSH_LE_U8(data, 1, 42); + + result = CVAL_NC(data, 0); + assert_int_equal(result, 23); + + result = PVAL(data, 1); + assert_int_equal(result, 42); + + /* Test SCVAL */ + SCVAL(data, 0, 42); + SCVAL(data, 1, 23); + + result = PULL_LE_U8(data, 0); + assert_int_equal(result, 42); + + result = PULL_LE_U8(data, 1); + assert_int_equal(result, 23); +} + +static void torture_le_u16(void **state) +{ + uint8_t data[2] = {0}; + uint16_t result; + + (void)state; + + /* Test SVAL */ + PUSH_LE_U16(data, 0, 0xff00); + result = SVAL(data, 0); + assert_int_equal(result, 0xff00); + + /* Test SSVAL */ + SSVAL(data, 0, 0x00ff); + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0x00ff); + + /* Test SSVALX */ + SSVALX(data, 0, 0x00fa); + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0x00fa); + + /* Test SSVALS */ + SSVALS(data, 0, 0x00fb); + result = PULL_LE_U16(data, 0); + assert_int_equal(result, 0x00fb); +} + +static void torture_le_u32(void **state) +{ + uint8_t data[4] = {0}; + uint32_t result; + + (void)state; + + /* Test IVAL */ + PUSH_LE_U32(data, 0, 0xff000000); + result = IVAL(data, 0); + assert_int_equal(result, 0xff000000); + + /* Test SIVAL */ + SIVAL(data, 0, 0xffaabbcc); + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xffaabbcc); + + /* Test SIVALX */ + SIVALX(data, 0, 0xffbbccdd); + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xffbbccdd); + + /* Test SIVALS */ + SIVALS(data, 0, 0xffccddee); + result = PULL_LE_U32(data, 0); + assert_int_equal(result, 0xffccddee); +} + +static void torture_le_u64(void **state) +{ + uint8_t data[8] = {0}; + uint64_t result; + + (void)state; + + PUSH_LE_U64(data, 0, 0xfffefffefffefffeUL); + result = BVAL(data, 0); + assert_int_equal(result, 0xfffefffefffefffeUL); + + SBVAL(data, 0, 0xfffafffafffafffaUL); + result = PULL_LE_U64(data, 0); + assert_int_equal(result, 0xfffafffafffafffaUL); +} + +static void torture_be_u8(void **state) +{ + uint8_t data[2] = {0}; + uint8_t result; + + (void)state; + + PUSH_BE_U8(data, 0, 23); + PUSH_BE_U8(data, 1, 42); + + result = CVAL(data, 0); + assert_int_equal(result, 23); + + result = CVAL(data, 1); + assert_int_equal(result, 42); + + SCVAL(data, 0, 42); + SCVAL(data, 1, 23); + + result = PULL_BE_U8(data, 0); + assert_int_equal(result, 42); + + result = PULL_BE_U8(data, 1); + assert_int_equal(result, 23); +} + +static void torture_be_u16(void **state) +{ + uint8_t data[2] = {0}; + uint16_t result; + + (void)state; + + /* Test RSVAL */ + PUSH_BE_U16(data, 0, 0xff00); + result = RSVAL(data, 0); + assert_int_equal(result, 0xff00); + + /* Test RSVALS */ + PUSH_BE_U16(data, 0, 0xffaa); + result = RSVALS(data, 0); + assert_int_equal(result, 0xffaa); + + /* Test RSSVAL */ + RSSVAL(data, 0, 0x00ff); + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 0x00ff); + + /* Test RSSVALS */ + RSSVALS(data, 0, 0x00fa); + result = PULL_BE_U16(data, 0); + assert_int_equal(result, 0x00fa); +} + +static void torture_be_u32(void **state) +{ + uint8_t data[4] = {0}; + uint32_t result; + + (void)state; + + /* Test RIVAL */ + PUSH_BE_U32(data, 0, 0xff000000); + result = RIVAL(data, 0); + assert_int_equal(result, 0xff000000); + + /* Test RIVALS */ + PUSH_BE_U32(data, 0, 0xff0000aa); + result = RIVALS(data, 0); + assert_int_equal(result, 0xff0000aa); + + /* Test RSIVAL */ + RSIVAL(data, 0, 0xffeeddcc); + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xffeeddcc); + + /* Test RSIVALS */ + RSIVALS(data, 0, 0xffaaddcc); + result = PULL_BE_U32(data, 0); + assert_int_equal(result, 0xffaaddcc); +} + +static void torture_be_u64(void **state) +{ + uint8_t data[8] = {0}; + uint64_t result; + + (void)state; + + /* Test RBVAL */ + PUSH_BE_U64(data, 0, 0xfffefffefffefffeUL); + result = RBVAL(data, 0); + assert_int_equal(result, 0xfffefffefffefffeUL); + + /* Test RBVALS */ + PUSH_BE_U64(data, 0, 0xfffafffafffafffaUL); + result = RBVALS(data, 0); + assert_int_equal(result, 0xfffafffafffafffaUL); + + /* Test RSBVAL */ + RSBVAL(data, 0, 0xfffbfffbfffbfffbUL); + result = PULL_BE_U64(data, 0); + assert_int_equal(result, 0xfffbfffbfffbfffbUL); + + /* Test RSBVALS */ + RSBVALS(data, 0, 0xfffcfffcfffcfffcUL); + result = PULL_BE_U64(data, 0); + assert_int_equal(result, 0xfffcfffcfffcfffcUL); +} + +int main(int argc, char *argv[]) +{ + int rc; + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_le_u8), + cmocka_unit_test(torture_le_u16), + cmocka_unit_test(torture_le_u32), + cmocka_unit_test(torture_le_u64), + + cmocka_unit_test(torture_be_u8), + cmocka_unit_test(torture_be_u16), + cmocka_unit_test(torture_be_u32), + cmocka_unit_test(torture_be_u64), + }; + + if (argc == 2) { + cmocka_set_test_filter(argv[1]); + } + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/lib/util/tests/test_logging.c b/lib/util/tests/test_logging.c new file mode 100644 index 0000000..9b13b05 --- /dev/null +++ b/lib/util/tests/test_logging.c @@ -0,0 +1,146 @@ +/* + Unix SMB/CIFS implementation. + + A test server that only does logging. + + Copyright (C) Andrew Tridgell 1992-2005 + Copyright (C) Martin Pool 2002 + Copyright (C) Jelmer Vernooij 2002 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + Copyright (C) Douglas Bagnall 2022 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" + +#ifdef USING_CMDLINE_S3 +#include "lib/util/debug_s3.h" +#endif + +#define BINARY_NAME "test_s4_logging" + +static int log_level = 1; + + +#include "lib/util/debug-classes/debug-classname-table.c" + +static int log_all_classes(int level) +{ + size_t i; + const char *name = NULL; + for (i = 0; i < ARRAY_SIZE(default_classname_table); i++) { + name = default_classname_table[i]; + DEBUGC(i, level, + ("logging for '%s' [%zu], at level %d\n", + name, i, level)); + + /* + * That's it for the tests *here*. The invoker of this + * process will have set up an smb.conf that directs the + * output in particular ways, and will be looking to see that + * happens correctly. + */ + } + return 0; +} + + +static int init_daemon(TALLOC_CTX *mem_ctx, + int argc, + const char *argv[], + const char **error) +{ + poptContext pc; + int opt; + bool ok; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "level", + .shortName = 'L', + .argInfo = POPT_ARG_INT, + .arg = &log_level, + .descrip = "log at this level", + .argDescrip = "LEVEL", + }, + POPT_COMMON_SAMBA + POPT_COMMON_DAEMON + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + setproctitle(BINARY_NAME); + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_SERVER, + true /* require_smbconf */); + if (!ok) { + *error = "Failed to init cmdline parser!\n"; + return EINVAL; + } + + pc = samba_popt_get_context(BINARY_NAME, + argc, + argv, + long_options, + 0); + if (pc == NULL) { + *error = "Failed to setup popt context!\n"; + return ENOTRECOVERABLE; + } + + while((opt = poptGetNextOpt(pc)) != -1) { + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + + poptFreeContext(pc); + +#ifdef USING_CMDLINE_S3 + reopen_logs(); +#endif + return 0; +} + + +int main(int argc, const char *argv[]) +{ + int rc; + const char *error = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + if (mem_ctx == NULL) { + exit(ENOMEM); + } + + setproctitle_init(argc, discard_const(argv), environ); + + rc = init_daemon(mem_ctx, argc, argv, &error); + if (rc != 0) { + fprintf(stderr, "error [%d]: %s\n", rc, error); + exit_daemon(error, rc); + } + + rc = log_all_classes(log_level); + if (rc != 0) { + fprintf(stderr, "error in log_all_classes [%d]\n", rc); + exit_daemon("logging error", rc); + } + + TALLOC_FREE(mem_ctx); + return rc; +} diff --git a/lib/util/tests/test_memcache.c b/lib/util/tests/test_memcache.c new file mode 100644 index 0000000..8a39978 --- /dev/null +++ b/lib/util/tests/test_memcache.c @@ -0,0 +1,161 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2021 Andreas Schneider <asn@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/memcache.h" + +static int setup_talloc_context(void **state) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + *state = frame; + return 0; +} + +static int teardown_talloc_context(void **state) +{ + TALLOC_CTX *frame = *state; + TALLOC_FREE(frame); + return 0; +} + +static void torture_memcache_init(void **state) +{ + TALLOC_CTX *mem_ctx = *state; + struct memcache *cache = NULL; + + cache = memcache_init(mem_ctx, 0); + assert_non_null(cache); + + TALLOC_FREE(cache); + + cache = memcache_init(mem_ctx, 10); + assert_non_null(cache); + + TALLOC_FREE(cache); +} + +static void torture_memcache_add_lookup_delete(void **state) +{ + TALLOC_CTX *mem_ctx = *state; + struct memcache *cache = NULL; + DATA_BLOB key1, key2; + char *path1 = NULL, *path2 = NULL; + + cache = memcache_init(mem_ctx, 0); + assert_non_null(cache); + + key1 = data_blob_const("key1", 4); + path1 = talloc_strdup(mem_ctx, "/tmp/one"); + assert_non_null(path1); + + key2 = data_blob_const("key2", 4); + path2 = talloc_strdup(mem_ctx, "/tmp/two"); + assert_non_null(path1); + + memcache_add_talloc(cache, GETWD_CACHE, key1, &path1); + assert_null(path1); + + memcache_add_talloc(cache, GETWD_CACHE, key2, &path2); + assert_null(path2); + + path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1); + assert_non_null(path1); + assert_string_equal(path1, "/tmp/one"); + + path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2); + assert_non_null(path2); + assert_string_equal(path2, "/tmp/two"); + + memcache_delete(cache, GETWD_CACHE, key1); + path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1); + assert_null(path1); + + memcache_flush(cache, GETWD_CACHE); + path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2); + assert_null(path2); + + TALLOC_FREE(path1); + TALLOC_FREE(path2); + TALLOC_FREE(cache); +} + +static void torture_memcache_add_oversize(void **state) +{ + TALLOC_CTX *mem_ctx = *state; + struct memcache *cache = NULL; + DATA_BLOB key1, key2; + char *path1 = NULL, *path2 = NULL; + + cache = memcache_init(mem_ctx, 10); + assert_non_null(cache); + + key1 = data_blob_const("key1", 4); + path1 = talloc_strdup(mem_ctx, "/tmp/one"); + assert_non_null(path1); + + key2 = data_blob_const("key2", 4); + path2 = talloc_strdup(mem_ctx, "/tmp/two"); + assert_non_null(path1); + + memcache_add_talloc(cache, GETWD_CACHE, key1, &path1); + assert_null(path1); + + memcache_add_talloc(cache, GETWD_CACHE, key2, &path2); + assert_null(path2); + + path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1); + assert_null(path1); + + path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2); + assert_non_null(path2); + assert_string_equal(path2, "/tmp/two"); + + TALLOC_FREE(path1); + TALLOC_FREE(path2); + TALLOC_FREE(cache); +} + +int main(int argc, char *argv[]) +{ + int rc; + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_memcache_init), + cmocka_unit_test(torture_memcache_add_lookup_delete), + cmocka_unit_test(torture_memcache_add_oversize), + }; + + if (argc == 2) { + cmocka_set_test_filter(argv[1]); + } + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + rc = cmocka_run_group_tests(tests, + setup_talloc_context, + teardown_talloc_context); + + return rc; +} diff --git a/lib/util/tests/test_ms_fnmatch.c b/lib/util/tests/test_ms_fnmatch.c new file mode 100644 index 0000000..d11c7be --- /dev/null +++ b/lib/util/tests/test_ms_fnmatch.c @@ -0,0 +1,114 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2018 David Disseldorp <ddiss@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include <errno.h> + +#include "lib/replace/replace.h" +#include "lib/util/samba_util.h" +#include "libcli/smb/smb_constants.h" + +static void test_ms_fn_match_protocol_no_wildcard(void **state) +{ + int cmp; + + /* no wildcards in pattern, a simple strcasecmp_m */ + cmp = ms_fnmatch_protocol("pattern", "string", PROTOCOL_COREPLUS, + true); /* case sensitive */ + assert_int_equal(cmp, -3); +} + +static void test_ms_fn_match_protocol_pattern_upgraded(void **state) +{ + int cmp; + + /* protocol < PROTOCOL_NT1 pattern is "upgraded" */ + cmp = ms_fnmatch_protocol("??????", "string", PROTOCOL_COREPLUS, + false); + assert_int_equal(cmp, 0); +} + +static void test_ms_fn_match_protocol_match_zero_or_more(void **state) +{ + int cmp; + + /* '*' matches zero or more characters. handled via recursive calls */ + cmp = ms_fnmatch_protocol("********", "string", PROTOCOL_COREPLUS, + true); + assert_int_equal(cmp, 0); +} + +static void test_ms_fn_match_protocol_mapped_char(void **state) +{ + int cmp; + + /* '?' is mapped to '>', which matches any char or a '\0' */ + cmp = ms_fnmatch_protocol("???????", "string", PROTOCOL_COREPLUS, + false); + assert_int_equal(cmp, 0); +} + +static void test_ms_fn_match_protocol_nt1_any_char(void **state) +{ + int cmp; + + /* PROTOCOL_NT1 '?' matches any char, '\0' is not included */ + cmp = ms_fnmatch_protocol("???????", "string", PROTOCOL_NT1, + false); + assert_int_equal(cmp, -1); +} + +static void test_ms_fn_match_protocol_nt1_case_sensitive(void **state) +{ + int cmp; + + cmp = ms_fnmatch_protocol("StRinG", "string", PROTOCOL_NT1, + true); /* case sensitive */ + assert_int_equal(cmp, 0); + + cmp = ms_fnmatch_protocol("StRin?", "string", PROTOCOL_NT1, + true); /* case sensitive */ + assert_int_equal(cmp, -1); + + cmp = ms_fnmatch_protocol("StRin?", "string", PROTOCOL_NT1, + false); + assert_int_equal(cmp, 0); + cmp = ms_fnmatch_protocol("strin?", "string", PROTOCOL_NT1, + true); /* case sensitive */ + assert_int_equal(cmp, 0); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_ms_fn_match_protocol_no_wildcard), + cmocka_unit_test(test_ms_fn_match_protocol_pattern_upgraded), + cmocka_unit_test(test_ms_fn_match_protocol_match_zero_or_more), + cmocka_unit_test(test_ms_fn_match_protocol_mapped_char), + cmocka_unit_test(test_ms_fn_match_protocol_nt1_any_char), + cmocka_unit_test(test_ms_fn_match_protocol_nt1_case_sensitive), + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/lib/util/tests/test_sys_rw.c b/lib/util/tests/test_sys_rw.c new file mode 100644 index 0000000..22688fb --- /dev/null +++ b/lib/util/tests/test_sys_rw.c @@ -0,0 +1,178 @@ +/* + * Unix SMB/CIFS implementation. + * + * Unit test for sys_rw.c + * + * Copyright (C) Ralph Böhme 2021 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include "system/dir.h" + +#include "lib/util/sys_rw.c" + +static void test_sys_io_ranges_overlap(void **state) +{ + bool overlap; + + /* + * sys_io_ranges_overlap() args are: + * + * src size, src offset, dst size, dst offset + */ + + /* src and dst size 0 => no overlap */ + overlap = sys_io_ranges_overlap(0, 0, 0, 0); + assert_false(overlap); + + /* dst size 0 => no overlap */ + overlap = sys_io_ranges_overlap(1, 0, 0, 0); + assert_false(overlap); + + /* src size 0 => no overlap */ + overlap = sys_io_ranges_overlap(0, 0, 1, 0); + assert_false(overlap); + + /* same range => overlap */ + overlap = sys_io_ranges_overlap(1, 0, 1, 0); + assert_true(overlap); + + /* + * |.| + * |.| + * src before dst => no overlap + */ + overlap = sys_io_ranges_overlap(1, 0, 1, 1); + assert_false(overlap); + + /* + * |..| + * |..| + * src into dst => overlap + */ + overlap = sys_io_ranges_overlap(2, 0, 2, 1); + assert_true(overlap); + + /* + * |....| + * |..| + * src encompasses dst => overlap + */ + overlap = sys_io_ranges_overlap(4, 0, 1, 2); + assert_true(overlap); + + + /* + * |..| + * |..| + * dst into src => overlap + */ + overlap = sys_io_ranges_overlap(2, 1, 2, 0); + assert_true(overlap); + + /* + * |..| + * |....| + * dst encompasses src => overlap + */ + overlap = sys_io_ranges_overlap(2, 1, 4, 0); + assert_true(overlap); +} + +static void test_sys_block_align(void **state) +{ + int asize; + + asize = sys_block_align(0, 2); + assert_int_equal(asize, 0); + asize = sys_block_align(1, 2); + assert_int_equal(asize, 2); + asize = sys_block_align(2, 2); + assert_int_equal(asize, 2); + asize = sys_block_align(3, 2); + assert_int_equal(asize, 4); + + asize = sys_block_align(0, 4); + assert_int_equal(asize, 0); + asize = sys_block_align(1, 4); + assert_int_equal(asize, 4); + asize = sys_block_align(3, 4); + assert_int_equal(asize, 4); + asize = sys_block_align(4, 4); + assert_int_equal(asize, 4); + asize = sys_block_align(5, 4); + assert_int_equal(asize, 8); + asize = sys_block_align(7, 4); + assert_int_equal(asize, 8); + asize = sys_block_align(8, 4); + assert_int_equal(asize, 8); + asize = sys_block_align(9, 4); + assert_int_equal(asize, 12); +} + +static void test_sys_block_align_truncate(void **state) +{ + int asize; + + asize = sys_block_align_truncate(0, 2); + assert_int_equal(asize, 0); + asize = sys_block_align_truncate(1, 2); + assert_int_equal(asize, 0); + asize = sys_block_align_truncate(2, 2); + assert_int_equal(asize, 2); + asize = sys_block_align_truncate(3, 2); + assert_int_equal(asize, 2); + asize = sys_block_align_truncate(4, 2); + assert_int_equal(asize, 4); + asize = sys_block_align_truncate(5, 2); + assert_int_equal(asize, 4); + + asize = sys_block_align_truncate(0, 4); + assert_int_equal(asize, 0); + asize = sys_block_align_truncate(1, 4); + assert_int_equal(asize, 0); + asize = sys_block_align_truncate(3, 4); + assert_int_equal(asize, 0); + asize = sys_block_align_truncate(4, 4); + assert_int_equal(asize, 4); + asize = sys_block_align_truncate(5, 4); + assert_int_equal(asize, 4); + asize = sys_block_align_truncate(7, 4); + assert_int_equal(asize, 4); + asize = sys_block_align_truncate(8, 4); + assert_int_equal(asize, 8); + asize = sys_block_align_truncate(9, 4); + assert_int_equal(asize, 8); +} + +int main(int argc, char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sys_io_ranges_overlap), + cmocka_unit_test(test_sys_block_align), + cmocka_unit_test(test_sys_block_align_truncate), + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/lib/util/tests/test_talloc_keep_secret.c b/lib/util/tests/test_talloc_keep_secret.c new file mode 100644 index 0000000..1462dab --- /dev/null +++ b/lib/util/tests/test_talloc_keep_secret.c @@ -0,0 +1,94 @@ +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include <string.h> +#include <talloc.h> +#include "lib/util/talloc_keep_secret.h" + +int rep_memset_s(void *dest, size_t destsz, int ch, size_t count); + +int rep_memset_s(void *dest, size_t destsz, int ch, size_t count) +{ + check_expected_ptr(dest); + check_expected(destsz); + check_expected(ch); + check_expected(count); + + return 0; +} + +static void test_talloc_keep_secret(void ** state) +{ + TALLOC_CTX *pool = NULL; + char *ptr1 = NULL; + char *ptr2 = NULL; + const char *ptr1_talloc_name = NULL; + size_t ptr1_size; + size_t i; + + pool = talloc_pool(NULL, 256); + assert_non_null(pool); + + ptr1 = talloc_strdup(pool, "secret"); + assert_non_null(ptr1); + assert_string_equal(ptr1, "secret"); + + talloc_keep_secret(ptr1); + + ptr1_talloc_name = talloc_get_name(ptr1); + assert_string_equal(ptr1_talloc_name, "ptr1"); + + ptr1_size = talloc_get_size(ptr1); + assert_int_equal(ptr1_size, strlen(ptr1) + 1); + + expect_string(rep_memset_s, dest, "secret"); + expect_value(rep_memset_s, destsz, strlen(ptr1) + 1); + expect_value(rep_memset_s, ch, (int)'\0'); + expect_value(rep_memset_s, count, strlen(ptr1) + 1); + + talloc_free(ptr1); + + ptr2 = talloc_size(pool, ptr1_size); + assert_ptr_equal(ptr1, ptr2); + + for (i = 1; i < ptr1_size; i++) { + assert_int_not_equal(ptr2[0], ptr2[i]); + } + + talloc_free(pool); +} + +static void test_talloc_keep_secret_validate_memset(void **state) +{ + TALLOC_CTX *mem_ctx = NULL; + char *password = NULL; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + + password = talloc_strdup(mem_ctx, "secret"); + assert_non_null(password); + talloc_keep_secret(password); + + expect_string(rep_memset_s, dest, "secret"); + expect_value(rep_memset_s, destsz, strlen(password) + 1); + expect_value(rep_memset_s, ch, (int)'\0'); + expect_value(rep_memset_s, count, strlen(password) + 1); + + talloc_free(mem_ctx); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_talloc_keep_secret), + cmocka_unit_test(test_talloc_keep_secret_validate_memset), + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/lib/util/tests/test_util.c b/lib/util/tests/test_util.c new file mode 100644 index 0000000..62f10ed --- /dev/null +++ b/lib/util/tests/test_util.c @@ -0,0 +1,344 @@ +/* + * Unix SMB/CIFS implementation. + * + * Unit test for util.c + * + * Copyright (C) Christof Schmitt 2020 + * Copyright (C) Andreas Schneider 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include "system/dir.h" + +#include "lib/util/util.c" + +struct test_paths { + char testdir[PATH_MAX]; + char none[PATH_MAX]; + char dir[PATH_MAX]; + char dir_recursive[PATH_MAX]; + mode_t dir_mode; + char file[PATH_MAX]; + mode_t file_mode; + char symlink_none[PATH_MAX]; + char symlink_dir[PATH_MAX]; + char symlink_file[PATH_MAX]; +}; + +static int group_setup(void **state) +{ + struct test_paths *paths = NULL; + char *testdir = NULL; + int ret, fd; + + umask(0); + + paths = malloc(sizeof(struct test_paths)); + assert_non_null(paths); + + strlcpy(paths->testdir, tmpdir(), sizeof(paths->testdir)); + strlcat(paths->testdir, "/test_util_XXXXXX", sizeof(paths->testdir)); + testdir = mkdtemp(paths->testdir); + assert_non_null(testdir); + + strlcpy(paths->none, testdir, sizeof(paths->none)); + strlcat(paths->none, "/none", sizeof(paths->none)); + + strlcpy(paths->dir, testdir, sizeof(paths->dir)); + strlcat(paths->dir, "/dir", sizeof(paths->dir)); + paths->dir_mode = 0750; + ret = mkdir(paths->dir, paths->dir_mode); + assert_return_code(ret, errno); + + strlcpy(paths->dir_recursive, testdir, sizeof(paths->dir)); + strlcat(paths->dir_recursive, "/dir_recursive", sizeof(paths->dir)); + paths->dir_mode = 0750; + ret = mkdir(paths->dir_recursive, paths->dir_mode); + assert_return_code(ret, errno); + + strlcpy(paths->file, testdir, sizeof(paths->file)); + strlcat(paths->file, "/file", sizeof(paths->file)); + paths->file_mode = 0640; + fd = creat(paths->file, paths->file_mode); + assert_return_code(fd, errno); + ret = close(fd); + assert_return_code(ret, errno); + + strlcpy(paths->symlink_none, testdir, sizeof(paths->symlink_none)); + strlcat(paths->symlink_none, "/symlink_none", + sizeof(paths->symlink_none)); + ret = symlink("/none", paths->symlink_none); + assert_return_code(ret, errno); + + strlcpy(paths->symlink_dir, testdir, sizeof(paths->symlink_dir)); + strlcat(paths->symlink_dir, "/symlink_dir", sizeof(paths->symlink_dir)); + ret = symlink(paths->dir, paths->symlink_dir); + assert_return_code(ret, errno); + + strlcpy(paths->symlink_file, testdir, sizeof(paths->symlink_file)); + strlcat(paths->symlink_file, "/symlink_file", + sizeof(paths->symlink_file)); + ret = symlink(paths->file, paths->symlink_file); + assert_return_code(ret, errno); + + *state = paths; + + return 0; +} + +static int torture_rmdirs(const char *path) +{ + DIR *d; + struct dirent *dp; + struct stat sb; + char *fname; + + if ((d = opendir(path)) != NULL) { + while(stat(path, &sb) == 0) { + /* if we can remove the directory we're done */ + if (rmdir(path) == 0) { + break; + } + switch (errno) { + case ENOTEMPTY: + case EEXIST: + case EBADF: + break; /* continue */ + default: + closedir(d); + return 0; + } + + while ((dp = readdir(d)) != NULL) { + size_t len; + /* skip '.' and '..' */ + if (dp->d_name[0] == '.' && + (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) { + continue; + } + + len = strlen(path) + strlen(dp->d_name) + 2; + fname = malloc(len); + if (fname == NULL) { + closedir(d); + return -1; + } + snprintf(fname, len, "%s/%s", path, dp->d_name); + + /* stat the file */ + if (lstat(fname, &sb) != -1) { + if (S_ISDIR(sb.st_mode) && !S_ISLNK(sb.st_mode)) { + if (rmdir(fname) < 0) { /* can't be deleted */ + if (errno == EACCES) { + closedir(d); + SAFE_FREE(fname); + return -1; + } + torture_rmdirs(fname); + } + } else { + unlink(fname); + } + } /* lstat */ + SAFE_FREE(fname); + } /* readdir */ + + rewinddir(d); + } + } else { + return -1; + } + + closedir(d); + return 0; +} + +static int group_teardown(void **state) +{ + struct test_paths *paths = *state; + int ret; + + ret = unlink(paths->file); + assert_return_code(ret, errno); + + ret = unlink(paths->symlink_none); + assert_return_code(ret, errno); + + ret = unlink(paths->symlink_dir); + assert_return_code(ret, errno); + + ret = unlink(paths->symlink_file); + assert_return_code(ret, errno); + + ret = torture_rmdirs(paths->testdir); + assert_return_code(ret, errno); + + free(paths); + return 0; +} + +static void test_directory_create_or_exists_none(void **state) +{ + struct test_paths *paths = *state; + bool b; + struct stat sbuf; + int ret; + + b = directory_create_or_exist(paths->none, 0775); + assert_true(b); + + ret = lstat(paths->none, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, 0775); + assert_true(S_ISDIR(sbuf.st_mode)); +} + +static int teardown_none_directory(void **state) +{ + struct test_paths *paths = *state; + + rmdir(paths->none); + return 0; +} + +static void test_directory_create_or_exists_dir(void **state) +{ + struct test_paths *paths = *state; + bool b; + struct stat sbuf; + int ret; + + b = directory_create_or_exist(paths->dir, 770); + assert_true(b); + + ret = lstat(paths->dir, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, paths->dir_mode); + assert_true(S_ISDIR(sbuf.st_mode)); +} + +static void test_directory_create_or_exists_file(void **state) +{ + struct test_paths *paths = *state; + bool b; + struct stat sbuf; + int ret; + + b = directory_create_or_exist(paths->file, 770); + assert_false(b); + + ret = lstat(paths->file, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, paths->file_mode); + assert_true(S_ISREG(sbuf.st_mode)); +} + +static void test_directory_create_or_exists_symlink_none(void **state) +{ + struct test_paths *paths = *state; + bool b; + struct stat sbuf; + int ret; + + b = directory_create_or_exist(paths->symlink_none, 770); + assert_false(b); + + ret = lstat(paths->symlink_none, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, 0777); + assert_true(S_ISLNK(sbuf.st_mode)); +} + +static void test_directory_create_or_exists_symlink_dir(void **state) +{ + struct test_paths *paths = *state; + bool b; + struct stat sbuf; + int ret; + + b = directory_create_or_exist(paths->symlink_dir, 770); + assert_true(b); + + ret = lstat(paths->symlink_dir, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, 0777); + assert_true(S_ISLNK(sbuf.st_mode)); +} + +static void test_directory_create_or_exists_symlink_file(void **state) +{ + struct test_paths *paths = *state; + bool b; + struct stat sbuf; + int ret; + + b = directory_create_or_exist(paths->symlink_file, 770); + assert_false(b); + + ret = lstat(paths->symlink_file, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, 0777); + assert_true(S_ISLNK(sbuf.st_mode)); +} + +static void test_directory_create_or_exists_recursive(void **state) +{ + struct test_paths *paths = *state; + char recursive_testdir[PATH_MAX] = {0}; + struct stat sbuf = {0}; + bool ok; + int ret; + + ret = snprintf(recursive_testdir, + sizeof(recursive_testdir), + "%s/wurst/brot", + paths->dir_recursive); + assert_int_not_equal(ret, -1); + + ok = directory_create_or_exists_recursive(recursive_testdir, + 0700); + assert_true(ok); + + ret = lstat(recursive_testdir, &sbuf); + assert_return_code(ret, errno); + assert_int_equal(sbuf.st_mode & 0777, 0700); + assert_true(S_ISDIR(sbuf.st_mode)); +} + +int main(int argc, char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_teardown(test_directory_create_or_exists_none, + teardown_none_directory), + cmocka_unit_test(test_directory_create_or_exists_dir), + cmocka_unit_test(test_directory_create_or_exists_file), + cmocka_unit_test(test_directory_create_or_exists_symlink_none), + cmocka_unit_test(test_directory_create_or_exists_symlink_dir), + cmocka_unit_test(test_directory_create_or_exists_symlink_file), + cmocka_unit_test(test_directory_create_or_exists_recursive), + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + return cmocka_run_group_tests(tests, group_setup, group_teardown); +} diff --git a/lib/util/tests/test_util_paths.c b/lib/util/tests/test_util_paths.c new file mode 100644 index 0000000..4dfe11c --- /dev/null +++ b/lib/util/tests/test_util_paths.c @@ -0,0 +1,127 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2020 Andreas Schneider <asn@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "lib/replace/replace.h" +#include <talloc.h> + +#include "lib/util/util_paths.c" + +static int setup(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + assert_non_null(mem_ctx); + *state = mem_ctx; + + return 0; +} + +static int teardown(void **state) +{ + TALLOC_CTX *mem_ctx = *state; + TALLOC_FREE(mem_ctx); + + return 0; +} + +static void test_get_user_home_dir(void **state) +{ + TALLOC_CTX *mem_ctx = *state; + struct passwd *pwd = getpwuid(getuid()); + char *user; + + user = get_user_home_dir(mem_ctx); + assert_non_null(user); + assert_string_equal(user, pwd->pw_dir); + + TALLOC_FREE(user); +} + +static void test_path_expand_tilde(void **state) +{ + TALLOC_CTX *mem_ctx = *state; + char h[256] = {0}; + char *d = NULL; + const char *user = NULL; + char *home = NULL; + + user = getenv("USER"); + if (user == NULL){ + user = getenv("LOGNAME"); + } + + /* In certain CIs there no such variables */ + if (user == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw){ + user = pw->pw_name; + } + } + + home = getenv("HOME"); + assert_non_null(home); + snprintf(h, sizeof(h), "%s/.cache", home); + + d = path_expand_tilde(mem_ctx, "~/.cache"); + assert_non_null(d); + assert_string_equal(d, h); + TALLOC_FREE(d); + + snprintf(h, sizeof(h), "%s/.cache/X~", home); + d = path_expand_tilde(mem_ctx, "~/.cache/X~"); + assert_string_equal(d, h); + TALLOC_FREE(d); + + d = path_expand_tilde(mem_ctx, "/guru/meditation"); + assert_non_null(d); + assert_string_equal(d, "/guru/meditation"); + TALLOC_FREE(d); + + snprintf(h, sizeof(h), "~%s/.cache", user); + d = path_expand_tilde(mem_ctx, h); + assert_non_null(d); + + snprintf(h, sizeof(h), "%s/.cache", home); + assert_string_equal(d, h); + TALLOC_FREE(d); +} + +int main(int argc, char *argv[]) +{ + int rc; + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_get_user_home_dir), + cmocka_unit_test(test_path_expand_tilde), + }; + + if (argc == 2) { + cmocka_set_test_filter(argv[1]); + } + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + rc = cmocka_run_group_tests(tests, setup, teardown); + + return rc; +} diff --git a/lib/util/tests/tfork-drd.supp b/lib/util/tests/tfork-drd.supp new file mode 100644 index 0000000..7d0544b --- /dev/null +++ b/lib/util/tests/tfork-drd.supp @@ -0,0 +1,14 @@ +{ + tfork_pthread_cond_init + drd:CondErr + fun:pthread_cond_init_intercept + fun:pthread_cond_init@* + fun:tfork_atfork_child + fun:fork + fun:tfork_start_waiter_and_worker + fun:tfork_create + fun:tfork_thread + fun:vgDrd_thread_wrapper + fun:start_thread + fun:clone +} diff --git a/lib/util/tests/tfork-helgrind.supp b/lib/util/tests/tfork-helgrind.supp new file mode 100644 index 0000000..4b62b2a --- /dev/null +++ b/lib/util/tests/tfork-helgrind.supp @@ -0,0 +1,32 @@ +{ + tfork_atexit_unknown1 + Helgrind:Misc + fun:mutex_destroy_WRK + fun:pthread_mutex_destroy + obj:/usr/lib64/libp11-kit.so.0.3.0 + fun:_dl_fini + fun:__run_exit_handlers + fun:exit + fun:(below main) +} + +{ + tfork_atexit_unknown2 + Helgrind:Misc + fun:mutex_destroy_WRK + fun:pthread_mutex_destroy + fun:_dl_fini + fun:__run_exit_handlers + fun:exit + fun:(below main) +} +{ + tfork_pthread_get_specific + Helgrind:Race + fun:tfork_global_get + fun:tfork_create + fun:tfork_thread + fun:mythread_wrapper + fun:start_thread + fun:clone +} diff --git a/lib/util/tests/tfork.c b/lib/util/tests/tfork.c new file mode 100644 index 0000000..70ae975 --- /dev/null +++ b/lib/util/tests/tfork.c @@ -0,0 +1,855 @@ +/* + * Tests for tfork + * + * Copyright Ralph Boehme <slow@samba.org> 2017 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include <talloc.h> +#include <tevent.h> +#include "system/filesys.h" +#include "system/wait.h" +#include "system/select.h" +#include "libcli/util/ntstatus.h" +#include "torture/torture.h" +#include "lib/util/data_blob.h" +#include "torture/local/proto.h" +#include "lib/util/tfork.h" +#include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" +#ifdef HAVE_PTHREAD +#include <pthread.h> +#endif + +static bool test_tfork_simple(struct torture_context *tctx) +{ + pid_t parent = getpid(); + struct tfork *t = NULL; + pid_t child; + int ret; + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + torture_comment(tctx, "my parent pid is %d\n", parent); + torture_assert(tctx, getpid() != parent, "tfork failed\n"); + _exit(0); + } + + ret = tfork_destroy(&t); + torture_assert(tctx, ret == 0, "tfork_destroy failed\n"); + + return true; +} + +static bool test_tfork_status(struct torture_context *tctx) +{ + struct tfork *t = NULL; + int status; + pid_t child; + bool ok = true; + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + _exit(123); + } + + status = tfork_status(&t, true); + if (status == -1) { + torture_fail(tctx, "tfork_status failed\n"); + } + + torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done, + "tfork failed\n"); + torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done, + "tfork failed\n"); + + torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status)); + +done: + return ok; +} + +static bool test_tfork_sigign(struct torture_context *tctx) +{ + struct tfork *t = NULL; + struct sigaction act; + pid_t child; + int status; + bool ok = true; + int ret; + + act = (struct sigaction) { + .sa_flags = SA_NOCLDWAIT, + .sa_handler = SIG_IGN, + }; + + ret = sigaction(SIGCHLD, &act, NULL); + torture_assert_goto(tctx, ret == 0, ok, done, "sigaction failed\n"); + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + sleep(1); + _exit(123); + } + + child = fork(); + if (child == -1) { + torture_fail(tctx, "fork failed\n"); + return false; + } + if (child == 0) { + _exit(0); + } + + status = tfork_status(&t, true); + if (status == -1) { + torture_fail(tctx, "tfork_status failed\n"); + } + + torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done, + "tfork failed\n"); + torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done, + "tfork failed\n"); + torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status)); + +done: + return ok; +} + +static void sigchld_handler1(int signum, siginfo_t *si, void *u) +{ + pid_t pid; + int status; + + if (signum != SIGCHLD) { + abort(); + } + + pid = waitpid(si->si_pid, &status, 0); + if (pid != si->si_pid) { + abort(); + } +} + +static bool test_tfork_sighandler(struct torture_context *tctx) +{ + struct tfork *t = NULL; + struct sigaction act; + struct sigaction oldact; + pid_t child; + int status; + bool ok = true; + int ret; + + act = (struct sigaction) { + .sa_flags = SA_SIGINFO, + .sa_sigaction = sigchld_handler1, + }; + + ret = sigaction(SIGCHLD, &act, &oldact); + torture_assert_goto(tctx, ret == 0, ok, done, "sigaction failed\n"); + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + sleep(1); + _exit(123); + } + + child = fork(); + if (child == -1) { + torture_fail(tctx, "fork failed\n"); + return false; + } + if (child == 0) { + _exit(0); + } + + status = tfork_status(&t, true); + if (status == -1) { + torture_fail(tctx, "tfork_status failed\n"); + } + + torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done, + "tfork failed\n"); + torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done, + "tfork failed\n"); + torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status)); + +done: + sigaction(SIGCHLD, &oldact, NULL); + + return ok; +} + +static bool test_tfork_process_hierarchy(struct torture_context *tctx) +{ + struct tfork *t = NULL; + pid_t pid = getpid(); + pid_t child; + pid_t pgid = getpgid(0); + pid_t sid = getsid(0); + char *procpath = NULL; + int status; + struct stat st; + int ret; + bool ok = true; + + procpath = talloc_asprintf(tctx, "/proc/%d/status", getpid()); + torture_assert_not_null(tctx, procpath, "talloc_asprintf failed\n"); + + ret = stat(procpath, &st); + TALLOC_FREE(procpath); + if (ret != 0) { + if (errno == ENOENT) { + torture_skip(tctx, "/proc missing\n"); + } + torture_fail(tctx, "stat failed\n"); + } + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + char *cmd = NULL; + FILE *fp = NULL; + char line[64]; + char *p; + pid_t ppid; + + torture_assert_goto(tctx, pgid == getpgid(0), ok, child_fail, "tfork failed\n"); + torture_assert_goto(tctx, sid == getsid(0), ok, child_fail, "tfork failed\n"); + + cmd = talloc_asprintf(tctx, "cat /proc/%d/status | awk '/^PPid:/ {print $2}'", getppid()); + torture_assert_goto(tctx, cmd != NULL, ok, child_fail, "talloc_asprintf failed\n"); + + fp = popen(cmd, "r"); + torture_assert_goto(tctx, fp != NULL, ok, child_fail, "popen failed\n"); + + p = fgets(line, sizeof(line) - 1, fp); + pclose(fp); + torture_assert_goto(tctx, p != NULL, ok, child_fail, "popen failed\n"); + + ret = sscanf(line, "%d", &ppid); + torture_assert_goto(tctx, ret == 1, ok, child_fail, "sscanf failed\n"); + torture_assert_goto(tctx, ppid == pid, ok, child_fail, "process hierarchy not rooted at caller\n"); + + _exit(0); + + child_fail: + _exit(1); + } + + status = tfork_status(&t, true); + if (status == -1) { + torture_fail(tctx, "tfork_status failed\n"); + } + + torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done, + "tfork failed\n"); + torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done, + "tfork failed\n"); + torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status)); + +done: + return ok; +} + +static bool test_tfork_pipe(struct torture_context *tctx) +{ + struct tfork *t = NULL; + int status; + pid_t child; + int up[2]; + int down[2]; + char c; + int ret; + bool ok = true; + + ret = pipe(&up[0]); + torture_assert(tctx, ret == 0, "pipe failed\n"); + + ret = pipe(&down[0]); + torture_assert(tctx, ret == 0, "pipe failed\n"); + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + close(up[0]); + close(down[1]); + + ret = read(down[0], &c, 1); + torture_assert_goto(tctx, ret == 1, ok, child_fail, "read failed\n"); + torture_assert_goto(tctx, c == 1, ok, child_fail, "read failed\n"); + + ret = write(up[1], &(char){2}, 1); + torture_assert_goto(tctx, ret == 1, ok, child_fail, "write failed\n"); + + _exit(0); + + child_fail: + _exit(1); + } + + close(up[1]); + close(down[0]); + + ret = write(down[1], &(char){1}, 1); + torture_assert(tctx, ret == 1, "read failed\n"); + + ret = read(up[0], &c, 1); + torture_assert(tctx, ret == 1, "read failed\n"); + torture_assert(tctx, c == 2, "read failed\n"); + + status = tfork_status(&t, true); + if (status == -1) { + torture_fail(tctx, "tfork_status failed\n"); + } + + torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done, + "tfork failed\n"); + torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done, + "tfork failed\n"); +done: + return ok; +} + +static bool test_tfork_twice(struct torture_context *tctx) +{ + struct tfork *t = NULL; + int status; + pid_t child; + pid_t pid; + int up[2]; + int ret; + bool ok = true; + + ret = pipe(&up[0]); + torture_assert(tctx, ret == 0, "pipe failed\n"); + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + close(up[0]); + + t = tfork_create(); + if (t == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child = tfork_child_pid(t); + if (child == 0) { + sleep(1); + pid = getpid(); + ret = write(up[1], &pid, sizeof(pid_t)); + torture_assert_goto(tctx, ret == sizeof(pid_t), ok, child_fail, "write failed\n"); + + _exit(0); + + child_fail: + _exit(1); + } + + _exit(0); + } + + close(up[1]); + + ret = read(up[0], &pid, sizeof(pid_t)); + torture_assert(tctx, ret == sizeof(pid_t), "read failed\n"); + + status = tfork_status(&t, true); + torture_assert_goto(tctx, status != -1, ok, done, "tfork_status failed\n"); + + torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done, + "tfork failed\n"); + torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done, + "tfork failed\n"); +done: + return ok; +} + +static void *tfork_thread(void *p) +{ + struct tfork *t = NULL; + int status; + pid_t child; + uint64_t tid = (uint64_t)pthread_self(); + uint64_t *result = NULL; + int up[2]; + ssize_t nread; + int ret; + + ret = pipe(up); + if (ret != 0) { + pthread_exit(NULL); + } + + t = tfork_create(); + if (t == NULL) { + pthread_exit(NULL); + } + child = tfork_child_pid(t); + if (child == 0) { + ssize_t nwritten; + + close(up[0]); + tid++; + nwritten = sys_write(up[1], &tid, sizeof(uint64_t)); + if (nwritten != sizeof(uint64_t)) { + _exit(1); + } + _exit(0); + } + close(up[1]); + + result = malloc(sizeof(uint64_t)); + if (result == NULL) { + pthread_exit(NULL); + } + + nread = sys_read(up[0], result, sizeof(uint64_t)); + if (nread != sizeof(uint64_t)) { + pthread_exit(NULL); + } + + status = tfork_status(&t, true); + if (status == -1) { + pthread_exit(NULL); + } + + pthread_exit(result); +} + +static bool test_tfork_threads(struct torture_context *tctx) +{ + int ret; + bool ok = true; + const int num_threads = 64; + pthread_t threads[num_threads]; + sigset_t set; + int i; + +#ifndef HAVE_PTHREAD + torture_skip(tctx, "no pthread support\n"); +#endif + + /* + * Be nasty and taste for the worst case: ensure all threads start with + * SIGCHLD unblocked so we have the most fun with SIGCHLD being + * delivered to a random thread. :) + */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); +#ifdef HAVE_PTHREAD + ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL); +#else + ret = sigprocmask(SIG_UNBLOCK, &set, NULL); +#endif + if (ret != 0) { + return false; + } + + for (i = 0; i < num_threads; i++) { + ret = pthread_create(&threads[i], NULL, tfork_thread, NULL); + torture_assert_goto(tctx, ret == 0, ok, done, + "pthread_create failed\n"); + } + + for (i = 0; i < num_threads; i++) { + void *p; + uint64_t *result; + + ret = pthread_join(threads[i], &p); + torture_assert_goto(tctx, ret == 0, ok, done, + "pthread_join failed\n"); + result = (uint64_t *)p; + torture_assert_goto(tctx, *result == (uint64_t)threads[i] + 1, + ok, done, "thread failed\n"); + free(p); + } + +done: + return ok; +} + +static bool test_tfork_cmd_send(struct torture_context *tctx) +{ + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + const char *cmd[2] = { NULL, NULL }; + bool ok = true; + + ev = tevent_context_init(tctx); + torture_assert_goto(tctx, ev != NULL, ok, done, + "tevent_context_init failed\n"); + + cmd[0] = talloc_asprintf(tctx, "%s/testprogs/blackbox/tfork.sh", SRCDIR); + torture_assert_goto(tctx, cmd[0] != NULL, ok, done, + "talloc_asprintf failed\n"); + + req = samba_runcmd_send(tctx, ev, timeval_zero(), 0, 0, + cmd, "foo", NULL); + torture_assert_goto(tctx, req != NULL, ok, done, + "samba_runcmd_send failed\n"); + + ok = tevent_req_poll(req, ev); + torture_assert_goto(tctx, ok, ok, done, "tevent_req_poll failed\n"); + + torture_comment(tctx, "samba_runcmd_send test finished\n"); + +done: + TALLOC_FREE(ev); + + return ok; +} + +/* + * Test to ensure that the event_fd becomes readable after + * a tfork_process terminates. + */ +static bool test_tfork_event_file_handle(struct torture_context *tctx) +{ + bool ok = true; + + struct tfork *t1 = NULL; + pid_t child1; + struct pollfd poll1[] = { + { + .fd = -1, + .events = POLLIN, + }, + }; + + struct tfork *t2 = NULL; + pid_t child2; + struct pollfd poll2[] = { + { + .fd = -1, + .events = POLLIN, + }, + }; + + + t1 = tfork_create(); + if (t1 == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + + child1 = tfork_child_pid(t1); + if (child1 == 0) { + /* + * Parent process will kill this with a SIGTERM + * so 10 seconds should be plenty + */ + sleep(10); + exit(1); + } + poll1[0].fd = tfork_event_fd(t1); + + t2 = tfork_create(); + if (t2 == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child2 = tfork_child_pid(t2); + if (child2 == 0) { + /* + * Parent process will kill this with a SIGTERM + * so 10 seconds should be plenty + */ + sleep(10); + exit(2); + } + poll2[0].fd = tfork_event_fd(t2); + + /* + * Have forked two process and are in the master process + * Expect that both event_fds are unreadable + */ + poll(poll1, 1, 0); + ok = !(poll1[0].revents & POLLIN); + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd readable\n"); + poll(poll2, 1, 0); + ok = !(poll2[0].revents & POLLIN); + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd readable\n"); + + /* Kill the first child process */ + kill(child1, SIGKILL); + sleep(1); + + /* + * Have killed the first child, so expect it's event_fd to have gone + * readable. + * + */ + poll(poll1, 1, 0); + ok = (poll1[0].revents & POLLIN); + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd not readable\n"); + poll(poll2, 1, 0); + ok = !(poll2[0].revents & POLLIN); + torture_assert_goto(tctx, ok, ok, done, + "tfork process 2 event fd readable\n"); + + /* Kill the secind child process */ + kill(child2, SIGKILL); + sleep(1); + /* + * Have killed the children, so expect their event_fd's to have gone + * readable. + * + */ + poll(poll1, 1, 0); + ok = (poll1[0].revents & POLLIN); + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd not readable\n"); + poll(poll2, 1, 0); + ok = (poll2[0].revents & POLLIN); + torture_assert_goto(tctx, ok, ok, done, + "tfork process 2 event fd not readable\n"); + +done: + free(t1); + free(t2); + + return ok; +} + +/* + * Test to ensure that the status calls behave as expected after a process + * terminates. + * + * As the parent process owns the status fd's they get passed to all + * subsequent children after a tfork. So it's possible for another + * child process to hold the status pipe open. + * + * The event fd needs to be left open by tfork, as a close in the status + * code can cause issues in tevent code. + * + */ +static bool test_tfork_status_handle(struct torture_context *tctx) +{ + bool ok = true; + + struct tfork *t1 = NULL; + pid_t child1; + + struct tfork *t2 = NULL; + pid_t child2; + + int status; + int fd; + int ev1_fd; + int ev2_fd; + + + t1 = tfork_create(); + if (t1 == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + + child1 = tfork_child_pid(t1); + if (child1 == 0) { + /* + * Parent process will kill this with a SIGTERM + * so 10 seconds should be plenty + */ + sleep(10); + exit(1); + } + ev1_fd = tfork_event_fd(t1); + + t2 = tfork_create(); + if (t2 == NULL) { + torture_fail(tctx, "tfork failed\n"); + return false; + } + child2 = tfork_child_pid(t2); + if (child2 == 0) { + /* + * Parent process will kill this with a SIGTERM + * so 10 seconds should be plenty + */ + sleep(10); + exit(2); + } + ev2_fd = tfork_event_fd(t2); + + /* + * Have forked two process and are in the master process + * expect that the status call will block, and hence return -1 + * as the processes are still running + * The event fd's should be open. + */ + status = tfork_status(&t1, false); + ok = status == -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork status available for non terminated " + "process 1\n"); + /* Is the event fd open? */ + fd = dup(ev1_fd); + ok = fd != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd is not open"); + + status = tfork_status(&t2, false); + ok = status == -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork status available for non terminated " + "process 2\n"); + /* Is the event fd open? */ + fd = dup(ev2_fd); + ok = fd != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork process 2 event fd is not open"); + + /* + * Kill the first process, it's status should be readable + * and it's event_fd should be open + * The second process's status should be unreadable. + */ + kill(child1, SIGTERM); + sleep(1); + status = tfork_status(&t1, false); + ok = status != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork status for child 1 not available after " + "termination\n"); + /* Is the event fd open? */ + fd = dup(ev2_fd); + ok = fd != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd is not open"); + + status = tfork_status(&t2, false); + ok = status == -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork status available for child 2 after " + "termination of child 1\n"); + + /* + * Kill the second process, it's status should be readable + */ + kill(child2, SIGTERM); + sleep(1); + status = tfork_status(&t2, false); + ok = status != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork status for child 2 not available after " + "termination\n"); + + /* Check that the event fd's are still open */ + /* Is the event fd open? */ + fd = dup(ev1_fd); + ok = fd != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork process 1 event fd is not open"); + /* Is the event fd open? */ + fd = dup(ev2_fd); + ok = fd != -1; + torture_assert_goto(tctx, ok, ok, done, + "tfork process 2 event fd is not open"); + +done: + return ok; +} + +struct torture_suite *torture_local_tfork(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = + torture_suite_create(mem_ctx, "tfork"); + + torture_suite_add_simple_test(suite, + "tfork_simple", + test_tfork_simple); + + torture_suite_add_simple_test(suite, + "tfork_status", + test_tfork_status); + + torture_suite_add_simple_test(suite, + "tfork_sigign", + test_tfork_sigign); + + torture_suite_add_simple_test(suite, + "tfork_sighandler", + test_tfork_sighandler); + + torture_suite_add_simple_test(suite, + "tfork_process_hierarchy", + test_tfork_process_hierarchy); + + torture_suite_add_simple_test(suite, + "tfork_pipe", + test_tfork_pipe); + + torture_suite_add_simple_test(suite, + "tfork_twice", + test_tfork_twice); + + torture_suite_add_simple_test(suite, + "tfork_threads", + test_tfork_threads); + + torture_suite_add_simple_test(suite, + "tfork_cmd_send", + test_tfork_cmd_send); + + torture_suite_add_simple_test(suite, + "tfork_event_file_handle", + test_tfork_event_file_handle); + + torture_suite_add_simple_test(suite, + "tfork_status_handle", + test_tfork_status_handle); + + return suite; +} diff --git a/lib/util/tests/time.c b/lib/util/tests/time.c new file mode 100644 index 0000000..ec27f56 --- /dev/null +++ b/lib/util/tests/time.c @@ -0,0 +1,151 @@ +/* + Unix SMB/CIFS implementation. + + util time testing + + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static bool test_null_time(struct torture_context *tctx) +{ + torture_assert(tctx, null_time(0), "0"); + torture_assert(tctx, null_time(0xFFFFFFFF), "0xFFFFFFFF"); + torture_assert(tctx, null_time(-1), "-1"); + torture_assert(tctx, !null_time(42), "42"); + return true; +} + +static bool test_null_nttime(struct torture_context *tctx) +{ + torture_assert(tctx, null_nttime(0), "0"); + torture_assert(tctx, !null_nttime(NTTIME_FREEZE), "-1"); + torture_assert(tctx, !null_nttime(NTTIME_THAW), "-2"); + torture_assert(tctx, !null_nttime(42), "42"); + return true; +} + + +static bool test_http_timestring(struct torture_context *tctx) +{ + const char *start = "Thu, 01 Jan 1970"; + char *result; + /* + * Correct test for negative UTC offset. Without the correction, the + * test fails when run on hosts with negative UTC offsets, as the date + * returned is back in 1969 (pre-epoch). + */ + time_t now = time(NULL); + struct tm local = *localtime(&now); + struct tm gmt = *gmtime(&now); + time_t utc_offset = mktime(&local) - mktime(&gmt); + + result = http_timestring(tctx, 42 - (utc_offset < 0 ? utc_offset : 0)); + torture_assert(tctx, !strncmp(start, result, + strlen(start)), result); + torture_assert_str_equal(tctx, "never", + http_timestring(tctx, get_time_t_max()), "42"); + return true; +} + +static bool test_timestring(struct torture_context *tctx) +{ + const char *start = "Thu Jan 1"; + char *result; + /* + * Correct test for negative UTC offset. Without the correction, the + * test fails when run on hosts with negative UTC offsets, as the date + * returned is back in 1969 (pre-epoch). + */ + time_t now = time(NULL); + struct tm local = *localtime(&now); + struct tm gmt = *gmtime(&now); + time_t utc_offset = mktime(&local) - mktime(&gmt); + + result = timestring(tctx, 42 - (utc_offset < 0 ? utc_offset : 0)); + torture_assert(tctx, !strncmp(start, result, strlen(start)), result); + return true; +} + +static bool test_normalize_timespec(struct torture_context *tctx) +{ + const struct { + time_t in_s; long in_ns; + time_t out_s; long out_ns; + } data [] = { + { 0, 0, 0, 0 } + , { 1, 0, 1, 0 } + , { -1, 0, -1, 0 } + , { 0, 1000000000, 1, 0 } + , { 0, 2000000000, 2, 0 } + , { 0, 1000000001, 1, 1 } + , { 0, 2000000001, 2, 1 } + , { 0, -1000000000, -1, 0 } + , { 0, -2000000000, -2, 0 } + , { 0, -1000000001, -2, 999999999 } + , { 0, -2000000001, -3, 999999999 } + , { 0, -1, -1, 999999999 } + , { 1, -1, 0, 999999999 } + , { -1, -1, -2, 999999999 } + , { 0, 999999999, 0, 999999999 } + , { 0, 1999999999, 1, 999999999 } + , { 0, 2999999999, 2, 999999999 } + , { 0, -999999999, -1, 1 } + , { 0, -1999999999, -2, 1 } + , { 0, -2999999999, -3, 1 } + , { LONG_MAX, 1000000001, LONG_MAX, 999999999 } /* overflow */ + , { LONG_MAX, 999999999, LONG_MAX, 999999999 } /* harmless */ + , { LONG_MAX, -1, LONG_MAX-1, 999999999 } /* -1 */ + , { LONG_MIN, -1000000001, LONG_MIN, 0 } /* overflow */ + , { LONG_MIN, 0, LONG_MIN, 0 } /* harmless */ + , { LONG_MIN, 1000000000, LONG_MIN+1, 0 } /* +1 */ + }; + int i; + + for (i = 0; i < sizeof(data) / sizeof(data[0]); ++i) { + struct timespec ts = (struct timespec) + { .tv_sec = data[i].in_s + , .tv_nsec = data[i].in_ns }; + + normalize_timespec(&ts); + + torture_assert_int_equal(tctx, ts.tv_sec, data[i].out_s, + "mismatch in tv_sec"); + torture_assert_int_equal(tctx, ts.tv_nsec, data[i].out_ns, + "mismatch in tv_nsec"); + } + + return true; +} + +struct torture_suite *torture_local_util_time(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "time"); + + torture_suite_add_simple_test(suite, "null_time", test_null_time); + torture_suite_add_simple_test(suite, "null_nttime", test_null_nttime); + torture_suite_add_simple_test(suite, "http_timestring", + test_http_timestring); + torture_suite_add_simple_test(suite, "timestring", + test_timestring); + torture_suite_add_simple_test(suite, "normalize_timespec", + test_normalize_timespec); + + return suite; +} diff --git a/lib/util/tests/util.c b/lib/util/tests/util.c new file mode 100644 index 0000000..0b69e21 --- /dev/null +++ b/lib/util/tests/util.c @@ -0,0 +1,652 @@ +/* + * Tests for strv_util + * + * Copyright Martin Schwenke <martin@meltin.net> 2016 + * Copyright Christof Schmitt <cs@samba.org> 2018 + * Copyright Swen Schillig <swen@linux.ibm.com> 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <talloc.h> + +#include "replace.h" +#include "system/filesys.h" + +#include "libcli/util/ntstatus.h" +#include "torture/torture.h" +#include "lib/util/data_blob.h" +#include "torture/local/proto.h" + +#include "lib/util/samba_util.h" +#include "lib/util/smb_strtox.h" + +#include "limits.h" +#include "string.h" + +struct test_trim_string_data { + const char *desc; + const char *in; + const char *front; + const char *back; + const char *out; + bool ret; +}; + +static const struct test_trim_string_data test_trim_string_data[] = { + { + .desc = "All NULL", + .in = NULL, + .front = NULL, + .back = NULL, + .out = NULL, + .ret = false, + }, + { + .desc = "Input NULL", + .in = NULL, + .front = "abc", + .back = "123", + .out = NULL, + .ret = false, + }, + { + .desc = "Trim NULL", + .in = "abc", + .front = NULL, + .back = NULL, + .out = "abc", + .ret = false, + }, + { + .desc = "Trim empty", + .in = "abc", + .front = "", + .back = "", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim front, non-matching", + .in = "abc", + .front = "x", + .back = "", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim front, matches back", + .in = "abc", + .front = "c", + .back = "", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim front, partial-match", + .in = "abc", + .front = "ac", + .back = "", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim front, too long", + .in = "aaa", + .front = "aaaa", + .back = "", + .out = "aaa", + .ret = false, + }, + { + .desc = "Trim front, 1 char, 1x", + .in = "abc", + .front = "a", + .back = "", + .out = "bc", + .ret = true, + }, + { + .desc = "Trim front, 1 char, 2x", + .in = "aabc", + .front = "a", + .back = "", + .out = "bc", + .ret = true, + }, + { + .desc = "Trim front, 1 char, 3x", + .in = "aaabc", + .front = "a", + .back = "", + .out = "bc", + .ret = true, + }, + { + .desc = "Trim front, 1 char, matches all", + .in = "aaa", + .front = "a", + .back = "", + .out = "", + .ret = true, + }, + { + .desc = "Trim front, 2 chars, 1x", + .in = "abc", + .front = "ab", + .back = "", + .out = "c", + .ret = true, + }, + { + .desc = "Trim front, 2 chars, 2x", + .in = "ababc", + .front = "ab", + .back = "", + .out = "c", + .ret = true, + }, + { + .desc = "Trim front, 3 chars, matches all", + .in = "abc", + .front = "abc", + .back = "", + .out = "", + .ret = true, + }, + { + .desc = "Trim back, non-matching", + .in = "abc", + .front = "", + .back = "x", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim back, matches front", + .in = "abc", + .front = "", + .back = "a", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim back, partial-match", + .in = "abc", + .front = "", + .back = "xc", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim back, too long", + .in = "aaa", + .front = "", + .back = "aaaa", + .out = "aaa", + .ret = false, + }, + { + .desc = "Trim back, 1 char, 1x", + .in = "abc", + .front = "", + .back = "c", + .out = "ab", + .ret = true, + }, + { + .desc = "Trim back, 1 char, 2x", + .in = "abcc", + .front = "", + .back = "c", + .out = "ab", + .ret = true, + }, + { + .desc = "Trim back, 1 char, 3x", + .in = "abccc", + .front = "", + .back = "c", + .out = "ab", + .ret = true, + }, + { + .desc = "Trim back, 1 char, matches all", + .in = "aaa", + .front = "", + .back = "a", + .out = "", + .ret = true, + }, + { + .desc = "Trim back, 2 chars, 1x", + .in = "abc", + .front = "", + .back = "bc", + .out = "a", + .ret = true, + }, + { + .desc = "Trim back, 2 chars, 2x", + .in = "abcbc", + .front = "", + .back = "bc", + .out = "a", + .ret = true, + }, + { + .desc = "Trim back, 3 chars, matches all", + .in = "abc", + .front = "", + .back = "abc", + .out = "", + .ret = true, + }, + { + .desc = "Trim both, non-matching", + .in = "abc", + .front = "x", + .back = "y", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim both, reversed", + .in = "abc", + .front = "c", + .back = "a", + .out = "abc", + .ret = false, + }, + { + .desc = "Trim both, 1 char, 1x", + .in = "abc", + .front = "a", + .back = "c", + .out = "b", + .ret = true, + }, + { + .desc = "Trim both, 1 char, 2x", + .in = "aabcc", + .front = "a", + .back = "c", + .out = "b", + .ret = true, + }, + { + .desc = "Trim both, 1 char, 3x", + .in = "aaabccc", + .front = "a", + .back = "c", + .out = "b", + .ret = true, + }, + { + .desc = "Trim both, 1 char, matches all", + .in = "aaabbb", + .front = "a", + .back = "b", + .out = "", + .ret = true, + }, + { + .desc = "Trim both, 2 chars, 1x", + .in = "abxbc", + .front = "ab", + .back = "bc", + .out = "x", + .ret = true, + }, + { + .desc = "Trim both, 2 chars, 2x", + .in = "ababxyzbcbc", + .front = "ab", + .back = "bc", + .out = "xyz", + .ret = true, + }, + { + .desc = "Trim both, 2 chars, front matches, back doesn't", + .in = "abcde", + .front = "ab", + .back = "xy", + .out = "cde", + .ret = true, + }, + { + .desc = "Trim both, 2 chars, back matches, front doesn't", + .in = "abcde", + .front = "xy", + .back = "de", + .out = "abc", + .ret = true, + }, + { + .desc = "Trim back, 3 chars, matches all", + .in = "abcxyz", + .front = "abc", + .back = "xyz", + .out = "", + .ret = true, + }, +}; + +static bool test_trim_string(struct torture_context *tctx) +{ + int j; + for (j = 0; j < ARRAY_SIZE(test_trim_string_data); j++) { + bool ret; + const struct test_trim_string_data *d = + &test_trim_string_data[j]; + char *str = talloc_strdup(tctx, d->in); + torture_assert(tctx, d->in == NULL || str != NULL, + "Out of memory"); + + torture_comment(tctx, "%s\n", d->desc); + ret = trim_string(str, d->front, d->back); + torture_assert(tctx, ret == d->ret, + "Incorrect return from trim_string()"); + if (d->out == NULL) { + torture_assert(tctx, str == NULL, "Expected NULL"); + } else { + torture_assert(tctx, str != NULL, "Expected non-NULL"); + torture_assert_str_equal(tctx, str, d->out, + "Incorrect output"); + } + TALLOC_FREE(str); + } + + return true; +} + +static bool test_directory_create_or_exist(struct torture_context *tctx) +{ + char *path = NULL, *new_path = NULL, *file_path = NULL; + bool ret = true, b = true; + int fd; + NTSTATUS status; + const mode_t perms = 0741; + + status = torture_temp_dir(tctx, "util_dir", &path);; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Creating test directory failed.\n"); + + b = directory_create_or_exist(path, perms); + torture_assert_goto(tctx, b == true, ret, done, + "directory_create_or_exist on " + "existing directory failed.\n"); + + new_path = talloc_asprintf(tctx, "%s/%s", path, "dir"); + torture_assert_goto(tctx, new_path != NULL, ret, done, + "Could not allocate memory for directory path\n"); + + b = directory_exist(new_path); + torture_assert_goto(tctx, b == false, ret, done, + "Check for non-existing directory failed.\n"); + + b = directory_create_or_exist(new_path, perms); + torture_assert_goto(tctx, b == true, ret, done, + "directory_create_or_exist for " + "new directory failed.\n"); + + b = directory_exist(new_path); + torture_assert_goto(tctx, b == true, ret, done, + "Check for existing directory failed.\n"); + + b = file_check_permissions(new_path, geteuid(), perms, NULL); + torture_assert_goto(tctx, b == true, ret, done, + "Permission check for directory failed.\n"); + + file_path = talloc_asprintf(tctx, "%s/%s", path, "file"); + torture_assert_goto(tctx, file_path != NULL, ret, done, + "Could not allocate memory for file path\n"); + fd = creat(file_path, perms); + torture_assert_goto(tctx, fd != -1, ret, done, + "Creating file failed.\n"); + close(fd); + + b = directory_create_or_exist(file_path, perms); + torture_assert_goto(tctx, b == false, ret, done, + "directory_create_or_exist for " + "existing file failed.\n"); + +done: + return ret; +} + +static bool test_mem_equal_const_time(struct torture_context *tctx) +{ + const char *test_string = "abcdabcd"; + + torture_assert(tctx, mem_equal_const_time("", "", 0), + "zero-length comparison failed"); + + torture_assert(tctx, mem_equal_const_time(test_string, test_string, 8), + "comparison with equal pointers failed"); + + torture_assert(tctx, mem_equal_const_time(test_string, test_string + 4, 4), + "comparison with non-equal pointers failed"); + + torture_assert(tctx, !mem_equal_const_time("abcd", "efgh", 4), + "comparison with different values failed"); + + return true; +} + +static bool test_smb_strtoul_errno_check(struct torture_context *tctx) +{ + const char *number = "123"; + unsigned long int val = 0; + unsigned long long int vall = 0; + int err; + + /* select an error code which is not set by the smb_strtoul routines */ + errno = EAGAIN; + err = EAGAIN; + val = smb_strtoul(number, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, errno == EAGAIN, "smb_strtoul: Expected EAGAIN"); + torture_assert(tctx, err == 0, "smb_strtoul: Expected err = 0"); + torture_assert(tctx, val == 123, "smb_strtoul: Expected value 123"); + + /* set err to an impossible value again before continuing */ + err = EAGAIN; + vall = smb_strtoull(number, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, errno == EAGAIN, "smb_strtoull: Expected EAGAIN"); + torture_assert(tctx, err == 0, "smb_strtoul: Expected err = 0"); + torture_assert(tctx, vall == 123, "smb_strtoul: Expected value 123"); + + return true; +} + +static bool test_smb_strtoul_negative(struct torture_context *tctx) +{ + const char *number = "-132"; + const char *number2 = "132-"; + unsigned long int val = 0; + unsigned long long int vall = 0; + int err; + + err = 0; + smb_strtoul(number, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == EINVAL, "smb_strtoul: Expected EINVAL"); + + err = 0; + smb_strtoull(number, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == EINVAL, "smb_strtoull: Expected EINVAL"); + + /* it is allowed to have a "-" sign after a number, + * e.g. as part of a formular, however, it is not supposed to + * have an effect on the converted value. + */ + + err = 0; + val = smb_strtoul(number2, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == 0, "smb_strtoul: Expected no error"); + torture_assert(tctx, val == 132, "smb_strtoul: Wrong value"); + + err = 0; + vall = smb_strtoull(number2, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == 0, "smb_strtoull: Expected no error"); + torture_assert(tctx, vall == 132, "smb_strtoull: Wrong value"); + + return true; +} + +static bool test_smb_strtoul_no_number(struct torture_context *tctx) +{ + const char *number = "ghijk"; + const char *blank = ""; + int err; + + err = 0; + smb_strtoul(number, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == EINVAL, "smb_strtoul: Expected EINVAL"); + + err = 0; + smb_strtoull(number, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == EINVAL, "smb_strtoull: Expected EINVAL"); + + err = 0; + smb_strtoul(blank, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == EINVAL, "smb_strtoul: Expected EINVAL"); + + err = 0; + smb_strtoull(blank, NULL, 0, &err, SMB_STR_STANDARD); + torture_assert(tctx, err == EINVAL, "smb_strtoull: Expected EINVAL"); + + return true; +} + +static bool test_smb_strtoul_allow_negative(struct torture_context *tctx) +{ + const char *number = "-1"; + const char *number2 = "-1-1"; + unsigned long res = 0; + unsigned long long res2 = 0; + char *end_ptr = NULL; + int err; + + err = 0; + res = smb_strtoul(number, NULL, 0, &err, SMB_STR_ALLOW_NEGATIVE); + torture_assert(tctx, err == 0, "strtoul_err: Unexpected error"); + torture_assert(tctx, res == ULONG_MAX, "strtoul_err: Unexpected value"); + + err = 0; + res2 = smb_strtoull(number, NULL, 0, &err, SMB_STR_ALLOW_NEGATIVE); + torture_assert(tctx, err == 0, "strtoull_err: Unexpected error"); + torture_assert(tctx, res2 == ULLONG_MAX, "strtoull_err: Unexpected value"); + + err = 0; + smb_strtoul(number2, &end_ptr, 0, &err, SMB_STR_ALLOW_NEGATIVE); + torture_assert(tctx, err == 0, "strtoul_err: Unexpected error"); + torture_assert(tctx, end_ptr[0] == '-', "strtoul_err: Unexpected end pointer"); + + err = 0; + smb_strtoull(number2, &end_ptr, 0, &err, SMB_STR_ALLOW_NEGATIVE); + torture_assert(tctx, err == 0, "strtoull_err: Unexpected error"); + torture_assert(tctx, end_ptr[0] == '-', "strtoull_err: Unexpected end pointer"); + + return true; +} + +static bool test_smb_strtoul_full_string(struct torture_context *tctx) +{ + const char *number = "123 "; + const char *number2 = "123"; + int err; + + err = 0; + smb_strtoul(number, NULL, 0, &err, SMB_STR_FULL_STR_CONV); + torture_assert(tctx, err == EINVAL, "strtoul_err: Expected EINVAL"); + + err = 0; + smb_strtoull(number, NULL, 0, &err, SMB_STR_FULL_STR_CONV); + torture_assert(tctx, err == EINVAL, "strtoull_err: Expected EINVAL"); + + err = 0; + smb_strtoul(number2, NULL, 0, &err, SMB_STR_FULL_STR_CONV); + torture_assert(tctx, err == 0, "strtoul_err: Unexpected error"); + + err = 0; + smb_strtoull(number2, NULL, 0, &err, SMB_STR_FULL_STR_CONV); + torture_assert(tctx, err == 0, "strtoull_err: Unexpected error"); + + return true; +} + +static bool test_smb_strtoul_allow_no_conversion(struct torture_context *tctx) +{ + const char *number = ""; + const char *number2 = "xyz"; + unsigned long int n1 = 0; + unsigned long long int n2 = 0; + int err; + + err = 0; + smb_strtoul(number, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION); + torture_assert(tctx, err == 0, "strtoul_err: Unexpected error"); + torture_assert(tctx, n1 == 0, "strtoul_err: Unexpected value"); + + err = 0; + smb_strtoull(number, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION); + torture_assert(tctx, err == 0, "strtoull_err: Unexpected error"); + torture_assert(tctx, n2 == 0, "strtoull_err: Unexpected value"); + + err = 0; + smb_strtoul(number2, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION); + torture_assert(tctx, err == 0, "strtoul_err: Unexpected error"); + torture_assert(tctx, n1 == 0, "strtoul_err: Unexpected value"); + + err = 0; + smb_strtoull(number2, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION); + torture_assert(tctx, err == 0, "strtoull_err: Unexpected error"); + torture_assert(tctx, n2 == 0, "strtoull_err: Unexpected value"); + + return true; +} +struct torture_suite *torture_local_util(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = + torture_suite_create(mem_ctx, "util"); + + torture_suite_add_simple_test(suite, + "trim_string", + test_trim_string); + torture_suite_add_simple_test(suite, + "directory_create_or_exist", + test_directory_create_or_exist); + torture_suite_add_simple_test(suite, + "mem_equal_const_time", + test_mem_equal_const_time); + torture_suite_add_simple_test(suite, + "smb_strtoul(l) errno", + test_smb_strtoul_errno_check); + torture_suite_add_simple_test(suite, + "smb_strtoul(l) negative", + test_smb_strtoul_negative); + torture_suite_add_simple_test(suite, + "smb_strtoul(l) no number", + test_smb_strtoul_no_number); + torture_suite_add_simple_test(suite, + "smb_strtoul(l) allow_negative", + test_smb_strtoul_allow_negative); + torture_suite_add_simple_test(suite, + "smb_strtoul(l) full string conversion", + test_smb_strtoul_full_string); + torture_suite_add_simple_test(suite, + "smb_strtoul(l) allow no conversion", + test_smb_strtoul_allow_no_conversion); + return suite; +} diff --git a/lib/util/tests/util_str_escape.c b/lib/util/tests/util_str_escape.c new file mode 100644 index 0000000..82e2209 --- /dev/null +++ b/lib/util/tests/util_str_escape.c @@ -0,0 +1,90 @@ +/* + + util_str_escape testing + + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "lib/util/util_str_escape.h" + +static bool test_log_escape_empty_string(struct torture_context *tctx) +{ + char *result = log_escape( tctx, ""); + torture_assert_str_equal(tctx, result, "", "Empty string handling"); + return true; +} + +static bool test_log_escape_null_string(struct torture_context *tctx) +{ + char *result = log_escape( tctx, NULL); + torture_assert(tctx, (result == NULL), "Empty string handling"); + return true; +} + +static bool test_log_escape_plain_string(struct torture_context *tctx) +{ + const char *input = "a plain string with no escapable characters"; + const char *expected = "a plain string with no escapable characters"; + + char *result = log_escape( tctx, input); + torture_assert_str_equal(tctx, result, expected, + "Plain string handling"); + return true; +} + +static bool test_log_escape_string(struct torture_context *tctx) +{ + const char *input = "\a\b\f\n\r\t\v\\\x01"; + const char *expected = "\\a\\b\\f\\n\\r\\t\\v\\\\\\x01"; + + char *result = log_escape( tctx, input); + torture_assert_str_equal(tctx, result, expected, + "Escapable characters in string"); + return true; +} + +static bool test_log_escape_hex_string(struct torture_context *tctx) +{ + const char *input = "\x01\x1F "; + const char *expected = "\\x01\\x1F "; + + char *result = log_escape( tctx, input); + torture_assert_str_equal(tctx, result, expected, + "hex escaping"); + return true; +} +struct torture_suite *torture_local_util_str_escape(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "util_str_escape"); + + torture_suite_add_simple_test(suite, "log_escape_empty_string", + test_log_escape_empty_string); + torture_suite_add_simple_test(suite, "log_escape_null_string", + test_log_escape_null_string); + torture_suite_add_simple_test(suite, "log_escape_plain_string", + test_log_escape_plain_string); + torture_suite_add_simple_test(suite, "log_escape_string", + test_log_escape_string); + torture_suite_add_simple_test(suite, "log_escape_hex_string", + test_log_escape_hex_string); + + + return suite; +} |