From 7ea9a9e84941bb453a006e7035c48f344e0d1d6a Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 21 Nov 2025 15:22:02 +0100 Subject: [PATCH] Addressing 8372256 by catching CCE and NPE during invocation to Map::get in CHM::equals --- .../util/concurrent/ConcurrentHashMap.java | 18 +++++++++++++----- .../concurrent/tck/ConcurrentHashMapTest.java | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index 4cb7048a798e3..9295f0deb59b8 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -1372,12 +1372,20 @@ public boolean equals(Object o) { Node[] t; int f = (t = table) == null ? 0 : t.length; Traverser it = new Traverser(t, f, 0, f); - for (Node p; (p = it.advance()) != null; ) { - V val = p.val; - Object v = m.get(p.key); - if (v == null || (v != val && !v.equals(val))) - return false; + + try { + for (Node p; (p = it.advance()) != null; ) { + V val = p.val; + Object v = m.get(p.key); + if (v == null || (v != val && !v.equals(val))) + return false; + } + } catch (ClassCastException | NullPointerException _) { + // m.get(p.key) is contractually allowed to throw CCE or NPE + // but CHM doesn't allow null keys, so NPE shouldn't occur in practice + return false; } + for (Map.Entry e : m.entrySet()) { Object mk, mv, v; if ((mk = e.getKey()) == null || diff --git a/test/jdk/java/util/concurrent/tck/ConcurrentHashMapTest.java b/test/jdk/java/util/concurrent/tck/ConcurrentHashMapTest.java index c87571b01f7b3..168594832c2f8 100644 --- a/test/jdk/java/util/concurrent/tck/ConcurrentHashMapTest.java +++ b/test/jdk/java/util/concurrent/tck/ConcurrentHashMapTest.java @@ -40,6 +40,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; +import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.Set; @@ -878,6 +879,24 @@ public void testReentrantComputeIfAbsent() { } catch (IllegalStateException success) {} } + public void testMapEqualsIfClassCastExceptionOccurs() { + class MonotypeKeyMap extends HashMap { + @Override public Object get(Object key) { + return super.get((Byte)key); // Force cast, allowed by spec + } + } + + var mkm = new MonotypeKeyMap(); + mkm.put((byte)1, "value1"); + var similar = new ConcurrentHashMap(); + similar.put((byte)1, "value1"); + var different = new ConcurrentHashMap(); + different.put("test1", "value1"); + + assertTrue(similar.equals(mkm)); + assertFalse(different.equals(mkm)); + } + private static Item findValue(ConcurrentHashMap map, Item key) { return (key.value % 5 == 0) ? key :