~ chicken-core (master) da64a64bf375aec52c61752dc9ef11adb0f0c2fa
commit da64a64bf375aec52c61752dc9ef11adb0f0c2fa
Author: Peter Bex <peter@more-magic.net>
AuthorDate: Tue Sep 1 12:00:36 2026 +0200
Commit: felix <felix@call-with-current-continuation.org>
CommitDate: Tue Sep 1 12:13:53 2026 +0200
Fix out of bounds read in bit->boolean
Because we store only the sign and absolute value in a bignum's
digits, a negative bignum would need to get its bit-pattern negated as
per twos complement before checking whether a particular bit is set.
After calculating the digit index to read from the output, we would
negate the bignum and pass that as the size (so it can stop early if
we're not interested in higher bits). The size needs to be big enough
to accomodate that digit, so it needs to be one higher than the digit
index.
Reported by crzcrz
Signed-off-by: felix <felix@call-with-current-continuation.org>
diff --git a/NEWS b/NEWS
index e652310f..ebcfb6bc 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,8 @@
- The procedures in "(scheme write)" respect any print-length limits
imposed by the REPL.
- Fixed a bug in "read-string" when reading from a binary port.
+ - Fixed a bug in bit->boolean when given negative bignums which would give
+ incorrect output or crash due to out of bounds read (reported by crzcrz)
- Syntax expander
- Fixed bug in handling of "export-all" library declaration which caused
diff --git a/runtime.c b/runtime.c
index 1a2d6715..00fb7c2f 100644
--- a/runtime.c
+++ b/runtime.c
@@ -6588,8 +6588,7 @@ C_regparm C_word C_i_bit_to_bool(C_word n, C_word i)
d = i / C_BIGNUM_DIGIT_LENGTH;
if (d >= C_bignum_size(n)) return C_mk_bool(C_bignum_negativep(n));
- /* TODO: this isn't necessary, is it? */
- if (C_truep(nn = maybe_negate_bignum_for_bitwise_op(n, d))) n = nn;
+ if (C_truep(nn = maybe_negate_bignum_for_bitwise_op(n, d+1))) n = nn;
i %= C_BIGNUM_DIGIT_LENGTH;
d = C_mk_bool((C_bignum_digits(n)[d] & (C_uword)1 << i) != 0);
diff --git a/tests/numbers-test.scm b/tests/numbers-test.scm
index 2898f2fb..f1d95386 100644
--- a/tests/numbers-test.scm
+++ b/tests/numbers-test.scm
@@ -952,7 +952,15 @@
(test-equal (bit->boolean -2 0) #f)
(test-equal (bit->boolean -2 1) #t)
(test-equal (bit->boolean (expt -2 63) 256) #t)
+ (test-equal (bit->boolean (expt -2 65) 256) #t)
+ (test-equal (bit->boolean (expt -2 65) 1) #f)
+ (test-equal (bit->boolean (expt -2 65) 0) #f)
+ (test-equal (bit->boolean (expt -2 65) 65) #t)
(test-equal (bit->boolean (expt 2 63) 256) #f)
+ (test-equal (bit->boolean (expt 2 65) 256) #f)
+ (test-equal (bit->boolean (expt 2 65) 65) #t)
+ (test-equal (bit->boolean (expt 2 65) 1) #f)
+ (test-equal (bit->boolean (expt 2 65) 0) #f)
(test-equal (arithmetic-shift 15 2) 60)
(test-equal (arithmetic-shift 15 -2) 3)
(test-equal (arithmetic-shift -15 2) -60)
Trap