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
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.