Skip to content

Conversation

@trinistr
Copy link
Contributor

@trinistr trinistr commented Nov 20, 2025

Main issues that are fixed:

  • Documentation was very outdated to the point that it was straight-up wrong.
  • Buffer's size didn't account for offset, leading to crashes on something like
    buffer = IO::Buffer.map(File.open("data.txt"), nil, IO::Buffer::PAGE_SIZE, IO::Buffer::READONLY)
    data = buffer.get_string

Other issues were about not checking arguments enough, leading to a proliferation of Errno::EINVAL with generic messages:

  • Empty file and/or size of 0 was allowed.
    • This is a breaking change, as this worked on Windows. This seems entirely useless, however, as nothing can be done with such a buffer, as far as I'm aware.
  • Size and offset could be larger than the file.
  • Offset was not checked for negative values.

I've also added a bunch of tests. Note, however, that there are no tests for successfully using offset, as it's impossible to reliably use (see https://bugs.ruby-lang.org/issues/21700).

@trinistr trinistr changed the title Fix many bugs in IO::Buffer.map and update its documentation Fix multiple bugs in IO::Buffer.map and update its documentation Nov 20, 2025
@trinistr
Copy link
Contributor Author

trinistr commented Nov 20, 2025

The test failure doesn't seem connected with my changes in any way and didn't happen before fixing style.

  1. Failure:
    TestTimeExtension#test_rfc2822_nonlinear [/Users/runner/work/ruby/ruby/src/test/test_time.rb:67]:
    [ArgumentError] exception expected, not #<Timeout::Error: [5000]: in 0.19219318140835537s (tmin: 1.1790999999983232e-05, tmax: 5.9792000000002954e-05, tbase: 0.0038438636281671076)>.


rb_off_t file_size = rb_file_size(io);
// Compiler can confirm that we handled file_size <= 0 case:
if (UNLIKELY(file_size <= 0)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (UNLIKELY(file_size <= 0)) {
if (UNLIKELY(file_size < 0)) {

In wonder if we should allow mapping zero-sized files? Not sure what the point would be, but is it technically invalid?

rb_raise(rb_eArgError, "Invalid negative or zero file size!");
}
// Here, we assume that file_size is positive:
else if (UNLIKELY((uintmax_t)file_size > SIZE_MAX)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it only matters if the mapped size is going to be too big (bigger than SIZE_MAX).

Also, I believe RB_NUM2SIZE will already raise if out of range, e.g.:

unsigned long
rb_big2ulong(VALUE x)
{
    unsigned long num = big2ulong(x, "unsigned long");

    if (BIGNUM_POSITIVE_P(x)) {
        return num;
    }
    else {
        if (num <= 1+(unsigned long)(-(LONG_MIN+1)))
            return -(long)(num-1)-1;
    }
    rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
}

- Buffer's size did not account for offset when mapping the file, leading to possible crashes.
- Size and offset were not checked properly, leading to many situations raising Errno errors with generic messages.
- Documentation was wrong.
Copy link
Member

@ioquatix ioquatix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly agree with this change. I may make some follow up refinements once this is merged.

@ioquatix ioquatix enabled auto-merge (squash) November 21, 2025 00:14
@ioquatix ioquatix merged commit aa9e15c into ruby:master Nov 21, 2025
87 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants