io-event 1.14.5 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/io/event/selector/epoll.c +10 -0
- data/ext/io/event/selector/kqueue.c +10 -0
- data/ext/io/event/selector/selector.c +13 -2
- data/ext/io/event/selector/uring.c +20 -0
- data/lib/io/event/selector/select.rb +45 -4
- data/lib/io/event/version.rb +1 -1
- data/readme.md +4 -4
- data/releases.md +4 -0
- data.tar.gz.sig +2 -2
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9fe7dd3dfb4c546053629b992abb50bbeda10d0149fc5b331c1e930379441478
|
|
4
|
+
data.tar.gz: cb1ba0e99fa681daa746d2c235beec989e2e6cb82d255dcc4ba18b0b85178544
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b14c4daf4e03541e971447c19497a708cb3588a23fa14cd235517bc1932e27005f6eca8a9ffb7f509900b95f75135912ad936b04e6f0bdeaad1cf6e59321853
|
|
7
|
+
data.tar.gz: 827b0313e900636ee96016f905550f2443e522d769a2d1c93b165c0c1896a279a19bb895b094028dd1dd359f8cd29702c20fd4be500f5e3c03cc96e589bef07b
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -615,6 +615,11 @@ VALUE io_read_loop(VALUE _arguments) {
|
|
|
615
615
|
size_t offset = arguments->offset;
|
|
616
616
|
size_t total = 0;
|
|
617
617
|
|
|
618
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
619
|
+
if (offset > size) {
|
|
620
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
621
|
+
}
|
|
622
|
+
|
|
618
623
|
size_t maximum_size = size - offset;
|
|
619
624
|
while (maximum_size) {
|
|
620
625
|
ssize_t result = read(arguments->descriptor, (char*)base+offset, maximum_size);
|
|
@@ -713,6 +718,11 @@ VALUE io_write_loop(VALUE _arguments) {
|
|
|
713
718
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
714
719
|
}
|
|
715
720
|
|
|
721
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
722
|
+
if (offset > size) {
|
|
723
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
724
|
+
}
|
|
725
|
+
|
|
716
726
|
size_t maximum_size = size - offset;
|
|
717
727
|
while (maximum_size) {
|
|
718
728
|
ssize_t result = write(arguments->descriptor, (char*)base+offset, maximum_size);
|
|
@@ -605,6 +605,11 @@ VALUE io_read_loop(VALUE _arguments) {
|
|
|
605
605
|
|
|
606
606
|
if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu)\n", arguments->descriptor, length);
|
|
607
607
|
|
|
608
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
609
|
+
if (offset > size) {
|
|
610
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
611
|
+
}
|
|
612
|
+
|
|
608
613
|
size_t maximum_size = size - offset;
|
|
609
614
|
while (maximum_size) {
|
|
610
615
|
if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld)\n", arguments->descriptor, offset, maximum_size);
|
|
@@ -713,6 +718,11 @@ VALUE io_write_loop(VALUE _arguments) {
|
|
|
713
718
|
|
|
714
719
|
if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu)\n", arguments->descriptor, length);
|
|
715
720
|
|
|
721
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
722
|
+
if (offset > size) {
|
|
723
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
724
|
+
}
|
|
725
|
+
|
|
716
726
|
size_t maximum_size = size - offset;
|
|
717
727
|
while (maximum_size) {
|
|
718
728
|
if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld, length=%zu)\n", arguments->descriptor, offset, maximum_size, length);
|
|
@@ -106,8 +106,19 @@ VALUE IO_Event_Selector_loop_resume(struct IO_Event_Selector *backend, VALUE fib
|
|
|
106
106
|
|
|
107
107
|
VALUE IO_Event_Selector_loop_yield(struct IO_Event_Selector *backend)
|
|
108
108
|
{
|
|
109
|
-
//
|
|
110
|
-
//
|
|
109
|
+
// Under normal operation, a user fiber yields back to the event loop fiber.
|
|
110
|
+
// However, in some cases (e.g. blocking IO called from within the scheduler
|
|
111
|
+
// fiber itself), the current fiber may already be the loop fiber. In that case,
|
|
112
|
+
// transferring to ourselves would be a no-op in Ruby, but it signals a misuse:
|
|
113
|
+
// the event loop fiber should never need to yield to itself, as nothing else
|
|
114
|
+
// would be running to resume it. We return immediately rather than self-transferring.
|
|
115
|
+
if (backend->loop == IO_Event_Fiber_current()) {
|
|
116
|
+
// Uncomment to investigate the callsite that triggers this condition:
|
|
117
|
+
// rb_warning("IO_Event_Selector_loop_yield: current fiber is the loop fiber");
|
|
118
|
+
// rb_funcall(rb_mKernel, rb_intern("puts"), 1, rb_funcall(rb_cThread, rb_intern("current"), 0));
|
|
119
|
+
return Qnil;
|
|
120
|
+
}
|
|
121
|
+
|
|
111
122
|
return IO_Event_Fiber_transfer(backend->loop, 0, NULL);
|
|
112
123
|
}
|
|
113
124
|
|
|
@@ -710,6 +710,11 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
710
710
|
size_t total = 0;
|
|
711
711
|
off_t from = io_seekable(descriptor);
|
|
712
712
|
|
|
713
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
714
|
+
if (offset > size) {
|
|
715
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
716
|
+
}
|
|
717
|
+
|
|
713
718
|
size_t maximum_size = size - offset;
|
|
714
719
|
|
|
715
720
|
// Are we performing a non-blocking read?
|
|
@@ -775,6 +780,11 @@ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
775
780
|
size_t total = 0;
|
|
776
781
|
off_t from = NUM2OFFT(_from);
|
|
777
782
|
|
|
783
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
784
|
+
if (offset > size) {
|
|
785
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
786
|
+
}
|
|
787
|
+
|
|
778
788
|
size_t maximum_size = size - offset;
|
|
779
789
|
while (maximum_size) {
|
|
780
790
|
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -892,6 +902,11 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
892
902
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
893
903
|
}
|
|
894
904
|
|
|
905
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
906
|
+
if (offset > size) {
|
|
907
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
908
|
+
}
|
|
909
|
+
|
|
895
910
|
size_t maximum_size = size - offset;
|
|
896
911
|
while (maximum_size) {
|
|
897
912
|
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -947,6 +962,11 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
947
962
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
948
963
|
}
|
|
949
964
|
|
|
965
|
+
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
966
|
+
if (offset > size) {
|
|
967
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
968
|
+
}
|
|
969
|
+
|
|
950
970
|
size_t maximum_size = size - offset;
|
|
951
971
|
while (maximum_size) {
|
|
952
972
|
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -188,6 +188,11 @@ module IO::Event
|
|
|
188
188
|
# @parameter length [Integer] The minimum number of bytes to read.
|
|
189
189
|
# @parameter offset [Integer] The offset into the buffer to read to.
|
|
190
190
|
def io_read(fiber, io, buffer, length, offset = 0)
|
|
191
|
+
# Ensure offset is within the bounds of the buffer to avoid ArgumentError
|
|
192
|
+
if offset > buffer.size
|
|
193
|
+
return -Errno::EINVAL::Errno
|
|
194
|
+
end
|
|
195
|
+
|
|
191
196
|
total = 0
|
|
192
197
|
|
|
193
198
|
Selector.nonblock(io) do
|
|
@@ -218,6 +223,11 @@ module IO::Event
|
|
|
218
223
|
# @parameter length [Integer] The minimum number of bytes to write.
|
|
219
224
|
# @parameter offset [Integer] The offset into the buffer to write from.
|
|
220
225
|
def io_write(fiber, io, buffer, length, offset = 0)
|
|
226
|
+
# Ensure offset is within the bounds of the buffer to avoid ArgumentError
|
|
227
|
+
if offset > buffer.size
|
|
228
|
+
return -Errno::EINVAL::Errno
|
|
229
|
+
end
|
|
230
|
+
|
|
221
231
|
total = 0
|
|
222
232
|
|
|
223
233
|
Selector.nonblock(io) do
|
|
@@ -278,12 +288,14 @@ module IO::Event
|
|
|
278
288
|
duration = 0
|
|
279
289
|
end
|
|
280
290
|
|
|
291
|
+
closed = nil
|
|
281
292
|
readable = Array.new
|
|
282
293
|
writable = Array.new
|
|
283
294
|
priority = Array.new
|
|
284
295
|
|
|
285
296
|
@waiting.delete_if do |io, waiter|
|
|
286
297
|
if io.closed?
|
|
298
|
+
(closed ||= Array.new) << waiter
|
|
287
299
|
true
|
|
288
300
|
else
|
|
289
301
|
waiter.each do |fiber, events|
|
|
@@ -304,6 +316,14 @@ module IO::Event
|
|
|
304
316
|
end
|
|
305
317
|
end
|
|
306
318
|
|
|
319
|
+
closed&.each do |waiter|
|
|
320
|
+
waiter.each do |fiber, _|
|
|
321
|
+
fiber.raise(IOError, "closed stream") if fiber.alive?
|
|
322
|
+
rescue
|
|
323
|
+
# The fiber didn't handle the exception; it is now terminated.
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
307
327
|
duration = 0 unless @ready.empty?
|
|
308
328
|
error = nil
|
|
309
329
|
|
|
@@ -328,7 +348,26 @@ module IO::Event
|
|
|
328
348
|
end
|
|
329
349
|
|
|
330
350
|
if error
|
|
331
|
-
#
|
|
351
|
+
# `IO.select` can raise both IOError and Errno::EBADF when one of the given IOs is closed. In that case, we enumerate all waiting IOs to find the closed one(s) and raise on their waiters. Then, we return 0 so the event loop retries cleanly.
|
|
352
|
+
if error.is_a?(IOError) || error.is_a?(Errno::EBADF)
|
|
353
|
+
closed = []
|
|
354
|
+
@waiting.delete_if do |io, waiter|
|
|
355
|
+
if io.closed?
|
|
356
|
+
waiter.each{|fiber, _| closed << fiber if fiber.alive?}
|
|
357
|
+
true
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
closed.each do |fiber|
|
|
362
|
+
fiber.raise(IOError, "closed stream")
|
|
363
|
+
rescue
|
|
364
|
+
# The fiber didn't handle the exception; it is now terminated.
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
return 0
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# For all other errors (e.g. thread interrupts), re-queue on the scheduler thread:
|
|
332
371
|
Thread.current.raise(error)
|
|
333
372
|
return 0
|
|
334
373
|
end
|
|
@@ -336,15 +375,17 @@ module IO::Event
|
|
|
336
375
|
ready = Hash.new(0).compare_by_identity
|
|
337
376
|
|
|
338
377
|
readable&.each do |io|
|
|
339
|
-
|
|
378
|
+
# Skip any IO that was closed/reused after IO.select returned - its fd number
|
|
379
|
+
# may now belong to a different file, so resuming the waiter would be wrong:
|
|
380
|
+
ready[io] |= IO::READABLE unless io.closed?
|
|
340
381
|
end
|
|
341
382
|
|
|
342
383
|
writable&.each do |io|
|
|
343
|
-
ready[io] |= IO::WRITABLE
|
|
384
|
+
ready[io] |= IO::WRITABLE unless io.closed?
|
|
344
385
|
end
|
|
345
386
|
|
|
346
387
|
priority&.each do |io|
|
|
347
|
-
ready[io] |= IO::PRIORITY
|
|
388
|
+
ready[io] |= IO::PRIORITY unless io.closed?
|
|
348
389
|
end
|
|
349
390
|
|
|
350
391
|
ready.each do |io, events|
|
data/lib/io/event/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
|
|
|
18
18
|
|
|
19
19
|
Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
|
|
20
20
|
|
|
21
|
+
### v1.15.0
|
|
22
|
+
|
|
23
|
+
- Add bounds checks, in the unlikely event of a user providing an invalid offset that exceeds the buffer size. This prevents potential memory corruption and ensures safe operation when using buffered IO methods.
|
|
24
|
+
|
|
21
25
|
### v1.14.4
|
|
22
26
|
|
|
23
27
|
- Allow `epoll_pwait2` to be disabled via `--disable-epoll_pwait2`.
|
|
@@ -55,10 +59,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
|
|
|
55
59
|
|
|
56
60
|
- Improved `IO::Event::Profiler` for detecting stalls.
|
|
57
61
|
|
|
58
|
-
### v1.8.0
|
|
59
|
-
|
|
60
|
-
- Detecting fibers that are stalling the event loop.
|
|
61
|
-
|
|
62
62
|
## Contributing
|
|
63
63
|
|
|
64
64
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v1.15.0
|
|
4
|
+
|
|
5
|
+
- Add bounds checks, in the unlikely event of a user providing an invalid offset that exceeds the buffer size. This prevents potential memory corruption and ensures safe operation when using buffered IO methods.
|
|
6
|
+
|
|
3
7
|
## v1.14.4
|
|
4
8
|
|
|
5
9
|
- Allow `epoll_pwait2` to be disabled via `--disable-epoll_pwait2`.
|
data.tar.gz.sig
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
�
|
|
1
|
+
!�_�!Մ_M�C���qg�y��[2����|.�_ 6����@�e�ѲC\tK]~z���(��U��tx�Iz@��,ﰛt��/7A&���5�Ų��Z��,��i�q�����>�IŝC��!��uap?Z�Z�fx4z�j,ԿmX%����7I�-N~|*պV��͎��E#P�}�����*Y��'(��ōK�E��ɫen>�S^�b���QȶNw�M�<lDZm������[��+e�����]��C���kl�} �PR�$�_�j�;cC�����$�yt�嫭�&����՚��i-̎4IӔc�h)T� [�d��e�N\�:���Kއ�E3�W�v(پD?�&�1��%�_��y���U�7�
|
|
2
|
+
ptn�nn
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|