Author: brane
Date: Thu Jan 15 23:45:34 2026
New Revision: 1931334

Log:
Remove another unchecked narrowing cast, this time in the implementation
of the adler32 checksum. Make sure we consume all the data, no matter
how improbably large it may be.


* build/ac-macros/zlib.m4
  (SVN_LIB_Z): Check if we're using a version of zlib that provides the
   function adler32_z(), which uses size_t instead of unsigned int for
   the length of the input data.

* build.conf
  (adler32-test): Define a new unit test program.

* subversion/libsvn_subr/adler32.c: Include svn_private_config.h.
  (svn__adler32_impl, svn__adler32_fn, svn__adler32_size_t,
   SVN__ADLER32_SIZE_MAX): New type and macros to use adler32_z() if
   available and adler32() if not, without too much clutter elsewhere.
  (SVN__ADLER_MOD_BASE): Renamed from ADLER_MOD_BASE and made unsigned.
  (svn__adler32): Consume huge input data sets in SVN__ADLER32_SIZE_MAX
   -sized chunks. Shortcut the result if someone fed us zero-length data.

* subversion/tests/libsvn_subr/adler32-test.c: New file with tests
   that compare our adler32 implementation with the one in zlib.

Added:
   subversion/trunk/subversion/tests/libsvn_subr/adler32-test.c   (contents, 
props changed)
Modified:
   subversion/trunk/build.conf
   subversion/trunk/build/ac-macros/zlib.m4
   subversion/trunk/subversion/libsvn_subr/adler32.c

Modified: subversion/trunk/build.conf
==============================================================================
--- subversion/trunk/build.conf Thu Jan 15 22:21:35 2026        (r1931333)
+++ subversion/trunk/build.conf Thu Jan 15 23:45:34 2026        (r1931334)
@@ -932,6 +932,14 @@ libs = libsvn_test libsvn_wc libsvn_repo
 # ----------------------------------------------------------------------------
 # Tests for libsvn_subr
 
+[adler32-test]
+description = Test our implementation of the adler32 checksum.
+type = exe
+path = subversion/tests/libsvn_subr
+sources = adler32-test.c
+install = test
+libs = libsvn_test libsvn_subr apr
+
 [auth-test]
 description = Test platform-specific auth provider access
 type = exe

Modified: subversion/trunk/build/ac-macros/zlib.m4
==============================================================================
--- subversion/trunk/build/ac-macros/zlib.m4    Thu Jan 15 22:21:35 2026        
(r1931333)
+++ subversion/trunk/build/ac-macros/zlib.m4    Thu Jan 15 23:45:34 2026        
(r1931334)
@@ -73,11 +73,20 @@ AC_DEFUN(SVN_LIB_Z,
       ])
     fi
   fi
- 
+
   if test "$zlib_found" = "no"; then
     AC_MSG_ERROR([subversion requires zlib])
   fi
 
+  dnl Check if this zlib provides adler32_z().
+  save_ldflags="$LDFLAGS"
+  LDFLAGS="$SVN_ZLIB_LIBS"
+  AC_CHECK_FUNC([adler32_z],[
+    AC_DEFINE([SVN_ZLIB_HAS_ADLER32_Z],[1],
+              [Define if the function adler32_z() is available])
+  ],[])
+  LDFLAGS="$save_ldflags"
+
   SVN_DOT_CLANGD([$SVN_ZLIB_INCLUDES])
   AC_SUBST(SVN_ZLIB_INCLUDES)
   AC_SUBST(SVN_ZLIB_LIBS)

Modified: subversion/trunk/subversion/libsvn_subr/adler32.c
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/adler32.c   Thu Jan 15 22:21:35 
2026        (r1931333)
+++ subversion/trunk/subversion/libsvn_subr/adler32.c   Thu Jan 15 23:45:34 
2026        (r1931334)
@@ -25,6 +25,7 @@
 #include <apr.h>
 #include <zlib.h>
 
+#include "svn_private_config.h"
 #include "private/svn_adler32.h"
 
 /**
@@ -34,12 +35,28 @@
  * still provides an extremely low probability of undetected errors"
  */
 
+/* Prefer adler32_z() over adler32(). It should be more efficient, especially
+ * if the length of the data is larger then the size of zlib's uInt. The
+ * current implementation of adler32() in zlib just calls adler32_z() anyway.
+ *
+ * adler32_z() was added in zlib 1.2.9.
+ */
+#if SVN_ZLIB_HAS_ADLER32_Z
+#define svn__adler32_impl adler32_z
+typedef z_size_t svn__adler32_size_t;
+#else
+typedef uInt svn__adler32_size_t;
+#define svn__adler32_impl adler32
+#endif
+#define SVN__ADLER32_SIZE_MAX (~(svn__adler32_size_t)0)
+#define svn__adler32_fn(c,d,s) svn__adler32_impl((c), (const Bytef *)(d), (s))
+
 /*
  * 65521 is the largest prime less than 65536.
  * "That 65521 is prime is important to avoid a possible large class of
  *  two-byte errors that leave the check unchanged."
  */
