summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2023-08-22 09:58:00 +0200
committerJo-Philipp Wich <jo@mein.io>2023-08-22 10:34:45 +0200
commit8f852eaf9c870de2078649410f029e0846117de2 (patch)
treefb5736feee7dce6e15f38d79c2fcfaf81dcd9ebc
parent5d265bd52f40dd94671783148e4e6ff068c8153a (diff)
types: improve comparison reliability of binary strings
The existing `ucv_compare()` implementation utilized `strcmp()` to compare two ucode string values, which may lead to incorrect results for strings containing null bytes as the comparison prematurely aborts when encountering the first null. Rework the string comparison logic to use `memcmp()` for comparing both ucv strings with each other in order to ensure that expressions such as `"" == "\u0000"` lead to the expected `false` result. Ref: https://github.com/openwrt/luci/issues/6530 Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--tests/custom/99_bugs/42_types_binary_string_comparison28
-rw-r--r--types.c11
2 files changed, 38 insertions, 1 deletions
diff --git a/tests/custom/99_bugs/42_types_binary_string_comparison b/tests/custom/99_bugs/42_types_binary_string_comparison
new file mode 100644
index 0000000..bc566c6
--- /dev/null
+++ b/tests/custom/99_bugs/42_types_binary_string_comparison
@@ -0,0 +1,28 @@
+When comparing strings with embedded null bytes, ensure that the comparison
+takes the entire string length into account.
+
+-- Testcase --
+printf("%.J\n", [
+ "" == "\u0000",
+ "" < "\u0000",
+ "" > "\u0000",
+ "foo\u0000bar" == "foo\u0000baz",
+ "foo\u0000bar" < "foo\u0000baz",
+ "foo\u0000bar" > "foo\u0000baz",
+]);
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[
+ false,
+ true,
+ false,
+ false,
+ true,
+ false
+]
+-- End --
diff --git a/types.c b/types.c
index 226c874..c9472f7 100644
--- a/types.c
+++ b/types.c
@@ -2006,7 +2006,16 @@ ucv_compare(int how, uc_value_t *v1, uc_value_t *v2, int *deltap)
/* ... otherwise if both operands are strings, compare bytewise ... */
else if (t1 == UC_STRING && t2 == UC_STRING) {
- delta = strcmp(ucv_string_get(v1), ucv_string_get(v2));
+ u1 = ucv_string_length(v1);
+ u2 = ucv_string_length(v2);
+
+ delta = memcmp(ucv_string_get(v1), ucv_string_get(v2),
+ (u1 < u2) ? u1 : u2);
+
+ if (delta == 0 && u1 < u2)
+ delta = -1;
+ else if (delta == 0 && u1 > u2)
+ delta = 1;
}
/* handle non-string cases... */