summaryrefslogtreecommitdiff
path: root/io_buffer.c
diff options
context:
space:
mode:
authorKasumi Hanazuki <kasumi@rollingapple.net>2024-11-07 05:55:39 +0900
committerGitHub <noreply@github.com>2024-11-06 20:55:39 +0000
commit35bf6603372360c7680653328274a670fa1d9f38 (patch)
treef6982777da87e3f404b8d45478e80bef97dba3cf /io_buffer.c
parent56ecc243e230e8e99761ec0ffc5116601f094bb0 (diff)
io_buffer.c: Allow copies between overlapping buffers with #copy and #set_string (#11640)
The current implementation of `IO::Buffer#copy` and `#set_string` has an undefined behavior when the source and destination memory overlaps, due to the underlying use of the `memcpy` C function. This patch guarantees the methods to be safe even when copying between overlapping buffers by replacing `memcpy` with `memmove`, Fixes: [Bug #20745]
Notes
Notes: Merged-By: ioquatix <samuel@codeotaku.com>
Diffstat (limited to 'io_buffer.c')
-rw-r--r--io_buffer.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 51ddde8c29..53733c8ff4 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -2328,7 +2328,7 @@ io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values
}
static void
-io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
+io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
{
void *base;
size_t size;
@@ -2340,7 +2340,9 @@ io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_
rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
}
- memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
+ if (length != 0) {
+ memmove((unsigned char*)base+offset, (const unsigned char*)source_base+source_offset, length);
+ }
}
// (offset, length, source_offset) -> length
@@ -2377,7 +2379,7 @@ io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t
length = source_size - source_offset;
}
- io_buffer_memcpy(buffer, offset, source_base, source_offset, source_size, length);
+ io_buffer_memmove(buffer, offset, source_base, source_offset, source_size, length);
return SIZET2NUM(length);
}
@@ -2420,7 +2422,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* copy(source, [offset, [length, [source_offset]]]) -> size
*
* Efficiently copy from a source IO::Buffer into the buffer, at +offset+
- * using +memcpy+. For copying String instances, see #set_string.
+ * using +memmove+. For copying String instances, see #set_string.
*
* buffer = IO::Buffer.new(32)
* # =>
@@ -2469,6 +2471,19 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* buffer = IO::Buffer.new(2)
* buffer.copy(IO::Buffer.for('test'), 0)
* # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
+ *
+ * It is safe to copy between memory regions that overlaps each other.
+ * In such case, the data is copied as if the data was first copied from the source buffer to
+ * a temporary buffer, and then copied from the temporary buffer to the destination buffer.
+ *
+ * buffer = IO::Buffer.new(10)
+ * buffer.set_string("0123456789")
+ * buffer.copy(buffer, 3, 7)
+ * # => 7
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
+ * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
*/
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
@@ -2530,7 +2545,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
* call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
*
* Efficiently copy from a source String into the buffer, at +offset+ using
- * +memcpy+.
+ * +memmove+.
*
* buf = IO::Buffer.new(8)
* # =>