Commit Graph

18188 Commits

Author SHA1 Message Date
Yukihiro "Matz" Matsumoto 7feba44aa3 mruby-bigint: add scratch buffer to D&C to_s for reduced allocations
Add dc_to_s_scratch_t structure to preallocate work buffers for the
base case of divide-and-conquer decimal string conversion. This
eliminates repeated malloc/free calls in the inner loop where digits
are extracted 9 at a time.

Previously, each iteration of the base case loop allocated and freed
a quotient buffer. Now the same two buffers are reused with pointer
swapping, reducing allocation overhead by ~10-18% for large numbers.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-13 12:47:45 +09:00
Yukihiro "Matz" Matsumoto b479f97458 mruby-bigint: fix heap-buffer-overflow in bitwise OR/XOR early returns
When mpz_or or mpz_xor copies an operand when the other is zero,
the copied mpz_t may have an inflated sz field (larger than actual
allocated limbs). Add trim() after mpz_set to normalize the size.

This is a follow-up fix to commit 61aa2234d8 which addressed the
same issue in shift operations.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-13 12:47:45 +09:00
Yukihiro "Matz" Matsumoto 97835e5678 Merge pull request #6700 from khasinski/fix-pack-float-endianness 2026-01-13 12:35:04 +09:00
Yukihiro "Matz" Matsumoto 61aa2234d8 mruby-bigint: add missing trim in shift functions
Add trim() calls after mpz_set/mpz_move in shift operations where
actual bit manipulation is skipped:

- mpz_mul_2exp when e==0 (no shift needed)
- mpz_mul_2exp when bs==0 (limb-only shift)
- mpz_div_2exp when e==0 (no shift needed)
- mpz_div_2exp when bs==0 (limb-only shift)

This prevents inflated sz values from propagating through operations,
complementing the earlier fix to urshift/ulshift when n==0.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 22:27:04 +09:00
Yukihiro "Matz" Matsumoto f2f385f572 mruby-bigint: fix missing trim in urshift/ulshift when n==0
When shift amount is 0, urshift() and ulshift() called mpz_set() which
copies data without trimming leading zero limbs. This caused bigint
values to have inflated sz fields, making ucmp() comparisons incorrect.

For example, a 256-bit remainder from division could have sz=18 instead
of sz=8 because the divisor had 18 limbs. This made it compare greater
than values with fewer limbs, even when numerically smaller.

The bug also caused memory leaks when the incorrect comparison led to
taking wrong code paths in division, triggering size overflow exceptions
after memory was allocated.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 21:04:13 +09:00
Yukihiro "Matz" Matsumoto 78fe8a0476 mruby-rational: fix infinite recursion with bigint comparison
rational_eq_b was using wrong struct fields (p1->numerator/denominator
which access i.num/i.den) for bigint-backed rationals that use b.num/b.den.
Also added missing MRB_TT_BIGINT case to prevent fallthrough to default
case which caused ping-pong recursion between Rational#== and Integer#==.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 19:23:15 +09:00
Yukihiro "Matz" Matsumoto 26a1064d99 mruby-bigint: fix memory leak in mpz_mul_all_ones
Add MRB_TRY/MRB_CATCH to ensure local mpz_t variables are freed when
an exception (e.g., RangeError from shift overflow) occurs during
the all-ones multiplication optimization.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 17:57:22 +09:00
Yukihiro "Matz" Matsumoto 9471b132c4 mruby-bigint: fix potential memory leak in mpz_to_s_dc
Use stack allocation with zero-initialization and MRB_TRY/MRB_CATCH
to ensure heap-allocated mpz_t data is freed even when an exception
occurs during conversion.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 16:42:59 +09:00
Chris Hasiński 946e8c2464 Fix float/double pack/unpack on big-endian architectures
The pack_float, pack_double, unpack_float, and unpack_double functions
accessed float/double bytes via a union with uint8_t array, assuming
bytes[0] is always the LSB. This is only true on little-endian hosts.

Fix by using the same bit-shift approach as the integer pack functions
(pack_quad, unpack_quad, etc). Reinterpret float/double as uint32/uint64
and use shifts to extract/assemble bytes in an endian-independent way.

