From: James Jones Date: Thu, 8 Oct 2020 14:57:45 +0000 (-0500) Subject: Add fr_dbuff_move() and tests (#3675) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6b6e4991d78feb798ea5a54cdfe04b4a82e2afb6;p=thirdparty%2Ffreeradius-server.git Add fr_dbuff_move() and tests (#3675) --- diff --git a/src/lib/util/dbuff.c b/src/lib/util/dbuff.c new file mode 100644 index 00000000000..fcc29a2deb1 --- /dev/null +++ b/src/lib/util/dbuff.c @@ -0,0 +1,159 @@ +/* + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + /** A generic data buffer structure for encoding and decoding + * + * Because doing manual length checks is error prone and a waste of everyone's time. + * + * @file src/lib/util/dbuff.c + * + * @copyright 2020 Arran Cudbard-Bell + */ + +RCSID("$Id:") + +#include + +#if defined(__clang_analyzer__) || !defined(NDEBUG) +# define CHECK_DBUFF_INIT(_sbuff) if (!(_sbuff)->extend && (unlikely(!(_sbuff)->buff) || unlikely(!(_sbuff)->start) || unlikely(!(_sbuff)->end) || unlikely(!(_sbuff)->p))) return 0; +#else +# define CHECK_DBUFF_INIT(_sbuff) +#endif + +/** start/end flavored mem{cpy,move} wrapper with sanity checks + */ +static inline CC_HINT(always_inline) ssize_t safecpy(uint8_t *o_start, uint8_t *o_end, + uint8_t const *i_start, uint8_t const *i_end) +{ + ssize_t diff; + size_t i_len = i_end - i_start; + + if (unlikely((o_end < o_start) || (i_end < i_start))) return 0; /* sanity check */ + + diff = (o_end - o_start) - (i_len); + if (diff < 0) return diff; + + if ((i_start > o_end) || (i_end < o_start)) { /* no-overlap */ + memcpy(o_start, i_start, i_len); + } else { /* overlap */ + memmove(o_start, i_start, i_len); + } + + return (i_len); +} + +/** Move data from one dbuff to another + * + * @note Do not call this function directly; use #fr_dbuff_move + * + * Both in and out will be advanced by + * min {len, fr_dbuff_remaining(out), fr_dbuff_remaining(in)}; eventually, + * we'll attempt to extend dbuffs where possible and needed to make len bytes + * available in both in and out. + * + * @param[in] out dbuff to copy data to. + * @param[in] in dbuff to copy data from. + * @param[in] len Maximum number of bytes to copy. + * @return The amount of data copied. + */ +size_t _fr_dbuff_move_dbuff_to_dbuff(fr_dbuff_t *out, fr_dbuff_t *in, size_t len) +{ + size_t o_remaining = fr_dbuff_remaining(out); + size_t i_remaining = fr_dbuff_remaining(in); + size_t to_copy = len; + if (to_copy > o_remaining) to_copy = o_remaining; + if (to_copy > i_remaining) to_copy = i_remaining; + safecpy(fr_dbuff_current(out), fr_dbuff_end(out), fr_dbuff_current(in), fr_dbuff_current(in) + to_copy); + return fr_dbuff_advance(out, fr_dbuff_advance(in, to_copy)); +} + +/** Move data from a marker to a dbuff + * + * @note Do not call this function directly; use #fr_dbuff_move + * + * Both in and out will be advanced by + * min {len, fr_dbuff_remaining(out), fr_dbuff_marker_remaining(in)}; eventually, + * we'll attempt to extend dbuffs where possible and needed to make len bytes + * available in both in and out. + * + * @param[in] out dbuff to copy data to. + * @param[in] in marker to copy data from. + * @param[in] len Maximum number of bytes to copy. + * @return The amount of data copied. + */ +size_t _fr_dbuff_move_marker_to_dbuff(fr_dbuff_t *out, fr_dbuff_marker_t *in, size_t len) +{ + size_t o_remaining = fr_dbuff_remaining(out); + size_t i_remaining = fr_dbuff_marker_remaining(in); + size_t to_copy = len; + if (to_copy > o_remaining) to_copy = o_remaining; + if (to_copy > i_remaining) to_copy = i_remaining; + safecpy(fr_dbuff_current(out), fr_dbuff_end(out), fr_dbuff_marker_current(in), + fr_dbuff_marker_current(in) + to_copy); + return fr_dbuff_advance(out, fr_dbuff_marker_advance(in, to_copy)); +} + +/** Move data from one marker to another + * + * @note Do not call this function directly; use #fr_dbuff_move + * + * Both in and out will be advanced by + * min {len, fr_dbuff_marker_remaining(out), fr_dbuff_marker_remaining(in)}; eventually, + * we'll attempt to extend dbuffs where possible and needed to make len bytes + * available in both in and out. + * + * @param[in] out dbuff to copy data to. + * @param[in] in marker to copy data from. + * @param[in] len Maximum number of bytes to copy. + * @return The amount of data copied. + */ +size_t _fr_dbuff_move_marker_to_marker(fr_dbuff_marker_t *out, fr_dbuff_marker_t *in, size_t len) +{ + size_t o_remaining = fr_dbuff_marker_remaining(out); + size_t i_remaining = fr_dbuff_marker_remaining(in); + size_t to_copy = len; + if (to_copy > o_remaining) to_copy = o_remaining; + if (to_copy > i_remaining) to_copy = i_remaining; + safecpy(fr_dbuff_marker_current(out), fr_dbuff_marker_end(out), fr_dbuff_marker_current(in), + fr_dbuff_marker_current(in) + to_copy); + return fr_dbuff_marker_advance(out, fr_dbuff_marker_advance(in, to_copy)); +} + +/** Move data from a dbuff to a marker + * + * @note Do not call this function directly; use #fr_dbuff_move + * + * Both in and out will be advanced by + * min {len, fr_dbuff_marker_remaining(out), fr_dbuff_marker_remaining(in)}; eventually, + * we'll attempt to extend dbuffs where possible and needed to make len bytes + * available in both in and out. + * + * @param[in] out dbuff to copy data to. + * @param[in] in marker to copy data from. + * @param[in] len Maximum number of bytes to copy. + * @return The amount of data copied. + */ +size_t _fr_dbuff_move_dbuff_to_marker(fr_dbuff_marker_t *out, fr_dbuff_t *in, size_t len) +{ + size_t o_remaining = fr_dbuff_marker_remaining(out); + size_t i_remaining = fr_dbuff_remaining(in); + size_t to_copy = len; + if (to_copy > o_remaining) to_copy = o_remaining; + if (to_copy > i_remaining) to_copy = i_remaining; + safecpy(fr_dbuff_marker_current(out), fr_dbuff_marker_end(out), fr_dbuff_current(in), + fr_dbuff_current(in) + to_copy); + return fr_dbuff_marker_advance(out, fr_dbuff_advance(in, to_copy)); +} diff --git a/src/lib/util/dbuff.h b/src/lib/util/dbuff.h index 9e0df708cd2..e8fb540f7f7 100644 --- a/src/lib/util/dbuff.h +++ b/src/lib/util/dbuff.h @@ -530,6 +530,14 @@ static inline size_t fr_dbuff_marker_used(fr_dbuff_marker_t *m) { return m->p - m->parent->start; } + +/* what is the end of the buffer (determined from marker) + * + */ +static inline uint8_t *fr_dbuff_marker_end(fr_dbuff_marker_t *m) +{ + return m->parent->end; +} /** @} */ /** @name copy data to dbuff @@ -766,6 +774,39 @@ static inline ssize_t fr_dbuff_uint64v_in(fr_dbuff_t *dbuff, uint64_t num) return fr_dbuff_memcpy_in(dbuff, ((uint8_t *)&num) + (sizeof(uint64_t) - ret), ret); } + +size_t _fr_dbuff_move_dbuff_to_dbuff(fr_dbuff_t *out, fr_dbuff_t *in, size_t len); + +size_t _fr_dbuff_move_marker_to_dbuff(fr_dbuff_t *out, fr_dbuff_marker_t *in, size_t len); + +size_t _fr_dbuff_move_marker_to_marker(fr_dbuff_marker_t *out, fr_dbuff_marker_t *in, size_t len); + +size_t _fr_dbuff_move_dbuff_to_marker(fr_dbuff_marker_t *out, fr_dbuff_t *in, size_t len); + +/** Copy in as many bytes as possible from one dbuff or marker to another + * + * @param[in] out to copy into. + * @param[in] in to copy from. + * @param[in] len The maximum length to copy. + * @return Number of bytes to copy. + */ +#define fr_dbuff_move(_out, _in, _len) \ + _Generic((_out), \ + fr_dbuff_t * : \ + _Generic((_in), \ + fr_dbuff_t * : _fr_dbuff_move_dbuff_to_dbuff((fr_dbuff_t *)_out, \ + (fr_dbuff_t *)_in, _len), \ + fr_dbuff_marker_t * : _fr_dbuff_move_marker_to_dbuff((fr_dbuff_t *)_out, \ + (fr_dbuff_marker_t *)_in, _len) \ + ), \ + fr_dbuff_marker_t * : \ + _Generic((_in), \ + fr_dbuff_t * : _fr_dbuff_move_dbuff_to_marker((fr_dbuff_marker_t *)_out, \ + (fr_dbuff_t *)_in, _len), \ + fr_dbuff_marker_t * : _fr_dbuff_move_marker_to_marker((fr_dbuff_marker_t *)_out, \ + (fr_dbuff_marker_t *)_in, _len) \ + ) \ + ) /** @} */ #ifdef __cplusplus diff --git a/src/lib/util/dbuff_tests.c b/src/lib/util/dbuff_tests.c index 5a2c6b13aa7..50fe77fc11d 100644 --- a/src/lib/util/dbuff_tests.c +++ b/src/lib/util/dbuff_tests.c @@ -219,6 +219,47 @@ static void test_dbuff_net_encode(void) TEST_CHECK(fr_dbuff_in(&dbuff, u64val) == -(ssize_t)(sizeof(uint64_t) - sizeof(uint32_t))); } +static void test_dbuff_move(void) +{ + uint8_t buff1[26], buff2[26], buff3[10]; + fr_dbuff_t dbuff1, dbuff2, dbuff3; + fr_dbuff_marker_t marker1, marker2; + + memcpy(buff1, "abcdefghijklmnopqrstuvwxyz", sizeof(buff1)); + memcpy(buff2, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sizeof(buff2)); + memcpy(buff3, "0123456789", sizeof(buff3)); + fr_dbuff_init(&dbuff1, buff1, sizeof(buff1)); + fr_dbuff_init(&dbuff2, buff2, sizeof(buff2)); + fr_dbuff_init(&dbuff3, buff3, sizeof(buff3)); + fr_dbuff_marker(&marker1, &dbuff1); + fr_dbuff_marker(&marker2, &dbuff2); + + TEST_CASE("move dbuff to dbuff"); + TEST_CHECK(fr_dbuff_move(&dbuff1, &dbuff2, 13) == 13); + TEST_CHECK(fr_dbuff_used(&dbuff1) == 13); + TEST_CHECK(fr_dbuff_used(&dbuff2) == 13); + TEST_CHECK(memcmp(dbuff1.start, "ABCDEFGHIJKLMnopqrstuvwxyz", 26) == 0); + + TEST_CASE("move dbuff to marker"); + fr_dbuff_marker_advance(&marker2, 4); + TEST_CHECK(fr_dbuff_move(&marker2, &dbuff3, 10) == 10); + TEST_CHECK(fr_dbuff_marker_used(&marker2) == 14); + TEST_CHECK(memcmp(dbuff2.start, "ABCD0123456789OPQRSTUVWXYZ", 26) == 0); + + TEST_CASE("move marker to dbuff"); + fr_dbuff_marker_advance(&marker1, 7); + TEST_CHECK(fr_dbuff_move(&dbuff1, &marker1, 6) == 6); + TEST_CHECK(fr_dbuff_used(&dbuff1) == 19); + TEST_CHECK(fr_dbuff_marker_used(&marker1) == 13); + TEST_CHECK(memcmp(dbuff1.start, "ABCDEFGHIJKLMHIJKLMtuvwxyz", 26) == 0); + + TEST_CASE("move marker to marker"); + TEST_CHECK(fr_dbuff_move(&marker2, &marker1, 8) == 8); + TEST_CHECK(fr_dbuff_marker_used(&marker1) == 21); + TEST_CHECK(fr_dbuff_marker_used(&marker2) == 22); + TEST_CHECK(memcmp(dbuff2.start, "ABCD0123456789HIJKLMtuWXYZ", 26) == 0); +} + TEST_LIST = { /* @@ -228,6 +269,7 @@ TEST_LIST = { { "fr_dbuff_init_no_parent", test_dbuff_init_no_parent }, { "fr_dbuff_max", test_dbuff_max }, { "fr_dbuff_in", test_dbuff_net_encode }, + { "fr_dbuff_move", test_dbuff_move }, { NULL } }; diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index c557b6d2392..f471aa95b53 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -10,6 +10,7 @@ SOURCES := \ base64.c \ cap.c \ cursor.c \ + dbuff.c \ debug.c \ dict_print.c \ dict_tokenize.c \