Skip to content

Commit 6454847

Browse files
authored
Merge pull request jruby#9344 from kares/digest-sync-10
[fix] Digest::Base: synchronize MessageDigest mutation
2 parents 1f23f8d + b33febf commit 6454847

2 files changed

Lines changed: 23 additions & 5 deletions

File tree

core/src/main/java/org/jruby/ext/digest/RubyDigest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,9 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject obj) {
532532
from.checkFrozen();
533533

534534
try {
535-
this.algo = (MessageDigest) from.algo.clone();
535+
synchronized (from) {
536+
this.algo = (MessageDigest) from.algo.clone();
537+
}
536538
} catch (CloneNotSupportedException e) {
537539
String name = from.algo.getAlgorithm();
538540
throw typeError(context, "Could not initialize copy of digest (" + name + ")");
@@ -541,7 +543,7 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject obj) {
541543
}
542544

543545
@JRubyMethod(name = {"update", "<<"})
544-
public IRubyObject update(IRubyObject obj) {
546+
public synchronized IRubyObject update(IRubyObject obj) {
545547
ByteList bytes = obj.convertToString().getByteList();
546548
algo.update(bytes.getUnsafeBytes(), bytes.getBegin(), bytes.getRealSize());
547549
return this;
@@ -553,7 +555,7 @@ public IRubyObject finish() {
553555
}
554556

555557
@JRubyMethod()
556-
public IRubyObject finish(ThreadContext context) {
558+
public synchronized IRubyObject finish(ThreadContext context) {
557559
IRubyObject digest = RubyString.newStringNoCopy(context.runtime, algo.digest());
558560
algo.reset();
559561
return digest;
@@ -581,13 +583,13 @@ public IRubyObject block_length(ThreadContext context) {
581583
}
582584

583585
@JRubyMethod()
584-
public IRubyObject reset() {
586+
public synchronized IRubyObject reset() {
585587
algo.reset();
586588
return this;
587589
}
588590

589591
@JRubyMethod()
590-
public IRubyObject bubblebabble(ThreadContext context) {
592+
public synchronized IRubyObject bubblebabble(ThreadContext context) {
591593
final byte[] digest = algo.digest();
592594
return newString(context, BubbleBabble.bubblebabble(digest, 0, digest.length));
593595
}

test/jruby/test_digest2.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ def test_equality
1717
assert_equal(false, (a.eql?(b)))
1818
end
1919

20+
# Concurrent update on a shared Digest::Base instance should not raise.
21+
# Without synchronization, java.security.MessageDigest corrupts its
22+
# internal state under concurrent access, typically throwing
23+
# ArrayIndexOutOfBoundsException from the JDK's digest implementation.
24+
def test_concurrent_update
25+
digest = Digest::SHA256.new
26+
27+
threads = 10.times.map do
28+
Thread.new do
29+
100.times { digest.update("hello") }
30+
end
31+
end
32+
33+
assert_nothing_raised { threads.each(&:join) }
34+
end
35+
2036
# JRUBY-5147
2137
def test_initialize_args
2238
assert_raises(ArgumentError) do

0 commit comments

Comments
 (0)