Author: rinrab
Date: Thu Aug 27 21:10:27 2026
New Revision: 1937538

Log:
Fix a potential memory corruption in utf.c:convert_to_stringbuf().

It was decided privatly by the PMC not to treat this as a security issue
because this bug is not exploitable in a resonable setup [1]. The only way to
actually crash the program would be by something like:

[[[
$ python -c 'print( "\x86" * 100000 )' > commit
$ svn ci --encoding windows-1251 -F commit
# should segfault
]]]

The reason be that convert_to_stringbuf allocates twice the amount of memory
for dest buffer than src, but it has a retry loop in case the conversion failed
which updates the length but does not expand the buffer. This will cause the
program to segfault with encodings, in which certain 3-byte wide characters are
represented by a single byte.

This code has been broken 18 years ago since r869736 (in which
svn_stringbuf_ensure() call was removed).

* subversion/libsvn_subr/utf.c
  (convert_to_stringbuf): Call svn_stringbuf_ensure() 
* subversion/tests/libsvn_subr/utf-test.c
  (test_utf_cstring_to_utf8_ex2_CVE): New test.
  (test_funcs): Run new test.

[1] https://lists.apache.org/thread/56qkrznbco93ymg1n4lmgzz99bhx0kx9 
    (private, requires login)

Modified:
   subversion/trunk/subversion/libsvn_subr/utf.c
   subversion/trunk/subversion/tests/libsvn_subr/utf-test.c

Modified: subversion/trunk/subversion/libsvn_subr/utf.c
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/utf.c       Thu Aug 27 20:39:05 
2026        (r1937537)
+++ subversion/trunk/subversion/libsvn_subr/utf.c       Thu Aug 27 21:10:27 
2026        (r1937538)
@@ -545,6 +545,7 @@ convert_to_stringbuf(xlate_handle_node_t
                                for all characters in the buffer, 4 is
                                maximum character size (currently) */
 
+      svn_stringbuf_ensure(*dest, buflen);
 
     } while (apr_err == APR_SUCCESS && srclen != 0);
 #endif

Modified: subversion/trunk/subversion/tests/libsvn_subr/utf-test.c
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/utf-test.c    Thu Aug 27 
20:39:05 2026        (r1937537)
+++ subversion/trunk/subversion/tests/libsvn_subr/utf-test.c    Thu Aug 27 
21:10:27 2026        (r1937538)
@@ -256,6 +256,27 @@ test_utf_cstring_to_utf8_ex2(apr_pool_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_utf_cstring_to_utf8_ex2_CVE(apr_pool_t *pool)
+{
+  apr_size_t i;
+  svn_stringbuf_t *src = svn_stringbuf_create_empty(pool);
+  svn_stringbuf_t *expected = svn_stringbuf_create_empty(pool);
+  const char *dest;
+
+  for (i = 0; i < 10000; i++)
+    {
+      svn_stringbuf_appendcstr(src, "\xfb");
+      svn_stringbuf_appendcstr(expected, "\xe2\x88\x9a");
+    }
+
+  SVN_ERR(svn_utf_cstring_to_utf8_ex2(&dest, src->data, "cp866", pool));
+
+  SVN_TEST_ASSERT(strcmp(dest, expected->data) == 0);
+
+  return SVN_NO_ERROR;
+}
+
 /* Test conversion to different codepages from utf8. */
 static svn_error_t *
 test_utf_cstring_from_utf8_ex2(apr_pool_t *pool)
@@ -1318,6 +1339,8 @@ test_utf8_align(apr_pool_t *pool)
   return SVN_NO_ERROR;
 }
 
+
+
 
 /* The test table.  */
 
@@ -1332,6 +1355,8 @@ static struct svn_test_descriptor_t test
                    "test last_valid/last_valid2"),
     SVN_TEST_PASS2(test_utf_cstring_to_utf8_ex2,
                    "test svn_utf_cstring_to_utf8_ex2"),
+    SVN_TEST_PASS2(test_utf_cstring_to_utf8_ex2_CVE,
+                   "test svn_utf_cstring_to_utf8_ex2 CVE"),
     SVN_TEST_PASS2(test_utf_cstring_from_utf8_ex2,
                    "test svn_utf_cstring_from_utf8_ex2"),
     SVN_TEST_PASS2(test_utf_collated_compare,

Reply via email to