Skip to content

Commit 1de3b80

Browse files
committed
cipher: make output buffer String independent
OpenSSL::Cipher#update accepts a String as the second argument to be used as the output buffer. The buffer must be directly writable, in other words, it must not be frozen and not a shared string. rb_str_resize() does not make the String independent if the String already has the intended length. Use the rb_str_modify() family instead to check it. Fixes: https://bugs.ruby-lang.org/issues/20937
1 parent f9ec66f commit 1de3b80

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

ext/openssl/ossl_cipher.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,10 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
409409
str = rb_str_new(0, out_len);
410410
} else {
411411
StringValue(str);
412-
rb_str_resize(str, out_len);
412+
if ((long)rb_str_capacity(str) >= out_len)
413+
rb_str_modify(str);
414+
else
415+
rb_str_modify_expand(str, out_len - RSTRING_LEN(str));
413416
}
414417

415418
if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))

test/openssl/test_cipher.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,30 @@ def test_ctr_if_exists
134134
assert_equal pt, cipher.update(ct) << cipher.final
135135
end
136136

137+
def test_update_with_buffer
138+
cipher = OpenSSL::Cipher.new("aes-128-ecb").encrypt
139+
cipher.random_key
140+
expected = cipher.update("data") << cipher.final
141+
assert_equal 16, expected.bytesize
142+
143+
# Buffer is supplied
144+
cipher.reset
145+
buf = String.new
146+
assert_same buf, cipher.update("data", buf)
147+
assert_equal expected, buf + cipher.final
148+
149+
# Buffer is frozen
150+
cipher.reset
151+
assert_raise(FrozenError) { cipher.update("data", String.new.freeze) }
152+
153+
# Buffer is a shared string [ruby-core:120141] [Bug #20937]
154+
cipher.reset
155+
buf = "x" * 1024
156+
shared = buf[-("data".bytesize + 32)..-1]
157+
assert_same shared, cipher.update("data", shared)
158+
assert_equal expected, shared + cipher.final
159+
end
160+
137161
def test_ciphers
138162
ciphers = OpenSSL::Cipher.ciphers
139163
assert_kind_of Array, ciphers

0 commit comments

Comments
 (0)