From 17ef95aed82fd059c103707315a1025ea6f335fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 18 Dec 2021 23:16:24 +0100 Subject: [PATCH] Make rz_strbuf_slice() truncate on excessive len (#2115) It is often useful to get a slice of some maximum len, also accepting smaller results if the source is not large enough. This also makes the function always operate on byte indices. Before, it was inconsistently skipping ansi codes sometimes. This also fixes potential oob writes in panels.c --- librz/include/rz_util/rz_strbuf.h | 2 +- librz/util/strbuf.c | 33 ++++++++++++------------------- test/unit/test_strbuf.c | 20 ++++++++++++++----- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/librz/include/rz_util/rz_strbuf.h b/librz/include/rz_util/rz_strbuf.h index 143ca31e1a..0c67f5e320 100644 --- a/librz/include/rz_util/rz_strbuf.h +++ b/librz/include/rz_util/rz_strbuf.h @@ -16,7 +16,7 @@ typedef struct { #define RZ_STRBUF_SAFEGET(sb) (rz_strbuf_get(sb) ? rz_strbuf_get(sb) : "") RZ_API RzStrBuf *rz_strbuf_new(const char *s); RZ_API const char *rz_strbuf_set(RzStrBuf *sb, const char *s); // return = the string or NULL on fail -RZ_API bool rz_strbuf_slice(RzStrBuf *sb, int from, int len); +RZ_API bool rz_strbuf_slice(RzStrBuf *sb, size_t from, size_t len); RZ_API bool rz_strbuf_setbin(RzStrBuf *sb, const ut8 *s, size_t len); RZ_API ut8 *rz_strbuf_getbin(RzStrBuf *sb, int *len); RZ_API const char *rz_strbuf_setf(RzStrBuf *sb, const char *fmt, ...) RZ_PRINTF_CHECK(2, 3); // return = the string or NULL on fail diff --git a/librz/util/strbuf.c b/librz/util/strbuf.c index f85e3fcbfd..0fccf44538 100644 --- a/librz/util/strbuf.c +++ b/librz/util/strbuf.c @@ -110,36 +110,29 @@ RZ_API bool rz_strbuf_setbin(RzStrBuf *sb, const ut8 *s, size_t l) { /** * \brief Cuts the current string into a substring * + * Only to be used on strings, not binary buffers. + * If `len > sb->len - from`, then the resulting size will be truncated appropriately. + * * \param sb RzStrBuf to use * \param from Begin index from where to cut * \param len Length of the substring to cut * * \return false when fails to to cut the current buffer into a substring */ -RZ_API bool rz_strbuf_slice(RZ_NONNULL RzStrBuf *sb, int from, int len) { +RZ_API bool rz_strbuf_slice(RZ_NONNULL RzStrBuf *sb, size_t from, size_t len) { rz_return_val_if_fail(sb && from >= 0 && len >= 0, false); - if (from < 0 && len >= sb->len) { - return false; + if (from >= sb->len) { + // trying to cut outside the buf + return !sb->len && !from; // but it's fine if both are 0 } char *s = rz_strbuf_get(sb); - if (!from) { - sb->len = len; - sb->ptrlen = len + 1; - s[len] = 0; - return true; + len = RZ_MIN(sb->len - from, len); + if (from) { + memmove(s, s + from, len); } - const char *fr = rz_str_ansi_chrn(s, from + 1); - const char *to = rz_str_ansi_chrn(s, from + len + 1); - char *r = rz_str_newlen(fr, to - fr); - rz_strbuf_fini(sb); - rz_strbuf_init(sb); - if (from >= len) { - rz_strbuf_set(sb, ""); - free(r); - return false; - } - rz_strbuf_set(sb, r); - free(r); + sb->len = len; + sb->ptrlen = len + 1; + s[len] = 0; return true; } diff --git a/test/unit/test_strbuf.c b/test/unit/test_strbuf.c index 3de849befd..8c2e54c066 100644 --- a/test/unit/test_strbuf.c +++ b/test/unit/test_strbuf.c @@ -6,10 +6,20 @@ bool test_rz_strbuf_slice(void) { RzStrBuf *sa = rz_strbuf_new("foo,bar,cow"); - rz_strbuf_slice(sa, 2, 4); // should be from/to instead of from/len ? - char *a = rz_strbuf_drain(sa); - mu_assert_streq(a, "o,ba", "slicing fails"); - free(a); + rz_strbuf_slice(sa, 2, 4); + mu_assert_streq_free(rz_strbuf_drain(sa), "o,ba", "from + len"); + + sa = rz_strbuf_new("Restore our vision of natural progression"); + rz_strbuf_slice(sa, 0, 18); + mu_assert_streq_free(rz_strbuf_drain(sa), "Restore our vision", "len"); + + sa = rz_strbuf_new("Drift with the ebb and flow"); + rz_strbuf_slice(sa, 15, 9001); + mu_assert_streq_free(rz_strbuf_drain(sa), "ebb and flow", "from + escessive len"); + + sa = rz_strbuf_new("Intuition speak to me"); + rz_strbuf_slice(sa, 0, 9001); + mu_assert_streq_free(rz_strbuf_drain(sa), "Intuition speak to me", "escessive len"); mu_end; } @@ -233,4 +243,4 @@ bool all_tests() { return tests_passed != tests_run; } -mu_main(all_tests) \ No newline at end of file +mu_main(all_tests)