diff options
author | Jo-Philipp Wich <jo@mein.io> | 2023-08-22 09:58:00 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2023-08-22 10:34:45 +0200 |
commit | 8f852eaf9c870de2078649410f029e0846117de2 (patch) | |
tree | fb5736feee7dce6e15f38d79c2fcfaf81dcd9ebc | |
parent | 5d265bd52f40dd94671783148e4e6ff068c8153a (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_comparison | 28 | ||||
-rw-r--r-- | types.c | 11 |
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 -- @@ -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... */ |