From 3d8680e6a879c0c785e491a3fa78ba8ce86c2380 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 20 Dec 2018 08:37:32 +0100 Subject: lib:util: Add bytearray.h This is an implementation which doesn't have undefined behavior problems. It casts correctly that calculations are don in the correct integer space. Also the naming is less confusing than what we have in byteorder.h. Signed-off-by: Andreas Schneider Reviewed-by: Andrew Bartlett --- lib/util/bytearray.h | 92 +++++++++ lib/util/tests/test_bytearray.c | 435 ++++++++++++++++++++++++++++++++++++++++ lib/util/wscript_build | 7 + selftest/tests.py | 2 + 4 files changed, 536 insertions(+) create mode 100644 lib/util/bytearray.h create mode 100644 lib/util/tests/test_bytearray.c diff --git a/lib/util/bytearray.h b/lib/util/bytearray.h new file mode 100644 index 00000000000..b48d9c1dc45 --- /dev/null +++ b/lib/util/bytearray.h @@ -0,0 +1,92 @@ +/* + * Macros for handling integer types in byte arrays + * + * This file is originally from the libssh.org project + * + * Copyright (c) 2018 Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _BYTEARRAY_H +#define _BYTEARRAY_H + +#define _DATA_BYTE_CONST(data, pos) \ + ((uint8_t)(((const uint8_t *)(data))[(pos)])) + +#define _DATA_BYTE(data, pos) \ + (((uint8_t *)(data))[(pos)]) + +/* + * These macros pull or push integer values from byte arrays stored in + * little-endian byte order. + */ +#define PULL_LE_U8(data, pos) \ + (_DATA_BYTE_CONST(data, pos)) + +#define PULL_LE_U16(data, pos) \ + ((uint16_t)PULL_LE_U8(data, pos) | ((uint16_t)(PULL_LE_U8(data, (pos) + 1))) << 8) + +#define PULL_LE_U32(data, pos) \ + ((uint32_t)(PULL_LE_U16(data, pos) | ((uint32_t)PULL_LE_U16(data, (pos) + 2)) << 16)) + +#define PULL_LE_U64(data, pos) \ + ((uint64_t)(PULL_LE_U32(data, pos) | ((uint64_t)PULL_LE_U32(data, (pos) + 4)) << 32)) + + +#define PUSH_LE_U8(data, pos, val) \ + (_DATA_BYTE(data, pos) = ((uint8_t)(val))) + +#define PUSH_LE_U16(data, pos, val) \ + (PUSH_LE_U8((data), (pos), (uint8_t)((uint16_t)(val) & 0xff)), PUSH_LE_U8((data), (pos) + 1, (uint8_t)((uint16_t)(val) >> 8))) + +#define PUSH_LE_U32(data, pos, val) \ + (PUSH_LE_U16((data), (pos), (uint16_t)((uint32_t)(val) & 0xffff)), PUSH_LE_U16((data), (pos) + 2, (uint16_t)((uint32_t)(val) >> 16))) + +#define PUSH_LE_U64(data, pos, val) \ + (PUSH_LE_U32((data), (pos), (uint32_t)((uint64_t)(val) & 0xffffffff)), PUSH_LE_U32((data), (pos) + 4, (uint32_t)((uint64_t)(val) >> 32))) + + + +/* + * These macros pull or push integer values from byte arrays stored in + * big-endian byte order (network byte order). + */ +#define PULL_BE_U8(data, pos) \ + (_DATA_BYTE_CONST(data, pos)) + +#define PULL_BE_U16(data, pos) \ + ((((uint16_t)(PULL_BE_U8(data, pos))) << 8) | (uint16_t)PULL_BE_U8(data, (pos) + 1)) + +#define PULL_BE_U32(data, pos) \ + ((((uint32_t)PULL_BE_U16(data, pos)) << 16) | (uint32_t)(PULL_BE_U16(data, (pos) + 2))) + +#define PULL_BE_U64(data, pos) \ + ((((uint64_t)PULL_BE_U32(data, pos)) << 32) | (uint64_t)(PULL_BE_U32(data, (pos) + 4))) + + + +#define PUSH_BE_U8(data, pos, val) \ + (_DATA_BYTE(data, pos) = ((uint8_t)(val))) + +#define PUSH_BE_U16(data, pos, val) \ + (PUSH_BE_U8((data), (pos), (uint8_t)(((uint16_t)(val)) >> 8)), PUSH_BE_U8((data), (pos) + 1, (uint8_t)((val) & 0xff))) + +#define PUSH_BE_U32(data, pos, val) \ + (PUSH_BE_U16((data), (pos), (uint16_t)(((uint32_t)(val)) >> 16)), PUSH_BE_U16((data), (pos) + 2, (uint16_t)((val) & 0xffff))) + +#define PUSH_BE_U64(data, pos, val) \ + (PUSH_BE_U32((data), (pos), (uint32_t)(((uint64_t)(val)) >> 32)), PUSH_BE_U32((data), (pos) + 4, (uint32_t)((val) & 0xffffffff))) + +#endif /* _BYTEARRAY_H */ diff --git a/lib/util/tests/test_bytearray.c b/lib/util/tests/test_bytearray.c new file mode 100644 index 00000000000..fcf63d8226f --- /dev/null +++ b/lib/util/tests/test_bytearray.c @@ -0,0 +1,435 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2018-2019 Andreas Schneider + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#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/wscript_build b/lib/util/wscript_build index a827eea3ed9..f181e82fa3a 100644 --- a/lib/util/wscript_build +++ b/lib/util/wscript_build @@ -154,6 +154,7 @@ else: public_deps='talloc tevent execinfo pthread LIBCRYPTO charset util_setid', public_headers=''' attr.h + bytearray.h byteorder.h data_blob.h debug.h @@ -288,3 +289,9 @@ else: deps='cmocka replace samba-util', local_include=False, for_selftest=True) + + bld.SAMBA_BINARY('test_bytearray', + source='tests/test_bytearray.c', + deps='cmocka replace samba-util', + local_include=False, + for_selftest=True) diff --git a/selftest/tests.py b/selftest/tests.py index 96d3f8d6317..a15a5384897 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -389,6 +389,8 @@ plantestsuite("samba.unittests.ms_fnmatch", "none", [os.path.join(bindir(), "default/lib/util/test_ms_fnmatch")]) plantestsuite("samba.unittests.byteorder", "none", [os.path.join(bindir(), "default/lib/util/test_byteorder")]) +plantestsuite("samba.unittests.bytearray", "none", + [os.path.join(bindir(), "default/lib/util/test_bytearray")]) plantestsuite("samba.unittests.ntlm_check", "none", [os.path.join(bindir(), "default/libcli/auth/test_ntlm_check")]) plantestsuite("samba.unittests.gnutls", "none", -- cgit v1.2.1