Commit Graph

18756 Commits

Author SHA1 Message Date
Yukihiro "Matz" Matsumoto 91b60802db mruby-regexp: cap character class count and free named_captures
Two related bugs uncovered by OSS-Fuzz testcase 6692915710853120:

1. add_class allowed unbounded growth of c->classes. Class IDs are
   stored in re_inst.a (uint8_t), so any ID >= 256 silently aliases
   another class via the cast at emit sites. Worse, c->class_capa
   (uint16_t) overflows on doubling past 32768 -> 0, then
   mrb_realloc(..., 0) returns NULL, and the next memset(&c->classes[id])
   segfaults at NULL+offset. Cap with RE_MAX_CLASSES = 256 (the encoding
   limit) and raise via compile_error past that.

2. Once the crash is fixed, the testcase exposes a leak of
   c->named_captures: compile_error frees c->code, c->classes, and
   c->stripped (commit 3f321f09bc) but missed named_captures. Add it
   to the same cleanup block.

Reported by OSS-Fuzz (clusterfuzz testcase 6692915710853120).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-08 10:32:03 +09:00
Yukihiro "Matz" Matsumoto 28624ecfd8 mruby-regexp: cap {n}/{n,m} quantifiers to prevent overflow
parse_quantifier read digits via min = min * 10 + d with no upper
bound, allowing patterns like /a{1111558833}/ to overflow int and
trigger signed-integer-overflow UB. Even without UB, the value
flows into compile_quantified's emit loop where it would attempt
to emit a billion copies of the atom.

Add RE_MAX_REPEAT = 32768 (the largest value that still fits in
re_inst.offset, the uint16_t jump field) and reject quantifiers
beyond that during parsing via compile_error. Apply the same cap
to the max field.

Reported by OSS-Fuzz (clusterfuzz testcase 6152367367323648).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-07 16:25:12 +09:00
Yukihiro "Matz" Matsumoto f267925d45 mruby-socket: translate Winsock errors to errno on Windows
Winsock APIs (socket, bind, connect, accept, recv, send, ...) report
errors via WSAGetLastError() and do not set errno, so mrb_sys_fail
on Windows was reading a stale or zero errno. Result: every socket
failure raised SystemCallError with errno 0 ("Success") instead of
the appropriate Errno::* class.

Add mrb_hal_socket_set_errno_from_last_error() to the HAL:
- POSIX: no-op (failed calls already set errno)
- Windows: maps WSAGetLastError() to a POSIX errno via wsa_to_errno()
  with 32 cases covering the common Winsock error codes; unmapped
  codes fall back to EIO. Each case is #ifdef-guarded against older
  MSVC CRTs that lack a particular Exxx.

In src/socket.c, route the 22 socket-API failure sites through a new
sock_sys_fail() helper that calls the HAL translator before
mrb_sys_fail. Also fix mrb_hal_socket_set_nonblock() on Windows,
which was returning -1 without setting errno after ioctlsocket
failure.

POSIX behavior unchanged (verified: TCPSocket connect refused ->
Errno::ECONNREFUSED, bind to privileged port -> Errno::EACCES, bad
sockopt -> Errno::EOPNOTSUPP).