Fixes: #6698 (s390x test failures)
2026-01-12 03:00:53 +01:00
Yukihiro "Matz" Matsumoto a225aaa185 numeric.c: fix bigint comparison precision loss
when comparing bigint values with <=> operator, the comparison would
convert both operands to float, losing precision for values > 2^53.
this caused incorrect results like (10^20+1) <=> (10^20+2) returning 0
instead of -1.

add direct bigint comparison paths in cmpnum() to avoid float conversion
when both operands can be handled by mrb_bint_cmp().

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 09:21:32 +09:00
Yukihiro "Matz" Matsumoto ece641c56f mruby-bigint: optimize to_s with 10^k = 2^k * 5^k factorization
use the mathematical identity 10^k = 2^k * 5^k to speed up the
divide-and-conquer decimal string conversion. dividing by 5^k
is faster than dividing by 10^k because 5^k has ~30% fewer bits
(log2(5) ≈ 2.32 vs log2(10) ≈ 3.32). the 2^k component is handled
with fast bit shifts.

benchmarks show 3-8% improvement for large numbers:
- 800K bits: 1.00s -> 0.97s
- 1.6M bits: 3.95s -> 3.84s
- 2.4M bits: 8.79s -> 8.46s

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 09:21:32 +09:00
Yukihiro "Matz" Matsumoto de3c1a1317 mruby-bigint: use lookup table for digit pair conversion in to_s
Apply Lemire's small table technique: use a 200-byte lookup table to
convert digit pairs (00-99) instead of computing each digit separately.
Reduces operations from 9 to 5 per 9-digit batch in the base case.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 09:21:29 +09:00
Yukihiro "Matz" Matsumoto 951a5753af mruby-bigint: rename mpz_to_s_dc_rec to mpz_to_s_dc_recur
Follow the codebase convention of using _recur suffix for recursive
functions (e.g., codedump_recur, dump_recur).

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 11:58:08 +09:00
Yukihiro "Matz" Matsumoto 48d5678f2a mruby-bigint: optimize to_s base case with batch digit extraction
Extract 9 decimal digits at once by dividing by 10^9 instead of 10.
This reduces the number of divisions in the base case by 9x, improving
performance of large bigint to_s conversion by approximately 2x.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 09:58:47 +09:00
Yukihiro "Matz" Matsumoto 990ff90fb4 mruby-bigint: add divide-and-conquer optimization for to_s
For base-10 conversion of numbers with >1000 digits, use a recursive
divide-and-conquer algorithm that splits the number using precomputed
powers of 10. This reduces complexity from O(n^2) to O(n log^2 n).

The algorithm:
1. Precompute 10^1, 10^2, 10^4, 10^8, ... by repeated squaring
2. Find the largest power that splits digits roughly in half
3. Divide by this power to get high and low parts
4. Recursively convert each part, padding low part with zeros
5. Base case: use simple divide-by-10 for <= 1000 digits

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 08:50:06 +09:00
Yukihiro "Matz" Matsumoto 9d04c74ed8 mruby-bigint: fix carry placement in uadd()
The final carry was stored at z->p[y->sz], but when x is larger
than y, this index falls within the already-computed result and
corrupts it. Store at z->p[i] instead, which correctly points to
max(x->sz, y->sz) after all loops complete.

This bug caused incorrect results when adding a small number to
an all-ones number with 1124+ limbs (35968+ bits).

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 08:30:18 +09:00
Yukihiro "Matz" Matsumoto 512fffdac8 mruby-bigint: fix division bug with non-standard qhat refinement
The udiv function had two buggy modifications to Knuth's Algorithm D:
1. A "three-limb pre-adjustment" that only decremented qhat once
2. A "3-limb refinement" loop with incorrect carry handling

These caused incorrect quotients for certain decimal divisions like
10^52 / 10^26. Restored standard Knuth Algorithm D which uses only
2-limb qhat refinement with correction via subtract and add-back.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 22:09:51 +09:00
Yukihiro "Matz" Matsumoto 1f590521b0 mruby-bigint: add sparse number optimization for multiplication
Numbers with few bits set (popcount <= 8) are multiplied using
shift-add instead of Karatsuba. This is O(k*n) where k is the
popcount, much faster than O(n^1.585) for sparse patterns like
2^100000 + 2^50000 commonly generated by fuzzers.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 17:14:44 +09:00
Yukihiro "Matz" Matsumoto 3e49b5187a mruby-bigint: add squaring optimization for internal multiplication
Add optimized squaring algorithm that exploits symmetry for ~1.5x speedup
over general multiplication. Includes both schoolbook and Karatsuba variants.