-#define ADLER_MOD_BASE 65521
+#define SVN__ADLER_MOD_BASE 65521U
 
 /*
  * Start with CHECKSUM and update the checksum by processing a chunk
@@ -48,6 +65,26 @@
 apr_uint32_t
 svn__adler32(apr_uint32_t checksum, const char *data, apr_off_t len)
 {
+  /* Process large amounts of data in max-sized chunks.
+   *
+   * Note: > not >=, then if sizeof(apr_off_t) <= SVN__ADLER32_SIZE_MAX
+   *                 this whole block can be deleted at compile time ...
+   */
+  if (SVN__PREDICT_FALSE(len > SVN__ADLER32_SIZE_MAX))
+    {
+      uLong partial = checksum;
+      /* ... but >= here because we just might get lucky and
+       *     consume all the data in this loop.
+       */
+      while (len >= SVN__ADLER32_SIZE_MAX)
+        {
+          partial = svn__adler32_fn(partial, data, SVN__ADLER32_SIZE_MAX);
+          len -= SVN__ADLER32_SIZE_MAX;
+          data += SVN__ADLER32_SIZE_MAX;
+        }
+      checksum = (apr_uint32_t)partial;
+    }
+
   /* The actual limit can be set somewhat higher but should
    * not be lower because the SIMD code would not be used
    * in that case.
@@ -61,11 +98,10 @@ svn__adler32(apr_uint32_t checksum, cons
        * optimized code. Also, new zlib versions will come with
        * SIMD code for x86 and x64.
        */
-      return (apr_uint32_t)adler32(checksum,
-                                   (const Bytef *)data,
-                                   (uInt)len);
+      return (apr_uint32_t)svn__adler32_fn(checksum, data,
+                                           (svn__adler32_size_t)len);
     }
-  else
+  else if (len > 0)
     {
       const unsigned char *input = (const unsigned char *)data;
       apr_uint32_t s1 = checksum & 0xFFFF;
@@ -96,6 +132,8 @@ svn__adler32(apr_uint32_t checksum, cons
           s2 += s1;
         }
 
-      return ((s2 % ADLER_MOD_BASE) << 16) | (s1 % ADLER_MOD_BASE);
+      return ((s2 % SVN__ADLER_MOD_BASE) << 16) | (s1 % SVN__ADLER_MOD_BASE);
     }
+
+  return checksum;
 }