Closes #6819, reported by Asmod4n.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-07 11:01:46 +09:00
Yukihiro "Matz" Matsumoto ada9ca7236 Merge pull request #6818 from mruby/dependabot/github_actions/github-actions-dependencies-7e7af3d814
build(deps): bump j178/prek-action from 2.0.2 to 2.0.3 in the github-actions-dependencies group
2026-05-07 08:54:57 +09:00
dependabot[bot] 428c9b5b03 build(deps): bump j178/prek-action
Bumps the github-actions-dependencies group with 1 update: [j178/prek-action](https://github.com/j178/prek-action).


Updates `j178/prek-action` from 2.0.2 to 2.0.3
- [Release notes](https://github.com/j178/prek-action/releases)
- [Commits](https://github.com/j178/prek-action/compare/cbc2f23eb5539cf20d82d1aabd0d0ecbcc56f4e3...6ad80277337ad479fe43bd70701c3f7f8aa74db3)

---
updated-dependencies:
- dependency-name: j178/prek-action
  dependency-version: 2.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-06 15:03:04 +00:00
Yukihiro "Matz" Matsumoto c6836f494a fp_uscale.c: fix uninitialized *fp when exponent is malformed
mrb_read_float jumped past the *fp assignment via `goto done` when
the exponent had no digits (e.g., "5e", "5e+"). It returned TRUE
without setting *fp, leaving the caller (mrb_str_to_dbl etc.) to
return whatever was on the stack. MSan flagged this; on most runs
the uninitialized read happens to yield 0.0, so the bug is silently
incorrect rather than crashing.

Refactor the finalization (compute res from d, final_p, sign, etc.)
to run once after the optional-exponent block. The malformed-exponent
case now falls through using the mantissa-only `final_p = trunc - dp`,
producing the same result strtod gives for the same input ("5e" -> 5.0
with endp at 'e'). Float("5e") still raises because mrb_str_len_to_dbl
rejects trailing characters under badcheck.

Reported by OSS-Fuzz (MSan).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-06 09:34:41 +09:00
Yukihiro "Matz" Matsumoto 3cc60d31c6 error.h, array.h: support MRB_NAN_BOXING on 32-bit
On 32-bit platforms whose ABI gives 8-byte members 8-byte alignment
(xtensa, ARM, MIPS, PowerPC, ...), MRB_NAN_BOXING failed to build with
"RVALUE size must be within 5 words" because two structs got padded
past the budget:

- struct RBreak: had an existing MRB_USE_RBREAK_VALUE_UNION workaround
  that stores the value as uint32_t[] to avoid forcing 8-byte alignment
  on the struct, but the gate only enabled it for MRB_NO_BOXING.
  Extend to NAN_BOXING + 32-bit, with a NAN_BOXING-specific get/set
  (no separate tt to stash since nan-boxing encodes type in the bits).

- struct RArray: MRB_ARY_NO_EMBED was similarly gated to NO_BOXING;
  embedded mrb_value[] forces 8-byte alignment of the inner union and
  pads the heap-form layout. Extend the gate to NAN_BOXING + 32-bit.

Both gates now name the structural property (32-bit + mrb_value has an
8-byte aligned member) rather than enumerating boxing modes, so adding
new boxing modes won't silently miss this class of bug again.

i386's System V ABI gives uint64_t only 4-byte alignment, hiding the
problem on x86 -m32; -malign-double simulates the strict-alignment ABI
that exhibits the failure, and is what was used to verify the fix.

Closes #6815, reported by dearblue.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-06 07:56:25 +09:00
Yukihiro "Matz" Matsumoto 90ead4bdaf Merge pull request #6816 from dearblue/array-ext
Rename the members of the `mrb_combination_state` structure
2026-05-05 23:24:08 +09:00
dearblue a8c841433f Rename the members of the mrb_combination_state structure
Since these are expressed as "nPk" or "nCk" in mathematics, rename `n` to `k` and `array_size` to `n`.
Additionally, rename the parameters `#__repeated_combination` and `#__combination_init` from `n` to `k`.

However, the parameter `n` in `#repeated_permutation` and `#repeated_combination` remains unchanged to align with CRuby.
2026-05-05 22:25:26 +09:00
Yukihiro "Matz" Matsumoto 3f321f09bc mruby-regexp: fix leak and UAF on compile error paths
compile_error is the chokepoint for all regex-compile errors;
mrb_raisef longjmps out of re_compile, abandoning the stack-local
re_compiler struct. Three connected bugs:

1. Memory leak: c->code and c->classes (grown by emit/add_class
   via mrb_realloc) were never freed before raising, leaking on
   any compile error like /[/. c->stripped was already cleaned up
   here for the same reason; the other two buffers were missed.

2. Use-after-free: c->src aliases c->stripped when RE_FLAG_EXTENDED
   is set, but the original code freed c->stripped before passing
   c->src to mrb_raisef's "%s" formatter. Format the message into
   an mrb_value first (mruby's GC-managed string survives the
   longjmp), then free, then raise.

3. Heap-buffer-overflow: strip_extended returns a non-NUL-terminated
   buffer of size len. Even with format-before-free, "%s" called
   strlen and read past the buffer end. Use mruby's %l directive
   which takes an explicit (char*, size_t) and avoids strlen.

Reported by OSS-Fuzz (clusterfuzz testcase 5394267353972736).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-05 09:31:38 +09:00
Yukihiro "Matz" Matsumoto ddcbd2dc90 fp_uscale.c: fix shift and clz UB in tiny-float formatting
Two UBSan issues exposed by sprintf("%f", 1e-7) and similar:

1. uscale() shifted hi by c.s without bounding c.s, hitting UB
   when c.s >= 64. The mask line had `c.s & 63`, but the actual
   `hi >> c.s` line did not, so the partial guard was incomplete.
   On x86 the hardware silently masks the shift, producing wrong
   output ("1844674407370.955078" for 1e-7) instead of crashing.
   When c.s >= 64 the value rounds to 0 with sticky=1, so we can
   bail early.

2. count_digits(0) called bits_len64(0) -> clz64(0), which is UB.
   The only other bits_len64 caller already guards d == 0; align
   count_digits with that pattern. Returning 1 (since "0" is one
   digit) preserves output formatting.

Reported by OSS-Fuzz (clusterfuzz testcase 5210395240628224).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-04 07:53:00 +09:00
Yukihiro "Matz" Matsumoto 479af5c1bd mruby-regexp: bounds-check non-ASCII RE_CHAR in first_set_walk
The first-byte bitmap (bm[16]) is intentionally ASCII-only
(include/re_internal.h:75 documents it as 128 bits / ASCII), and
the matcher at re_exec.c:39 short-circuits for bytes >= 128. But
first_set_walk's RE_CHAR case wrote bm[a >> 3] without checking
a, overflowing the 16-byte stack buffer for any pattern
containing a byte >= 128.

When a >= 128, return FALSE so compute_first_set marks the filter
unusable, matching the bail-out pattern already used for RE_NCLASS
and RE_ANY. The pattern still compiles and matches; only the
first-byte optimization is skipped.

Reported by OSS-Fuzz (clusterfuzz testcase 4909069193510912).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-03 22:43:22 +09:00
Yukihiro "Matz" Matsumoto c24d01c6fa Merge pull request #6776 from dearblue/array-combination.3
Return nil if a number less than 1 is passed to `Array#__combination_init`
2026-05-02 23:41:04 +09:00
Yukihiro "Matz" Matsumoto a18467da3f Merge pull request #6814 from dearblue/mruby-bin-mrb
Don't use `#puts` in bintest for "mruby-bin-mrb"
2026-05-02 23:37:26 +09:00
dearblue 525ab7a800 Return nil if a number less than 1 is passed to Array#__combination_init
This simplifies the subsequent processing.
2026-05-02 22:16:56 +09:00
dearblue a98c6b62ea Don't use #puts in bintest for "mruby-bin-mrb"
Since `Kernel#puts` is undefined in unit tests, using `Kernel#print` is required.
Previously, for example, running `rake test` with the following build configuration caused the tests to fail.

```ruby
MRuby::Build.new do
  toolchain
  enable_debug
  enable_test
  enable_bintest
  gem core: "mruby-bin-mrb"
end
```
2026-05-02 20:52:00 +09:00
Yukihiro "Matz" Matsumoto 7dfd560df8 mruby-io: cap puts recursion depth to prevent C stack overflow
io_puts_ary recursed unconditionally on nested arrays. For cyclic
arrays (a = []; a << a; puts a) or pathologically deep arrays,
this caused a C stack overflow.

Add a depth cap (IO_PUTS_MAX_DEPTH = 16); on overflow, write
"[...]\n" and return, matching CRuby's behavior on cycles. The
pattern mirrors mruby-set's MAX_NESTED_DEPTH for the same problem
shape (pure C recursion not dispatched as a Ruby method).

Reported by OSS-Fuzz (clusterfuzz testcase 6233530857488384).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-02 11:27:01 +09:00
Yukihiro "Matz" Matsumoto 8a73faf61e vm.c: refresh ci after mrb_const_set in OP_SETCONST
mrb_const_set may invoke const_added via mrb_funcall_argv, which
re-enters the VM and can reallocate cibase. This invalidates the
local ci pointer (and thus the regs macro that expands to ci->stack),
causing a use-after-free on the next opcode dispatch.

OP_SETMCNST and the OP_GET* / OP_*IDX opcodes already follow this
pattern; align OP_SETCONST with them.

Reported by OSS-Fuzz (clusterfuzz testcase 5886006653157376).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-02 10:42:41 +09:00
Yukihiro "Matz" Matsumoto adb27caaa2 Merge pull request #6813 from mruby/dependabot/pre_commit/pre-commit-hooks-4c01ccd002
build(deps): bump https://github.com/oxipng/oxipng from v10.1.0 to 10.1.1 in the pre-commit-hooks group
2026-05-01 11:01:42 +09:00
dependabot[bot] 7579a70974 build(deps): bump https://github.com/oxipng/oxipng
Bumps the pre-commit-hooks group with 1 update: [https://github.com/oxipng/oxipng](https://github.com/oxipng/oxipng).


Updates `https://github.com/oxipng/oxipng` from v10.1.0 to 10.1.1
- [Release notes](https://github.com/oxipng/oxipng/releases)
- [Changelog](https://github.com/oxipng/oxipng/blob/master/CHANGELOG.md)
- [Commits](https://github.com/oxipng/oxipng/compare/v10.1.0...v10.1.1)

---
updated-dependencies:
- dependency-name: https://github.com/oxipng/oxipng
  dependency-version: 10.1.1
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-30 14:59:05 +00:00
Yukihiro "Matz" Matsumoto 7e73392fc1 Merge pull request #6812 from DavidKorczynski/oss-fuzz-ext
Add new fuzzing harness to be consumed by OSS-Fuzz
2026-04-30 08:55:34 +09:00
David Korczynski 923c2e6a73 Add new fuzzing harness to be consumed by OSS-Fuzz
Adds 6 new fuzzing harnesses to be consumed by OSS-Fuzz. Have confirmed
locally this results in significant coverage gains relative to the
current code coverage in OSS-Fuzz:
https://storage.googleapis.com/oss-fuzz-coverage/mruby/reports/20260427/linux/src/report.html

Signed-off-by: David Korczynski <david@adalogics.com>
2026-04-29 06:49:03 -07:00
Yukihiro "Matz" Matsumoto 7c30c2f62b Merge pull request #6811 from mruby/dependabot/bundler/bundler-dependencies-59f8ea4567 2026-04-29 06:49:35 +09:00
dependabot[bot] fb41438cec build(deps): bump yard
Bumps the bundler-dependencies group with 1 update in the / directory: [yard](https://yardoc.org).


Updates `yard` from 0.9.42 to 0.9.43

---
updated-dependencies:
- dependency-name: yard
  dependency-version: 0.9.43
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-28 14:53:43 +00:00
Yukihiro "Matz" Matsumoto e4cd7e6daf mruby-compiler: place newline before else
The mruby C style places `else` on its own line. Reformat the
remaining `} else if (...)` occurrence in the C action block of
NODE_SYMBOLS dump.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-28 12:56:25 +09:00
Yukihiro "Matz" Matsumoto 16f1f4418a mruby-bigint: place newline before else
The mruby C style places `else` on its own line. Reformat the
remaining `} else {` occurrences.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-28 12:56:25 +09:00
Yukihiro "Matz" Matsumoto d6556195ef boxing_nan.h, boxing_word.h: place newline before else
The mruby C style places `else` on its own line. Reformat the
remaining `} else {` / `} else if (...)` occurrences.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-28 12:56:24 +09:00
Yukihiro "Matz" Matsumoto 1702b89c25 Merge pull request #6787 from dearblue/sprintf 2026-04-28 12:56:07 +09:00
Yukihiro "Matz" Matsumoto 06d236d535 mruby-bin-config: register installer in products instead of bins
`build.bins` is for compiled binaries with bare names; entries are
fed through `exefile()` in `tasks/bin.rake`, which appends
`exts.executable` (e.g. `.exe`) when no extension is present.

When `ENV['OS']` is not `Windows_NT` but the toolchain has
`exts.executable=".exe"` (e.g. cross builds, or environments where
`OS` is unset under visualcpp), `mruby-bin-config` ended up with a
file task at `.../mruby-config` while rake asked for
`.../mruby-config.exe`, aborting with "Don't know how to build task".

Skip the `bins` path and call `build.define_installer` directly with
`mruby_config_path`, mirroring the existing `iscross` branch. The
file task path now always matches the script content's extension
(`.bat` on Windows, no extension elsewhere).

close #6807, reported by UENO, M. (@eunos-1128).

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-25 19:57:44 +09:00
Yukihiro "Matz" Matsumoto baf4145fa1 Merge pull request #6806 from mruby/dependabot/bundler/bundler-dependencies-55d95e1de2 2026-04-24 14:07:17 +09:00
Yukihiro "Matz" Matsumoto b70d160273 Revert "gha: add hosts file workaround for Windows localhost resolution"
This reverts commit eed32b752b.

Windows's Winsock getaddrinfo does not consult the hosts file for
"localhost" because an internal hard-coded rule short-circuits first,
so the Add-Content step had no effect on the failing tests. Remove
the ineffective workaround now that a skip guard replaces it.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 08:19:24 +09:00
Yukihiro "Matz" Matsumoto 00e2c2fb29 Revert "gha: start DNS Client service and probe Windows localhost resolution"
This reverts commit 7ae25febbb.

CI runs with this change showed Dnscache was already Running on all
Windows jobs and Resolve-DnsName resolved "localhost" successfully,
yet Winsock getaddrinfo still failed. The service start and probe
had no effect on the failing tests, so they add noise without value.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 08:19:15 +09:00
Yukihiro "Matz" Matsumoto 550be1445f Revert "mruby-socket: add temporary getaddrinfo diagnostic for Windows CI"
This reverts commit a69d12aa54.

The diagnostic served its purpose: it isolated the failure to
Winsock's getaddrinfo for the "localhost" hostname. The permanent
fix (skip guard) is now in place, so remove the temporary probe.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 08:19:04 +09:00
Yukihiro "Matz" Matsumoto 57ca531f76 mruby-socket: skip localhost-dependent addrinfo tests on Windows
Addrinfo.getaddrinfo("localhost", ...) and Addrinfo.foreach("localhost",
...) crash on GitHub Actions Windows runners (Windows Server 2022 and
2025, both mingw-gcc and MSVC). Diagnostic showed 127.0.0.1 as a
numeric literal resolves fine via Winsock getaddrinfo, but "localhost"
as a hostname returns WSAHOST_NOT_FOUND under every address family
(AF_INET, AF_INET6, AF_UNSPEC, nil). PowerShell Resolve-DnsName on
the same runner succeeds, so the failure is Winsock-specific rather
than OS-level. Earlier workarounds (Dnscache service start, hosts
file append) had no effect.

Skip both tests on Windows with the existing SocketTest.win? guard,
matching the pattern already used for Addrinfo.unix and
Addrinfo#afamily. Linux and macOS coverage is unaffected.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 08:18:41 +09:00
Yukihiro "Matz" Matsumoto 7ae25febbb gha: start DNS Client service and probe Windows localhost resolution
Addrinfo.getaddrinfo on the Windows CI runners fails for "localhost"
regardless of address family (diagnostic commit a69d12aa54 showed
127.0.0.1 literal resolves fine while "localhost" never does under
AF_INET, AF_INET6, AF_UNSPEC, or nil). That rules out the earlier
KB4057932/AF_INET hypothesis and points at the Dnscache service
being stopped on the runner image.

Start Dnscache if not already running (and set it to Automatic),
then Resolve-DnsName localhost to print the OS-level resolver view
to the log. Existing hosts file and diagnostic assert stay in place
so the single CI round exposes both the service state and its
effect on getaddrinfo.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 01:39:08 +09:00
Yukihiro "Matz" Matsumoto a69d12aa54 mruby-socket: add temporary getaddrinfo diagnostic for Windows CI
Addrinfo.getaddrinfo("localhost", 53, AF_INET, SOCK_STREAM) crashes
on GitHub Actions Windows runners with WSAHOST_NOT_FOUND, while the
same call works on Linux and macOS. Earlier hypotheses (winsock link
missing; hosts file not populated) have been ruled out on CI.

Probe five variants in one run to isolate the failing condition:
numeric literal 127.0.0.1, localhost with AF_UNSPEC, localhost with
nil family, localhost with AF_INET6, and the original AF_INET path.
Results go to stdout via puts so they are visible in CI logs. This
commit is temporary and will be reverted once the real fix lands.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 01:20:15 +09:00
Yukihiro "Matz" Matsumoto eed32b752b gha: add hosts file workaround for Windows localhost resolution
GitHub Actions Windows Server 2022/2025 runner images ship with the
localhost entries commented out in C:\Windows\System32\drivers\etc\hosts
and rely on the DNS Client service's built-in rule. That path is subject
to Microsoft KB4057932 (getaddrinfo fails with WSAHOST_NOT_FOUND after
an AF_INET6 negative cache), which makes Addrinfo.getaddrinfo("localhost",
53, AF_INET, ...) in mrbgems/mruby-socket/test/addrinfo.rb flaky on CI.

Prepend explicit 127.0.0.1/::1 localhost entries to the hosts file on
all three Windows CI jobs (two mingw-gcc matrix entries and Windows-VC)
before running the build, so the resolver avoids the DNS negative cache
path and returns the expected IPv4 address.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 00:34:37 +09:00
dependabot[bot] e3dadc083c build(deps): bump rake in the bundler-dependencies group
Bumps the bundler-dependencies group with 1 update: [rake](https://github.com/ruby/rake).


Updates `rake` from 13.4.1 to 13.4.2
- [Release notes](https://github.com/ruby/rake/releases)
- [Changelog](https://github.com/ruby/rake/blob/master/History.rdoc)
- [Commits](https://github.com/ruby/rake/compare/v13.4.1...v13.4.2)

---
updated-dependencies:
- dependency-name: rake
  dependency-version: 13.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-23 14:58:23 +00:00
Yukihiro "Matz" Matsumoto 2ff6563484 mruby-task: restore Windows link library dropped during HAL migration
Commit be6413f0d86a ("mruby-task: migrate HAL to ports/ directories")
moved the Windows HAL source into mruby-task/ports/win/ but dropped
the linker.libraries declaration that previously lived in
hal-win-task/mrbgem.rake. Both mingw and MSVC builds now fail to link
task_hal.o/obj with undefined references to timeBeginPeriod,
timeEndPeriod, timeSetEvent, and timeKillEvent. Re-declare winmm
under a for_windows? guard.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:16:21 +09:00
Yukihiro "Matz" Matsumoto 3f27eac308 mruby-io: restore Windows link library dropped during HAL migration
Commit 9965f11cfe1c ("mruby-io: migrate HAL to ports/ directories")
moved the Windows HAL source into mruby-io/ports/win/ but dropped the
linker.libraries declaration that previously lived in
hal-win-io/mrbgem.rake. Both mingw and MSVC builds now fail to link
with undefined references to closesocket, WSAStartup, WSACleanup,
select, and WSAGetLastError referenced from io.c and io_hal.c.
Re-declare ws2_32 under a for_windows? guard.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:16:11 +09:00
Yukihiro "Matz" Matsumoto 22df8d61be mruby-socket: restore Windows link libraries dropped during HAL migration
Commit d9e107c801 ("mruby-socket: migrate HAL to ports/ directories")
moved the Windows HAL source into mruby-socket/ports/win/ but dropped
the linker.libraries declarations that previously lived in
hal-win-socket/mrbgem.rake. Both mingw and MSVC builds now fail to
link with undefined references to WSAStartup, socket, send, and other
Winsock APIs. Re-declare wsock32 and ws2_32 under a for_windows? guard.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:16:02 +09:00
Yukihiro "Matz" Matsumoto ad8fc7d918 mruby-bigint: add multi-precision gcd tests
Cover zero operands, power-of-2 fast path, negative operands,
balanced multi-limb pairs with a shared Fibonacci factor, highly
unbalanced pairs (to exercise the Euclidean fallback), and
Fibonacci neighbors (always coprime). Declare a test dependency
on mruby-numeric-ext since Integer#gcd is defined there.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 22:56:44 +09:00
Yukihiro "Matz" Matsumoto 8e91554c6d mruby-bigint: rewrite mpz_gcd main loop as binary Stein algorithm
Replace the classical Euclidean main loop (mpz_mod per iteration)
with Stein's binary GCD: subtract + factor out trailing 2s on
odd-maintained operands. Keep an mpz_mod fallback for heavily
unbalanced pairs (the smaller operand has at least two fewer limbs)
where one long division replaces many Stein subtracts.

The old loop allocated a temporary mpz_t every iteration to hold the
mod result; the Stein loop is allocation-free thanks to the in-place
paths in mpz_sub and mpz_div_2exp. This matches the "memory first"
priority and also happens to be faster on typical inputs because
mpz_mod's setup cost dominates when the quotient is small (the
classical Fibonacci-neighbor worst case).

Also add a small mpz_swap helper used by the new loop.

Benchmark (bin/mruby benchmark/bm_bigint_gcd.rb, median of 3):

  case                       before    after    ratio
  single-limb                 42 ms    50 ms    1.19  (fast-path
                                                      unchanged;
                                                      noise)
  fib(200) vs fib(201)        82 ms    23 ms    0.28
  balanced ~700-bit shared    46 ms    32 ms    0.70
  unbalanced big vs small     35 ms    28 ms    0.80
  power-of-2 path             38 ms    42 ms    1.11  (fast-path
                                                      unchanged;
                                                      noise)
  balanced ~2800-bit shared   19 ms    15 ms    0.79

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 22:52:05 +09:00
Yukihiro "Matz" Matsumoto ec55e0d0f1 mruby-bigint: correct mpz_gcd comment to describe hybrid behavior
The preceding comment claimed "Binary GCD (Stein's algorithm)",
but the multi-limb main loop is classical Euclidean using mpz_mod.
Only the prelude (factoring out common 2s, single-limb and
power-of-2 fast paths) is Stein-flavored. Describe what the code
actually does so readers are not misled.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 22:36:18 +09:00
Yukihiro "Matz" Matsumoto 7e8d74793c doc/guides/rom-method-table.md: sync with new MRB_MT_ENTRY shape
Per gemini-code-assist review on #6790: the documentation still
showed the pre-#6790 union mrb_mt_ptr member order (proc first)
and the C99 designated-initializer form of MRB_MT_ENTRY.  Update
both to match the merged code, and add a note explaining why func
must come first in the union.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto 81bcd4e931 mruby-bigint: simplify MPZ_CTX_INIT with positional initializer
Per gemini-code-assist review on #6791: replace member-by-member
assignment with C89/C++98-style positional aggregate initialization,
keeping the macro a single declaration and matching the existing
pool_storage initializer style in the same macro.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto bbf7c43c37 array.c: restore arena around heap sort per-call protections
heapify() and heap_delete_root() call mrb_gc_protect() to guard a
C-local mrb_value across potential GC points inside sort_cmp().
They were missing the matching mrb_gc_arena_save/restore pair, so
each call leaked one arena slot.  For O(n log n) heapify calls under
MRB_GC_FIXED_ARENA this overflows the 100-slot arena and raises
NoMemoryError, e.g. during Array#repeated_permutation tests.

Wrap each function body with mrb_gc_arena_save/restore, matching
the pattern already used in insertion_sort() above.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto 449040400a mruby-regexp: keep MatchData source/regexp GC-reachable
The mrb_match_data struct stores `source` and `regexp` as plain
mrb_value members of a C-allocated struct, which the GC does not
scan.  Under MRB_GC_STRESS the source string could be collected
while the MatchData was still alive, causing md[0] to read freed
memory (observed as "\xff\xff\xff").

Also stash source and regexp as instance variables on the MatchData
object so they remain reachable via the object's iv_tbl during GC.
The C struct members continue to provide fast direct access, and
no other call sites need to change.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto eb5480a6b3 hash.c: fix float_hash_code() for zero values
Remove early return of hardcoded 0 for zero floats. Instead, normalize
-0.0 to +0.0 and pass through mrb_byte_hash() for better distribution.
The previous second condition (f == -0.0) was dead code since IEEE 754
-0.0 == 0.0 is true.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto b3777110ab hash.c, string.c: improve hash function quality
Use FNV-1a (xor-then-multiply) instead of FNV-1 for better avalanche
in byte hashing. Strengthen the hash finalizer in mrb_obj_hash_code()
with multiply-xorshift to improve distribution for integer and symbol
keys with power-of-two table sizes.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:38 +09:00