From e443ee357ab0ed8fba2b20cb797802254419ea4a Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Wed, 20 Dec 2023 19:54:20 +0100
Subject: [PATCH] tag/IcyMetadataParser: do not null-terminate strings in
 icy_parse_tag()

Just pass std::string_views into the unmodified buffer to
icy_parse_tag_item().
---
 src/tag/IcyMetaDataParser.cxx | 23 +++++++++++------------
 test/test_icy_parser.cxx      |  6 +-----
 2 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/src/tag/IcyMetaDataParser.cxx b/src/tag/IcyMetaDataParser.cxx
index b85de2439..fac8f6a78 100644
--- a/src/tag/IcyMetaDataParser.cxx
+++ b/src/tag/IcyMetaDataParser.cxx
@@ -99,10 +99,10 @@ icy_parse_tag_item(TagBuilder &tag,
  * of the string).  If that fails, return the first single quote.  If
  * that also fails, return #end.
  */
-static char *
-find_end_quote(char *p, char *const end) noexcept
+static const char *
+find_end_quote(const char *p, const char *const end) noexcept
 {
-	char *fallback = std::find(p, end, '\'');
+	const char *fallback = std::find(p, end, '\'');
 	if (fallback >= end - 1 || fallback[1] == ';')
 		return fallback;
 
@@ -124,7 +124,7 @@ icy_parse_tag(
 #ifdef HAVE_ICU_CONVERTER
 	      const IcuConverter *icu_converter,
 #endif
-	      char *p, char *const end) noexcept
+	      const char *p, const char *const end) noexcept
 {
 	assert(p != nullptr);
 	assert(end != nullptr);
@@ -133,18 +133,18 @@ icy_parse_tag(
 	TagBuilder tag;
 
 	while (p != end) {
-		const char *const name = p;
-		char *eq = std::find(p, end, '=');
+		const char *eq = std::find(p, end, '=');
 		if (eq == end)
 			break;
 
-		*eq = 0;
+		const std::string_view name{p, eq};
+
 		p = eq + 1;
 
 		if (*p != '\'') {
 			/* syntax error; skip to the next semicolon,
 			   try to recover */
-			char *semicolon = std::find(p, end, ';');
+			const char *semicolon = std::find(p, end, ';');
 			if (semicolon == end)
 				break;
 			p = semicolon + 1;
@@ -153,12 +153,11 @@ icy_parse_tag(
 
 		++p;
 
-		const char *const value = p;
-		char *quote = find_end_quote(p, end);
+		const char *quote = find_end_quote(p, end);
 		if (quote == end)
 			break;
 
-		*quote = 0;
+		const std::string_view value{p, quote};
 		p = quote + 1;
 
 		icy_parse_tag_item(tag,
@@ -167,7 +166,7 @@ icy_parse_tag(
 #endif
 				   name, value);
 
-		char *semicolon = std::find(p, end, ';');
+		const char *semicolon = std::find(p, end, ';');
 		if (semicolon == end)
 			break;
 		p = semicolon + 1;
diff --git a/test/test_icy_parser.cxx b/test/test_icy_parser.cxx
index e5c546e4a..5fddc2dd6 100644
--- a/test/test_icy_parser.cxx
+++ b/test/test_icy_parser.cxx
@@ -2,8 +2,6 @@
  * Unit tests for class IcyMetaDataParser.
  */
 
-#include "util/ScopeExit.hxx"
-
 /* include the .cxx file to get access to internal functions */
 #include "tag/IcyMetaDataParser.cxx"
 
@@ -16,13 +14,11 @@
 static std::unique_ptr<Tag>
 icy_parse_tag(const char *p)
 {
-	char *q = strdup(p);
-	AtScopeExit(q) { free(q); };
 	return icy_parse_tag(
 #ifdef HAVE_ICU_CONVERTER
 			     nullptr,
 #endif
-			     q, q + strlen(q));
+			     p, p + strlen(p));
 }
 
 static void