JDK-8202760 : UUID.fromString() accepts invalid (short) strings
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 8,9,10,11
  • Priority: P4
  • Status: Resolved
  • Resolution: Won't Fix
  • OS: linux_ubuntu
  • CPU: x86_64
  • Submitted: 2018-05-07
  • Updated: 2019-01-09
  • Resolved: 2018-06-21
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
java version "10.0.1" 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)


A DESCRIPTION OF THE PROBLEM :
Within https://bugs.openjdk.java.net/browse/JDK-8006627 maximum UUID string length check was introduced.
If UUID string is shorter then 36 characters it still will be successfully parsed in certain cases without an error.
For example is "0-0-0-0-0" which would be parsed into NIL UUID "00000000-0000-0000-0000-000000000000".
Another example is "00112233-4455-6677-8899-aabbccddee" which would be incorrectly parsed into "00112233-4455-6677-8899-00aabbccddee" (note 00 in last part)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Call UUID.fromString("0-0-0-0-0" )

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Call fails
ACTUAL -
Call succeeds with UUID result of "00000000-0000-0000-0000-000000000000"

---------- BEGIN SOURCE ----------
package ru.ay;

import org.junit.Test;

import java.util.UUID;

import static org.junit.Assert.fail;

public class UUIDFromStringTest {

    @Test
    public void shouldFailOnInvalidUUIDFormat() {
        testFromStringError("00112233-4455-6677-8899-aabbccddee");
        testFromStringError("0-0-0-0-0");
    }

    private static void testFromStringError(String str) {
        try {
            UUID.fromString(str);
            fail("Should have thrown IAE");
        } catch (IllegalArgumentException iae) {
            // pass
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I propose to check exact UUID string length and offsets of separators ('-').

    public static UUID fromString(String name) {
        // Canonical UUID format is 8-4-4-4-12 for a total of 36 characters
        if (name.length() != 36 ||
                name.charAt(8) != '-' ||
                name.charAt(13) != '-' ||
                name.charAt(18) != '-' ||
                name.charAt(23) != '-') {
            throw new IllegalArgumentException("Invalid UUID string: " + name);
        }

        long mostSigBits = Long.parseLong(name, 0, 8, 16) << 32
                | Long.parseLong(name, 8 + 1, 13, 16) << 16
                | Long.parseLong(name, 13 + 1, 18, 16);

        long leastSigBits = Long.parseLong(name, 18 + 1, 23, 16) << 48
                | Long.parseLong(name, 23 + 1, 36, 16);

        return new UUID(mostSigBits, leastSigBits);
    }


Comments
Incompatible change.
21-06-2018

RFC 4122 "Universally Unique IDentifier (UUID) URN Namespace" on which the java.util.UUID class is based on does not seem to define any short versions for a UUID string representation. That said, all valid UUIDs have to be exactly 36 characters long. rev. 23010:6dadb192ad8172467b5b8c703deb18fffcf5d8c6 ("8006627: UUID to/from String performance should be improved by reducing object allocations") has indeed introduced a maximum length check. It is both convenient and tempting to provide a single character solution to the issue in question, namely if (len > 36) { throw new IllegalArgumentException("UUID string too large"); } --------- if (len != 36) { throw new IllegalArgumentException("UUID string too large"); } However, the current behaviour seems to be long-standing (since at least 2008) and I am incapable of telling if the fix will cause more issues than it solves. In other words, this needs to be evaluated from the compatibility point of view.
09-05-2018

To reproduce the issue, run the attached test case. JDK 8u172 - Fail JDK 10 + 46 - Fail JDK 10.0.1+10 - Fail JDK 11-ea+10 - Fail Output : 1) shouldFailOnInvalidUUIDFormat(JI9053716) java.lang.AssertionError: Should have thrown IAE at org.junit.Assert.fail(Assert.java:74) at JI9053716.testFromStringError(JI9053716.java:17) at JI9053716.shouldFailOnInvalidUUIDFormat(JI9053716.java:10) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79) at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87) at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77) at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42) at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88) at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51) at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44) at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27) at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37) at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42) at org.junit.internal.runners.CompositeRunner.runChildren(CompositeRunner.java:33) at org.junit.internal.runners.CompositeRunner.run(CompositeRunner.java:28) at org.junit.runner.JUnitCore.run(JUnitCore.java:130) at org.junit.runner.JUnitCore.run(JUnitCore.java:109) at org.junit.runner.JUnitCore.run(JUnitCore.java:100) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81) at org.junit.runner.JUnitCore.main(JUnitCore.java:44) FAILURES!!! Tests run: 1, Failures: 1
08-05-2018