- mpz_sqr_basic_limbs: O(n(n+1)/2) multiplications instead of O(n^2)
- mpz_sqr_karatsuba: 3 recursive squarings instead of 3 multiplications
- mpz_sqr: high-level wrapper with fast paths for power-of-2 and all-ones

The optimization triggers when mpz_mul is called with identical pointers
(u == v), which occurs in internal operations like mpz_pow.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 15:17:36 +09:00
Yukihiro "Matz" Matsumoto ef64ca32a1 mruby-bigint: optimize multiplication for power-of-2 numbers
Add fast path for multiplying by powers of 2 (2^n). Uses left shift
instead of Karatsuba multiplication: x * 2^n = x << n.

This optimizes "mostly-zero" patterns common in fuzzing tests, where
numbers like 2^2097150 (single bit set) would otherwise trigger slow
Karatsuba multiplication.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 12:29:50 +09:00
Yukihiro "Matz" Matsumoto b7593cde15 mruby-bigint: optimize multiplication for all-ones numbers
Add fast path for multiplying numbers of form 2^n - 1 (all bits set).

Uses algebraic identities:
- (2^n - 1) * (2^m - 1) = 2^(n+m) - 2^n - 2^m + 1
- (2^n - 1) * y = (y << n) - y

These are O(n) operations instead of O(n^1.585) for Karatsuba.
Fuzzing test cases using all-ones patterns now complete in 0.01s
instead of 13+ seconds.

Also raises KARATSUBA_THRESHOLD from 8 to 32 for ~32% speedup
on general large number multiplication.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 12:25:22 +09:00
Yukihiro "Matz" Matsumoto df778e09d2 mruby-bigint: raise Karatsuba threshold from 8 to 32
Reduces recursion overhead for large number multiplication.
Benchmarks show ~32% speedup for million-bit operands.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 11:36:38 +09:00
Yukihiro "Matz" Matsumoto 0a5ec60e5c mruby-bigint: fix OOM by limiting left shift size in mpz_mul_2exp
Add MRB_BIGINT_BIT_LIMIT (1 billion bits / 128MB) to prevent
unreasonably large allocations when left-shifting by huge amounts.
Raises RangeError instead of attempting multi-GB allocations.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 08:33:58 +09:00
Yukihiro "Matz" Matsumoto 1768b0c6eb Merge pull request #6697 from mruby/dependabot/github_actions/actions/cache-5 2026-01-09 08:23:27 +09:00
Yukihiro "Matz" Matsumoto a6b7f3b018 mruby-bigint: fix memory leak by trimming bitwise operation results
mpz_and, mpz_or, and mpz_xor were not calling trim() on their results,
causing inflated size values with trailing zero limbs. This led to
incorrect comparisons in ucmp() and caused udiv() to take wrong code
paths, resulting in memory leaks when exceptions occurred.

Also added defensive overflow checks in mpz_init_heap and udiv.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 08:18:15 +09:00
dependabot[bot] 5dbce935d8 build(deps): bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-08 14:59:52 +00:00
Yukihiro "Matz" Matsumoto c46f9e0793 mruby-compiler: fix uninitialized memory in realloc_pool_str()
when converting a shared/static string (IREP_TT_SSTR) to heap-allocated
(IREP_TT_STR), copy the original content to the new buffer.

previously, the original content was lost when allocating new memory,
leaving the first bytes uninitialized. this caused find_pool_str() to
read uninitialized memory via memcmp() when searching for duplicate
strings.

reported by OSS-Fuzz.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-08 12:58:22 +09:00
Yukihiro "Matz" Matsumoto ad5301970f Merge pull request #6694 from katafrakt/mirb-cosmopolitan 2026-01-08 12:58:09 +09:00
Yukihiro "Matz" Matsumoto 9a48049109 Merge pull request #6696 from khasinski/fix-required-kwarg-parsing 2026-01-08 11:02:18 +09:00
Yukihiro "Matz" Matsumoto 7bc1c44c3b Merge pull request #6695 from jbampton/add-dependabot-cooldown 2026-01-08 08:56:43 +09:00
Chris Hasiński 1e932dd161 Fix parse error with required kwargs and omitted parens
When defining a method with a required keyword argument without
parentheses, mruby incorrectly parsed the next line as the default
value:

    def foo arg:
      123
    end

