Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8300416

java.security.MessageDigestSpi clone can result in thread-unsafe clones

XMLWordPrintable

    • b09
    • generic
    • generic
    • Verified

        A DESCRIPTION OF THE PROBLEM :
        The tempArray in MessageDigestSpi is shallow copied on clone, thus clones of MessageDigestSpi are not thread-safe.

        The error is very unlikely to happen in reality as tempArray is only allocated if engineUpdate was called with a ByteBuffer that is not array backed before clone.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        See test example code

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Cloning MessageDigest objects results in two objects that are safe to be used in different threads.
        ACTUAL -
        Cloned MessageDigest can share some memory and thus influence their behaviour.

        ---------- BEGIN SOURCE ----------
        import java.nio.ByteBuffer;
        import java.security.MessageDigest;
        import java.util.Arrays;
        import java.util.Random;

        public class CloneDigest {
            public static void main(final String[] args) throws Exception {
                final var d1 = MessageDigest.getInstance("MD5");

                final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
                final var r = new Random(1024);

                fillBuffer(r, buffer);

                d1.update(buffer); // this statement triggers tempArray allocation

                final var d2 = (MessageDigest) d1.clone();

                assert Arrays.equals(d1.digest(), d2.digest());

                final var t1 = updateThread(d1);
                final var t2 = updateThread(d2);
                t1.join();
                t2.join();

                if (!Arrays.equals(d1.digest(), d2.digest())) {
                    throw new AssertionError("digests differ");
                    // cloned digest shares some memory with original one
                    // and is thus not thread safe
                }

            }

            private static void fillBuffer(final Random r, final ByteBuffer buffer) {
                final byte[] bytes = new byte[buffer.capacity()];
                r.nextBytes(bytes);
                buffer.clear();
                buffer.put(bytes);
                buffer.flip();
            }

            public static Thread updateThread(final MessageDigest d) {
                final var t = new Thread(() -> {
                    final var r = new Random(1024);
                    final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
                    for (int i = 0; i < 1024; i++) {
                        fillBuffer(r, buffer);
                        d.update(buffer);
                    }
                });
                t.start();
                return t;
            }
        }

        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        Do not clone MessageDigest that were already updated.

        FREQUENCY : always


              mpowers Mark Powers
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Created:
                Updated:
                Resolved: