Mercurial > hg > release > icedtea7-forest-2.6 > hotspot
changeset 6791:a4c6e7e7efc5
Merge jdk7u281-ga
author | Andrew John Hughes <gnu.andrew@redhat.com> |
---|---|
date | Mon, 02 Nov 2020 02:26:23 +0000 |
parents | 6de046544eec (current diff) ca0fd562f883 (diff) |
children | 77288aadc5a7 |
files | .hgtags src/os/posix/vm/os_posix.cpp src/share/vm/classfile/classFileParser.cpp src/share/vm/classfile/systemDictionary.cpp src/share/vm/opto/loopTransform.cpp src/share/vm/opto/loopnode.hpp src/share/vm/opto/subnode.cpp src/share/vm/opto/type.cpp src/share/vm/prims/nativeLookup.cpp src/share/vm/runtime/advancedThresholdPolicy.cpp src/share/vm/runtime/globals.hpp src/share/vm/utilities/globalDefinitions.hpp |
diffstat | 15 files changed, 735 insertions(+), 405 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Sat Sep 26 19:05:33 2020 +0100 +++ b/.hgtags Mon Nov 02 02:26:23 2020 +0000 @@ -971,3 +971,6 @@ debb2913070604f611eaf916cbc90f024d17ef24 jdk7u271-ga 2c74ba717122cba9979a05f7fb22bebc5ccbf543 icedtea-2.6.23 2c74ba717122cba9979a05f7fb22bebc5ccbf543 icedtea-2.6.24pre00 +debb2913070604f611eaf916cbc90f024d17ef24 jdk7u281-b00 +e692f9f04b16a59a0959521d697bccff1a744160 jdk7u281-b01 +e692f9f04b16a59a0959521d697bccff1a744160 jdk7u281-ga
--- a/src/share/vm/classfile/altHashing.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/classfile/altHashing.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,29 @@ * */ +/* + * halfsiphash code adapted from reference implementation + * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c) + * which is distributed with the following copyright: + * + * SipHash reference C implementation + * + * Copyright (c) 2016 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com> + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along + * with this software. If not, see + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + #include "precompiled.hpp" #include "classfile/altHashing.hpp" -#include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "oops/markOop.hpp" -#include "runtime/thread.hpp" +#include "runtime/os.hpp" // Get the hash code of the classes mirror if it exists, otherwise just // return a random number, which is one of the possible hash code used for @@ -39,266 +56,292 @@ } // Seed value used for each alternative hash calculated. -jint AltHashing::compute_seed() { - jlong nanos = os::javaTimeNanos(); - jlong now = os::javaTimeMillis(); - jint SEED_MATERIAL[8] = { - (jint) object_hash(SystemDictionary::String_klass()), - (jint) object_hash(SystemDictionary::System_klass()), - (jint) os::random(), // current thread isn't a java thread - (jint) (((julong)nanos) >> 32), - (jint) nanos, - (jint) (((julong)now) >> 32), - (jint) now, - (jint) (os::javaTimeNanos() >> 2) +uint64_t AltHashing::compute_seed() { + uint64_t nanos = os::javaTimeNanos(); + uint64_t now = os::javaTimeMillis(); + uint32_t SEED_MATERIAL[8] = { + (uint32_t) object_hash(SystemDictionary::String_klass()), + (uint32_t) object_hash(SystemDictionary::System_klass()), + (uint32_t) os::random(), // current thread isn't a java thread + (uint32_t) (((uint64_t)nanos) >> 32), + (uint32_t) nanos, + (uint32_t) (((uint64_t)now) >> 32), + (uint32_t) now, + (uint32_t) (os::javaTimeNanos() >> 2) }; - return murmur3_32(SEED_MATERIAL, 8); + return halfsiphash_64(SEED_MATERIAL, 8); +} + +// utility function copied from java/lang/Integer +static uint32_t Integer_rotateLeft(uint32_t i, int distance) { + return (i << distance) | (i >> (32 - distance)); +} + +static void halfsiphash_rounds(uint32_t v[4], int rounds) { + while (rounds-- > 0) { + v[0] += v[1]; + v[1] = Integer_rotateLeft(v[1], 5); + v[1] ^= v[0]; + v[0] = Integer_rotateLeft(v[0], 16); + v[2] += v[3]; + v[3] = Integer_rotateLeft(v[3], 8); + v[3] ^= v[2]; + v[0] += v[3]; + v[3] = Integer_rotateLeft(v[3], 7); + v[3] ^= v[0]; + v[2] += v[1]; + v[1] = Integer_rotateLeft(v[1], 13); + v[1] ^= v[2]; + v[2] = Integer_rotateLeft(v[2], 16); + } + } + +static void halfsiphash_adddata(uint32_t v[4], uint32_t newdata, int rounds) { + v[3] ^= newdata; + halfsiphash_rounds(v, rounds); + v[0] ^= newdata; } +static void halfsiphash_init32(uint32_t v[4], uint64_t seed) { + v[0] = seed & 0xffffffff; + v[1] = seed >> 32; + v[2] = 0x6c796765 ^ v[0]; + v[3] = 0x74656462 ^ v[1]; +} -// Murmur3 hashing for Symbol -jint AltHashing::murmur3_32(jint seed, const jbyte* data, int len) { - jint h1 = seed; +static void halfsiphash_init64(uint32_t v[4], uint64_t seed) { + halfsiphash_init32(v, seed); + v[1] ^= 0xee; +} + +uint32_t halfsiphash_finish32(uint32_t v[4], int rounds) { + v[2] ^= 0xff; + halfsiphash_rounds(v, rounds); + return (v[1] ^ v[3]); +} + +static uint64_t halfsiphash_finish64(uint32_t v[4], int rounds) { + uint64_t rv; + v[2] ^= 0xee; + halfsiphash_rounds(v, rounds); + rv = v[1] ^ v[3]; + v[1] ^= 0xdd; + halfsiphash_rounds(v, rounds); + rv |= (uint64_t)(v[1] ^ v[3]) << 32; + return rv; +} + +// HalfSipHash-2-4 (32-bit output) for Symbols +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint8_t* data, int len) { + uint32_t v[4]; + uint32_t newdata; + int off = 0; int count = len; - int offset = 0; + halfsiphash_init32(v, seed); // body while (count >= 4) { - jint k1 = (data[offset] & 0x0FF) - | (data[offset + 1] & 0x0FF) << 8 - | (data[offset + 2] & 0x0FF) << 16 - | data[offset + 3] << 24; + // Avoid sign extension with 0x0ff + newdata = (data[off] & 0x0FF) + | (data[off + 1] & 0x0FF) << 8 + | (data[off + 2] & 0x0FF) << 16 + | data[off + 3] << 24; count -= 4; - offset += 4; + off += 4; - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, newdata, 2); } // tail + newdata = ((uint32_t)len) << 24; // (Byte.SIZE / Byte.SIZE); if (count > 0) { - jint k1 = 0; - switch (count) { case 3: - k1 ^= (data[offset + 2] & 0xff) << 16; + newdata |= (data[off + 2] & 0x0ff) << 16; // fall through case 2: - k1 ^= (data[offset + 1] & 0xff) << 8; + newdata |= (data[off + 1] & 0x0ff) << 8; // fall through case 1: - k1 ^= (data[offset] & 0xff); + newdata |= (data[off] & 0x0ff); // fall through - default: - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - h1 ^= k1; } } - // finalization - h1 ^= len; + halfsiphash_adddata(v, newdata, 2); - // finalization mix force all bits of a hash block to avalanche - h1 ^= ((unsigned int)h1) >> 16; - h1 *= 0x85ebca6b; - h1 ^= ((unsigned int)h1) >> 13; - h1 *= 0xc2b2ae35; - h1 ^= ((unsigned int)h1) >> 16; - - return h1; + // finalization + return halfsiphash_finish32(v, 4); } -// Murmur3 hashing for Strings -jint AltHashing::murmur3_32(jint seed, const jchar* data, int len) { - jint h1 = seed; - +// HalfSipHash-2-4 (32-bit output) for Strings +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint16_t* data, int len) { + uint32_t v[4]; + uint32_t newdata; int off = 0; int count = len; + halfsiphash_init32(v, seed); + // body while (count >= 2) { - jchar d1 = data[off++] & 0xFFFF; - jchar d2 = data[off++]; - jint k1 = (d1 | d2 << 16); + uint16_t d1 = data[off++] & 0x0FFFF; + uint16_t d2 = data[off++]; + newdata = (d1 | d2 << 16); count -= 2; - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, newdata, 2); } // tail - + newdata = ((uint32_t)len * 2) << 24; // (Character.SIZE / Byte.SIZE); if (count > 0) { - int k1 = data[off]; - - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - h1 ^= k1; + newdata |= (uint32_t)data[off]; } + halfsiphash_adddata(v, newdata, 2); // finalization - h1 ^= len * 2; // (Character.SIZE / Byte.SIZE); - - // finalization mix force all bits of a hash block to avalanche - h1 ^= ((unsigned int)h1) >> 16; - h1 *= 0x85ebca6b; - h1 ^= ((unsigned int)h1) >> 13; - h1 *= 0xc2b2ae35; - h1 ^= ((unsigned int)h1) >> 16; - - return h1; + return halfsiphash_finish32(v, 4); } -// Hash used for the seed. -jint AltHashing::murmur3_32(jint seed, const int* data, int len) { - jint h1 = seed; +// HalfSipHash-2-4 (64-bit output) for integers (used to create seed) +uint64_t AltHashing::halfsiphash_64(uint64_t seed, const uint32_t* data, int len) { + uint32_t v[4]; int off = 0; int end = len; + halfsiphash_init64(v, seed); + // body while (off < end) { - jint k1 = data[off++]; - - k1 *= 0xcc9e2d51; - k1 = Integer_rotateLeft(k1, 15); - k1 *= 0x1b873593; - - h1 ^= k1; - h1 = Integer_rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; + halfsiphash_adddata(v, (uint32_t)data[off++], 2); } // tail (always empty, as body is always 32-bit chunks) // finalization - - h1 ^= len * 4; // (Integer.SIZE / Byte.SIZE); - - // finalization mix force all bits of a hash block to avalanche - h1 ^= ((juint)h1) >> 16; - h1 *= 0x85ebca6b; - h1 ^= ((juint)h1) >> 13; - h1 *= 0xc2b2ae35; - h1 ^= ((juint)h1) >> 16; - - return h1; + halfsiphash_adddata(v, ((uint32_t)len * 4) << 24, 2); // (Integer.SIZE / Byte.SIZE); + return halfsiphash_finish64(v, 4); } -jint AltHashing::murmur3_32(const int* data, int len) { - return murmur3_32(0, data, len); +// HalfSipHash-2-4 (64-bit output) for integers (used to create seed) +uint64_t AltHashing::halfsiphash_64(const uint32_t* data, int len) { + return halfsiphash_64((uint64_t)0, data, len); } #ifndef PRODUCT -// Overloaded versions for internal test. -jint AltHashing::murmur3_32(const jbyte* data, int len) { - return murmur3_32(0, data, len); -} + void AltHashing::testHalfsiphash_32_ByteArray() { + const int factor = 4; -jint AltHashing::murmur3_32(const jchar* data, int len) { - return murmur3_32(0, data, len); -} + uint8_t vector[256]; + uint8_t hashes[factor * 256]; + + for (int i = 0; i < 256; i++) { + vector[i] = (uint8_t) i; + } -// Internal test for alternate hashing. Translated from JDK version -// test/sun/misc/Hashing.java -static const jbyte ONE_BYTE[] = { (jbyte) 0x80}; -static const jbyte TWO_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81}; -static const jchar ONE_CHAR[] = { (jchar) 0x8180}; -static const jbyte THREE_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82}; -static const jbyte FOUR_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83}; -static const jchar TWO_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382}; -static const jint ONE_INT[] = { 0x83828180}; -static const jbyte SIX_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85}; -static const jchar THREE_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382, (jchar) 0x8584}; -static const jbyte EIGHT_BYTE[] = { - (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, - (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85, - (jbyte) 0x86, (jbyte) 0x87}; -static const jchar FOUR_CHAR[] = { - (jchar) 0x8180, (jchar) 0x8382, - (jchar) 0x8584, (jchar) 0x8786}; + // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} + for (int i = 0; i < 256; i++) { + uint32_t hash = AltHashing::halfsiphash_32(256 - i, vector, i); + hashes[i * factor] = (uint8_t) hash; + hashes[i * factor + 1] = (uint8_t)(hash >> 8); + hashes[i * factor + 2] = (uint8_t)(hash >> 16); + hashes[i * factor + 3] = (uint8_t)(hash >> 24); + } + + // hash to get const result. + uint32_t final_hash = AltHashing::halfsiphash_32(0, hashes, factor*256); -static const jint TWO_INT[] = { 0x83828180, 0x87868584}; - -static const juint MURMUR3_32_X86_CHECK_VALUE = 0xB0F57EE3; + // Value found using reference implementation for the hashes array. + //uint64_t k = 0; // seed + //uint32_t reference; + //halfsiphash((const uint8_t*)hashes, factor*256, (const uint8_t *)&k, (uint8_t*)&reference, 4); + //printf("0x%x", reference); -void AltHashing::testMurmur3_32_ByteArray() { - // printf("testMurmur3_32_ByteArray\n"); + static const uint32_t HALFSIPHASH_32_BYTE_CHECK_VALUE = 0xd2be7fd8; - jbyte* vector = new jbyte[256]; - jbyte* hashes = new jbyte[4 * 256]; - - for (int i = 0; i < 256; i++) { - vector[i] = (jbyte) i; + assert (HALFSIPHASH_32_BYTE_CHECK_VALUE == final_hash, + err_msg( + "Calculated hash result not as expected. Expected " UINT32_FORMAT " got " UINT32_FORMAT, + HALFSIPHASH_32_BYTE_CHECK_VALUE, + final_hash)); } - // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} - for (int i = 0; i < 256; i++) { - jint hash = murmur3_32(256 - i, vector, i); - hashes[i * 4] = (jbyte) hash; - hashes[i * 4 + 1] = (jbyte) (((juint)hash) >> 8); - hashes[i * 4 + 2] = (jbyte) (((juint)hash) >> 16); - hashes[i * 4 + 3] = (jbyte) (((juint)hash) >> 24); + void AltHashing::testHalfsiphash_32_CharArray() { + const int factor = 2; + + uint16_t vector[256]; + uint16_t hashes[factor * 256]; + + for (int i = 0; i < 256; i++) { + vector[i] = (uint16_t) i; + } + + // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} + for (int i = 0; i < 256; i++) { + uint32_t hash = AltHashing::halfsiphash_32(256 - i, vector, i); + hashes[i * factor] = (uint16_t) hash; + hashes[i * factor + 1] = (uint16_t)(hash >> 16); + } + + // hash to get const result. + uint32_t final_hash = AltHashing::halfsiphash_32(0, hashes, factor*256); + + // Value found using reference implementation for the hashes array. + //uint64_t k = 0; // seed + //uint32_t reference; + //halfsiphash((const uint8_t*)hashes, 2*factor*256, (const uint8_t *)&k, (uint8_t*)&reference, 4); + //printf("0x%x", reference); + + static const uint32_t HALFSIPHASH_32_CHAR_CHECK_VALUE = 0x428bf8a5; + + assert(HALFSIPHASH_32_CHAR_CHECK_VALUE == final_hash, + err_msg( + "Calculated hash result not as expected. Expected " UINT32_FORMAT " got " UINT32_FORMAT, + HALFSIPHASH_32_CHAR_CHECK_VALUE, + final_hash)); } - // hash to get const result. - juint final_hash = murmur3_32(hashes, 4*256); - - assert (MURMUR3_32_X86_CHECK_VALUE == final_hash, - err_msg( - "Calculated hash result not as expected. Expected %08X got %08X\n", - MURMUR3_32_X86_CHECK_VALUE, - final_hash)); -} + // Test against sample hashes published with the reference implementation: + // https://github.com/veorq/SipHash + void AltHashing::testHalfsiphash_64_FromReference() { -void AltHashing::testEquivalentHashes() { - jint jbytes, jchars, ints; - - // printf("testEquivalentHashes\n"); - - jbytes = murmur3_32(TWO_BYTE, 2); - jchars = murmur3_32(ONE_CHAR, 1); - assert (jbytes == jchars, - err_msg("Hashes did not match. b:%08x != c:%08x\n", jbytes, jchars)); + const uint64_t seed = 0x0706050403020100; + const uint64_t results[16] = { + 0xc83cb8b9591f8d21, 0xa12ee55b178ae7d5, + 0x8c85e4bc20e8feed, 0x99c7f5ae9f1fc77b, + 0xb5f37b5fd2aa3673, 0xdba7ee6f0a2bf51b, + 0xf1a63fae45107470, 0xb516001efb5f922d, + 0x6c6211d8469d7028, 0xdc7642ec407ad686, + 0x4caec8671cc8385b, 0x5ab1dc27adf3301e, + 0x3e3ea94bc0a8eaa9, 0xe150f598795a4402, + 0x1d5ff142f992a4a1, 0x60e426bf902876d6 + }; + uint32_t vector[16]; - jbytes = murmur3_32(FOUR_BYTE, 4); - jchars = murmur3_32(TWO_CHAR, 2); - ints = murmur3_32(ONE_INT, 1); - assert ((jbytes == jchars) && (jbytes == ints), - err_msg("Hashes did not match. b:%08x != c:%08x != i:%08x\n", jbytes, jchars, ints)); - - jbytes = murmur3_32(SIX_BYTE, 6); - jchars = murmur3_32(THREE_CHAR, 3); - assert (jbytes == jchars, - err_msg("Hashes did not match. b:%08x != c:%08x\n", jbytes, jchars)); + for (int i = 0; i < 16; i++) + vector[i] = 0x03020100 + i * 0x04040404; - jbytes = murmur3_32(EIGHT_BYTE, 8); - jchars = murmur3_32(FOUR_CHAR, 4); - ints = murmur3_32(TWO_INT, 2); - assert ((jbytes == jchars) && (jbytes == ints), - err_msg("Hashes did not match. b:%08x != c:%08x != i:%08x\n", jbytes, jchars, ints)); -} + for (int i = 0; i < 16; i++) { + uint64_t hash = AltHashing::halfsiphash_64(seed, vector, i); + assert(results[i] == hash, + err_msg( + "Calculated hash result not as expected. Round %d: " + "Expected " UINT64_FORMAT_X " got " UINT64_FORMAT_X "\n", + i, + results[i], + hash)); + } + } -// Returns true if the alternate hashcode is correct void AltHashing::test_alt_hash() { - testMurmur3_32_ByteArray(); - testEquivalentHashes(); + testHalfsiphash_32_ByteArray(); + testHalfsiphash_32_CharArray(); + testHalfsiphash_64_FromReference(); } #endif // PRODUCT
--- a/src/share/vm/classfile/altHashing.hpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/classfile/altHashing.hpp Mon Nov 02 02:26:23 2020 +0000 @@ -26,37 +26,30 @@ #define SHARE_VM_CLASSFILE_ALTHASHING_HPP #include "prims/jni.h" -#include "classfile/symbolTable.hpp" +#include "memory/allocation.hpp" /** - * Hashing utilities. - * - * Implementation of Murmur3 hashing. - * This code was translated from src/share/classes/sun/misc/Hashing.java - * code in the JDK. + * Implementation of alternate more secure hashing. */ class AltHashing : AllStatic { - // utility function copied from java/lang/Integer - static jint Integer_rotateLeft(jint i, int distance) { - return (i << distance) | (((juint)i) >> (32-distance)); - } - static jint murmur3_32(const int* data, int len); - static jint murmur3_32(jint seed, const int* data, int len); + // For the seed computation + static uint64_t halfsiphash_64(const uint32_t* data, int len); + static uint64_t halfsiphash_64(uint64_t seed, const uint32_t* data, int len); + #ifndef PRODUCT + // Hashing functions used for internal testing + static void testHalfsiphash_32_ByteArray(); + static void testHalfsiphash_32_CharArray(); + static void testHalfsiphash_64_FromReference(); + #endif // PRODUCT + public: + static uint64_t compute_seed(); -#ifndef PRODUCT - // Hashing functions used for internal testing - static jint murmur3_32(const jbyte* data, int len); - static jint murmur3_32(const jchar* data, int len); - static void testMurmur3_32_ByteArray(); - static void testEquivalentHashes(); -#endif // PRODUCT - - public: - static jint compute_seed(); - static jint murmur3_32(jint seed, const jbyte* data, int len); - static jint murmur3_32(jint seed, const jchar* data, int len); + // For Symbols + static uint32_t halfsiphash_32(uint64_t seed, const uint8_t* data, int len); + // For Strings + static uint32_t halfsiphash_32(uint64_t seed, const uint16_t* data, int len); NOT_PRODUCT(static void test_alt_hash();) }; #endif // SHARE_VM_CLASSFILE_ALTHASHING_HPP
--- a/src/share/vm/classfile/classFileParser.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/classfile/classFileParser.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -2556,6 +2556,80 @@ // Inner classes can be static, private or protected (classic VM does this) #define RECOGNIZED_INNER_CLASS_MODIFIERS (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED | JVM_ACC_STATIC) +// Find index of the InnerClasses entry for the specified inner_class_info_index. +// Return -1 if none is found. +static int inner_classes_find_index(typeArrayHandle inner_classes, int inner, constantPoolHandle cp, int length) { + Symbol* cp_klass_name = cp->klass_name_at(inner); + for (int idx = 0; idx < length; idx += instanceKlass::inner_class_next_offset) { + int idx_inner = inner_classes->ushort_at(idx + instanceKlass::inner_class_inner_class_info_offset); + if (cp->klass_name_at(idx_inner) == cp_klass_name) { + return idx; + } + } + return -1; +} + +// Return the outer_class_info_index for the InnerClasses entry containing the +// specified inner_class_info_index. Return -1 if no InnerClasses entry is found. +static int inner_classes_jump_to_outer(typeArrayHandle inner_classes, int inner, constantPoolHandle cp, int length) { + if (inner == 0) return -1; + int idx = inner_classes_find_index(inner_classes, inner, cp, length); + if (idx == -1) return -1; + int result = inner_classes->ushort_at(idx + instanceKlass::inner_class_outer_class_info_offset); + return result; +} + +// Return true if circularity is found, false if no circularity is found. +// Use Floyd's cycle finding algorithm. +static bool inner_classes_check_loop_through_outer(typeArrayHandle inner_classes, int idx, constantPoolHandle cp, int length) { + int slow = inner_classes->ushort_at(idx + instanceKlass::inner_class_inner_class_info_offset); + int fast = inner_classes->ushort_at(idx + instanceKlass::inner_class_outer_class_info_offset); + while (fast != -1 && fast != 0) { + if (slow != 0 && (cp->klass_name_at(slow) == cp->klass_name_at(fast))) { + return true; // found a circularity + } + fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length); + if (fast == -1) return false; + fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length); + if (fast == -1) return false; + slow = inner_classes_jump_to_outer(inner_classes, slow, cp, length); + assert(slow != -1, "sanity check"); + } + return false; +} + +// Loop through each InnerClasses entry checking for circularities and duplications +// with other entries. If duplicate entries are found then throw CFE. Otherwise, +// return true if a circularity or entries with duplicate inner_class_info_indexes +// are found. +bool ClassFileParser::check_inner_classes_circularity(constantPoolHandle cp, int length, TRAPS) { + // Loop through each InnerClasses entry. + for (int idx = 0; idx < length; idx += instanceKlass::inner_class_next_offset) { + // Return true if there are circular entries. + if (inner_classes_check_loop_through_outer(_inner_classes, idx, cp, length)) { + return true; + } + // Check if there are duplicate entries or entries with the same inner_class_info_index. + for (int y = idx + instanceKlass::inner_class_next_offset; y < length; + y += instanceKlass::inner_class_next_offset) { + + // To maintain compatibility, throw an exception if duplicate inner classes + // entries are found. + guarantee_property((_inner_classes->ushort_at(idx) != _inner_classes->ushort_at(y) || + _inner_classes->ushort_at(idx+1) != _inner_classes->ushort_at(y+1) || + _inner_classes->ushort_at(idx+2) != _inner_classes->ushort_at(y+2) || + _inner_classes->ushort_at(idx+3) != _inner_classes->ushort_at(y+3)), + "Duplicate entry in InnerClasses attribute in class file %s", + CHECK_(true)); + // Return true if there are two entries with the same inner_class_info_index. + if (_inner_classes->ushort_at(y) == _inner_classes->ushort_at(idx)) { + return true; + } + } + } + return false; +} + // Return number of classes in the inner classes attribute table u2 ClassFileParser::parse_classfile_inner_classes_attribute(u1* inner_classes_attribute_start, bool parsed_enclosingmethod_attribute, @@ -2584,6 +2658,7 @@ int size = length * 4 + (parsed_enclosingmethod_attribute ? 2 : 0); typeArrayOop ic = oopFactory::new_permanent_shortArray(size, CHECK_0); typeArrayHandle inner_classes(THREAD, ic); + set_class_inner_classes(inner_classes); int index = 0; int cp_size = cp->length(); cfs->guarantee_more(8 * length, CHECK_0); // 4-tuples of u2 @@ -2632,15 +2707,17 @@ } // 4347400: make sure there's no duplicate entry in the classes array + bool has_circularity = false; if (_need_verify && _major_version >= JAVA_1_5_VERSION) { - for(int i = 0; i < length * 4; i += 4) { - for(int j = i + 4; j < length * 4; j += 4) { - guarantee_property((inner_classes->ushort_at(i) != inner_classes->ushort_at(j) || - inner_classes->ushort_at(i+1) != inner_classes->ushort_at(j+1) || - inner_classes->ushort_at(i+2) != inner_classes->ushort_at(j+2) || - inner_classes->ushort_at(i+3) != inner_classes->ushort_at(j+3)), - "Duplicate entry in InnerClasses in class file %s", - CHECK_0); + has_circularity = check_inner_classes_circularity(cp, length * 4, CHECK_0); + if (has_circularity) { + // If circularity check failed then ignore InnerClasses attribute. + index = 0; + if (parsed_enclosingmethod_attribute) { + ic = oopFactory::new_permanent_shortArray(size, CHECK_0); + inner_classes = typeArrayHandle(THREAD, ic); + } else { + inner_classes = Universe::the_empty_short_array(); } } } @@ -2650,7 +2727,7 @@ inner_classes->short_at_put(index++, enclosing_method_class_index); inner_classes->short_at_put(index++, enclosing_method_method_index); } - assert(index == size, "wrong size"); + assert(index == size || has_circularity, "wrong size"); // Update instanceKlass with inner class info. set_class_inner_classes(inner_classes);
--- a/src/share/vm/classfile/classFileParser.hpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/classfile/classFileParser.hpp Mon Nov 02 02:26:23 2020 +0000 @@ -205,6 +205,10 @@ u2 parse_generic_signature_attribute(constantPoolHandle cp, TRAPS); void parse_classfile_sourcefile_attribute(constantPoolHandle cp, TRAPS); void parse_classfile_source_debug_extension_attribute(constantPoolHandle cp, int length, TRAPS); + + // Check for circularity in InnerClasses attribute. + bool check_inner_classes_circularity(constantPoolHandle cp, int length, TRAPS); + u2 parse_classfile_inner_classes_attribute(u1* inner_classes_attribute_start, bool parsed_enclosingmethod_attribute, u2 enclosing_method_class_index,
--- a/src/share/vm/classfile/symbolTable.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/classfile/symbolTable.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -216,7 +216,7 @@ // Pick hashing algorithm. unsigned int SymbolTable::hash_symbol(const char* s, int len) { return use_alternate_hashcode() ? - AltHashing::murmur3_32(seed(), (const jbyte*)s, len) : + AltHashing::halfsiphash_32(seed(), (const uint8_t*)s, len) : java_lang_String::to_hash(s, len); } @@ -656,7 +656,7 @@ // Pick hashing algorithm unsigned int StringTable::hash_string(const jchar* s, int len) { - return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) : + return use_alternate_hashcode() ? AltHashing::halfsiphash_32(seed(), s, len) : java_lang_String::to_hash(s, len); }
--- a/src/share/vm/classfile/systemDictionary.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/classfile/systemDictionary.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1034,6 +1034,18 @@ return k(); } +static bool is_prohibited_package_slow(Symbol* class_name) { + // Caller has ResourceMark + int length; + jchar* unicode = class_name->as_unicode(length); + return (length >= 5 && + unicode[0] == 'j' && + unicode[1] == 'a' && + unicode[2] == 'v' && + unicode[3] == 'a' && + unicode[4] == '/'); +} + // Add a klass to the system from a stream (called by jni_DefineClass and // JVM_DefineClass). // Note: class_name can be NULL. In that case we do not know the name of @@ -1081,24 +1093,33 @@ if (!HAS_PENDING_EXCEPTION && !class_loader.is_null() && parsed_name != NULL && - parsed_name->utf8_length() >= (int)pkglen && - !strncmp((const char*)parsed_name->bytes(), pkg, pkglen)) { - // It is illegal to define classes in the "java." package from - // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader + parsed_name->utf8_length() >= (int)pkglen) { ResourceMark rm(THREAD); - char* name = parsed_name->as_C_string(); - char* index = strrchr(name, '/'); - assert(index != NULL, "must be"); - *index = '\0'; // chop to just the package name - while ((index = strchr(name, '/')) != NULL) { - *index = '.'; // replace '/' with '.' in package name + bool prohibited; + const jbyte* base = parsed_name->base(); + if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) { + prohibited = is_prohibited_package_slow(parsed_name); + } else { + char* name = parsed_name->as_C_string(); + prohibited = (strncmp(name, pkg, pkglen) == 0); } - const char* fmt = "Prohibited package name: %s"; - size_t len = strlen(fmt) + strlen(name); - char* message = NEW_RESOURCE_ARRAY(char, len); - jio_snprintf(message, len, fmt, name); - Exceptions::_throw_msg(THREAD_AND_LOCATION, - vmSymbols::java_lang_SecurityException(), message); + if (prohibited) { + // It is illegal to define classes in the "java." package from + // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader + char* name = parsed_name->as_C_string(); + char* index = strrchr(name, '/'); + assert(index != NULL, "must be"); + *index = '\0'; // chop to just the package name + while ((index = strchr(name, '/')) != NULL) { + *index = '.'; // replace '/' with '.' in package name + } + const char* fmt = "Prohibited package name: %s"; + size_t len = strlen(fmt) + strlen(name); + char* message = NEW_RESOURCE_ARRAY(char, len); + jio_snprintf(message, len, fmt, name); + Exceptions::_throw_msg(THREAD_AND_LOCATION, + vmSymbols::java_lang_SecurityException(), message); + } } if (!HAS_PENDING_EXCEPTION) {
--- a/src/share/vm/opto/addnode.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/opto/addnode.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -844,6 +844,14 @@ return TypeInt::make( MAX2(r0->_lo,r1->_lo), MAX2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) ); } +// Check if addition of an integer with type 't' and a constant 'c' can overflow +static bool can_overflow(const TypeInt* t, jint c) { + jint t_lo = t->_lo; + jint t_hi = t->_hi; + return ((c < 0 && (java_add(t_lo, c) > t_lo)) || + (c > 0 && (java_add(t_hi, c) < t_hi))); +} + //============================================================================= //------------------------------Idealize--------------------------------------- // MINs show up in range-check loop limit calculations. Look for @@ -866,7 +874,7 @@ // Get left input & constant Node *x = l; - int x_off = 0; + jint x_off = 0; if( x->Opcode() == Op_AddI && // Check for "x+c0" and collect constant x->in(2)->is_Con() ) { const Type *t = x->in(2)->bottom_type(); @@ -877,7 +885,7 @@ // Scan a right-spline-tree for MINs Node *y = r; - int y_off = 0; + jint y_off = 0; // Check final part of MIN tree if( y->Opcode() == Op_AddI && // Check for "y+c1" and collect constant y->in(2)->is_Con() ) { @@ -891,6 +899,7 @@ return this; } + const TypeInt* tx = phase->type(x)->isa_int(); if( r->Opcode() == Op_MinI ) { assert( r != r->in(2), "dead loop in MinINode::Ideal" ); @@ -907,18 +916,23 @@ if( x->_idx > y->_idx ) return new (phase->C) MinINode(r->in(1),phase->transform(new (phase->C) MinINode(l,r->in(2)))); - // See if covers: MIN2(x+c0,MIN2(y+c1,z)) - if( !phase->eqv(x,y) ) return NULL; - // If (y == x) transform MIN2(x+c0, MIN2(x+c1,z)) into - // MIN2(x+c0 or x+c1 which less, z). - return new (phase->C) MinINode(phase->transform(new (phase->C) AddINode(x,phase->intcon(MIN2(x_off,y_off)))),r->in(2)); + // Transform MIN2(x + c0, MIN2(x + c1, z)) into MIN2(x + MIN2(c0, c1), z) + // if x == y and the additions can't overflow. + if (phase->eqv(x,y) && + !can_overflow(tx, x_off) && + !can_overflow(tx, y_off)) { + return new (phase->C) MinINode(phase->transform(new (phase->C) AddINode(x, phase->intcon(MIN2(x_off, y_off)))), r->in(2)); + } } else { - // See if covers: MIN2(x+c0,y+c1) - if( !phase->eqv(x,y) ) return NULL; - // If (y == x) transform MIN2(x+c0,x+c1) into x+c0 or x+c1 which less. - return new (phase->C) AddINode(x,phase->intcon(MIN2(x_off,y_off))); + // Transform MIN2(x + c0, y + c1) into x + MIN2(c0, c1) + // if x == y and the additions can't overflow. + if (phase->eqv(x,y) && + !can_overflow(tx, x_off) && + !can_overflow(tx, y_off)) { + return new (phase->C) AddINode(x,phase->intcon(MIN2(x_off,y_off))); + } } - + return NULL; } //------------------------------add_ring---------------------------------------
--- a/src/share/vm/opto/loopTransform.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/opto/loopTransform.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -1517,58 +1517,78 @@ } //------------------------------adjust_limit----------------------------------- -// Helper function for add_constraint(). -Node* PhaseIdealLoop::adjust_limit(int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl) { - // Compute "I :: (limit-offset)/scale" - Node *con = new (C) SubINode(rc_limit, offset); - register_new_node(con, pre_ctrl); - Node *X = new (C) DivINode(0, con, scale); - register_new_node(X, pre_ctrl); +// Helper function that computes new loop limit as (rc_limit-offset)/scale +Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round) { + Node* sub = new (C) SubLNode(rc_limit, offset); + register_new_node(sub, pre_ctrl); + Node* limit = new (C) DivLNode(NULL, sub, scale); + register_new_node(limit, pre_ctrl); + + // When the absolute value of scale is greater than one, the division + // may round limit down/up, so add/sub one to/from the limit. + if (round) { + limit = new (C) AddLNode(limit, _igvn.longcon(is_positive_stride ? -1 : 1)); + register_new_node(limit, pre_ctrl); + } - // Adjust loop limit - loop_limit = (stride_con > 0) - ? (Node*)(new (C) MinINode(loop_limit, X)) - : (Node*)(new (C) MaxINode(loop_limit, X)); - register_new_node(loop_limit, pre_ctrl); - return loop_limit; + // Clamp the limit to handle integer under-/overflows. + // When reducing the limit, clamp to [min_jint, old_limit]: + // MIN(old_limit, MAX(limit, min_jint)) + // When increasing the limit, clamp to [old_limit, max_jint]: + // MAX(old_limit, MIN(limit, max_jint)) + Node* cmp = new (C) CmpLNode(limit, _igvn.longcon(is_positive_stride ? min_jint : max_jint)); + register_new_node(cmp, pre_ctrl); + Node* bol = new (C) BoolNode(cmp, is_positive_stride ? BoolTest::lt : BoolTest::gt); + register_new_node(bol, pre_ctrl); + limit = new (C) ConvL2INode(limit); + register_new_node(limit, pre_ctrl); + limit = new (C) CMoveINode(bol, limit, _igvn.intcon(is_positive_stride ? min_jint : max_jint), TypeInt::INT); + register_new_node(limit, pre_ctrl); + + limit = is_positive_stride ? (Node*)(new (C) MinINode(old_limit, limit)) + : (Node*)(new (C) MaxINode(old_limit, limit)); + register_new_node(limit, pre_ctrl); + return limit; } //------------------------------add_constraint--------------------------------- // Constrain the main loop iterations so the conditions: -// low_limit <= scale_con * I + offset < upper_limit -// always holds true. That is, either increase the number of iterations in -// the pre-loop or the post-loop until the condition holds true in the main -// loop. Stride, scale, offset and limit are all loop invariant. Further, -// stride and scale are constants (offset and limit often are). -void PhaseIdealLoop::add_constraint( int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ) { - // For positive stride, the pre-loop limit always uses a MAX function - // and the main loop a MIN function. For negative stride these are - // reversed. +// low_limit <= scale_con*I + offset < upper_limit +// always hold true. That is, either increase the number of iterations in the +// pre-loop or reduce the number of iterations in the main-loop until the condition +// holds true in the main-loop. Stride, scale, offset and limit are all loop +// invariant. Further, stride and scale are constants (offset and limit often are). +void PhaseIdealLoop::add_constraint(jlong stride_con, jlong scale_con, Node* offset, Node* low_limit, Node* upper_limit, Node* pre_ctrl, Node** pre_limit, Node** main_limit) { + assert(_igvn.type(offset)->isa_long() != NULL && _igvn.type(low_limit)->isa_long() != NULL && + _igvn.type(upper_limit)->isa_long() != NULL, "arguments should be long values"); - // Also for positive stride*scale the affine function is increasing, so the - // pre-loop must check for underflow and the post-loop for overflow. - // Negative stride*scale reverses this; pre-loop checks for overflow and - // post-loop for underflow. + // For a positive stride, we need to reduce the main-loop limit and + // increase the pre-loop limit. This is reversed for a negative stride. + bool is_positive_stride = (stride_con > 0); - Node *scale = _igvn.intcon(scale_con); + // If the absolute scale value is greater one, division in 'adjust_limit' may require + // rounding. Make sure the ABS method correctly handles min_jint. + // Only do this for the pre-loop, one less iteration of the main loop doesn't hurt. + bool round = ABS(scale_con) > 1; + + Node* scale = _igvn.longcon(scale_con); set_ctrl(scale, C->root()); if ((stride_con^scale_con) >= 0) { // Use XOR to avoid overflow + // Positive stride*scale: the affine function is increasing, + // the pre-loop checks for underflow and the post-loop for overflow. + // The overflow limit: scale*I+offset < upper_limit - // For main-loop compute + // For the main-loop limit compute: // ( if (scale > 0) /* and stride > 0 */ // I < (upper_limit-offset)/scale // else /* scale < 0 and stride < 0 */ // I > (upper_limit-offset)/scale // ) - // - // (upper_limit-offset) may overflow or underflow. - // But it is fine since main loop will either have - // less iterations or will be skipped in such case. - *main_limit = adjust_limit(stride_con, scale, offset, upper_limit, *main_limit, pre_ctrl); + *main_limit = adjust_limit(is_positive_stride, scale, offset, upper_limit, *main_limit, pre_ctrl, false); - // The underflow limit: low_limit <= scale*I+offset. - // For pre-loop compute + // The underflow limit: low_limit <= scale*I+offset + // For the pre-loop limit compute: // NOT(scale*I+offset >= low_limit) // scale*I+offset < low_limit // ( if (scale > 0) /* and stride > 0 */ @@ -1576,39 +1596,13 @@ // else /* scale < 0 and stride < 0 */ // I > (low_limit-offset)/scale // ) + *pre_limit = adjust_limit(!is_positive_stride, scale, offset, low_limit, *pre_limit, pre_ctrl, round); + } else { + // Negative stride*scale: the affine function is decreasing, + // the pre-loop checks for overflow and the post-loop for underflow. - if (low_limit->get_int() == -max_jint) { - if (!RangeLimitCheck) return; - // We need this guard when scale*pre_limit+offset >= limit - // due to underflow. So we need execute pre-loop until - // scale*I+offset >= min_int. But (min_int-offset) will - // underflow when offset > 0 and X will be > original_limit - // when stride > 0. To avoid it we replace positive offset with 0. - // - // Also (min_int+1 == -max_int) is used instead of min_int here - // to avoid problem with scale == -1 (min_int/(-1) == min_int). - Node* shift = _igvn.intcon(31); - set_ctrl(shift, C->root()); - Node* sign = new (C) RShiftINode(offset, shift); - register_new_node(sign, pre_ctrl); - offset = new (C) AndINode(offset, sign); - register_new_node(offset, pre_ctrl); - } else { - assert(low_limit->get_int() == 0, "wrong low limit for range check"); - // The only problem we have here when offset == min_int - // since (0-min_int) == min_int. It may be fine for stride > 0 - // but for stride < 0 X will be < original_limit. To avoid it - // max(pre_limit, original_limit) is used in do_range_check(). - } - // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond); - *pre_limit = adjust_limit((-stride_con), scale, offset, low_limit, *pre_limit, pre_ctrl); - - } else { // stride_con*scale_con < 0 - // For negative stride*scale pre-loop checks for overflow and - // post-loop for underflow. - // // The overflow limit: scale*I+offset < upper_limit - // For pre-loop compute + // For the pre-loop limit compute: // NOT(scale*I+offset < upper_limit) // scale*I+offset >= upper_limit // scale*I+offset+1 > upper_limit @@ -1617,56 +1611,24 @@ // else /* scale > 0 and stride < 0 */ // I > (upper_limit-(offset+1))/scale // ) - // - // (upper_limit-offset-1) may underflow or overflow. - // To avoid it min(pre_limit, original_limit) is used - // in do_range_check() for stride > 0 and max() for < 0. - Node *one = _igvn.intcon(1); + Node* one = _igvn.longcon(1); set_ctrl(one, C->root()); - - Node *plus_one = new (C) AddINode(offset, one); + Node* plus_one = new (C) AddLNode(offset, one); register_new_node( plus_one, pre_ctrl ); - // Pass (-stride) to indicate pre_loop_cond = NOT(main_loop_cond); - *pre_limit = adjust_limit((-stride_con), scale, plus_one, upper_limit, *pre_limit, pre_ctrl); + *pre_limit = adjust_limit(!is_positive_stride, scale, plus_one, upper_limit, *pre_limit, pre_ctrl, round); - if (low_limit->get_int() == -max_jint) { - if (!RangeLimitCheck) return; - // We need this guard when scale*main_limit+offset >= limit - // due to underflow. So we need execute main-loop while - // scale*I+offset+1 > min_int. But (min_int-offset-1) will - // underflow when (offset+1) > 0 and X will be < main_limit - // when scale < 0 (and stride > 0). To avoid it we replace - // positive (offset+1) with 0. - // - // Also (min_int+1 == -max_int) is used instead of min_int here - // to avoid problem with scale == -1 (min_int/(-1) == min_int). - Node* shift = _igvn.intcon(31); - set_ctrl(shift, C->root()); - Node* sign = new (C) RShiftINode(plus_one, shift); - register_new_node(sign, pre_ctrl); - plus_one = new (C) AndINode(plus_one, sign); - register_new_node(plus_one, pre_ctrl); - } else { - assert(low_limit->get_int() == 0, "wrong low limit for range check"); - // The only problem we have here when offset == max_int - // since (max_int+1) == min_int and (0-min_int) == min_int. - // But it is fine since main loop will either have - // less iterations or will be skipped in such case. - } - // The underflow limit: low_limit <= scale*I+offset. - // For main-loop compute + // The underflow limit: low_limit <= scale*I+offset + // For the main-loop limit compute: // scale*I+offset+1 > low_limit // ( if (scale < 0) /* and stride > 0 */ // I < (low_limit-(offset+1))/scale // else /* scale > 0 and stride < 0 */ // I > (low_limit-(offset+1))/scale // ) - - *main_limit = adjust_limit(stride_con, scale, plus_one, low_limit, *main_limit, pre_ctrl); + *main_limit = adjust_limit(is_positive_stride, scale, plus_one, low_limit, *main_limit, pre_ctrl, false); } } - //------------------------------is_scaled_iv--------------------------------- // Return true if exp is a constant times an induction var bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) { @@ -1826,22 +1788,14 @@ // Must know if its a count-up or count-down loop int stride_con = cl->stride_con(); - Node *zero = _igvn.intcon(0); - Node *one = _igvn.intcon(1); + Node* zero = _igvn.longcon(0); + Node* one = _igvn.longcon(1); // Use symmetrical int range [-max_jint,max_jint] - Node *mini = _igvn.intcon(-max_jint); + Node* mini = _igvn.longcon(-max_jint); set_ctrl(zero, C->root()); set_ctrl(one, C->root()); set_ctrl(mini, C->root()); - // Range checks that do not dominate the loop backedge (ie. - // conditionally executed) can lengthen the pre loop limit beyond - // the original loop limit. To prevent this, the pre limit is - // (for stride > 0) MINed with the original loop limit (MAXed - // stride < 0) when some range_check (rc) is conditionally - // executed. - bool conditional_rc = false; - // Check loop body for tests of trip-counter plus loop-invariant vs // loop-invariant. for( uint i = 0; i < loop->_body.size(); i++ ) { @@ -1920,15 +1874,20 @@ // stride_con and scale_con can be negative which will flip about the // sense of the test. + // Perform the limit computations in jlong to avoid overflow + jlong lscale_con = scale_con; + Node* int_offset = offset; + offset = new (C) ConvI2LNode(offset); + register_new_node(offset, pre_ctrl); + Node* int_limit = limit; + limit = new (C) ConvI2LNode(limit); + register_new_node(limit, pre_ctrl); + // Adjust pre and main loop limits to guard the correct iteration set if( cmp->Opcode() == Op_CmpU ) {// Unsigned compare is really 2 tests if( b_test._test == BoolTest::lt ) { // Range checks always use lt // The underflow and overflow limits: 0 <= scale*I+offset < limit - add_constraint( stride_con, scale_con, offset, zero, limit, pre_ctrl, &pre_limit, &main_limit ); - if (!conditional_rc) { - // (0-offset)/scale could be outside of loop iterations range. - conditional_rc = !loop->dominates_backedge(iff) || RangeLimitCheck; - } + add_constraint(stride_con, lscale_con, offset, zero, limit, pre_ctrl, &pre_limit, &main_limit); } else { #ifndef PRODUCT if( PrintOpto ) @@ -1942,16 +1901,16 @@ // Fall into GE case case BoolTest::ge: // Convert (I*scale+offset) >= Limit to (I*(-scale)+(-offset)) <= -Limit - scale_con = -scale_con; - offset = new (C) SubINode( zero, offset ); + lscale_con = -lscale_con; + offset = new (C) SubLNode(zero, offset); register_new_node( offset, pre_ctrl ); - limit = new (C) SubINode( zero, limit ); + limit = new (C) SubLNode(zero, limit); register_new_node( limit, pre_ctrl ); // Fall into LE case case BoolTest::le: if (b_test._test != BoolTest::gt) { // Convert X <= Y to X < Y+1 - limit = new (C) AddINode( limit, one ); + limit = new (C) AddLNode(limit, one); register_new_node( limit, pre_ctrl ); } // Fall into LT case @@ -1959,13 +1918,7 @@ // The underflow and overflow limits: MIN_INT <= scale*I+offset < limit // Note: (MIN_INT+1 == -MAX_INT) is used instead of MIN_INT here // to avoid problem with scale == -1: MIN_INT/(-1) == MIN_INT. - add_constraint( stride_con, scale_con, offset, mini, limit, pre_ctrl, &pre_limit, &main_limit ); - if (!conditional_rc) { - // ((MIN_INT+1)-offset)/scale could be outside of loop iterations range. - // Note: negative offset is replaced with 0 but (MIN_INT+1)/scale could - // still be outside of loop range. - conditional_rc = !loop->dominates_backedge(iff) || RangeLimitCheck; - } + add_constraint(stride_con, lscale_con, offset, mini, limit, pre_ctrl, &pre_limit, &main_limit); break; default: #ifndef PRODUCT @@ -2001,7 +1954,8 @@ } // Update loop limits - if (conditional_rc) { + if (pre_limit != orig_limit) { + // Computed pre-loop limit can be outside of loop iterations range. pre_limit = (stride_con > 0) ? (Node*)new (C) MinINode(pre_limit, orig_limit) : (Node*)new (C) MaxINode(pre_limit, orig_limit); register_new_node(pre_limit, pre_ctrl);
--- a/src/share/vm/opto/loopnode.hpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/opto/loopnode.hpp Mon Nov 02 02:26:23 2020 +0000 @@ -945,9 +945,9 @@ // always holds true. That is, either increase the number of iterations in // the pre-loop or the post-loop until the condition holds true in the main // loop. Scale_con, offset and limit are all loop invariant. - void add_constraint( int stride_con, int scale_con, Node *offset, Node *low_limit, Node *upper_limit, Node *pre_ctrl, Node **pre_limit, Node **main_limit ); + void add_constraint(jlong stride_con, jlong scale_con, Node* offset, Node* low_limit, Node* upper_limit, Node* pre_ctrl, Node** pre_limit, Node** main_limit); // Helper function for add_constraint(). - Node* adjust_limit( int stride_con, Node * scale, Node *offset, Node *rc_limit, Node *loop_limit, Node *pre_ctrl ); + Node* adjust_limit(bool reduce, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round); // Partially peel loop up through last_peel node. bool partial_peel( IdealLoopTree *loop, Node_List &old_new );
--- a/src/share/vm/opto/type.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/opto/type.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -2780,12 +2780,8 @@ // Type-specific hashing function. int TypeOopPtr::hash(void) const { return - (const_oop() ? const_oop()->hash() : 0) + - _klass_is_exact + - _instance_id + - TypePtr::hash(); java_add(java_add(const_oop() ? const_oop()->hash() : 0, _klass_is_exact), - java_add(_instance_id, TypePtr::hash())); + java_add(_instance_id , TypePtr::hash())); } //------------------------------dump2------------------------------------------ @@ -3916,7 +3912,7 @@ //------------------------------hash------------------------------------------- // Type-specific hashing function. int TypeKlassPtr::hash(void) const { - return klass()->hash() + TypeOopPtr::hash(); + return java_add(klass()->hash(), TypeOopPtr::hash()); }
--- a/src/share/vm/prims/nativeLookup.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/prims/nativeLookup.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -57,27 +57,118 @@ #endif -static void mangle_name_on(outputStream* st, Symbol* name, int begin, int end) { +/* + +The JNI specification defines the mapping from a Java native method name to +a C native library implementation function name as follows: + + The mapping produces a native method name by concatenating the following components + derived from a `native` method declaration: + + 1. the prefix Java_ + 2. given the binary name, in internal form, of the class which declares the native method: + the result of escaping the name. + 3. an underscore ("_") + 4. the escaped method name + 5. if the native method declaration is overloaded: two underscores ("__") followed by the + escaped parameter descriptor (JVMS 4.3.3) of the method declaration. + + Escaping leaves every alphanumeric ASCII character (A-Za-z0-9) unchanged, and replaces each + UTF-16 code unit n the table below with the corresponding escape sequence. If the name to be + escaped contains a surrogate pair, then the high-surrogate code unit and the low-surrogate code + unit are escaped separately. The result of escaping is a string consisting only of the ASCII + characters A-Za-z0-9 and underscore. + + ------------------------------ ------------------------------------ + UTF-16 code unit Escape sequence + ------------------------------ ------------------------------------ + Forward slash (/, U+002F) _ + Underscore (_, U+005F) _1 + Semicolon (;, U+003B) _2 + Left square bracket ([, U+005B) _3 + Any UTF-16 code unit \u_WXYZ_ that does not _0wxyz where w, x, y, and z are the lower-case + represent alphanumeric ASCII (A-Za-z0-9), forms of the hexadecimal digits W, X, Y, and Z. + forward slash, underscore, semicolon, (For example, U+ABCD becomes _0abcd.) + or left square bracket + ------------------------------ ------------------------------------ + + Note that escape sequences can safely begin _0, _1, etc, because class and method + names in Java source code never begin with a number. However, that is not the case in + class files that were not generated from Java source code. + + To preserve the 1:1 mapping to a native method name, the VM checks the resulting name as + follows. If the process of escaping any precursor string from the native method declaration + (class or method name, or argument type) causes a "0", "1", "2", or "3" character + from the precursor string to appear unchanged in the result *either* immediately after an + underscore *or* at the beginning of the escaped string (where it will follow an underscore + in the fully assembled name), then the escaping process is said to have "failed". + In such cases, no native library search is performed, and the attempt to link the native + method invocation will throw UnsatisfiedLinkError. + + +For example: + + package/my_class/method + +and + + package/my/1class/method + +both map to + + Java_package_my_1class_method + +To address this potential conflict we need only check if the character after +/ is a digit 0..3, or if the first character after an injected '_' seperator +is a digit 0..3. If we encounter an invalid identifier we reset the +stringStream and return false. Otherwise the stringStream contains the mapped +name and we return true. + +To address legacy compatibility, the UseLegacyJNINameEscaping flag can be set +which skips the extra checks. + +*/ +static bool map_escaped_name_on(stringStream* st, Symbol* name, int begin, int end) { char* bytes = (char*)name->bytes() + begin; char* end_bytes = (char*)name->bytes() + end; + bool check_escape_char = true; // initially true as first character here follows '_' while (bytes < end_bytes) { jchar c; bytes = UTF8::next(bytes, &c); if (c <= 0x7f && isalnum(c)) { + if (check_escape_char && (c >= '0' && c <= '3') && + !UseLegacyJNINameEscaping) { + // This is a non-Java identifier and we won't escape it to + // ensure no name collisions with a Java identifier. + if (PrintJNIResolving) { + ResourceMark rm; + tty->print_cr("[Lookup of native method with non-Java identifier rejected: %s]", + name->as_C_string()); + } + st->reset(); // restore to "" on error + return false; + } st->put((char) c); + check_escape_char = false; } else { - if (c == '_') st->print("_1"); - else if (c == '/') st->print("_"); + check_escape_char = false; + if (c == '_') st->print("_1"); + else if (c == '/') { + st->print("_"); + // Following a / we must have non-escape character + check_escape_char = true; + } else if (c == ';') st->print("_2"); else if (c == '[') st->print("_3"); else st->print("_%.5x", c); } } + return true; } -static void mangle_name_on(outputStream* st, Symbol* name) { - mangle_name_on(st, name, 0, name->utf8_length()); +static bool map_escaped_name_on(stringStream* st, Symbol* name) { + return map_escaped_name_on(st, name, 0, name->utf8_length()); } @@ -86,10 +177,14 @@ // Prefix st.print("Java_"); // Klass name - mangle_name_on(&st, method->klass_name()); + if (!map_escaped_name_on(&st, method->klass_name())) { + return NULL; + } st.print("_"); // Method name - mangle_name_on(&st, method->name()); + if (!map_escaped_name_on(&st, method->name())) { + return NULL; + } return st.as_string(); } @@ -99,16 +194,20 @@ // Prefix st.print("JavaCritical_"); // Klass name - mangle_name_on(&st, method->klass_name()); + if (!map_escaped_name_on(&st, method->klass_name())) { + return NULL; + } st.print("_"); // Method name - mangle_name_on(&st, method->name()); + if (!map_escaped_name_on(&st, method->name())) { + return NULL; + } return st.as_string(); } char* NativeLookup::long_jni_name(methodHandle method) { - // Signature ignore the wrapping parenteses and the trailing return type + // Signatures ignore the wrapping parentheses and the trailing return type stringStream st; Symbol* signature = method->signature(); st.print("__"); @@ -116,7 +215,10 @@ int end; for (end = 0; end < signature->utf8_length() && signature->byte_at(end) != ')'; end++); // skip first '(' - mangle_name_on(&st, signature, 1, end); + if (!map_escaped_name_on(&st, signature, 1, end)) { + return NULL; + } + return st.as_string(); } @@ -246,6 +348,11 @@ in_base_library = false; // Compute pure name char* pure_name = pure_jni_name(method); + if (pure_name == NULL) { + // JNI name mapping rejected this method so return + // NULL to indicate UnsatisfiedLinkError should be thrown. + return NULL; + } // Compute argument size int args_size = 1 // JNIEnv @@ -259,6 +366,11 @@ // Compute long name char* long_name = long_jni_name(method); + if (long_name == NULL) { + // JNI name mapping rejected this method so return + // NULL to indicate UnsatisfiedLinkError should be thrown. + return NULL; + } // 2) Try JNI long style entry = lookup_style(method, pure_name, long_name, args_size, true, in_base_library, CHECK_NULL); @@ -298,6 +410,11 @@ // Compute critical name char* critical_name = critical_jni_name(method); + if (critical_name == NULL) { + // JNI name mapping rejected this method so return + // NULL to indicate UnsatisfiedLinkError should be thrown. + return NULL; + } // Compute argument size int args_size = 1 // JNIEnv @@ -311,6 +428,11 @@ // Compute long name char* long_name = long_jni_name(method); + if (long_name == NULL) { + // JNI name mapping rejected this method so return + // NULL to indicate UnsatisfiedLinkError should be thrown. + return NULL; + } // 2) Try JNI long style entry = lookup_critical_style(method, critical_name, long_name, args_size, true);
--- a/src/share/vm/runtime/globals.hpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/runtime/globals.hpp Mon Nov 02 02:26:23 2020 +0000 @@ -658,6 +658,9 @@ product(bool, CriticalJNINatives, true, \ "check for critical JNI entry points") \ \ + product(bool, UseLegacyJNINameEscaping, false, \ + "Use the original JNI name escaping scheme") \ + \ notproduct(bool, StressCriticalJNINatives, false, \ "Exercise register saving code in critical natives") \ \
--- a/src/share/vm/utilities/hashtable.cpp Sat Sep 26 19:05:33 2020 +0100 +++ b/src/share/vm/utilities/hashtable.cpp Mon Nov 02 02:26:23 2020 +0000 @@ -97,7 +97,7 @@ template <class T, MEMFLAGS F> unsigned int Hashtable<T, F>::new_hash(Symbol* sym) { ResourceMark rm; // Use alternate hashing algorithm on this symbol. - return AltHashing::murmur3_32(seed(), (const jbyte*)sym->as_C_string(), sym->utf8_length()); + return AltHashing::halfsiphash_32(seed(), (const uint8_t*)sym->as_C_string(), sym->utf8_length()); } template <class T, MEMFLAGS F> unsigned int Hashtable<T, F>::new_hash(oop string) { @@ -105,7 +105,7 @@ int length; jchar* chars = java_lang_String::as_unicode_string(string, length); // Use alternate hashing algorithm on the string - return AltHashing::murmur3_32(seed(), chars, length); + return AltHashing::halfsiphash_32(seed(), chars, length); } // Create a new table and using alternate hash code, populate the new table
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rangechecks/RangeCheckEliminationScaleNotOne.java Mon Nov 02 02:26:23 2020 +0000 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8215265 + * @summary C2: range check elimination may allow illegal out of bound access + * + * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:-UseLoopPredicate RangeCheckEliminationScaleNotOne + * + */ + +import java.util.Arrays; + +public class RangeCheckEliminationScaleNotOne { + public static void main(String[] args) { + { + int[] array = new int[199]; + boolean[] flags = new boolean[100]; + Arrays.fill(flags, true); + flags[0] = false; + flags[1] = false; + for (int i = 0; i < 20_000; i++) { + test1(100, array, 0, flags); + } + boolean ex = false; + try { + test1(100, array, -5, flags); + } catch (ArrayIndexOutOfBoundsException aie) { + ex = true; + } + if (!ex) { + throw new RuntimeException("no AIOOB exception"); + } + } + + { + int[] array = new int[199]; + boolean[] flags = new boolean[100]; + Arrays.fill(flags, true); + flags[0] = false; + flags[1] = false; + for (int i = 0; i < 20_000; i++) { + test2(100, array, 198, flags); + } + boolean ex = false; + try { + test2(100, array, 203, flags); + } catch (ArrayIndexOutOfBoundsException aie) { + ex = true; + } + if (!ex) { + throw new RuntimeException("no AIOOB exception"); + } + } + } + + private static int test1(int stop, int[] array, int offset, boolean[] flags) { + if (array == null) {} + int res = 0; + for (int i = 0; i < stop; i++) { + if (flags[i]) { + res += array[2 * i + offset]; + } + } + return res; + } + + + private static int test2(int stop, int[] array, int offset, boolean[] flags) { + if (array == null) {} + int res = 0; + for (int i = 0; i < stop; i++) { + if (flags[i]) { + res += array[-2 * i + offset]; + } + } + return res; + } +}