Added: subversion/trunk/subversion/tests/libsvn_subr/adler32-test.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ subversion/trunk/subversion/tests/libsvn_subr/adler32-test.c        Thu Jan 
15 23:45:34 2026        (r1931334)
@@ -0,0 +1,186 @@
+/*
+ * adler32-test.c:  tests the adler32 implementation.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr.h>
+#include <apr_pools.h>
+#include <apr_time.h>
+
+#include <zlib.h>
+
+#include "private/svn_adler32.h"
+#include "../svn_test.h"
+
+
+/* Allocate a buffer of size LEN from POOL and fill it with pseudo-random
+   data. In fact, the size of the buffer will be rounded up to the next
+   multiple of sizeof(apr_uint32_t). */
+static const char *make_random_data(apr_uint32_t *initial_seed,
+                                    apr_off_t len,
+                                    apr_pool_t *pool)
+{
+  const apr_off_t count = (len /  sizeof(apr_uint32_t)
+                           + (len %  sizeof(apr_uint32_t) ? 1 : 0));
+  apr_uint32_t *data = apr_palloc(pool, len * sizeof(*data));
+  apr_uint32_t seed = *initial_seed = (apr_uint32_t)apr_time_now();
+  apr_off_t i;
+
+  for (i = 0; i < count; ++i)
+    data[i] = svn_test_rand(&seed);
+
+  return (void*)data;
+}
+
+
+static const apr_off_t magic[8] = {
+  0, 1, 79, 80, 81, 5551, 5552, 5553
+};
+
+static const apr_off_t prime[300] = {
+  /* first 100 */
+  2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
+  67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
+  139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
+  223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
+  293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
+  383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461,
+  463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
+
+  /* 901st..1000th */
+  7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
+  7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
+  7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
+  7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
+  7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
+  7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
+  7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
+  7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
+  7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
+  7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
+
+  /* 9901st..9000th */
+  92177, 92179, 92189, 92203, 92219, 92221, 92227, 92233, 92237, 92243,
+  92251, 92269, 92297, 92311, 92317, 92333, 92347, 92353, 92357, 92363,
+  92369, 92377, 92381, 92383, 92387, 92399, 92401, 92413, 92419, 92431,
+  92459, 92461, 92467, 92479, 92489, 92503, 92507, 92551, 92557, 92567,
+  92569, 92581, 92593, 92623, 92627, 92639, 92641, 92647, 92657, 92669,
+  92671, 92681, 92683, 92693, 92699, 92707, 92717, 92723, 92737, 92753,
+  92761, 92767, 92779, 92789, 92791, 92801, 92809, 92821, 92831, 92849,
+  92857, 92861, 92863, 92867, 92893, 92899, 92921, 92927, 92941, 92951,
+  92957, 92959, 92987, 92993, 93001, 93047, 93053, 93059, 93077, 93083,
+  93089, 93097, 93103, 93113, 93131, 93133, 93139, 93151, 93169, 93179
+
+};
+
+static const apr_off_t power2[66] = {
+  /* There'll be some overlap with the not-quite-primes.
+     That's all right, we won't use the same data. */
+#define VALUES_FOR(n) ((n) - 1), (n), ((n) + 1)
+#define _64k (64 * 1024)
+  VALUES_FOR(2),         VALUES_FOR(4),         VALUES_FOR(8),
+  VALUES_FOR(16),        VALUES_FOR(32),        VALUES_FOR(64),
+  VALUES_FOR(128),       VALUES_FOR(256),       VALUES_FOR(512),
+  VALUES_FOR(_64k / 64), VALUES_FOR(_64k / 32), VALUES_FOR(_64k / 16),
+  VALUES_FOR(_64k / 8),  VALUES_FOR(_64k / 4),  VALUES_FOR(_64k / 2),
+  VALUES_FOR(_64k),      VALUES_FOR(_64k * 2),  VALUES_FOR(_64k * 4),
+  VALUES_FOR(_64k * 8),  VALUES_FOR(_64k * 16), VALUES_FOR(_64k * 32),
+  VALUES_FOR(_64k * 64)
+#undef _64k
+#undef VALUES_FOR
+};
+
+
+static svn_error_t *
+do_random_test(apr_uint32_t *seed,
+               const apr_off_t lengths[],
+               const apr_size_t array_size,
+               apr_pool_t *pool)
+{
+
+  const char *data = make_random_data(seed, lengths[array_size - 1], pool);
+  int i;
+
+  for (i = 0; i < array_size; ++i)
+    {
+      apr_uint32_t value_from_svn = svn__adler32(0, data, lengths[i]);
+      uLong value_from_zlib = adler32(0, (const Bytef*)data, (uInt)lengths[i]);
+      SVN_TEST_ASSERT(value_from_svn == value_from_zlib);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+test_random_magic(apr_pool_t *pool)
+{
+  apr_uint32_t seed;
+  svn_error_t *err = do_random_test(&seed, magic,
+                                    sizeof(magic) / sizeof(magic[0]),
+                                    pool);
+  if (err)
+    fprintf(stderr, "SEED: %lu\n", (unsigned long)seed);
+  return err;
+}
+
+static svn_error_t *
+test_random_prime(apr_pool_t *pool)
+{
+  apr_uint32_t seed;
+  svn_error_t *err = do_random_test(&seed, prime,
+                                    sizeof(prime) / sizeof(prime[0]),
+                                    pool);
+  if (err)
+    fprintf(stderr, "SEED: %lu\n", (unsigned long)seed);
+  return err;
+}
+
+static svn_error_t *
+test_random_power2(apr_pool_t *pool)
+{
+  apr_uint32_t seed;
+  svn_error_t *err = do_random_test(&seed, power2,
+                                    sizeof(power2) / sizeof(power2[0]),
+                                    pool);
+  if (err)
+    fprintf(stderr, "SEED: %lu\n", (unsigned long)seed);
+  return err;
+}
+
+
+/* An array of all test functions */
+
+static int max_threads = 4;
+
+static struct svn_test_descriptor_t test_funcs[] =
+  {
+    SVN_TEST_NULL,
+    SVN_TEST_PASS2(test_random_magic,
+                   "adler32 random with magic length"),
+    SVN_TEST_PASS2(test_random_prime,
+                   "adler32 random with prime length"),
+    SVN_TEST_PASS2(test_random_power2,
+                   "adler32 random with 2^n length"),
+    SVN_TEST_NULL
+  };
+
+SVN_TEST_MAIN

Reply via email to