From c70162d46dd93974f3b4f16e7af3dff029d2d73d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Italo=20Brand=C3=A3o?= Date: Tue, 17 Mar 2026 22:05:52 -0300 Subject: [PATCH 1/3] Add io_pread and io_pwrite methods to IO_Event_Selector_KQueue --- ext/io/event/selector/kqueue.c | 184 +++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/ext/io/event/selector/kqueue.c b/ext/io/event/selector/kqueue.c index 9cab2c2..7b34d70 100644 --- a/ext/io/event/selector/kqueue.c +++ b/ext/io/event/selector/kqueue.c @@ -789,6 +789,188 @@ static VALUE IO_Event_Selector_KQueue_io_write_compatible(int argc, VALUE *argv, return IO_Event_Selector_KQueue_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset); } +struct io_pread_arguments { + VALUE self; + VALUE fiber; + VALUE io; + + int flags; + + int descriptor; + off_t from; + + VALUE buffer; + size_t length; + size_t offset; +}; + +static +VALUE io_pread_loop(VALUE _arguments) { + struct io_pread_arguments *arguments = (struct io_pread_arguments *)_arguments; + + void *base; + size_t size; + rb_io_buffer_get_bytes_for_writing(arguments->buffer, &base, &size); + + size_t length = arguments->length; + size_t offset = arguments->offset; + off_t from = arguments->from; + size_t total = 0; + + size_t maximum_size = size - offset; + while (maximum_size) { + ssize_t result = pread(arguments->descriptor, (char*)base+offset, maximum_size, from); + + if (result > 0) { + total += result; + offset += result; + from += result; + if ((size_t)result >= length) break; + length -= result; + } else if (result == 0) { + break; + } else if (length > 0 && IO_Event_try_again(errno)) { + IO_Event_Selector_KQueue_io_wait(arguments->self, arguments->fiber, arguments->io, RB_INT2NUM(IO_EVENT_READABLE)); + } else { + return rb_fiber_scheduler_io_result(-1, errno); + } + + maximum_size = size - offset; + } + + return rb_fiber_scheduler_io_result(total, 0); +} + +static +VALUE io_pread_ensure(VALUE _arguments) { + struct io_pread_arguments *arguments = (struct io_pread_arguments *)_arguments; + + IO_Event_Selector_nonblock_restore(arguments->descriptor, arguments->flags); + + return Qnil; +} + +VALUE IO_Event_Selector_KQueue_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) { + struct IO_Event_Selector_KQueue *selector = NULL; + TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector); + + int descriptor = IO_Event_Selector_io_descriptor(io); + + off_t from = NUM2OFFT(_from); + size_t length = NUM2SIZET(_length); + size_t offset = NUM2SIZET(_offset); + + struct io_pread_arguments io_pread_arguments = { + .self = self, + .fiber = fiber, + .io = io, + + .flags = IO_Event_Selector_nonblock_set(descriptor), + .descriptor = descriptor, + .from = from, + .buffer = buffer, + .length = length, + .offset = offset, + }; + + RB_OBJ_WRITTEN(self, Qundef, fiber); + + return rb_ensure(io_pread_loop, (VALUE)&io_pread_arguments, io_pread_ensure, (VALUE)&io_pread_arguments); +} + +struct io_pwrite_arguments { + VALUE self; + VALUE fiber; + VALUE io; + + int flags; + + int descriptor; + off_t from; + + VALUE buffer; + size_t length; + size_t offset; +}; + +static +VALUE io_pwrite_loop(VALUE _arguments) { + struct io_pwrite_arguments *arguments = (struct io_pwrite_arguments *)_arguments; + + const void *base; + size_t size; + rb_io_buffer_get_bytes_for_reading(arguments->buffer, &base, &size); + + size_t length = arguments->length; + size_t offset = arguments->offset; + off_t from = arguments->from; + size_t total = 0; + + if (length > size) { + rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!"); + } + + size_t maximum_size = size - offset; + while (maximum_size) { + ssize_t result = pwrite(arguments->descriptor, (char*)base+offset, maximum_size, from); + + if (result > 0) { + total += result; + offset += result; + from += result; + if ((size_t)result >= length) break; + length -= result; + } else if (result == 0) { + break; + } else if (length > 0 && IO_Event_try_again(errno)) { + IO_Event_Selector_KQueue_io_wait(arguments->self, arguments->fiber, arguments->io, RB_INT2NUM(IO_EVENT_WRITABLE)); + } else { + return rb_fiber_scheduler_io_result(-1, errno); + } + + maximum_size = size - offset; + } + + return rb_fiber_scheduler_io_result(total, 0); +} + +static +VALUE io_pwrite_ensure(VALUE _arguments) { + struct io_pwrite_arguments *arguments = (struct io_pwrite_arguments *)_arguments; + + IO_Event_Selector_nonblock_restore(arguments->descriptor, arguments->flags); + + return Qnil; +} + +VALUE IO_Event_Selector_KQueue_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) { + struct IO_Event_Selector_KQueue *selector = NULL; + TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector); + + int descriptor = IO_Event_Selector_io_descriptor(io); + + off_t from = NUM2OFFT(_from); + size_t length = NUM2SIZET(_length); + size_t offset = NUM2SIZET(_offset); + + struct io_pwrite_arguments io_pwrite_arguments = { + .self = self, + .fiber = fiber, + .io = io, + + .flags = IO_Event_Selector_nonblock_set(descriptor), + .descriptor = descriptor, + .from = from, + .buffer = buffer, + .length = length, + .offset = offset, + }; + + RB_OBJ_WRITTEN(self, Qundef, fiber); + + return rb_ensure(io_pwrite_loop, (VALUE)&io_pwrite_arguments, io_pwrite_ensure, (VALUE)&io_pwrite_arguments); +} + #endif static @@ -1094,6 +1276,8 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) { #ifdef HAVE_RUBY_IO_BUFFER_H rb_define_method(IO_Event_Selector_KQueue, "io_read", IO_Event_Selector_KQueue_io_read_compatible, -1); rb_define_method(IO_Event_Selector_KQueue, "io_write", IO_Event_Selector_KQueue_io_write_compatible, -1); + rb_define_method(IO_Event_Selector_KQueue, "io_pread", IO_Event_Selector_KQueue_io_pread, 6); + rb_define_method(IO_Event_Selector_KQueue, "io_pwrite", IO_Event_Selector_KQueue_io_pwrite, 6); #endif rb_define_method(IO_Event_Selector_KQueue, "process_wait", IO_Event_Selector_KQueue_process_wait, 3); From 596e472a72cea144a629942978b5a6c19c54863c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Italo=20Brand=C3=A3o?= Date: Tue, 17 Mar 2026 22:10:14 -0300 Subject: [PATCH 2/3] Refactor io_pread and io_pwrite methods in IO_Event_Selector_KQueue for improved readability and performance --- ext/io/event/selector/kqueue.c | 138 +++++---------------------------- 1 file changed, 21 insertions(+), 117 deletions(-) diff --git a/ext/io/event/selector/kqueue.c b/ext/io/event/selector/kqueue.c index 7b34d70..42eb3ab 100644 --- a/ext/io/event/selector/kqueue.c +++ b/ext/io/event/selector/kqueue.c @@ -789,37 +789,26 @@ static VALUE IO_Event_Selector_KQueue_io_write_compatible(int argc, VALUE *argv, return IO_Event_Selector_KQueue_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset); } -struct io_pread_arguments { - VALUE self; - VALUE fiber; - VALUE io; - - int flags; - - int descriptor; - off_t from; - - VALUE buffer; - size_t length; - size_t offset; -}; +VALUE IO_Event_Selector_KQueue_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) { + struct IO_Event_Selector_KQueue *selector = NULL; + TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector); -static -VALUE io_pread_loop(VALUE _arguments) { - struct io_pread_arguments *arguments = (struct io_pread_arguments *)_arguments; + int descriptor = IO_Event_Selector_io_descriptor(io); void *base; size_t size; - rb_io_buffer_get_bytes_for_writing(arguments->buffer, &base, &size); + rb_io_buffer_get_bytes_for_writing(buffer, &base, &size); - size_t length = arguments->length; - size_t offset = arguments->offset; - off_t from = arguments->from; + off_t from = NUM2OFFT(_from); + size_t length = NUM2SIZET(_length); + size_t offset = NUM2SIZET(_offset); size_t total = 0; + RB_OBJ_WRITTEN(self, Qundef, fiber); + size_t maximum_size = size - offset; while (maximum_size) { - ssize_t result = pread(arguments->descriptor, (char*)base+offset, maximum_size, from); + ssize_t result = pread(descriptor, (char*)base+offset, maximum_size, from); if (result > 0) { total += result; @@ -830,7 +819,7 @@ VALUE io_pread_loop(VALUE _arguments) { } else if (result == 0) { break; } else if (length > 0 && IO_Event_try_again(errno)) { - IO_Event_Selector_KQueue_io_wait(arguments->self, arguments->fiber, arguments->io, RB_INT2NUM(IO_EVENT_READABLE)); + IO_Event_Selector_KQueue_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_READABLE)); } else { return rb_fiber_scheduler_io_result(-1, errno); } @@ -841,78 +830,30 @@ VALUE io_pread_loop(VALUE _arguments) { return rb_fiber_scheduler_io_result(total, 0); } -static -VALUE io_pread_ensure(VALUE _arguments) { - struct io_pread_arguments *arguments = (struct io_pread_arguments *)_arguments; - - IO_Event_Selector_nonblock_restore(arguments->descriptor, arguments->flags); - - return Qnil; -} - -VALUE IO_Event_Selector_KQueue_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) { +VALUE IO_Event_Selector_KQueue_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) { struct IO_Event_Selector_KQueue *selector = NULL; TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector); int descriptor = IO_Event_Selector_io_descriptor(io); - off_t from = NUM2OFFT(_from); - size_t length = NUM2SIZET(_length); - size_t offset = NUM2SIZET(_offset); - - struct io_pread_arguments io_pread_arguments = { - .self = self, - .fiber = fiber, - .io = io, - - .flags = IO_Event_Selector_nonblock_set(descriptor), - .descriptor = descriptor, - .from = from, - .buffer = buffer, - .length = length, - .offset = offset, - }; - - RB_OBJ_WRITTEN(self, Qundef, fiber); - - return rb_ensure(io_pread_loop, (VALUE)&io_pread_arguments, io_pread_ensure, (VALUE)&io_pread_arguments); -} - -struct io_pwrite_arguments { - VALUE self; - VALUE fiber; - VALUE io; - - int flags; - - int descriptor; - off_t from; - - VALUE buffer; - size_t length; - size_t offset; -}; - -static -VALUE io_pwrite_loop(VALUE _arguments) { - struct io_pwrite_arguments *arguments = (struct io_pwrite_arguments *)_arguments; - const void *base; size_t size; - rb_io_buffer_get_bytes_for_reading(arguments->buffer, &base, &size); + rb_io_buffer_get_bytes_for_reading(buffer, &base, &size); - size_t length = arguments->length; - size_t offset = arguments->offset; - off_t from = arguments->from; + off_t from = NUM2OFFT(_from); + size_t length = NUM2SIZET(_length); + size_t offset = NUM2SIZET(_offset); size_t total = 0; if (length > size) { rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!"); } + RB_OBJ_WRITTEN(self, Qundef, fiber); + size_t maximum_size = size - offset; while (maximum_size) { - ssize_t result = pwrite(arguments->descriptor, (char*)base+offset, maximum_size, from); + ssize_t result = pwrite(descriptor, (char*)base+offset, maximum_size, from); if (result > 0) { total += result; @@ -923,7 +864,7 @@ VALUE io_pwrite_loop(VALUE _arguments) { } else if (result == 0) { break; } else if (length > 0 && IO_Event_try_again(errno)) { - IO_Event_Selector_KQueue_io_wait(arguments->self, arguments->fiber, arguments->io, RB_INT2NUM(IO_EVENT_WRITABLE)); + IO_Event_Selector_KQueue_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_WRITABLE)); } else { return rb_fiber_scheduler_io_result(-1, errno); } @@ -934,43 +875,6 @@ VALUE io_pwrite_loop(VALUE _arguments) { return rb_fiber_scheduler_io_result(total, 0); } -static -VALUE io_pwrite_ensure(VALUE _arguments) { - struct io_pwrite_arguments *arguments = (struct io_pwrite_arguments *)_arguments; - - IO_Event_Selector_nonblock_restore(arguments->descriptor, arguments->flags); - - return Qnil; -} - -VALUE IO_Event_Selector_KQueue_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) { - struct IO_Event_Selector_KQueue *selector = NULL; - TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector); - - int descriptor = IO_Event_Selector_io_descriptor(io); - - off_t from = NUM2OFFT(_from); - size_t length = NUM2SIZET(_length); - size_t offset = NUM2SIZET(_offset); - - struct io_pwrite_arguments io_pwrite_arguments = { - .self = self, - .fiber = fiber, - .io = io, - - .flags = IO_Event_Selector_nonblock_set(descriptor), - .descriptor = descriptor, - .from = from, - .buffer = buffer, - .length = length, - .offset = offset, - }; - - RB_OBJ_WRITTEN(self, Qundef, fiber); - - return rb_ensure(io_pwrite_loop, (VALUE)&io_pwrite_arguments, io_pwrite_ensure, (VALUE)&io_pwrite_arguments); -} - #endif static From 1473996ecc080dd1270bf7bb36f6097a3aadd957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Italo=20Brand=C3=A3o?= Date: Fri, 27 Mar 2026 09:04:45 -0300 Subject: [PATCH 3/3] Add offset bounds checks to io_pread and io_pwrite to prevent size_t underflow --- ext/io/event/selector/kqueue.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ext/io/event/selector/kqueue.c b/ext/io/event/selector/kqueue.c index 42eb3ab..4b49ebb 100644 --- a/ext/io/event/selector/kqueue.c +++ b/ext/io/event/selector/kqueue.c @@ -804,6 +804,14 @@ VALUE IO_Event_Selector_KQueue_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE size_t offset = NUM2SIZET(_offset); size_t total = 0; + if (offset > size) { + return rb_fiber_scheduler_io_result(-1, EINVAL); + } + + if (offset == size || length == 0) { + return rb_fiber_scheduler_io_result(0, 0); + } + RB_OBJ_WRITTEN(self, Qundef, fiber); size_t maximum_size = size - offset; @@ -849,6 +857,14 @@ VALUE IO_Event_Selector_KQueue_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALU rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!"); } + if (offset > size) { + return rb_fiber_scheduler_io_result(-1, EINVAL); + } + + if (offset == size || length == 0) { + return rb_fiber_scheduler_io_result(0, 0); + } + RB_OBJ_WRITTEN(self, Qundef, fiber); size_t maximum_size = size - offset;