Was parsed as: def foo(arg: 123); end  (optional kwarg, empty body)
Should be:     def foo(arg:); 123; end (required kwarg, body returns 123)

The fix sets EXPR_ARG lexer state after parsing f_label, making
newlines significant. This prevents the parser from consuming
expressions across line boundaries as default values for keyword
arguments.

Also fixes a pre-existing bug in f_label where tNUMPARAM (type <num>)
was implicitly assigned to $$ (type <id>) without conversion. Now
explicitly uses intern_numparam() to convert numbered parameters to
symbols.

Fixes https://github.com/mruby/mruby/issues/6268
2026-01-08 00:50:55 +01:00
Yukihiro "Matz" Matsumoto 7fe5c2e260 gc.c: rename mrb_alloca() to mrb_temp_alloc() and fix memory leaks
rename mrb_alloca() to mrb_temp_alloc() for clearer naming - the new name
better describes its purpose as GC-managed temporary allocation. keep
mrb_alloca() as a macro alias for backward compatibility.

apply mrb_temp_alloc() to fix potential memory leaks in:
- mruby-strftime: if mrb_str_cat() raises, allocated buffers now cleaned by GC
- mruby-io File.readlink: if mrb_str_new() raises, buffer now cleaned by GC

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-08 08:23:51 +09:00
Yukihiro "Matz" Matsumoto c9e3af60e1 mruby-set: fix memory leak caused by recursive hash computation
when a Set contains itself (directly or indirectly), computing its hash
would cause infinite recursion leading to SystemStackError. the exception
during khash rebuild leaked memory.

add recursion detection flag to Set#hash that returns 0 for recursive
references, similar to Ruby's behavior.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-08 08:23:43 +09:00
John Bampton 6d5de7b3c1 [CI] Dependabot: add a cooldown period for new releases
Enforces security best practices by requiring a minimum age for new dependency releases before they are automatically updated by Dependabot.

This practice, known as a "cooldown period," helps mitigate supply chain attacks by allowing time for frequently published malicious packages to be identified.

https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference#cooldown-
2026-01-08 01:02:15 +10:00
Yukihiro "Matz" Matsumoto 0e42c95df2 mruby-bigint: add exponent size check in mrb_bint_pow
prevent resource exhaustion when computing power with extremely large
exponents (e.g., 81.pow(51742871469327219)). the check estimates the
result size and raises RangeError if it would exceed 1 million bits.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-06 07:51:07 +09:00
Yukihiro "Matz" Matsumoto bbcadd6bf9 mruby-rational: fix left shift overflow in rational_new_f
Shifting 1 left by MRB_INT_BIT-1 (e.g., 63 on 64-bit) bits into the sign
bit is undefined behavior. Change the overflow check from >= MRB_INT_BIT
to >= MRB_INT_BIT-1 to prevent this.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 17:47:57 +09:00
Yukihiro "Matz" Matsumoto d5c7a906f9 mruby-time: fix integer overflow in time_mktime
When year value is close to MRB_INT_MIN, subtracting TM_YEAR_BASE (1900)
causes signed integer overflow. Add underflow check before the subtraction.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 12:35:09 +09:00
Paweł Świątkowski 1881a904a4 Add Cosmopolitan build to CI 2026-01-05 01:04:21 +01:00
Yukihiro "Matz" Matsumoto 5a1123ed22 mruby-io: remove unused flock function
The local flock() function for Windows is now dead code since the
HAL refactoring. The Windows implementation is in hal-win-io which
provides mrb_hal_io_flock().

Fixes warning: 'flock' defined but not used [-Wunused-function]

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 08:55:53 +09:00
Paweł Świątkowski f856cf811f Require sys/socket.h (for Cosmopolitan)
Compilation of mirb with Cosmopolitan fails because of missing include
(Cosmopolitan seems to be more strict than "traditional" compilers.
2026-01-04 19:52:11 +01:00
Yukihiro "Matz" Matsumoto ee06bbb417 vm.c: replace type assertions with runtime checks
Replace mrb_assert with mrb_ensure_*_type for VM opcodes that require
specific types:

- OP_ARYCAT: mrb_ensure_array_type
- OP_ARYPUSH: mrb_ensure_array_type
- OP_ASET: mrb_ensure_array_type (also fixed: was checking wrong register)
- OP_INTERN: mrb_ensure_string_type
- OP_HASHCAT: mrb_ensure_hash_type

These checks catch codegen bugs with clear error messages in both
debug and release builds.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 15:16:03 +09:00
Yukihiro "Matz" Matsumoto 6b482ee3f8 vm.c: add runtime type check for OP_STRCAT
Replace mrb_assert with mrb_ensure_string_type to catch codegen bugs
even in release builds. This prevents null-dereference crashes when
OP_STRCAT receives a non-string first operand due to compiler bugs.

Consistent with OP_HASH which uses mrb_ensure_hash_type for similar
type safety.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 14:09:57 +09:00
Yukihiro "Matz" Matsumoto 2e4a8e8edd mruby-compiler: fix sp tracking in pattern match failure path
After pattern matching code generation, the sp (stack pointer) must
be restored to match the success path value. The failure path (after
RAISEIF) left sp in a different state, causing incorrect register
allocation in subsequent code like string interpolation.

This caused OP_STRCAT to use the wrong register, leading to
null-dereference when trying to modify a non-string value as a string.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 09:12:35 +09:00
Yukihiro "Matz" Matsumoto 099d2c4771 array.c: fix heap-use-after-free in insertion_sort
The key variable in insertion_sort temporarily holds an array element
that's been removed from its slot during the sorting process. When
sort_cmp yields to a block that triggers GC, key wasn't protected
and could be collected.

Use arena save/restore around the loop to avoid arena overflow for
large arrays.

Test case from oss-fuzz: sort! with block containing rescue.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-03 20:41:47 +09:00
Yukihiro "Matz" Matsumoto af3f9b65f1 mruby-compiler: fix sp imbalance in pattern matching with rescue
The => pattern matching codegen was doing push() after RAISEIF, even though
RAISEIF never returns. This caused sp to be off by 1 when success and failure
paths joined, resulting in wrong register allocation for subsequent operations.

For string interpolation like "#{ expr => pattern rescue body }", the base
string would be at R2 but STRCAT would incorrectly use R3, causing memory
corruption and crashes.

Test case: %{#{.=>.,. rescue def .()end}} (from oss-fuzz)

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-03 20:21:44 +09:00
Yukihiro "Matz" Matsumoto a9825e92df mruby-rational: fix crash in rational_new_f with negative exponent
rational_new_b() expects both arguments to be bigints, but rational_new_f()
was passing an integer value for the numerator when the exponent was negative.
This caused a segfault in mrb_bint_reduce() which called RBIGINT() on the
integer value.

Test case: 5r**-92 (from oss-fuzz)

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-03 13:13:02 +09:00
Yukihiro "Matz" Matsumoto 53a25bab14 mruby-sleep, hal-posix-socket: fix amalgamation compatibility
mruby-sleep: declare slp_tm before #ifdef _WIN32 block to fix
undeclared variable error in non-Windows branch.

hal-posix-socket: use #if defined(HAVE_SA_LEN) && HAVE_SA_LEN instead
of #ifdef HAVE_SA_LEN, since mruby-socket defines HAVE_SA_LEN to 0 on
non-BSD platforms.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 18:28:31 +09:00
Yukihiro "Matz" Matsumoto 64f1436323 amalgamation.md: add mruby-rational/complex, clarify gem defines
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 18:04:39 +09:00
Yukihiro "Matz" Matsumoto f78ac530c8 amalgam.rb: support gems with core-affecting defines
Gems like mruby-task add preprocessor defines (MRB_USE_TASK_SCHEDULER)
that affect mrb_state structure. The amalgamation generator now detects
these defines from the build configuration and adds them at the top of
mruby.h before struct definitions are encountered.

Supported define patterns: MRB_USE_*, MRB_UTF8_*, HAVE_MRUBY_*

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 17:35:31 +09:00
Yukihiro "Matz" Matsumoto 07cd188264 README.md: update document index
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 17:02:12 +09:00