mirror of
https://github.com/ruby/ruby.git
synced 2026-11-08 01:20:26 +08:00
[ruby/openssl] cipher: fix use-after-free in in-place Cipher#update
Take RSTRING_PTR() of the input buffer after resizing the output buffer, as the input and output buffers may be the same String object. Also explicitly reject in-place Cipher#update when the input is too large to process in a single EVP_CipherUpdate() call. We should not implicitly split the input across multiple calls for in-place operation because this appears to be unreliable. Reported by TheSecguy <thesecguy45@gmail.com>. https://github.com/ruby/openssl/commit/8df76d73ba
This commit is contained in:
parent
11589a5b92
commit
9fc86fc5f9
@ -325,6 +325,9 @@ ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
||||
int limit = INT_MAX / 2 + 1;
|
||||
rb_len_t out_len = 0;
|
||||
|
||||
if (out == in && in_len > limit)
|
||||
ossl_raise(eCipherError, "input too large for in-place update");
|
||||
|
||||
do {
|
||||
int in_part_len = in_len > limit ? limit : (int)in_len;
|
||||
|
||||
@ -361,19 +364,19 @@ static VALUE
|
||||
ossl_cipher_update(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
unsigned char *in;
|
||||
rb_len_t in_len, out_len;
|
||||
VALUE data, str;
|
||||
|
||||
rb_scan_args(argc, argv, "11", &data, &str);
|
||||
GetCipher(self, ctx);
|
||||
|
||||
if (!RTEST(rb_attr_get(self, id_key_set)))
|
||||
ossl_raise(eCipherError, "key not set");
|
||||
|
||||
StringValue(data);
|
||||
in = (unsigned char *)RSTRING_PTR(data);
|
||||
if (!NIL_P(str))
|
||||
StringValue(str);
|
||||
in_len = RSTRING_LEN(data);
|
||||
GetCipher(self, ctx);
|
||||
|
||||
/*
|
||||
* As of OpenSSL 3.2, there is no reliable way to determine the required
|
||||
@ -396,15 +399,15 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
|
||||
if (NIL_P(str))
|
||||
str = rb_str_buf_new(out_len);
|
||||
else {
|
||||
StringValue(str);
|
||||
if ((rb_len_t)rb_str_capacity(str) >= out_len)
|
||||
rb_str_modify(str);
|
||||
else
|
||||
rb_str_modify_expand(str, out_len - RSTRING_LEN(str));
|
||||
}
|
||||
|
||||
if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str),
|
||||
&out_len, in, in_len))
|
||||
if (!ossl_cipher_update_long(ctx,
|
||||
(unsigned char *)RSTRING_PTR(str), &out_len,
|
||||
(unsigned char *)RSTRING_PTR(data), in_len))
|
||||
ossl_raise(eCipherError, "EVP_CipherUpdate");
|
||||
rb_str_set_len(str, out_len);
|
||||
|
||||
|
||||
@ -117,7 +117,7 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
def test_ctr_if_exists
|
||||
def test_aes_ctr
|
||||
# NIST SP 800-38A F.5.1
|
||||
key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
|
||||
iv = ["f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"].pack("H*")
|
||||
@ -125,10 +125,16 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
|
||||
"ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*")
|
||||
ct = ["874d6191b620e3261bef6864990db6ce" \
|
||||
"9806f66b7970fdff8617187bb9fffdff"].pack("H*")
|
||||
cipher = new_encryptor("aes-128-ctr", key: key, iv: iv, padding: 0)
|
||||
cipher = new_encryptor("aes-128-ctr", key: key, iv: iv)
|
||||
assert_equal ct, cipher.update(pt) << cipher.final
|
||||
cipher = new_decryptor("aes-128-ctr", key: key, iv: iv, padding: 0)
|
||||
cipher = new_decryptor("aes-128-ctr", key: key, iv: iv)
|
||||
assert_equal pt, cipher.update(ct) << cipher.final
|
||||
|
||||
# in-place update
|
||||
cipher = new_encryptor("aes-128-ctr", key: key, iv: iv)
|
||||
buf = Marshal.load(Marshal.dump(pt))
|
||||
assert_equal(ct, cipher.update(buf, buf))
|
||||
assert_equal(ct, buf)
|
||||
end
|
||||
|
||||
def test_update_with_buffer
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user