Skip to content

Commit e72d960

Browse files
committed
Merge branch 'maint'
* maint: Ruby/OpenSSL 2.0.6 test/test_engine: check if RC4 is supported test/test_engine: suppress stderr ossl.c: make legacy locking callbacks reentrant ossl.c: use struct CRYPTO_dynlock_value for non-dynamic locks ssl: prevent SSLSocket#sysread* from leaking uninitialized data test/test_pair: replace sleep with IO.select tool/ruby-openssl-docker: update test/test_ssl: do not run NPN tests for LibreSSL >= 2.6.1 test/test_ssl: skip tmp_ecdh_callback test for LibreSSL >= 2.6.1 test/test_pair: disable compression test/test_ssl: suppress warning in test_alpn_protocol_selection_cancel ruby.h: unnormalized Fixnum value test/test_pair: fix test_write_nonblock{,_no_exceptions}
2 parents 51ff816 + 14e1165 commit e72d960

File tree

11 files changed

+181
-108
lines changed

11 files changed

+181
-108
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ matrix:
2323
- env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=openssl-1.1.0
2424
- env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.4
2525
- env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.5
26+
- env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.6
2627
- language: ruby
2728
rvm: ruby-head
2829
before_install:

History.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ Notable changes
2929
[[GitHub #143]](https://github.com/ruby/openssl/pull/143)
3030

3131

32+
Version 2.0.6
33+
=============
34+
35+
Bug fixes
36+
---------
37+
38+
* The session_remove_cb set to an OpenSSL::SSL::SSLContext is no longer called
39+
during GC.
40+
* A possible deadlock in OpenSSL::SSL::SSLSocket#sysread is fixed.
41+
[[GitHub #139]](https://github.com/ruby/openssl/pull/139)
42+
* OpenSSL::BN#hash could return an unnormalized fixnum value on Windows.
43+
[[Bug #13877]](https://bugs.ruby-lang.org/issues/13877)
44+
* OpenSSL::SSL::SSLSocket#sysread and #sysread_nonblock set the length of the
45+
destination buffer String to 0 on error.
46+
[[GitHub #153]](https://github.com/ruby/openssl/pull/153)
47+
* Possible deadlock is fixed. This happened only when built with older versions
48+
of OpenSSL (before 1.1.0) or LibreSSL.
49+
[[GitHub #155]](https://github.com/ruby/openssl/pull/155)
50+
51+
3252
Version 2.0.5
3353
=============
3454

ext/openssl/ossl.c

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -517,40 +517,53 @@ print_mem_leaks(VALUE self)
517517
/**
518518
* Stores locks needed for OpenSSL thread safety
519519
*/
520-
static rb_nativethread_lock_t *ossl_locks;
520+
struct CRYPTO_dynlock_value {
521+
rb_nativethread_lock_t lock;
522+
rb_nativethread_id_t owner;
523+
size_t count;
524+
};
521525

522526
static void
523-
ossl_lock_unlock(int mode, rb_nativethread_lock_t *lock)
527+
ossl_lock_init(struct CRYPTO_dynlock_value *l)
524528
{
525-
if (mode & CRYPTO_LOCK) {
526-
rb_nativethread_lock_lock(lock);
527-
} else {
528-
rb_nativethread_lock_unlock(lock);
529-
}
529+
rb_nativethread_lock_initialize(&l->lock);
530+
l->count = 0;
530531
}
531532

532533
static void
533-
ossl_lock_callback(int mode, int type, const char *file, int line)
534+
ossl_lock_unlock(int mode, struct CRYPTO_dynlock_value *l)
534535
{
535-
ossl_lock_unlock(mode, &ossl_locks[type]);
536+
if (mode & CRYPTO_LOCK) {
537+
/* TODO: rb_nativethread_id_t is not necessarily compared with ==. */
538+
rb_nativethread_id_t tid = rb_nativethread_self();
539+
if (l->count && l->owner == tid) {
540+
l->count++;
541+
return;
542+
}
543+
rb_nativethread_lock_lock(&l->lock);
544+
l->owner = tid;
545+
l->count = 1;
546+
} else {
547+
if (!--l->count)
548+
rb_nativethread_lock_unlock(&l->lock);
549+
}
536550
}
537551

538-
struct CRYPTO_dynlock_value {
539-
rb_nativethread_lock_t lock;
540-
};
541-
542552
static struct CRYPTO_dynlock_value *
543553
ossl_dyn_create_callback(const char *file, int line)
544554
{
545-
struct CRYPTO_dynlock_value *dynlock = (struct CRYPTO_dynlock_value *)OPENSSL_malloc((int)sizeof(struct CRYPTO_dynlock_value));
546-
rb_nativethread_lock_initialize(&dynlock->lock);
555+
/* Do not use xmalloc() here, since it may raise NoMemoryError */
556+
struct CRYPTO_dynlock_value *dynlock =
557+
OPENSSL_malloc(sizeof(struct CRYPTO_dynlock_value));
558+
if (dynlock)
559+
ossl_lock_init(dynlock);
547560
return dynlock;
548561
}
549562

550563
static void
551564
ossl_dyn_lock_callback(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
552565
{
553-
ossl_lock_unlock(mode, &l->lock);
566+
ossl_lock_unlock(mode, l);
554567
}
555568

556569
static void
@@ -566,21 +579,22 @@ static void ossl_threadid_func(CRYPTO_THREADID *id)
566579
CRYPTO_THREADID_set_pointer(id, (void *)rb_nativethread_self());
567580
}
568581

582+
static struct CRYPTO_dynlock_value *ossl_locks;
583+
584+
static void
585+
ossl_lock_callback(int mode, int type, const char *file, int line)
586+
{
587+
ossl_lock_unlock(mode, &ossl_locks[type]);
588+
}
589+
569590
static void Init_ossl_locks(void)
570591
{
571592
int i;
572593
int num_locks = CRYPTO_num_locks();
573594

574-
if ((unsigned)num_locks >= INT_MAX / (int)sizeof(VALUE)) {
575-
rb_raise(rb_eRuntimeError, "CRYPTO_num_locks() is too big: %d", num_locks);
576-
}
577-
ossl_locks = (rb_nativethread_lock_t *) OPENSSL_malloc(num_locks * (int)sizeof(rb_nativethread_lock_t));
578-
if (!ossl_locks) {
579-
rb_raise(rb_eNoMemError, "CRYPTO_num_locks() is too big: %d", num_locks);
580-
}
581-
for (i = 0; i < num_locks; i++) {
582-
rb_nativethread_lock_initialize(&ossl_locks[i]);
583-
}
595+
ossl_locks = ALLOC_N(struct CRYPTO_dynlock_value, num_locks);
596+
for (i = 0; i < num_locks; i++)
597+
ossl_lock_init(&ossl_locks[i]);
584598

585599
CRYPTO_THREADID_set_callback(ossl_threadid_func);
586600
CRYPTO_set_locking_callback(ossl_lock_callback);

ext/openssl/ossl_bn.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ ossl_bn_hash(VALUE self)
991991
ossl_raise(eBNError, NULL);
992992
}
993993

994-
hash = INT2FIX(rb_memhash(buf, len));
994+
hash = ST2FIX(rb_memhash(buf, len));
995995
xfree(buf);
996996

997997
return hash;

ext/openssl/ossl_ssl.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,20 +1694,26 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
16941694
}
16951695

16961696
ilen = NUM2INT(len);
1697-
if(NIL_P(str)) str = rb_str_new(0, ilen);
1698-
else{
1699-
StringValue(str);
1700-
rb_str_modify(str);
1701-
rb_str_resize(str, ilen);
1697+
if (NIL_P(str))
1698+
str = rb_str_new(0, ilen);
1699+
else {
1700+
StringValue(str);
1701+
if (RSTRING_LEN(str) >= ilen)
1702+
rb_str_modify(str);
1703+
else
1704+
rb_str_modify_expand(str, ilen - RSTRING_LEN(str));
17021705
}
1703-
if(ilen == 0) return str;
1706+
OBJ_TAINT(str);
1707+
rb_str_set_len(str, 0);
1708+
if (ilen == 0)
1709+
return str;
17041710

17051711
GetSSL(self, ssl);
17061712
io = rb_attr_get(self, id_i_io);
17071713
GetOpenFile(io, fptr);
17081714
if (ssl_started(ssl)) {
17091715
for (;;){
1710-
nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LENINT(str));
1716+
nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
17111717
switch(ssl_get_error(ssl, nread)){
17121718
case SSL_ERROR_NONE:
17131719
goto end;
@@ -1757,8 +1763,6 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
17571763

17581764
end:
17591765
rb_str_set_len(str, nread);
1760-
OBJ_TAINT(str);
1761-
17621766
return str;
17631767
}
17641768

ext/openssl/ruby_missing.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
#if !defined(_OSSL_RUBY_MISSING_H_)
1111
#define _OSSL_RUBY_MISSING_H_
1212

13+
/* Ruby 2.4 */
1314
#ifndef RB_INTEGER_TYPE_P
14-
/* for Ruby 2.3 compatibility */
15-
#define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM))
15+
# define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM))
16+
#endif
17+
18+
/* Ruby 2.5 */
19+
#ifndef ST2FIX
20+
# define RB_ST2FIX(h) LONG2FIX((long)(h))
21+
# define ST2FIX(h) RB_ST2FIX(h)
1622
#endif
1723

1824
#endif /* _OSSL_RUBY_MISSING_H_ */

test/test_bn.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def test_comparison
270270
assert_equal(1, @e1.cmp(-999))
271271
assert_equal(0, @e1.ucmp(999))
272272
assert_equal(0, @e1.ucmp(-999))
273+
assert_instance_of(String, @e1.hash.to_s)
273274
end
274275
end
275276

test/test_engine.rb

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,32 +52,28 @@ def test_openssl_engine_digest_sha1
5252
end
5353

5454
def test_openssl_engine_cipher_rc4
55-
with_openssl <<-'end;'
56-
begin
57-
engine = get_engine
58-
algo = "RC4" #AES is not supported by openssl Engine (<=1.0.0e)
59-
data = "a" * 1000
60-
key = OpenSSL::Random.random_bytes(16)
61-
# suppress message from openssl Engine's RC4 cipher [ruby-core:41026]
62-
err_back = $stderr.dup
63-
$stderr.reopen(IO::NULL)
64-
encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
65-
decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
66-
assert_equal(data, decrypted)
67-
ensure
68-
if err_back
69-
$stderr.reopen(err_back)
70-
err_back.close
71-
end
72-
end
55+
begin
56+
OpenSSL::Cipher.new("rc4")
57+
rescue OpenSSL::Cipher::CipherError
58+
pend "RC4 is not supported"
59+
end
60+
61+
with_openssl(<<-'end;', ignore_stderr: true)
62+
engine = get_engine
63+
algo = "RC4"
64+
data = "a" * 1000
65+
key = OpenSSL::Random.random_bytes(16)
66+
encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
67+
decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
68+
assert_equal(data, decrypted)
7369
end;
7470
end
7571

7672
private
7773

7874
# this is required because OpenSSL::Engine methods change global state
79-
def with_openssl(code)
80-
assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
75+
def with_openssl(code, **opts)
76+
assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;", **opts)
8177
require #{__FILE__.dump}
8278
include OpenSSL::TestEngine::Utils
8379
#{code}

test/test_pair.rb

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def ssl_pair
2424
sctx.cert = @svr_cert
2525
sctx.key = @svr_key
2626
sctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") }
27+
sctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION
2728
ssls = OpenSSL::SSL::SSLServer.new(tcps, sctx)
2829
ns = ssls.accept
2930
ssls.close
@@ -217,7 +218,7 @@ def test_read_nonblock
217218
assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) }
218219
assert_equal("def\n", ret)
219220
s1.close
220-
sleep 0.1
221+
IO.select([s2])
221222
assert_raise(EOFError) { s2.read_nonblock(10) }
222223
}
223224
end
@@ -233,49 +234,71 @@ def test_read_nonblock_no_exception
233234
assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) }
234235
assert_equal("def\n", ret)
235236
s1.close
236-
sleep 0.1
237+
IO.select([s2])
237238
assert_equal(nil, s2.read_nonblock(10, exception: false))
238239
}
239240
end
240241

241-
def write_nonblock(socket, meth, str)
242-
ret = socket.send(meth, str)
243-
ret.is_a?(Symbol) ? 0 : ret
244-
end
242+
def test_read_with_outbuf
243+
ssl_pair { |s1, s2|
244+
s1.write("abc\n")
245+
buf = ""
246+
ret = s2.read(2, buf)
247+
assert_same ret, buf
248+
assert_equal "ab", ret
249+
250+
buf = "garbage"
251+
ret = s2.read(2, buf)
252+
assert_same ret, buf
253+
assert_equal "c\n", ret
245254

246-
def write_nonblock_no_ex(socket, str)
247-
ret = socket.write_nonblock str, exception: false
248-
ret.is_a?(Symbol) ? 0 : ret
255+
buf = "garbage"
256+
assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false)
257+
assert_equal "", buf
258+
259+
s1.close
260+
buf = "garbage"
261+
assert_equal nil, s2.read(100, buf)
262+
assert_equal "", buf
263+
}
249264
end
250265

251266
def test_write_nonblock
252267
ssl_pair {|s1, s2|
253-
n = 0
254-
begin
255-
n += write_nonblock s1, :write_nonblock, "a" * 100000
256-
n += write_nonblock s1, :write_nonblock, "b" * 100000
257-
n += write_nonblock s1, :write_nonblock, "c" * 100000
258-
n += write_nonblock s1, :write_nonblock, "d" * 100000
259-
n += write_nonblock s1, :write_nonblock, "e" * 100000
260-
n += write_nonblock s1, :write_nonblock, "f" * 100000
261-
rescue IO::WaitWritable
268+
assert_equal 3, s1.write_nonblock("foo")
269+
assert_equal "foo", s2.read(3)
270+
271+
data = "x" * 16384
272+
written = 0
273+
while true
274+
begin
275+
written += s1.write_nonblock(data)
276+
rescue IO::WaitWritable, IO::WaitReadable
277+
break
278+
end
262279
end
263-
s1.close
264-
assert_equal(n, s2.read.length)
280+
assert written > 0
281+
assert_equal written, s2.read(written).bytesize
265282
}
266283
end
267284

268285
def test_write_nonblock_no_exceptions
269286
ssl_pair {|s1, s2|
270-
n = 0
271-
n += write_nonblock_no_ex s1, "a" * 100000
272-
n += write_nonblock_no_ex s1, "b" * 100000
273-
n += write_nonblock_no_ex s1, "c" * 100000
274-
n += write_nonblock_no_ex s1, "d" * 100000
275-
n += write_nonblock_no_ex s1, "e" * 100000
276-
n += write_nonblock_no_ex s1, "f" * 100000
277-
s1.close
278-
assert_equal(n, s2.read.length)
287+
assert_equal 3, s1.write_nonblock("foo", exception: false)
288+
assert_equal "foo", s2.read(3)
289+
290+
data = "x" * 16384
291+
written = 0
292+
while true
293+
case ret = s1.write_nonblock(data, exception: false)
294+
when :wait_readable, :wait_writable
295+
break
296+
else
297+
written += ret
298+
end
299+
end
300+
assert written > 0
301+
assert_equal written, s2.read(written).bytesize
279302
}
280303
end
281304

0 commit comments

Comments
 (0)