OpenJDK / jdk / jdk
changeset 56115:e28ccaff2f84
Merge
author | psadhukhan |
---|---|
date | Wed, 24 Jul 2019 12:21:36 +0530 |
parents | ce786c3f1f1c 042dfb697624 |
children | 70865ef2afc7 |
files | src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java src/jdk.javadoc/share/legal/pako.md test/jdk/ProblemList.txt |
diffstat | 147 files changed, 8240 insertions(+), 1610 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Mon Jul 22 11:08:27 2019 +0530 +++ b/.hgtags Wed Jul 24 12:21:36 2019 +0530 @@ -573,4 +573,5 @@ 19d0b382f0869f72d4381b54fa129f1c74b6e766 jdk-14+4 3081f39a3d30d63b112098386ac2bb027c2b7223 jdk-13+29 0f1e29c77e50c7da11d83df410026392c4d1a28c jdk-14+5 +2e63fb0a885fa908a97bbb0da8d7c3de11536aca jdk-13+30 443f7359b34d60e7821216ffc60f88b6ffe0ccdd jdk-14+6
--- a/doc/building.html Mon Jul 22 11:08:27 2019 +0530 +++ b/doc/building.html Wed Jul 24 12:21:36 2019 +0530 @@ -297,10 +297,7 @@ </tr> </tbody> </table> -<p>All compilers are expected to be able to compile to the C99 language standard, -as some C99 features are used in the source code. Microsoft Visual Studio -doesn't fully support C99 so in practice shared code is limited to using C99 -features that it does support.</p> +<p>All compilers are expected to be able to compile to the C99 language standard, as some C99 features are used in the source code. Microsoft Visual Studio doesn't fully support C99 so in practice shared code is limited to using C99 features that it does support.</p> <h3 id="gcc">gcc</h3> <p>The minimum accepted version of gcc is 4.8. Older versions will generate a warning by <code>configure</code> and are unlikely to work.</p> <p>The JDK is currently known to be able to compile with at least version 7.4 of gcc.</p>
--- a/doc/testing.html Mon Jul 22 11:08:27 2019 +0530 +++ b/doc/testing.html Wed Jul 24 12:21:36 2019 +0530 @@ -154,6 +154,9 @@ <p>Use additional problem lists file or files, in addition to the default ProblemList.txt located at the JTReg test roots.</p> <p>If multiple file names are specified, they should be separated by space (or, to help avoid quoting issues, the special value <code>%20</code>).</p> <p>The file names should be either absolute, or relative to the JTReg test root of the tests to be run.</p> +<h4 id="run_problem_lists">RUN_PROBLEM_LISTS</h4> +<p>Use the problem lists to select tests instead of excluding them.</p> +<p>Set to <code>true</code> or <code>false</code>. If <code>true</code>, JTReg will use <code>-match:</code> option, otherwise <code>-exclude:</code> will be used. Default is <code>false</code>.</p> <h4 id="options">OPTIONS</h4> <p>Additional options to the JTReg test framework.</p> <p>Use <code>JTREG="OPTIONS=--help all"</code> to see all available JTReg options.</p>
--- a/doc/testing.md Mon Jul 22 11:08:27 2019 +0530 +++ b/doc/testing.md Wed Jul 24 12:21:36 2019 +0530 @@ -306,6 +306,14 @@ The file names should be either absolute, or relative to the JTReg test root of the tests to be run. +#### RUN_PROBLEM_LISTS + +Use the problem lists to select tests instead of excluding them. + +Set to `true` or `false`. +If `true`, JTReg will use `-match:` option, otherwise `-exclude:` will be used. +Default is `false`. + #### OPTIONS Additional options to the JTReg test framework.
--- a/make/RunTests.gmk Mon Jul 22 11:08:27 2019 +0530 +++ b/make/RunTests.gmk Wed Jul 24 12:21:36 2019 +0530 @@ -278,7 +278,7 @@ $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR TEST_MODE ASSERT VERBOSE RETAIN \ - MAX_MEM, \ + MAX_MEM RUN_PROBLEM_LISTS, \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ EXTRA_PROBLEM_LISTS AOT_MODULES, \ )) @@ -828,6 +828,7 @@ endif JTREG_VERBOSE ?= fail,error,summary JTREG_RETAIN ?= fail,error + JTREG_RUN_PROBLEM_LISTS ?= false ifneq ($$($1_JTREG_MAX_MEM), 0) $1_JTREG_BASIC_OPTIONS += -vmoption:-Xmx$$($1_JTREG_MAX_MEM) @@ -865,13 +866,19 @@ $1_JTREG_BASIC_OPTIONS += -nativepath:$$($1_JTREG_NATIVEPATH) endif + ifeq ($$(JTREG_RUN_PROBLEM_LISTS), true) + JTREG_PROBLEM_LIST_PREFIX := -match: + else + JTREG_PROBLEM_LIST_PREFIX := -exclude: + endif + ifneq ($$($1_JTREG_PROBLEM_LIST), ) - $1_JTREG_BASIC_OPTIONS += $$(addprefix -exclude:, $$($1_JTREG_PROBLEM_LIST)) + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$($1_JTREG_PROBLEM_LIST)) endif ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), ) # Accept both absolute paths as well as relative to the current test root. - $1_JTREG_BASIC_OPTIONS += $$(addprefix -exclude:, $$(wildcard \ + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ $$(JTREG_EXTRA_PROBLEM_LISTS) \ $$(addprefix $$($1_TEST_ROOT)/, $$(JTREG_EXTRA_PROBLEM_LISTS)) \ ))
--- a/make/autoconf/flags-cflags.m4 Mon Jul 22 11:08:27 2019 +0530 +++ b/make/autoconf/flags-cflags.m4 Wed Jul 24 12:21:36 2019 +0530 @@ -229,7 +229,7 @@ ;; xlc) - DISABLE_WARNING_PREFIX="-qsuppress=" + DISABLE_WARNING_PREFIX="-Wno-" CFLAGS_WARNINGS_ARE_ERRORS="-qhalt=w" # Possibly a better subset than "all" is "lan:trx:ret:zea:cmp:ret"
--- a/make/hotspot/lib/CompileJvm.gmk Mon Jul 22 11:08:27 2019 +0530 +++ b/make/hotspot/lib/CompileJvm.gmk Wed Jul 24 12:21:36 2019 +0530 @@ -97,8 +97,7 @@ unknownpragma doubunder w_enumnotused w_toomanyenumnotused \ wvarhidenmem wunreachable wnoretvalue notemsource -DISABLED_WARNINGS_xlc := 1540-0216 1540-0198 1540-1090 1540-1639 1540-1088 \ - 1500-010 +DISABLED_WARNINGS_xlc := tautological-compare shift-negative-value DISABLED_WARNINGS_microsoft :=
--- a/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java Mon Jul 22 11:08:27 2019 +0530 +++ b/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java Wed Jul 24 12:21:36 2019 +0530 @@ -53,7 +53,7 @@ writer.println("</head>"); writer.println("<body>"); writer.println("<div class=\"centered\" role=\"banner\">"); - writer.println("<h1 id=\"Protocol Details\">Java Debug Wire Protocol Details</h1>"); + writer.println("<h1 id=\"Protocol_Details\">Java Debug Wire Protocol Details</h1>"); writer.println("</div>"); writer.println("<nav>"); writer.println("<ul>");
--- a/make/lib/Awt2dLibraries.gmk Mon Jul 22 11:08:27 2019 +0530 +++ b/make/lib/Awt2dLibraries.gmk Wed Jul 24 12:21:36 2019 +0530 @@ -471,7 +471,6 @@ $(LIBAWT_HEADLESS_CFLAGS), \ EXTRA_HEADER_DIRS := $(LIBAWT_HEADLESS_EXTRA_HEADER_DIRS), \ DISABLED_WARNINGS_gcc := unused-function, \ - DISABLED_WARNINGS_xlc := 1506-356, \ DISABLED_WARNINGS_solstudio := E_EMPTY_TRANSLATION_UNIT, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ @@ -481,10 +480,6 @@ LIBS_solaris := $(LIBM) $(LIBDL) $(LIBCXX), \ )) - # AIX warning explanation: - # 1506-356 : (W) Compilation unit is empty. - # This happens during the headless build - $(BUILD_LIBAWT_HEADLESS): $(BUILD_LIBAWT) TARGETS += $(BUILD_LIBAWT_HEADLESS)
--- a/src/hotspot/os/aix/os_aix.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/os/aix/os_aix.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -487,8 +487,7 @@ if (::shmctl(shmid, SHM_PAGESIZE, &shm_buf) != 0) { const int en = errno; ::shmctl(shmid, IPC_RMID, NULL); // As early as possible! - trcVerbose("shmctl(SHM_PAGESIZE) failed with errno=%n", - errno); + trcVerbose("shmctl(SHM_PAGESIZE) failed with errno=%d", errno); } else { // Attach and double check pageisze. void* p = ::shmat(shmid, NULL, 0); @@ -496,7 +495,7 @@ guarantee0(p != (void*) -1); // Should always work. const size_t real_pagesize = os::Aix::query_pagesize(p); if (real_pagesize != pagesize) { - trcVerbose("real page size (0x%llX) differs.", real_pagesize); + trcVerbose("real page size (" SIZE_FORMAT_HEX ") differs.", real_pagesize); } else { can_use = true; } @@ -1888,12 +1887,12 @@ if (!contains_range(p, s)) { trcVerbose("[" PTR_FORMAT " - " PTR_FORMAT "] is not a sub " "range of [" PTR_FORMAT " - " PTR_FORMAT "].", - p, p + s, addr, addr + size); + p2i(p), p2i(p + s), p2i(addr), p2i(addr + size)); guarantee0(false); } if (!is_aligned_to(p, pagesize) || !is_aligned_to(p + s, pagesize)) { trcVerbose("range [" PTR_FORMAT " - " PTR_FORMAT "] is not" - " aligned to pagesize (%lu)", p, p + s, (unsigned long) pagesize); + " aligned to pagesize (%lu)", p2i(p), p2i(p + s), (unsigned long) pagesize); guarantee0(false); } } @@ -1964,7 +1963,7 @@ trcVerbose("reserve_shmated_memory " UINTX_FORMAT " bytes, wishaddress " PTR_FORMAT ", alignment_hint " UINTX_FORMAT "...", - bytes, requested_addr, alignment_hint); + bytes, p2i(requested_addr), alignment_hint); // Either give me wish address or wish alignment but not both. assert0(!(requested_addr != NULL && alignment_hint != 0)); @@ -1973,7 +1972,7 @@ // BRK because that may cause malloc OOM. if (requested_addr != NULL && is_close_to_brk((address)requested_addr)) { trcVerbose("Wish address " PTR_FORMAT " is too close to the BRK segment. " - "Will attach anywhere.", requested_addr); + "Will attach anywhere.", p2i(requested_addr)); // Act like the OS refused to attach there. requested_addr = NULL; } @@ -2025,7 +2024,7 @@ // Handle shmat error. If we failed to attach, just return. if (addr == (char*)-1) { - trcVerbose("Failed to attach segment at " PTR_FORMAT " (%d).", requested_addr, errno_shmat); + trcVerbose("Failed to attach segment at " PTR_FORMAT " (%d).", p2i(requested_addr), errno_shmat); return NULL; } @@ -2033,15 +2032,15 @@ // work (see above), the system may have given us something other then 4K (LDR_CNTRL). const size_t real_pagesize = os::Aix::query_pagesize(addr); if (real_pagesize != shmbuf.shm_pagesize) { - trcVerbose("pagesize is, surprisingly, %h.", real_pagesize); + trcVerbose("pagesize is, surprisingly, " SIZE_FORMAT, real_pagesize); } if (addr) { trcVerbose("shm-allocated " PTR_FORMAT " .. " PTR_FORMAT " (" UINTX_FORMAT " bytes, " UINTX_FORMAT " %s pages)", - addr, addr + size - 1, size, size/real_pagesize, describe_pagesize(real_pagesize)); + p2i(addr), p2i(addr + size - 1), size, size/real_pagesize, describe_pagesize(real_pagesize)); } else { if (requested_addr != NULL) { - trcVerbose("failed to shm-allocate " UINTX_FORMAT " bytes at with address " PTR_FORMAT ".", size, requested_addr); + trcVerbose("failed to shm-allocate " UINTX_FORMAT " bytes at with address " PTR_FORMAT ".", size, p2i(requested_addr)); } else { trcVerbose("failed to shm-allocate " UINTX_FORMAT " bytes at any address.", size); } @@ -2057,7 +2056,7 @@ static bool release_shmated_memory(char* addr, size_t size) { trcVerbose("release_shmated_memory [" PTR_FORMAT " - " PTR_FORMAT "].", - addr, addr + size - 1); + p2i(addr), p2i(addr + size - 1)); bool rc = false; @@ -2073,12 +2072,12 @@ static bool uncommit_shmated_memory(char* addr, size_t size) { trcVerbose("uncommit_shmated_memory [" PTR_FORMAT " - " PTR_FORMAT "].", - addr, addr + size - 1); + p2i(addr), p2i(addr + size - 1)); const bool rc = my_disclaim64(addr, size); if (!rc) { - trcVerbose("my_disclaim64(" PTR_FORMAT ", " UINTX_FORMAT ") failed.\n", addr, size); + trcVerbose("my_disclaim64(" PTR_FORMAT ", " UINTX_FORMAT ") failed.\n", p2i(addr), size); return false; } return true; @@ -2095,11 +2094,11 @@ static char* reserve_mmaped_memory(size_t bytes, char* requested_addr, size_t alignment_hint) { trcVerbose("reserve_mmaped_memory " UINTX_FORMAT " bytes, wishaddress " PTR_FORMAT ", " "alignment_hint " UINTX_FORMAT "...", - bytes, requested_addr, alignment_hint); + bytes, p2i(requested_addr), alignment_hint); // If a wish address is given, but not aligned to 4K page boundary, mmap will fail. if (requested_addr && !is_aligned_to(requested_addr, os::vm_page_size()) != 0) { - trcVerbose("Wish address " PTR_FORMAT " not aligned to page boundary.", requested_addr); + trcVerbose("Wish address " PTR_FORMAT " not aligned to page boundary.", p2i(requested_addr)); return NULL; } @@ -2107,7 +2106,7 @@ // BRK because that may cause malloc OOM. if (requested_addr != NULL && is_close_to_brk((address)requested_addr)) { trcVerbose("Wish address " PTR_FORMAT " is too close to the BRK segment. " - "Will attach anywhere.", requested_addr); + "Will attach anywhere.", p2i(requested_addr)); // Act like the OS refused to attach there. requested_addr = NULL; } @@ -2154,7 +2153,7 @@ PROT_READ|PROT_WRITE|PROT_EXEC, flags, -1, 0); if (addr == MAP_FAILED) { - trcVerbose("mmap(" PTR_FORMAT ", " UINTX_FORMAT ", ..) failed (%d)", requested_addr, size, errno); + trcVerbose("mmap(" PTR_FORMAT ", " UINTX_FORMAT ", ..) failed (%d)", p2i(requested_addr), size, errno); return NULL; } @@ -2173,10 +2172,10 @@ if (addr) { trcVerbose("mmap-allocated " PTR_FORMAT " .. " PTR_FORMAT " (" UINTX_FORMAT " bytes)", - addr, addr + bytes, bytes); + p2i(addr), p2i(addr + bytes), bytes); } else { if (requested_addr != NULL) { - trcVerbose("failed to mmap-allocate " UINTX_FORMAT " bytes at wish address " PTR_FORMAT ".", bytes, requested_addr); + trcVerbose("failed to mmap-allocate " UINTX_FORMAT " bytes at wish address " PTR_FORMAT ".", bytes, p2i(requested_addr)); } else { trcVerbose("failed to mmap-allocate " UINTX_FORMAT " bytes at any address.", bytes); } @@ -2196,7 +2195,7 @@ assert0(is_aligned_to(size, os::vm_page_size())); trcVerbose("release_mmaped_memory [" PTR_FORMAT " - " PTR_FORMAT "].", - addr, addr + size - 1); + p2i(addr), p2i(addr + size - 1)); bool rc = false; if (::munmap(addr, size) != 0) { @@ -2216,7 +2215,7 @@ assert0(is_aligned_to(size, os::vm_page_size())); trcVerbose("uncommit_mmaped_memory [" PTR_FORMAT " - " PTR_FORMAT "].", - addr, addr + size - 1); + p2i(addr), p2i(addr + size - 1)); bool rc = false; // Uncommit mmap memory with msync MS_INVALIDATE. @@ -2247,7 +2246,7 @@ static void warn_fail_commit_memory(char* addr, size_t size, bool exec, int err) { warning("INFO: os::commit_memory(" PTR_FORMAT ", " SIZE_FORMAT - ", %d) failed; error='%s' (errno=%d)", addr, size, exec, + ", %d) failed; error='%s' (errno=%d)", p2i(addr), size, exec, os::errno_name(err), err); } #endif @@ -2275,7 +2274,7 @@ guarantee0(vmi); vmi->assert_is_valid_subrange(addr, size); - trcVerbose("commit_memory [" PTR_FORMAT " - " PTR_FORMAT "].", addr, addr + size - 1); + trcVerbose("commit_memory [" PTR_FORMAT " - " PTR_FORMAT "].", p2i(addr), p2i(addr + size - 1)); if (UseExplicitCommit) { // AIX commits memory on touch. So, touch all pages to be committed.
--- a/src/hotspot/share/aot/aotCompiledMethod.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/aot/aotCompiledMethod.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -168,7 +168,7 @@ int state() const { return *_state_adr; } // Non-virtual for speed - bool _is_alive() const { return state() < zombie; } + bool _is_alive() const { return state() < unloaded; } virtual bool is_zombie() const { return state() == zombie; } virtual bool is_unloaded() const { return state() == unloaded; }
--- a/src/hotspot/share/classfile/vmSymbols.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/classfile/vmSymbols.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -247,9 +247,6 @@ template(clazz_name, "clazz") \ template(exceptionTypes_name, "exceptionTypes") \ template(modifiers_name, "modifiers") \ - template(newConstructor_name, "newConstructor") \ - template(newField_name, "newField") \ - template(newMethod_name, "newMethod") \ template(invokeBasic_name, "invokeBasic") \ template(linkToVirtual_name, "linkToVirtual") \ template(linkToStatic_name, "linkToStatic") \
--- a/src/hotspot/share/code/compiledMethod.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/code/compiledMethod.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -208,9 +208,9 @@ not_used = 1, // not entrant, but revivable not_entrant = 2, // marked for deoptimization but activations may still exist, // will be transformed to zombie when all activations are gone - zombie = 3, // no activations exist, nmethod is ready for purge - unloaded = 4 // there should be no activations, should not be called, - // will be transformed to zombie immediately + unloaded = 3, // there should be no activations, should not be called, will be + // transformed to zombie by the sweeper, when not "locked in vm". + zombie = 4 // no activations exist, nmethod is ready for purge }; virtual bool is_in_use() const = 0;
--- a/src/hotspot/share/code/nmethod.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/code/nmethod.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -1136,6 +1136,20 @@ mdo->inc_decompile_count(); } +bool nmethod::try_transition(int new_state_int) { + signed char new_state = new_state_int; + for (;;) { + signed char old_state = Atomic::load(&_state); + if (old_state >= new_state) { + // Ensure monotonicity of transitions. + return false; + } + if (Atomic::cmpxchg(new_state, &_state, old_state) == old_state) { + return true; + } + } +} + void nmethod::make_unloaded() { post_compiled_method_unload(); @@ -1159,7 +1173,9 @@ } // Unlink the osr method, so we do not look this up again if (is_osr_method()) { - // Invalidate the osr nmethod only once + // Invalidate the osr nmethod only once. Note that with concurrent + // code cache unloading, OSR nmethods are invalidated before they + // are made unloaded. Therefore, this becomes a no-op then. if (is_in_use()) { invalidate_osr_method(); } @@ -1213,12 +1229,14 @@ set_osr_link(NULL); NMethodSweeper::report_state_change(this); - // The release is only needed for compile-time ordering, as accesses - // into the nmethod after the store are not safe due to the sweeper - // being allowed to free it when the store is observed, during - // concurrent nmethod unloading. Therefore, there is no need for - // acquire on the loader side. - OrderAccess::release_store(&_state, (signed char)unloaded); + bool transition_success = try_transition(unloaded); + + // It is an important invariant that there exists no race between + // the sweeper and GC thread competing for making the same nmethod + // zombie and unloaded respectively. This is ensured by + // can_convert_to_zombie() returning false for any is_unloading() + // nmethod, informing the sweeper not to step on any GC toes. + assert(transition_success, "Invalid nmethod transition to unloaded"); #if INCLUDE_JVMCI // Clear the link between this nmethod and a HotSpotNmethod mirror @@ -1283,7 +1301,7 @@ assert(state == zombie || state == not_entrant, "must be zombie or not_entrant"); assert(!is_zombie(), "should not already be a zombie"); - if (_state == state) { + if (Atomic::load(&_state) >= state) { // Avoid taking the lock if already in required state. // This is safe from races because the state is an end-state, // which the nmethod cannot back out of once entered. @@ -1318,7 +1336,7 @@ // Enter critical section. Does not block for safepoint. MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag); - if (_state == state) { + if (Atomic::load(&_state) >= state) { // another thread already performed this transition so nothing // to do, but return false to indicate this. return false; @@ -1354,7 +1372,18 @@ } // Change state - _state = state; + if (!try_transition(state)) { + // If the transition fails, it is due to another thread making the nmethod more + // dead. In particular, one thread might be making the nmethod unloaded concurrently. + // If so, having patched in the jump in the verified entry unnecessarily is fine. + // The nmethod is no longer possible to call by Java threads. + // Incrementing the decompile count is also fine as the caller of make_not_entrant() + // had a valid reason to deoptimize the nmethod. + // Marking the nmethod as seen on stack also has no effect, as the nmethod is now + // !is_alive(), and the seen on stack value is only used to convert not_entrant + // nmethods to zombie in can_convert_to_zombie(). + return false; + } // Log the transition once log_state_change();
--- a/src/hotspot/share/code/nmethod.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/code/nmethod.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -212,6 +212,9 @@ void* operator new(size_t size, int nmethod_size, int comp_level) throw(); const char* reloc_string_for(u_char* begin, u_char* end); + + bool try_transition(int new_state); + // Returns true if this thread changed the state of the nmethod or // false if another thread performed the transition. bool make_not_entrant_or_zombie(int state); @@ -339,7 +342,7 @@ // flag accessing and manipulation bool is_not_installed() const { return _state == not_installed; } bool is_in_use() const { return _state <= in_use; } - bool is_alive() const { return _state < zombie; } + bool is_alive() const { return _state < unloaded; } bool is_not_entrant() const { return _state == not_entrant; } bool is_zombie() const { return _state == zombie; } bool is_unloaded() const { return _state == unloaded; }
--- a/src/hotspot/share/gc/shared/memAllocator.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shared/memAllocator.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -173,11 +173,8 @@ assert(!_thread->has_pending_exception(), "shouldn't be allocating with pending exception"); if (StrictSafepointChecks) { - assert(_thread->allow_allocation(), - "Allocation done by thread for which allocation is blocked " - "by No_Allocation_Verifier!"); // Allocation of an oop can always invoke a safepoint, - // hence, the true argument + // hence, the true argument. _thread->check_for_valid_safepoint_state(true); } }
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -446,22 +446,11 @@ weak_refs_work(full_gc); } - weak_roots_work(); + _heap->parallel_cleaning(full_gc); - // And finally finish class unloading - if (_heap->unload_classes()) { - _heap->unload_classes_and_cleanup_tables(full_gc); - } else if (ShenandoahStringDedup::is_enabled()) { - ShenandoahIsAliveSelector alive; - BoolObjectClosure* is_alive = alive.is_alive_closure(); - ShenandoahStringDedup::unlink_or_oops_do(is_alive, NULL, false); - } assert(task_queues()->is_empty(), "Should be empty"); TASKQUEUE_STATS_ONLY(task_queues()->print_taskqueue_stats()); TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); - - // Resize Metaspace - MetaspaceGC::compute_new_size(); } // Weak Reference Closures @@ -556,26 +545,6 @@ void do_oop(oop* p) { do_oop_work(p); } }; -class ShenandoahWeakAssertNotForwardedClosure : public OopClosure { -private: - template <class T> - inline void do_oop_work(T* p) { -#ifdef ASSERT - T o = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); - shenandoah_assert_not_forwarded(p, obj); - } -#endif - } - -public: - ShenandoahWeakAssertNotForwardedClosure() {} - - void do_oop(narrowOop* p) { do_oop_work(p); } - void do_oop(oop* p) { do_oop_work(p); } -}; - class ShenandoahRefProcTaskProxy : public AbstractGangTask { private: AbstractRefProcTaskExecutor::ProcessTask& _proc_task; @@ -655,21 +624,6 @@ } -// Process leftover weak oops: update them, if needed or assert they do not -// need updating otherwise. -// Weak processor API requires us to visit the oops, even if we are not doing -// anything to them. -void ShenandoahConcurrentMark::weak_roots_work() { - WorkGang* workers = _heap->workers(); - OopClosure* keep_alive = &do_nothing_cl; -#ifdef ASSERT - ShenandoahWeakAssertNotForwardedClosure verify_cl; - keep_alive = &verify_cl; -#endif - ShenandoahIsAliveClosure is_alive; - WeakProcessor::weak_oops_do(workers, &is_alive, keep_alive, 1); -} - void ShenandoahConcurrentMark::weak_refs_work_doit(bool full_gc) { ReferenceProcessor* rp = _heap->ref_processor();
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -87,8 +87,6 @@ void weak_refs_work(bool full_gc); void weak_refs_work_doit(bool full_gc); - void weak_roots_work(); - public: void preclean_weak_refs();
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -29,7 +29,6 @@ #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/memAllocator.hpp" -#include "gc/shared/parallelCleaning.hpp" #include "gc/shared/plab.hpp" #include "gc/shenandoah/shenandoahAllocTracker.hpp" @@ -53,6 +52,7 @@ #include "gc/shenandoah/shenandoahNormalMode.hpp" #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" +#include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp" #include "gc/shenandoah/shenandoahPassiveMode.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" @@ -1950,16 +1950,8 @@ } } -void ShenandoahHeap::unload_classes_and_cleanup_tables(bool full_gc) { - assert(heuristics()->can_unload_classes(), "Class unloading should be enabled"); - - ShenandoahGCPhase root_phase(full_gc ? - ShenandoahPhaseTimings::full_gc_purge : - ShenandoahPhaseTimings::purge); - - ShenandoahIsAliveSelector alive; - BoolObjectClosure* is_alive = alive.is_alive_closure(); - +void ShenandoahHeap::stw_unload_classes(bool full_gc) { + if (!unload_classes()) return; bool purged_class; // Unload classes and purge SystemDictionary. @@ -1974,17 +1966,61 @@ ShenandoahGCPhase phase(full_gc ? ShenandoahPhaseTimings::full_gc_purge_par : ShenandoahPhaseTimings::purge_par); - uint active = _workers->active_workers(); - ParallelCleaningTask unlink_task(is_alive, active, purged_class, true); + ShenandoahIsAliveSelector is_alive; + uint num_workers = _workers->active_workers(); + ShenandoahClassUnloadingTask unlink_task(is_alive.is_alive_closure(), num_workers, purged_class); _workers->run_task(&unlink_task); } { ShenandoahGCPhase phase(full_gc ? - ShenandoahPhaseTimings::full_gc_purge_cldg : - ShenandoahPhaseTimings::purge_cldg); + ShenandoahPhaseTimings::full_gc_purge_cldg : + ShenandoahPhaseTimings::purge_cldg); ClassLoaderDataGraph::purge(); } + // Resize and verify metaspace + MetaspaceGC::compute_new_size(); + MetaspaceUtils::verify_metrics(); +} + +// Process leftover weak oops: update them, if needed or assert they do not +// need updating otherwise. +// Weak processor API requires us to visit the oops, even if we are not doing +// anything to them. +void ShenandoahHeap::stw_process_weak_roots(bool full_gc) { + ShenandoahGCPhase root_phase(full_gc ? + ShenandoahPhaseTimings::full_gc_purge : + ShenandoahPhaseTimings::purge); + uint num_workers = _workers->active_workers(); + ShenandoahPhaseTimings::Phase timing_phase = full_gc ? + ShenandoahPhaseTimings::full_gc_purge_par : + ShenandoahPhaseTimings::purge_par; + // Cleanup weak roots + ShenandoahGCPhase phase(timing_phase); + if (has_forwarded_objects()) { + ShenandoahForwardedIsAliveClosure is_alive; + ShenandoahUpdateRefsClosure keep_alive; + ShenandoahParallelWeakRootsCleaningTask<ShenandoahForwardedIsAliveClosure, ShenandoahUpdateRefsClosure> + cleaning_task(&is_alive, &keep_alive, num_workers); + _workers->run_task(&cleaning_task); + } else { + ShenandoahIsAliveClosure is_alive; +#ifdef ASSERT + ShenandoahAssertNotForwardedClosure verify_cl; + ShenandoahParallelWeakRootsCleaningTask<ShenandoahIsAliveClosure, ShenandoahAssertNotForwardedClosure> + cleaning_task(&is_alive, &verify_cl, num_workers); +#else + ShenandoahParallelWeakRootsCleaningTask<ShenandoahIsAliveClosure, DoNothingClosure> + cleaning_task(&is_alive, &do_nothing_cl, num_workers); +#endif + _workers->run_task(&cleaning_task); + } +} + +void ShenandoahHeap::parallel_cleaning(bool full_gc) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); + stw_process_weak_roots(full_gc); + stw_unload_classes(full_gc); } void ShenandoahHeap::set_has_forwarded_objects(bool cond) {
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -512,9 +512,12 @@ void set_unload_classes(bool uc); bool unload_classes() const; - // Delete entries for dead interned string and clean up unreferenced symbols - // in symbol table, possibly in parallel. - void unload_classes_and_cleanup_tables(bool full_gc); + // Perform STW class unloading and weak root cleaning + void parallel_cleaning(bool full_gc); + +private: + void stw_unload_classes(bool full_gc); + void stw_process_weak_roots(bool full_gc); // ---------- Generic interface hooks // Minor things that super-interface expects us to implement to play nice with
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * + * 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. + * + */ + + +#include "precompiled.hpp" + +#include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahCodeRoots.hpp" +#include "gc/shenandoah/shenandoahEvacOOMHandler.hpp" +#include "gc/shenandoah/shenandoahParallelCleaning.hpp" +#include "runtime/safepoint.hpp" + +ShenandoahClassUnloadingTask::ShenandoahClassUnloadingTask(BoolObjectClosure* is_alive, + uint num_workers, + bool unloading_occurred) : + AbstractGangTask("Parallel Class Unloading Task"), + _unloading_occurred(unloading_occurred), + _code_cache_task(num_workers, is_alive, unloading_occurred), + _klass_cleaning_task() { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); +} + +void ShenandoahClassUnloadingTask::work(uint worker_id) { + ShenandoahEvacOOMScope scope; + _code_cache_task.work(worker_id); + // Clean all klasses that were not unloaded. + // The weak metadata in klass doesn't need to be + // processed if there was no unloading. + if (_unloading_occurred) { + _klass_cleaning_task.work(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_HPP + +#include "gc/shared/parallelCleaning.hpp" +#include "gc/shared/weakProcessor.hpp" +#include "gc/shared/weakProcessorPhaseTimes.hpp" +#include "gc/shared/workgroup.hpp" +#include "memory/iterator.hpp" + +// Perform weak root cleaning at a pause +template <typename IsAlive, typename KeepAlive> +class ShenandoahParallelWeakRootsCleaningTask : public AbstractGangTask { +protected: + WeakProcessor::Task _weak_processing_task; + IsAlive* _is_alive; + KeepAlive* _keep_alive; +public: + ShenandoahParallelWeakRootsCleaningTask(IsAlive* is_alive, KeepAlive* keep_alive, uint num_workers); + ~ShenandoahParallelWeakRootsCleaningTask(); + + void work(uint worker_id); +}; + +// Perform class unloading at a pause +class ShenandoahClassUnloadingTask : public AbstractGangTask { +private: + bool _unloading_occurred; + CodeCacheUnloadingTask _code_cache_task; + KlassCleaningTask _klass_cleaning_task; +public: + ShenandoahClassUnloadingTask(BoolObjectClosure* is_alive, + uint num_workers, + bool unloading_occurred); + + void work(uint worker_id); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP + +#include "gc/shared/weakProcessor.inline.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahParallelCleaning.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "runtime/thread.hpp" +#include "runtime/safepoint.hpp" + +template<typename IsAlive, typename KeepAlive> +ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::ShenandoahParallelWeakRootsCleaningTask(IsAlive* is_alive, + KeepAlive* keep_alive, + uint num_workers) : + AbstractGangTask("Parallel Weak Root Cleaning Task"), + _weak_processing_task(num_workers), + _is_alive(is_alive), _keep_alive(keep_alive) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); + + if (ShenandoahStringDedup::is_enabled()) { + StringDedup::gc_prologue(false); + } +} + +template<typename IsAlive, typename KeepAlive> +ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::~ShenandoahParallelWeakRootsCleaningTask() { + if (StringDedup::is_enabled()) { + StringDedup::gc_epilogue(); + } +} + +template<typename IsAlive, typename KeepAlive> +void ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::work(uint worker_id) { + _weak_processing_task.work<IsAlive, KeepAlive>(worker_id, _is_alive, _keep_alive); + + if (ShenandoahStringDedup::is_enabled()) { + ShenandoahStringDedup::parallel_oops_do(_is_alive, _keep_alive, worker_id); + } +} + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP
--- a/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -75,16 +75,20 @@ assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); assert(is_enabled(), "String deduplication not enabled"); - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - StringDedupUnlinkOrOopsDoClosure sd_cl(is_alive, cl); + if (ShenandoahGCPhase::is_root_work_phase()) { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + { + ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id); + StringDedupQueue::unlink_or_oops_do(&sd_cl); + } - { - ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id); + { + ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id); + StringDedupTable::unlink_or_oops_do(&sd_cl, worker_id); + } + } else { StringDedupQueue::unlink_or_oops_do(&sd_cl); - } - { - ShenandoahWorkerTimingsTracker x(worker_times, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id); StringDedupTable::unlink_or_oops_do(&sd_cl, worker_id); } }
--- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -28,7 +28,6 @@ #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" #include "gc/shared/workgroup.hpp" -#include "gc/shared/weakProcessor.inline.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" @@ -596,9 +595,7 @@ if (!_heap->cancelled_gc()) { fixup_roots(); - if (_heap->unload_classes()) { - _heap->unload_classes_and_cleanup_tables(false); - } + _heap->parallel_cleaning(false); } if (!_heap->cancelled_gc()) {
--- a/src/hotspot/share/gc/z/zNMethod.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/gc/z/zNMethod.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -261,6 +261,24 @@ Atomic::store(true, &_failed); } + void unlink(nmethod* nm) { + // Unlinking of the dependencies must happen before the + // handshake separating unlink and purge. + nm->flush_dependencies(false /* delete_immediately */); + + // We don't need to take the lock when unlinking nmethods from + // the Method, because it is only concurrently unlinked by + // the entry barrier, which acquires the per nmethod lock. + nm->unlink_from_method(false /* acquire_lock */); + + if (nm->is_osr_method()) { + // Invalidate the osr nmethod before the handshake. The nmethod + // will be made unloaded after the handshake. Then invalidate_osr_method() + // will be called again, which will be a no-op. + nm->invalidate_osr_method(); + } + } + public: ZNMethodUnlinkClosure(bool unloading_occurred) : _unloading_occurred(unloading_occurred), @@ -278,14 +296,7 @@ ZLocker<ZReentrantLock> locker(ZNMethod::lock_for_nmethod(nm)); if (nm->is_unloading()) { - // Unlinking of the dependencies must happen before the - // handshake separating unlink and purge. - nm->flush_dependencies(false /* delete_immediately */); - - // We don't need to take the lock when unlinking nmethods from - // the Method, because it is only concurrently unlinked by - // the entry barrier, which acquires the per nmethod lock. - nm->unlink_from_method(false /* acquire_lock */); + unlink(nm); return; }
--- a/src/hotspot/share/jvmci/jvmci.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/jvmci/jvmci.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -91,6 +91,14 @@ return res; } +void JVMCI::destroy_global(jobject handle) { + // Assert before nulling out, for better debugging. + assert(is_global_handle(handle), "precondition"); + oop* oop_ptr = reinterpret_cast<oop*>(handle); + NativeAccess<>::oop_store(oop_ptr, (oop)NULL); + object_handles()->release(oop_ptr); +} + bool JVMCI::is_global_handle(jobject handle) { const oop* ptr = reinterpret_cast<oop*>(handle); return object_handles()->allocation_status(ptr) == OopStorage::ALLOCATED_ENTRY;
--- a/src/hotspot/share/jvmci/jvmci.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/jvmci/jvmci.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -90,6 +90,7 @@ static void initialize_compiler(TRAPS); static jobject make_global(const Handle& obj); + static void destroy_global(jobject handle); static bool is_global_handle(jobject handle); static jmetadata allocate_handle(const methodHandle& handle);
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -2208,8 +2208,7 @@ C2V_VMENTRY(void, deleteGlobalHandle, (JNIEnv* env, jobject, jlong h)) jobject handle = (jobject)(address)h; if (handle != NULL) { - assert(JVMCI::is_global_handle(handle), "Invalid delete of global JNI handle"); - *((oop*)handle) = NULL; // Mark the handle as deleted, allocate will reuse it + JVMCI::destroy_global(handle); } }
--- a/src/hotspot/share/runtime/arguments.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/runtime/arguments.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -550,18 +550,6 @@ { "SharedReadOnlySize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() }, { "SharedMiscDataSize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() }, { "SharedMiscCodeSize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() }, - { "ProfilerPrintByteCodeStatistics", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfilerRecordPC", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfileVM", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfileIntervals", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfileIntervalsTicks", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfilerCheckIntervals", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfilerNumberOfInterpretedMethods", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfilerNumberOfCompiledMethods", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfilerNumberOfStubMethods", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "ProfilerNumberOfRuntimeStubNodes", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "UseImplicitStableValues", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, - { "NeedsDeoptSuspend", JDK_Version::undefined(), JDK_Version::jdk(13), JDK_Version::jdk(14) }, { "FailOverToOldVerifier", JDK_Version::undefined(), JDK_Version::jdk(14), JDK_Version::jdk(15) }, #ifdef TEST_VERIFY_SPECIAL_JVM_FLAGS
--- a/src/hotspot/share/runtime/safepointVerifiers.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/runtime/safepointVerifiers.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -91,14 +91,12 @@ _activated(activated) { _thread = Thread::current(); if (_activated) { - _thread->_allow_allocation_count++; _thread->_allow_safepoint_count++; } } ~NoSafepointVerifier() { if (_activated) { - _thread->_allow_allocation_count--; _thread->_allow_safepoint_count--; } } @@ -126,14 +124,12 @@ _nsv = nsv; if (_nsv->_activated) { - _nsv->_thread->_allow_allocation_count--; _nsv->_thread->_allow_safepoint_count--; } } ~PauseNoSafepointVerifier() { if (_nsv->_activated) { - _nsv->_thread->_allow_allocation_count++; _nsv->_thread->_allow_safepoint_count++; } } @@ -144,32 +140,4 @@ #endif }; -// A NoAllocVerifier object can be placed in methods where one assumes that -// no allocation will occur. The destructor will verify this property -// unless the constructor is called with argument false (not activated). -// -// The check will only be done in debug mode and if activated. -// Note: this only makes sense at safepoints (otherwise, other threads may -// allocate concurrently.) - -class NoAllocVerifier : public StackObj { - private: - bool _activated; - - public: -#ifdef ASSERT - NoAllocVerifier(bool activated = true) { - _activated = activated; - if (_activated) Thread::current()->_allow_allocation_count++; - } - - ~NoAllocVerifier() { - if (_activated) Thread::current()->_allow_allocation_count--; - } -#else - NoAllocVerifier(bool activated = true) {} - ~NoAllocVerifier() {} -#endif -}; - #endif // SHARE_RUNTIME_SAFEPOINTVERIFIERS_HPP
--- a/src/hotspot/share/runtime/thread.cpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/runtime/thread.cpp Wed Jul 24 12:21:36 2019 +0530 @@ -250,7 +250,6 @@ // plain initialization debug_only(_owned_locks = NULL;) - debug_only(_allow_allocation_count = 0;) NOT_PRODUCT(_allow_safepoint_count = 0;) NOT_PRODUCT(_skip_gcalot = false;) _jvmti_env_iteration_count = 0;
--- a/src/hotspot/share/runtime/thread.hpp Mon Jul 22 11:08:27 2019 +0530 +++ b/src/hotspot/share/runtime/thread.hpp Wed Jul 24 12:21:36 2019 +0530 @@ -375,18 +375,14 @@ // GC points in the VM can happen because of allocation, invoking a VM operation, or blocking on // mutex, or blocking on an object synchronizer (Java locking). // If !allow_safepoint(), then an assertion failure will happen in any of the above cases - // If !allow_allocation(), then an assertion failure will happen during allocation - // (Hence, !allow_safepoint() => !allow_allocation()). // - // The two classes NoSafepointVerifier and No_Allocation_Verifier are used to set these counters. + // The class NoSafepointVerifier is used to set this counter. // NOT_PRODUCT(int _allow_safepoint_count;) // If 0, thread allow a safepoint to happen - debug_only(int _allow_allocation_count;) // If 0, the thread is allowed to allocate oops. // Used by SkipGCALot class. NOT_PRODUCT(bool _skip_gcalot;) // Should we elide gc-a-lot? - friend class NoAllocVerifier; friend class NoSafepointVerifier; friend class PauseNoSafepointVerifier; friend class GCLocker; @@ -754,7 +750,6 @@ bool owns_locks_but_compiled_lock() const; // Deadlock detection - bool allow_allocation() { return _allow_allocation_count == 0; } ResourceMark* current_resource_mark() { return _current_resource_mark; } void set_current_resource_mark(ResourceMark* rm) { _current_resource_mark = rm; } #endif
--- a/src/java.base/aix/native/libjli/java_md_aix.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/aix/native/libjli/java_md_aix.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018 SAP SE. All rights reserved. + * Copyright (c) 2016, 2019 SAP SE. 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 @@ -23,6 +23,7 @@ * questions. */ #include <stdio.h> +#include <string.h> #include <sys/ldr.h> #include "java_md_aix.h"
--- a/src/java.base/aix/native/libnio/fs/AixNativeDispatcher.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/aix/native/libnio/fs/AixNativeDispatcher.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 SAP SE. All rights reserved. + * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019 SAP SE. 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 @@ -25,6 +25,7 @@ */ #include <stdlib.h> +#include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/mntctl.h>
--- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Wed Jul 24 12:21:36 2019 +0530 @@ -30,6 +30,7 @@ import java.lang.ref.WeakReference; import java.security.AccessController; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; import jdk.internal.module.IllegalAccessLogger; import jdk.internal.reflect.CallerSensitive; @@ -77,6 +78,10 @@ * @spec JPMS */ public class AccessibleObject implements AnnotatedElement { + static { + // AccessibleObject is initialized early in initPhase1 + SharedSecrets.setJavaLangReflectAccess(new ReflectAccess()); + } static void checkPermission() { SecurityManager sm = System.getSecurityManager();
--- a/src/java.base/share/classes/java/lang/reflect/Constructor.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java Wed Jul 24 12:21:36 2019 +0530 @@ -111,7 +111,7 @@ /** * Package-private constructor used by ReflectAccess to enable * instantiation of these objects in Java code from the java.lang - * package via sun.reflect.LangReflectAccess. + * package via jdk.internal.access.JavaLangReflectAccess. */ Constructor(Class<T> declaringClass, Class<?>[] parameterTypes,
--- a/src/java.base/share/classes/java/lang/reflect/Field.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/lang/reflect/Field.java Wed Jul 24 12:21:36 2019 +0530 @@ -113,9 +113,7 @@ /** - * Package-private constructor used by ReflectAccess to enable - * instantiation of these objects in Java code from the java.lang - * package via sun.reflect.LangReflectAccess. + * Package-private constructor */ Field(Class<?> declaringClass, String name,
--- a/src/java.base/share/classes/java/lang/reflect/Method.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/lang/reflect/Method.java Wed Jul 24 12:21:36 2019 +0530 @@ -113,9 +113,7 @@ } /** - * Package-private constructor used by ReflectAccess to enable - * instantiation of these objects in Java code from the java.lang - * package via sun.reflect.LangReflectAccess. + * Package-private constructor */ Method(Class<?> declaringClass, String name,
--- a/src/java.base/share/classes/java/lang/reflect/Modifier.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/lang/reflect/Modifier.java Wed Jul 24 12:21:36 2019 +0530 @@ -25,10 +25,7 @@ package java.lang.reflect; -import java.security.AccessController; import java.util.StringJoiner; -import jdk.internal.reflect.LangReflectAccess; -import jdk.internal.reflect.ReflectionFactory; /** * The Modifier class provides {@code static} methods and @@ -47,16 +44,6 @@ */ public class Modifier { - /* - * Bootstrapping protocol between java.lang and java.lang.reflect - * packages - */ - static { - ReflectionFactory factory = AccessController.doPrivileged( - new ReflectionFactory.GetReflectionFactoryAction()); - factory.setLangReflectAccess(new java.lang.reflect.ReflectAccess()); - } - /** * Return {@code true} if the integer argument includes the * {@code public} modifier, {@code false} otherwise.
--- a/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2019, 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 @@ -29,52 +29,10 @@ import jdk.internal.reflect.ConstructorAccessor; /** Package-private class implementing the - sun.reflect.LangReflectAccess interface, allowing the java.lang + jdk.internal.access.JavaLangReflectAccess interface, allowing the java.lang package to instantiate objects in this package. */ -class ReflectAccess implements jdk.internal.reflect.LangReflectAccess { - public Field newField(Class<?> declaringClass, - String name, - Class<?> type, - int modifiers, - int slot, - String signature, - byte[] annotations) - { - return new Field(declaringClass, - name, - type, - modifiers, - slot, - signature, - annotations); - } - - public Method newMethod(Class<?> declaringClass, - String name, - Class<?>[] parameterTypes, - Class<?> returnType, - Class<?>[] checkedExceptions, - int modifiers, - int slot, - String signature, - byte[] annotations, - byte[] parameterAnnotations, - byte[] annotationDefault) - { - return new Method(declaringClass, - name, - parameterTypes, - returnType, - checkedExceptions, - modifiers, - slot, - signature, - annotations, - parameterAnnotations, - annotationDefault); - } - +class ReflectAccess implements jdk.internal.access.JavaLangReflectAccess { public <T> Constructor<T> newConstructor(Class<T> declaringClass, Class<?>[] parameterTypes, Class<?>[] checkedExceptions,
--- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, 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 @@ -85,7 +85,6 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.chrono.ChronoLocalDate; -import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoChronology; @@ -122,7 +121,6 @@ import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.locale.provider.CalendarDataUtility; import sun.util.locale.provider.LocaleProviderAdapter; -import sun.util.locale.provider.LocaleResources; import sun.util.locale.provider.TimeZoneNameUtility; /** @@ -3871,7 +3869,11 @@ if (offsetSecs == null) { return false; } - String gmtText = "GMT"; // TODO: get localized version of 'GMT' + String key = "timezone.gmtZeroFormat"; + String gmtText = DateTimeTextProvider.getLocalizedResource(key, context.getLocale()); + if (gmtText == null) { + gmtText = "GMT"; // Default to "GMT" + } buf.append(gmtText); int totalSecs = Math.toIntExact(offsetSecs); if (totalSecs != 0) { @@ -3917,7 +3919,11 @@ public int parse(DateTimeParseContext context, CharSequence text, int position) { int pos = position; int end = text.length(); - String gmtText = "GMT"; // TODO: get localized version of 'GMT' + String key = "timezone.gmtZeroFormat"; + String gmtText = DateTimeTextProvider.getLocalizedResource(key, context.getLocale()); + if (gmtText == null) { + gmtText = "GMT"; // Default to "GMT" + } if (!context.subSequenceEquals(text, pos, gmtText, 0, gmtText.length())) { return ~position; }
--- a/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -35,7 +35,11 @@ * it could be associated with a server or client who participates in many * sessions concurrently. * <p> - * Not all environments will contain session contexts. + * Not all environments will contain session contexts. For example, stateless + * session resumption. + * <p> + * Session contexts may not contain all sessions. For example, stateless + * sessions are not stored in the session context. * <p> * There are <code>SSLSessionContext</code> parameters that affect how * sessions are stored: @@ -68,8 +72,11 @@ public SSLSession getSession(byte[] sessionId); /** - * Returns an Enumeration of all session id's grouped under this + * Returns an Enumeration of all known session id's grouped under this * <code>SSLSessionContext</code>. + * <p>Session contexts may not contain all sessions. For example, + * stateless sessions are not stored in the session context. + * <p> * * @return an enumeration of all the Session id's */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2001, 2019, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.internal.access; + +import java.lang.reflect.*; +import jdk.internal.reflect.*; + +/** An interface which gives privileged packages Java-level access to + internals of java.lang.reflect. */ + +public interface JavaLangReflectAccess { + /** Creates a new java.lang.reflect.Constructor. Access checks as + per java.lang.reflect.AccessibleObject are not overridden. */ + public <T> Constructor<T> newConstructor(Class<T> declaringClass, + Class<?>[] parameterTypes, + Class<?>[] checkedExceptions, + int modifiers, + int slot, + String signature, + byte[] annotations, + byte[] parameterAnnotations); + + /** Gets the MethodAccessor object for a java.lang.reflect.Method */ + public MethodAccessor getMethodAccessor(Method m); + + /** Sets the MethodAccessor object for a java.lang.reflect.Method */ + public void setMethodAccessor(Method m, MethodAccessor accessor); + + /** Gets the ConstructorAccessor object for a + java.lang.reflect.Constructor */ + public ConstructorAccessor getConstructorAccessor(Constructor<?> c); + + /** Sets the ConstructorAccessor object for a + java.lang.reflect.Constructor */ + public void setConstructorAccessor(Constructor<?> c, + ConstructorAccessor accessor); + + /** Gets the byte[] that encodes TypeAnnotations on an Executable. */ + public byte[] getExecutableTypeAnnotationBytes(Executable ex); + + /** Gets the "slot" field from a Constructor (used for serialization) */ + public int getConstructorSlot(Constructor<?> c); + + /** Gets the "signature" field from a Constructor (used for serialization) */ + public String getConstructorSignature(Constructor<?> c); + + /** Gets the "annotations" field from a Constructor (used for serialization) */ + public byte[] getConstructorAnnotations(Constructor<?> c); + + /** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */ + public byte[] getConstructorParameterAnnotations(Constructor<?> c); + + /** Gets the shared array of parameter types of an Executable. */ + public Class<?>[] getExecutableSharedParameterTypes(Executable ex); + + // + // Copying routines, needed to quickly fabricate new Field, + // Method, and Constructor objects from templates + // + + /** Makes a "child" copy of a Method */ + public Method copyMethod(Method arg); + + /** Makes a copy of this non-root a Method */ + public Method leafCopyMethod(Method arg); + + /** Makes a "child" copy of a Field */ + public Field copyField(Field arg); + + /** Makes a "child" copy of a Constructor */ + public <T> Constructor<T> copyConstructor(Constructor<T> arg); + + /** Gets the root of the given AccessibleObject object; null if arg is the root */ + public <T extends AccessibleObject> T getRoot(T obj); + + /** Returns a new instance created by the given constructor with access check */ + public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller) + throws IllegalAccessException, InstantiationException, InvocationTargetException; +}
--- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java Wed Jul 24 12:21:36 2019 +0530 @@ -50,28 +50,29 @@ public class SharedSecrets { private static final Unsafe unsafe = Unsafe.getUnsafe(); - private static JavaUtilJarAccess javaUtilJarAccess; + private static JavaAWTAccess javaAWTAccess; + private static JavaAWTFontAccess javaAWTFontAccess; + private static JavaBeansAccess javaBeansAccess; private static JavaLangAccess javaLangAccess; + private static JavaLangInvokeAccess javaLangInvokeAccess; private static JavaLangModuleAccess javaLangModuleAccess; - private static JavaLangInvokeAccess javaLangInvokeAccess; private static JavaLangRefAccess javaLangRefAccess; + private static JavaLangReflectAccess javaLangReflectAccess; private static JavaIOAccess javaIOAccess; + private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; + private static JavaIOFilePermissionAccess javaIOFilePermissionAccess; + private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess; + private static JavaObjectInputStreamAccess javaObjectInputStreamAccess; + private static JavaObjectInputFilterAccess javaObjectInputFilterAccess; private static JavaNetInetAddressAccess javaNetInetAddressAccess; private static JavaNetHttpCookieAccess javaNetHttpCookieAccess; private static JavaNetUriAccess javaNetUriAccess; private static JavaNetURLAccess javaNetURLAccess; private static JavaNioAccess javaNioAccess; - private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; - private static JavaIOFilePermissionAccess javaIOFilePermissionAccess; - private static JavaSecurityAccess javaSecurityAccess; + private static JavaUtilJarAccess javaUtilJarAccess; private static JavaUtilZipFileAccess javaUtilZipFileAccess; private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess; - private static JavaAWTAccess javaAWTAccess; - private static JavaAWTFontAccess javaAWTFontAccess; - private static JavaBeansAccess javaBeansAccess; - private static JavaObjectInputStreamAccess javaObjectInputStreamAccess; - private static JavaObjectInputFilterAccess javaObjectInputFilterAccess; - private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess; + private static JavaSecurityAccess javaSecurityAccess; private static JavaSecuritySignatureAccess javaSecuritySignatureAccess; private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; @@ -129,6 +130,14 @@ return javaLangRefAccess; } + public static void setJavaLangReflectAccess(JavaLangReflectAccess jlra) { + javaLangReflectAccess = jlra; + } + + public static JavaLangReflectAccess getJavaLangReflectAccess() { + return javaLangReflectAccess; + } + public static void setJavaNetUriAccess(JavaNetUriAccess jnua) { javaNetUriAccess = jnua; }
--- a/src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java Mon Jul 22 11:08:27 2019 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2001, 2013, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package jdk.internal.reflect; - -import java.lang.reflect.*; - -/** An interface which gives privileged packages Java-level access to - internals of java.lang.reflect. */ - -public interface LangReflectAccess { - /** Creates a new java.lang.reflect.Field. Access checks as per - java.lang.reflect.AccessibleObject are not overridden. */ - public Field newField(Class<?> declaringClass, - String name, - Class<?> type, - int modifiers, - int slot, - String signature, - byte[] annotations); - - /** Creates a new java.lang.reflect.Method. Access checks as per - java.lang.reflect.AccessibleObject are not overridden. */ - public Method newMethod(Class<?> declaringClass, - String name, - Class<?>[] parameterTypes, - Class<?> returnType, - Class<?>[] checkedExceptions, - int modifiers, - int slot, - String signature, - byte[] annotations, - byte[] parameterAnnotations, - byte[] annotationDefault); - - /** Creates a new java.lang.reflect.Constructor. Access checks as - per java.lang.reflect.AccessibleObject are not overridden. */ - public <T> Constructor<T> newConstructor(Class<T> declaringClass, - Class<?>[] parameterTypes, - Class<?>[] checkedExceptions, - int modifiers, - int slot, - String signature, - byte[] annotations, - byte[] parameterAnnotations); - - /** Gets the MethodAccessor object for a java.lang.reflect.Method */ - public MethodAccessor getMethodAccessor(Method m); - - /** Sets the MethodAccessor object for a java.lang.reflect.Method */ - public void setMethodAccessor(Method m, MethodAccessor accessor); - - /** Gets the ConstructorAccessor object for a - java.lang.reflect.Constructor */ - public ConstructorAccessor getConstructorAccessor(Constructor<?> c); - - /** Sets the ConstructorAccessor object for a - java.lang.reflect.Constructor */ - public void setConstructorAccessor(Constructor<?> c, - ConstructorAccessor accessor); - - /** Gets the byte[] that encodes TypeAnnotations on an Executable. */ - public byte[] getExecutableTypeAnnotationBytes(Executable ex); - - /** Gets the "slot" field from a Constructor (used for serialization) */ - public int getConstructorSlot(Constructor<?> c); - - /** Gets the "signature" field from a Constructor (used for serialization) */ - public String getConstructorSignature(Constructor<?> c); - - /** Gets the "annotations" field from a Constructor (used for serialization) */ - public byte[] getConstructorAnnotations(Constructor<?> c); - - /** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */ - public byte[] getConstructorParameterAnnotations(Constructor<?> c); - - /** Gets the shared array of parameter types of an Executable. */ - public Class<?>[] getExecutableSharedParameterTypes(Executable ex); - - // - // Copying routines, needed to quickly fabricate new Field, - // Method, and Constructor objects from templates - // - - /** Makes a "child" copy of a Method */ - public Method copyMethod(Method arg); - - /** Makes a copy of this non-root a Method */ - public Method leafCopyMethod(Method arg); - - /** Makes a "child" copy of a Field */ - public Field copyField(Field arg); - - /** Makes a "child" copy of a Constructor */ - public <T> Constructor<T> copyConstructor(Constructor<T> arg); - - /** Gets the root of the given AccessibleObject object; null if arg is the root */ - public <T extends AccessibleObject> T getRoot(T obj); - - /** Returns a new instance created by the given constructor with access check */ - public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller) - throws IllegalAccessException, InstantiationException, InvocationTargetException; -}
--- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Wed Jul 24 12:21:36 2019 +0530 @@ -43,6 +43,8 @@ import java.util.Objects; import java.util.Properties; +import jdk.internal.access.JavaLangReflectAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; import sun.reflect.misc.ReflectUtil; import sun.security.action.GetPropertyAction; @@ -64,8 +66,7 @@ private static boolean initted = false; private static final ReflectionFactory soleInstance = new ReflectionFactory(); - // Provides access to package-private mechanisms in java.lang.reflect - private static volatile LangReflectAccess langReflectAccess; + /* Method for static class initializer <clinit>, or null */ private static volatile Method hasStaticInitializerMethod; @@ -90,7 +91,9 @@ // true if deserialization constructor checking is disabled private static boolean disableSerialConstructorChecks = false; + private final JavaLangReflectAccess langReflectAccess; private ReflectionFactory() { + this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess(); } /** @@ -160,12 +163,7 @@ // // - /** Called only by java.lang.reflect.Modifier's static initializer */ - public void setLangReflectAccess(LangReflectAccess access) { - langReflectAccess = access; - } - - /** + /* * Note: this routine can cause the declaring class for the field * be initialized and therefore must not be called until the * first get/set of this field. @@ -175,7 +173,7 @@ public FieldAccessor newFieldAccessor(Field field, boolean override) { checkInitted(); - Field root = langReflectAccess().getRoot(field); + Field root = langReflectAccess.getRoot(field); if (root != null) { // FieldAccessor will use the root unless the modifiers have // been overrridden @@ -197,7 +195,7 @@ } // use the root Method that will not cache caller class - Method root = langReflectAccess().getRoot(method); + Method root = langReflectAccess.getRoot(method); if (root != null) { method = root; } @@ -233,7 +231,7 @@ } // use the root Constructor that will not cache caller class - Constructor<?> root = langReflectAccess().getRoot(c); + Constructor<?> root = langReflectAccess.getRoot(c); if (root != null) { c = root; } @@ -268,52 +266,6 @@ // // - /** Creates a new java.lang.reflect.Field. Access checks as per - java.lang.reflect.AccessibleObject are not overridden. */ - public Field newField(Class<?> declaringClass, - String name, - Class<?> type, - int modifiers, - int slot, - String signature, - byte[] annotations) - { - return langReflectAccess().newField(declaringClass, - name, - type, - modifiers, - slot, - signature, - annotations); - } - - /** Creates a new java.lang.reflect.Method. Access checks as per - java.lang.reflect.AccessibleObject are not overridden. */ - public Method newMethod(Class<?> declaringClass, - String name, - Class<?>[] parameterTypes, - Class<?> returnType, - Class<?>[] checkedExceptions, - int modifiers, - int slot, - String signature, - byte[] annotations, - byte[] parameterAnnotations, - byte[] annotationDefault) - { - return langReflectAccess().newMethod(declaringClass, - name, - parameterTypes, - returnType, - checkedExceptions, - modifiers, - slot, - signature, - annotations, - parameterAnnotations, - annotationDefault); - } - /** Creates a new java.lang.reflect.Constructor. Access checks as per java.lang.reflect.AccessibleObject are not overridden. */ public Constructor<?> newConstructor(Class<?> declaringClass, @@ -325,30 +277,20 @@ byte[] annotations, byte[] parameterAnnotations) { - return langReflectAccess().newConstructor(declaringClass, - parameterTypes, - checkedExceptions, - modifiers, - slot, - signature, - annotations, - parameterAnnotations); - } - - /** Gets the MethodAccessor object for a java.lang.reflect.Method */ - public MethodAccessor getMethodAccessor(Method m) { - return langReflectAccess().getMethodAccessor(m); - } - - /** Sets the MethodAccessor object for a java.lang.reflect.Method */ - public void setMethodAccessor(Method m, MethodAccessor accessor) { - langReflectAccess().setMethodAccessor(m, accessor); + return langReflectAccess.newConstructor(declaringClass, + parameterTypes, + checkedExceptions, + modifiers, + slot, + signature, + annotations, + parameterAnnotations); } /** Gets the ConstructorAccessor object for a java.lang.reflect.Constructor */ public ConstructorAccessor getConstructorAccessor(Constructor<?> c) { - return langReflectAccess().getConstructorAccessor(c); + return langReflectAccess.getConstructorAccessor(c); } /** Sets the ConstructorAccessor object for a @@ -356,21 +298,21 @@ public void setConstructorAccessor(Constructor<?> c, ConstructorAccessor accessor) { - langReflectAccess().setConstructorAccessor(c, accessor); + langReflectAccess.setConstructorAccessor(c, accessor); } /** Makes a copy of the passed method. The returned method is a "child" of the passed one; see the comments in Method.java for details. */ public Method copyMethod(Method arg) { - return langReflectAccess().copyMethod(arg); + return langReflectAccess.copyMethod(arg); } /** Makes a copy of the passed method. The returned method is NOT * a "child" but a "sibling" of the Method in arg. Should only be * used on non-root methods. */ public Method leafCopyMethod(Method arg) { - return langReflectAccess().leafCopyMethod(arg); + return langReflectAccess.leafCopyMethod(arg); } @@ -378,30 +320,30 @@ "child" of the passed one; see the comments in Field.java for details. */ public Field copyField(Field arg) { - return langReflectAccess().copyField(arg); + return langReflectAccess.copyField(arg); } /** Makes a copy of the passed constructor. The returned constructor is a "child" of the passed one; see the comments in Constructor.java for details. */ public <T> Constructor<T> copyConstructor(Constructor<T> arg) { - return langReflectAccess().copyConstructor(arg); + return langReflectAccess.copyConstructor(arg); } /** Gets the byte[] that encodes TypeAnnotations on an executable. */ public byte[] getExecutableTypeAnnotationBytes(Executable ex) { - return langReflectAccess().getExecutableTypeAnnotationBytes(ex); + return langReflectAccess.getExecutableTypeAnnotationBytes(ex); } public Class<?>[] getExecutableSharedParameterTypes(Executable ex) { - return langReflectAccess().getExecutableSharedParameterTypes(ex); + return langReflectAccess.getExecutableSharedParameterTypes(ex); } public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller) throws IllegalAccessException, InstantiationException, InvocationTargetException { - return langReflectAccess().newInstance(ctor, args, caller); + return langReflectAccess.newInstance(ctor, args, caller); } //-------------------------------------------------------------------------- @@ -526,13 +468,13 @@ constructorToCall.getParameterTypes(), constructorToCall.getExceptionTypes(), constructorToCall.getModifiers(), - langReflectAccess(). + langReflectAccess. getConstructorSlot(constructorToCall), - langReflectAccess(). + langReflectAccess. getConstructorSignature(constructorToCall), - langReflectAccess(). + langReflectAccess. getConstructorAnnotations(constructorToCall), - langReflectAccess(). + langReflectAccess. getConstructorParameterAnnotations(constructorToCall)); setConstructorAccessor(c, acc); c.setAccessible(true); @@ -725,17 +667,6 @@ initted = true; } - private static LangReflectAccess langReflectAccess() { - if (langReflectAccess == null) { - // Call a static method to get class java.lang.reflect.Modifier - // initialized. Its static initializer will cause - // setLangReflectAccess() to be called from the context of the - // java.lang.reflect package. - Modifier.isPublic(Modifier.PUBLIC); - } - return langReflectAccess; - } - /** * Returns true if classes are defined in the classloader and same package, false * otherwise.
--- a/src/java.base/share/classes/sun/security/ssl/Finished.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/Finished.java Wed Jul 24 12:21:36 2019 +0530 @@ -482,7 +482,7 @@ shc.conContext.inputRecord.expectingFinishFlight(); } else { if (shc.handshakeSession.isRejoinable() && - !shc.statelessResumption) { + !shc.handshakeSession.isStatelessable(shc)) { ((SSLSessionContextImpl)shc.sslContext. engineGetServerSessionContext()).put( shc.handshakeSession); @@ -847,6 +847,8 @@ shc.conContext.serverVerifyData = fm.verifyData; } + shc.conContext.conSession = shc.handshakeSession.finish(); + // update the context shc.handshakeConsumers.put( SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
--- a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java Wed Jul 24 12:21:36 2019 +0530 @@ -45,7 +45,6 @@ */ final class NewSessionTicket { static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days - static final SSLConsumer handshakeConsumer = new T13NewSessionTicketConsumer(); static final SSLConsumer handshake12Consumer = @@ -60,7 +59,7 @@ */ abstract static class NewSessionTicketMessage extends HandshakeMessage { int ticketLifetime; - byte[] ticket; + byte[] ticket = new byte[0]; NewSessionTicketMessage(HandshakeContext context) { super(context); @@ -83,6 +82,9 @@ "TicketNonce not part of RFC 5077."); } + boolean isValid() { + return (ticket.length > 0); + } } /** * NewSessionTicket for TLS 1.2 and below (RFC 5077) @@ -102,13 +104,13 @@ // RFC5077 struct { // uint32 ticket_lifetime; - // opaque ticket<1..2^16-1>; + // opaque ticket<0..2^16-1>; // } NewSessionTicket; super(context); - if (m.remaining() < 14) { + if (m.remaining() < 6) { throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Invalid NewSessionTicket message: no sufficient data"); + "Invalid NewSessionTicket message: insufficient data"); } this.ticketLifetime = Record.getInt32(m); @@ -186,7 +188,7 @@ if (m.remaining() < 14) { throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Invalid NewSessionTicket message: no sufficient data"); + "Invalid NewSessionTicket message: insufficient data"); } this.ticketLifetime = Record.getInt32(m); @@ -195,18 +197,21 @@ if (m.remaining() < 5) { throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Invalid NewSessionTicket message: no sufficient data"); + "Invalid NewSessionTicket message: insufficient ticket" + + " data"); } this.ticket = Record.getBytes16(m); if (ticket.length == 0) { - throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( "No ticket in the NewSessionTicket handshake message"); + } } if (m.remaining() < 2) { throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Invalid NewSessionTicket message: no sufficient data"); + "Invalid NewSessionTicket message: extra data"); } SSLExtension[] supportedExtensions = @@ -310,36 +315,45 @@ @Override public byte[] produce(ConnectionContext context) throws IOException { - // The producing happens in server side only. - ServerHandshakeContext shc = (ServerHandshakeContext)context; + HandshakeContext hc = (HandshakeContext)context; - // Is this session resumable? - if (!shc.handshakeSession.isRejoinable()) { - return null; - } + // The producing happens in server side only. + if (hc instanceof ServerHandshakeContext) { + // Is this session resumable? + if (!hc.handshakeSession.isRejoinable()) { + return null; + } - // What's the requested PSK key exchange modes? - // - // Note that currently, the NewSessionTicket post-handshake is - // produced and delivered only in the current handshake context - // if required. - PskKeyExchangeModesSpec pkemSpec = - (PskKeyExchangeModesSpec)shc.handshakeExtensions.get( - SSLExtension.PSK_KEY_EXCHANGE_MODES); - if (pkemSpec == null || !pkemSpec.contains( - PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) { - // Client doesn't support PSK with (EC)DHE key establishment. - return null; + // What's the requested PSK key exchange modes? + // + // Note that currently, the NewSessionTicket post-handshake is + // produced and delivered only in the current handshake context + // if required. + PskKeyExchangeModesSpec pkemSpec = + (PskKeyExchangeModesSpec) hc.handshakeExtensions.get( + SSLExtension.PSK_KEY_EXCHANGE_MODES); + if (pkemSpec == null || !pkemSpec.contains( + PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) { + // Client doesn't support PSK with (EC)DHE key establishment. + return null; + } + } else { // PostHandshakeContext + + // Check if we have sent a PSK already, then we know it is using a + // allowable PSK exchange key mode + if (!hc.handshakeSession.isPSKable()) { + return null; + } } // get a new session ID SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) - shc.sslContext.engineGetServerSessionContext(); + hc.sslContext.engineGetServerSessionContext(); SessionId newId = new SessionId(true, - shc.sslContext.getSecureRandom()); + hc.sslContext.getSecureRandom()); SecretKey resumptionMasterSecret = - shc.handshakeSession.getResumptionMasterSecret(); + hc.handshakeSession.getResumptionMasterSecret(); if (resumptionMasterSecret == null) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( @@ -349,10 +363,10 @@ } // construct the PSK and handshake message - BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter(); + BigInteger nonce = hc.handshakeSession.incrTicketNonceCounter(); byte[] nonceArr = nonce.toByteArray(); SecretKey psk = derivePreSharedKey( - shc.negotiatedCipherSuite.hashAlg, + hc.negotiatedCipherSuite.hashAlg, resumptionMasterSecret, nonceArr); int sessionTimeoutSeconds = sessionCache.getSessionTimeout(); @@ -364,31 +378,35 @@ return null; } - NewSessionTicketMessage nstm; + NewSessionTicketMessage nstm = null; SSLSessionImpl sessionCopy = - new SSLSessionImpl(shc.handshakeSession, newId); + new SSLSessionImpl(hc.handshakeSession, newId); sessionCopy.setPreSharedKey(psk); sessionCopy.setPskIdentity(newId.getId()); - if (shc.statelessResumption) { - try { - nstm = new T13NewSessionTicketMessage(shc, - sessionTimeoutSeconds, shc.sslContext.getSecureRandom(), - nonceArr, new SessionTicketSpec().encrypt(shc, sessionCopy)); + // If a stateless ticket is allowed, attempt to make one + if (hc.handshakeSession.isStatelessable(hc)) { + nstm = new T13NewSessionTicketMessage(hc, + sessionTimeoutSeconds, + hc.sslContext.getSecureRandom(), + nonceArr, + new SessionTicketSpec().encrypt(hc, sessionCopy)); + // If ticket construction failed, switch to session cache + if (!nstm.isValid()) { + hc.statelessResumption = false; + } else { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced NewSessionTicket stateless " + "handshake message", nstm); } - } catch (Exception e) { - // Error with NST ticket, abort NST - shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e); - return null; } - } else { - nstm = new T13NewSessionTicketMessage(shc, sessionTimeoutSeconds, - shc.sslContext.getSecureRandom(), nonceArr, + } + // If a session cache ticket is being used, make one + if (!hc.handshakeSession.isStatelessable(hc)) { + nstm = new T13NewSessionTicketMessage(hc, sessionTimeoutSeconds, + hc.sslContext.getSecureRandom(), nonceArr, newId.getId()); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( @@ -399,13 +417,21 @@ // create and cache the new session // The new session must be a child of the existing session so // they will be invalidated together, etc. - shc.handshakeSession.addChild(sessionCopy); + hc.handshakeSession.addChild(sessionCopy); sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd()); sessionCache.put(sessionCopy); } + // Output the handshake message. - nstm.write(shc.handshakeOutput); - shc.handshakeOutput.flush(); + if (nstm != null) { + // should never be null + nstm.write(hc.handshakeOutput); + hc.handshakeOutput.flush(); + } + + if (hc instanceof PostHandshakeContext) { + ((PostHandshakeContext) hc).finish(); + } // The message has been delivered. return null; @@ -448,23 +474,16 @@ return null; } - NewSessionTicketMessage nstm; - SSLSessionImpl sessionCopy = new SSLSessionImpl(shc.handshakeSession, newId); sessionCopy.setPskIdentity(newId.getId()); - try { - nstm = new T12NewSessionTicketMessage(shc, sessionTimeoutSeconds, - new SessionTicketSpec().encrypt(shc, sessionCopy)); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Produced NewSessionTicket stateless handshake message", nstm); - } - } catch (Exception e) { - // Abort on error with NST ticket - shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e); - return null; + NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc, + sessionTimeoutSeconds, + new SessionTicketSpec().encrypt(shc, sessionCopy)); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced NewSessionTicket stateless handshake message", nstm); } // Output the handshake message. @@ -505,6 +524,9 @@ "Consuming NewSessionTicket message", nstm); } + SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) + hc.sslContext.engineGetClientSessionContext(); + // discard tickets with timeout 0 if (nstm.ticketLifetime <= 0 || nstm.ticketLifetime > MAX_TICKET_LIFETIME) { @@ -513,12 +535,10 @@ "Discarding NewSessionTicket with lifetime " + nstm.ticketLifetime, nstm); } + sessionCache.remove(hc.handshakeSession.getSessionId()); return; } - SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) - hc.sslContext.engineGetClientSessionContext(); - if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine(
--- a/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java Wed Jul 24 12:21:36 2019 +0530 @@ -54,6 +54,7 @@ handshakeConsumers = new LinkedHashMap<>(consumers); handshakeFinished = true; + handshakeSession = context.conSession; } @Override @@ -82,4 +83,9 @@ SSLHandshake.nameOf(handshakeType), be); } } + + // Finish this PostHandshake event + void finish() { + handshakeSession = null; + } }
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Wed Jul 24 12:21:36 2019 +0530 @@ -344,6 +344,12 @@ hsStatus = tryKeyUpdate(hsStatus); } + // Check if NewSessionTicket PostHandshake message needs to be sent + if (conContext.conSession.updateNST && + !conContext.sslConfig.isClientMode) { + hsStatus = tryNewSessionTicket(hsStatus); + } + // update context status ciphertext.handshakeStatus = hsStatus; @@ -397,6 +403,29 @@ return currentHandshakeStatus; } + // Try to generate a PostHandshake NewSessionTicket message. This is + // TLS 1.3 only. + private HandshakeStatus tryNewSessionTicket( + HandshakeStatus currentHandshakeStatus) throws IOException { + // Don't bother to kickstart if handshaking is in progress, or if the + // connection is not duplex-open. + if ((conContext.handshakeContext == null) && + conContext.protocolVersion.useTLS13PlusSpec() && + !conContext.isOutboundClosed() && + !conContext.isInboundClosed() && + !conContext.isBroken) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("trigger NST"); + } + conContext.conSession.updateNST = false; + NewSessionTicket.kickstartProducer.produce( + new PostHandshakeContext(conContext)); + return conContext.getHandshakeStatus(); + } + + return currentHandshakeStatus; + } + private static void checkParams( ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java Wed Jul 24 12:21:36 2019 +0530 @@ -69,7 +69,7 @@ private int cacheLimit; // the max cache size private int timeout; // timeout in seconds - // Does this context support stateless session (RFC 5077) + // Default setting for stateless session resumption support (RFC 5077) private boolean statelessSession = true; // package private @@ -239,6 +239,7 @@ st = GetPropertyAction.privilegedGetProperty( "jdk.tls.client.enableSessionTicketExtension", "true"); } + if (st.compareToIgnoreCase("false") == 0) { statelessSession = false; }
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Wed Jul 24 12:21:36 2019 +0530 @@ -27,6 +27,7 @@ import sun.security.x509.X509CertImpl; import java.io.IOException; +import java.lang.reflect.Array; import java.math.BigInteger; import java.net.InetAddress; import java.nio.ByteBuffer; @@ -35,6 +36,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Queue; import java.util.Collection; import java.util.Collections; @@ -104,7 +106,7 @@ private X509Certificate[] localCerts; private PrivateKey localPrivateKey; private final Collection<SignatureScheme> localSupportedSignAlgs; - private String[] peerSupportedSignAlgs; // for certificate + private Collection<SignatureScheme> peerSupportedSignAlgs; //for certificate private boolean useDefaultPeerSignAlgs = false; private List<byte[]> statusResponses; private SecretKey resumptionMasterSecret; @@ -236,7 +238,8 @@ baseSession.localSupportedSignAlgs == null ? Collections.emptySet() : baseSession.localSupportedSignAlgs; this.peerSupportedSignAlgs = - baseSession.getPeerSupportedSignatureAlgorithms(); + baseSession.peerSupportedSignAlgs == null ? + Collections.emptySet() : baseSession.peerSupportedSignAlgs; this.serverNameIndication = baseSession.serverNameIndication; this.requestedServerNames = baseSession.getRequestedServerNames(); this.masterSecret = baseSession.getMasterSecret(); @@ -261,8 +264,10 @@ /** * < 2 bytes > protocolVersion * < 2 bytes > cipherSuite - * < 2 bytes > localSupportedSignAlgs entries + * < 1 byte > localSupportedSignAlgs entries * < 2 bytes per entries > localSupportedSignAlgs + * < 1 bytes > peerSupportedSignAlgs entries + * < 2 bytes per entries > peerSupportedSignAlgs * < 2 bytes > preSharedKey length * < length in bytes > preSharedKey * < 1 byte > pskIdentity length @@ -281,6 +286,9 @@ * < 1 byte > ServerName length * < length in bytes > ServerName * < 4 bytes > creationTime + * < 2 byte > status response length + * < 2 byte > status response entry length + * < length in byte > status response entry * < 1 byte > Length of peer host * < length in bytes > peer host * < 2 bytes> peer port @@ -302,17 +310,17 @@ * < length in bytes> PSK identity * Anonymous * < 1 byte > + * < 4 bytes > maximumPacketSize + * < 4 bytes > negotiatedMaxFragSize */ SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException { int i = 0; byte[] b; - this.localSupportedSignAlgs = new ArrayList<>(); - - boundValues = null; - - this.protocolVersion = ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort())); + boundValues = new ConcurrentHashMap<>(); + this.protocolVersion = + ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort())); if (protocolVersion.useTLS13PlusSpec()) { this.sessionId = new SessionId(false, null); @@ -322,14 +330,26 @@ hc.sslContext.getSecureRandom()); } - this.cipherSuite = CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort())); + this.cipherSuite = + CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort())); // Local Supported signature algorithms - i = Short.toUnsignedInt(buf.getShort()); + ArrayList<SignatureScheme> list = new ArrayList<>(); + i = Byte.toUnsignedInt(buf.get()); while (i-- > 0) { - this.localSupportedSignAlgs.add(SignatureScheme.valueOf( + list.add(SignatureScheme.valueOf( Short.toUnsignedInt(buf.getShort()))); } + this.localSupportedSignAlgs = Collections.unmodifiableCollection(list); + + // Peer Supported signature algorithms + i = Byte.toUnsignedInt(buf.get()); + list.clear(); + while (i-- > 0) { + list.add(SignatureScheme.valueOf( + Short.toUnsignedInt(buf.getShort()))); + } + this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list); // PSK i = Short.toUnsignedInt(buf.getShort()); @@ -410,9 +430,27 @@ } } + maximumPacketSize = buf.getInt(); + negotiatedMaxFragLen = buf.getInt(); + // Get creation time this.creationTime = buf.getLong(); + // Get Buffer sizes + + // Status Response + len = Short.toUnsignedInt(buf.getShort()); + if (len == 0) { + statusResponses = Collections.emptyList(); + } else { + statusResponses = new ArrayList<>(); + } + while (len-- > 0) { + b = new byte[Short.toUnsignedInt(buf.getShort())]; + buf.get(b); + statusResponses.add(b); + } + // Get Peer host & port i = Byte.toUnsignedInt(buf.get()); if (i == 0) { @@ -484,6 +522,33 @@ context = (SSLSessionContextImpl) hc.sslContext.engineGetServerSessionContext(); + this.lastUsedTime = System.currentTimeMillis(); + } + + // Some situations we cannot provide a stateless ticket, but after it + // has been negotiated + boolean isStatelessable(HandshakeContext hc) { + if (!hc.statelessResumption) { + return false; + } + + // If there is no getMasterSecret with TLS1.2 or under, do not resume. + if (!protocolVersion.useTLS13PlusSpec() && + getMasterSecret().getEncoded() == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("No MasterSecret, cannot make stateless" + + " ticket"); + } + return false; + } + if (boundValues != null && boundValues.size() > 0) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("There are boundValues, cannot make" + + " stateless ticket"); + } + return false; + } + return true; } /** @@ -497,11 +562,14 @@ hos.putInt16(cipherSuite.id); // Local Supported signature algorithms - int l = localSupportedSignAlgs.size(); - hos.putInt16(l); - SignatureScheme[] sig = new SignatureScheme[l]; - localSupportedSignAlgs.toArray(sig); - for (SignatureScheme s : sig) { + hos.putInt8(localSupportedSignAlgs.size()); + for (SignatureScheme s : localSupportedSignAlgs) { + hos.putInt16(s.id); + } + + // Peer Supported signature algorithms + hos.putInt8(peerSupportedSignAlgs.size()); + for (SignatureScheme s : peerSupportedSignAlgs) { hos.putInt16(s.id); } @@ -564,16 +632,30 @@ // List of SNIServerName hos.putInt16(requestedServerNames.size()); if (requestedServerNames.size() > 0) { - for (SNIServerName host: requestedServerNames) { + for (SNIServerName host : requestedServerNames) { b = host.getEncoded(); hos.putInt8(b.length); hos.write(b, 0, b.length); } } + // Buffer sizes + hos.putInt32(maximumPacketSize); + hos.putInt32(negotiatedMaxFragLen); + + // creation time ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); hos.writeBytes(buffer.putLong(creationTime).array()); + // Status Responses + List<byte[]> list = getStatusResponses(); + int l = list.size(); + hos.putInt16(l); + for (byte[] e : list) { + hos.putInt16(e.length); + hos.write(e); + } + // peer Host & Port if (host == null || host.length() == 0) { hos.putInt8(0); @@ -649,10 +731,14 @@ BigInteger incrTicketNonceCounter() { BigInteger result = ticketNonceCounter; - ticketNonceCounter = ticketNonceCounter.add(BigInteger.valueOf(1)); + ticketNonceCounter = ticketNonceCounter.add(BigInteger.ONE); return result; } + boolean isPSKable() { + return (ticketNonceCounter.compareTo(BigInteger.ZERO) > 0); + } + /** * Returns the master secret ... treat with extreme caution! */ @@ -725,8 +811,7 @@ void setPeerSupportedSignatureAlgorithms( Collection<SignatureScheme> signatureSchemes) { - peerSupportedSignAlgs = - SignatureScheme.getAlgorithmNames(signatureSchemes); + peerSupportedSignAlgs = signatureSchemes; } // TLS 1.2 only @@ -740,16 +825,20 @@ // certificates and server key exchange), it MUST send the // signature_algorithms extension, listing the algorithms it // is willing to accept. + private static final ArrayList<SignatureScheme> defaultPeerSupportedSignAlgs = + new ArrayList<>(Arrays.asList(SignatureScheme.RSA_PKCS1_SHA1, + SignatureScheme.DSA_SHA1, + SignatureScheme.ECDSA_SHA1)); + void setUseDefaultPeerSignAlgs() { useDefaultPeerSignAlgs = true; - peerSupportedSignAlgs = new String[] { - "SHA1withRSA", "SHA1withDSA", "SHA1withECDSA"}; + peerSupportedSignAlgs = defaultPeerSupportedSignAlgs; } // Returns the connection session. SSLSessionImpl finish() { if (useDefaultPeerSignAlgs) { - this.peerSupportedSignAlgs = new String[0]; + peerSupportedSignAlgs = Collections.emptySet(); } return this; @@ -1212,6 +1301,7 @@ * sessions can be shared across different protection domains. */ private final ConcurrentHashMap<SecureKey, Object> boundValues; + boolean updateNST; /** * Assigns a session value. Session change events are given if @@ -1238,6 +1328,9 @@ e = new SSLSessionBindingEvent(this, key); ((SSLSessionBindingListener)value).valueBound(e); } + if (protocolVersion.useTLS13PlusSpec()) { + updateNST = true; + } } /** @@ -1273,6 +1366,9 @@ e = new SSLSessionBindingEvent(this, key); ((SSLSessionBindingListener)value).valueUnbound(e); } + if (protocolVersion.useTLS13PlusSpec()) { + updateNST = true; + } } @@ -1474,11 +1570,7 @@ */ @Override public String[] getPeerSupportedSignatureAlgorithms() { - if (peerSupportedSignAlgs != null) { - return peerSupportedSignAlgs.clone(); - } - - return new String[0]; + return SignatureScheme.getAlgorithmNames(peerSupportedSignAlgs); } /**
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Wed Jul 24 12:21:36 2019 +0530 @@ -1264,6 +1264,11 @@ conContext.outputRecord.writeCipher.atKeyLimit()) { tryKeyUpdate(); } + // Check if NewSessionTicket PostHandshake message needs to be sent + if (conContext.conSession.updateNST) { + conContext.conSession.updateNST = false; + tryNewSessionTicket(); + } } @Override @@ -1499,6 +1504,25 @@ } } + // Try to generate a PostHandshake NewSessionTicket message. This is + // TLS 1.3 only. + private void tryNewSessionTicket() throws IOException { + // Don't bother to kickstart if handshaking is in progress, or if the + // connection is not duplex-open. + if (!conContext.sslConfig.isClientMode && + conContext.protocolVersion.useTLS13PlusSpec() && + conContext.handshakeContext == null && + !conContext.isOutboundClosed() && + !conContext.isInboundClosed() && + !conContext.isBroken) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("trigger new session ticket"); + } + NewSessionTicket.kickstartProducer.produce( + new PostHandshakeContext(conContext)); + } + } + /** * Initialize the handshaker and socket streams. *
--- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java Wed Jul 24 12:21:36 2019 +0530 @@ -1155,14 +1155,14 @@ chc, chc.resumingSession.getMasterSecret()); } - chc.conContext.consumers.putIfAbsent( - ContentType.CHANGE_CIPHER_SPEC.id, - ChangeCipherSpec.t10Consumer); if (chc.statelessResumption) { chc.handshakeConsumers.putIfAbsent( SSLHandshake.NEW_SESSION_TICKET.id, SSLHandshake.NEW_SESSION_TICKET); } + chc.conContext.consumers.putIfAbsent( + ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t10Consumer); chc.handshakeConsumers.put( SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
--- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java Wed Jul 24 12:21:36 2019 +0530 @@ -46,7 +46,6 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.text.MessageFormat; -import java.util.Collection; import java.util.Locale; /** @@ -255,13 +254,17 @@ data = buf; } - public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) - throws IOException { + public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) { byte[] encrypted; - StatelessKey key = KeyState.getCurrentKey(hc); - byte[] iv = new byte[16]; + + if (!hc.handshakeSession.isStatelessable(hc)) { + return new byte[0]; + } try { + StatelessKey key = KeyState.getCurrentKey(hc); + byte[] iv = new byte[16]; + SecureRandom random = hc.sslContext.getSecureRandom(); random.nextBytes(iv); Cipher c = Cipher.getInstance("AES/GCM/NoPadding"); @@ -273,8 +276,11 @@ (byte)(key.num >>> 8), (byte)(key.num)} ); - encrypted = c.doFinal(session.write()); - + byte[] data = session.write(); + if (data.length == 0) { + return data; + } + encrypted = c.doFinal(data); byte[] result = new byte[encrypted.length + Integer.BYTES + iv.length]; result[0] = (byte)(key.num >>> 24); @@ -286,7 +292,10 @@ Integer.BYTES + iv.length, encrypted.length); return result; } catch (Exception e) { - throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Encryption failed." + e); + } + return new byte[0]; } } @@ -311,11 +320,7 @@ (byte)(keyID >>> 8), (byte)(keyID)} ); - /* - return ByteBuffer.wrap(c.doFinal(data, - Integer.BYTES + iv.length, - data.length - (Integer.BYTES + iv.length))); - */ + ByteBuffer out; out = ByteBuffer.allocate(data.remaining() - GCM_TAG_LEN / 8); c.doFinal(data, out);
--- a/src/java.base/share/classes/sun/security/util/ManifestDigester.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/util/ManifestDigester.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -25,10 +25,12 @@ package sun.security.util; -import java.security.*; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.HashMap; import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.IOException; import java.util.List; import static java.nio.charset.StandardCharsets.UTF_8; @@ -40,13 +42,27 @@ */ public class ManifestDigester { + /** + * The part "{@code Manifest-Main-Attributes}" of the main attributes + * digest header name in a signature file as described in the jar + * specification: + * <blockquote>{@code x-Digest-Manifest-Main-Attributes} + * (where x is the standard name of a {@link MessageDigest} algorithm): + * The value of this attribute is the digest value of the main attributes + * of the manifest.</blockquote> + * @see <a href="{@docRoot}/../specs/jar/jar.html#signature-file"> + * JAR File Specification, section Signature File</a> + * @see #getMainAttsEntry + */ public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes"; /** the raw bytes of the manifest */ - private byte[] rawBytes; + private final byte[] rawBytes; - /** the entries grouped by names */ - private HashMap<String, Entry> entries; // key is a UTF-8 string + private final Entry mainAttsEntry; + + /** individual sections by their names */ + private final HashMap<String, Entry> entries = new HashMap<>(); /** state returned by findSection */ static class Position { @@ -72,29 +88,31 @@ private boolean findSection(int offset, Position pos) { int i = offset, len = rawBytes.length; - int last = offset; + int last = offset - 1; int next; boolean allBlank = true; - pos.endOfFirstLine = -1; + /* denotes that a position is not yet assigned. + * As a primitive type int it cannot be null + * and -1 would be confused with (i - 1) when i == 0 */ + final int UNASSIGNED = Integer.MIN_VALUE; + + pos.endOfFirstLine = UNASSIGNED; while (i < len) { byte b = rawBytes[i]; switch(b) { case '\r': - if (pos.endOfFirstLine == -1) + if (pos.endOfFirstLine == UNASSIGNED) pos.endOfFirstLine = i-1; - if ((i < len) && (rawBytes[i+1] == '\n')) + if (i < len - 1 && rawBytes[i + 1] == '\n') i++; /* fall through */ case '\n': - if (pos.endOfFirstLine == -1) + if (pos.endOfFirstLine == UNASSIGNED) pos.endOfFirstLine = i-1; if (allBlank || (i == len-1)) { - if (i == len-1) - pos.endOfSection = i; - else - pos.endOfSection = last; + pos.endOfSection = allBlank ? last : i; pos.startOfNext = i+1; return true; } @@ -116,16 +134,17 @@ public ManifestDigester(byte[] bytes) { rawBytes = bytes; - entries = new HashMap<>(); Position pos = new Position(); - if (!findSection(0, pos)) + if (!findSection(0, pos)) { + mainAttsEntry = null; return; // XXX: exception? + } // create an entry for main attributes - entries.put(MF_MAIN_ATTRS, new Entry().addSection( - new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes))); + mainAttsEntry = new Entry().addSection(new Section( + 0, pos.endOfSection + 1, pos.startOfNext, rawBytes)); int start = pos.startOfNext; while(findSection(start, pos)) { @@ -133,14 +152,16 @@ int sectionLen = pos.endOfSection-start+1; int sectionLenWithBlank = pos.startOfNext-start; - if (len > 6) { + if (len >= 6) { // 6 == "Name: ".length() if (isNameAttr(bytes, start)) { ByteArrayOutputStream nameBuf = new ByteArrayOutputStream(); nameBuf.write(bytes, start+6, len-6); int i = start + len; if ((i-start) < sectionLen) { - if (bytes[i] == '\r') { + if (bytes[i] == '\r' + && i + 1 - start < sectionLen + && bytes[i + 1] == '\n') { i += 2; } else { i += 1; @@ -152,14 +173,16 @@ // name is wrapped int wrapStart = i; while (((i-start) < sectionLen) - && (bytes[i++] != '\n')); - if (bytes[i-1] != '\n') - return; // XXX: exception? - int wrapLen; - if (bytes[i-2] == '\r') - wrapLen = i-wrapStart-2; - else - wrapLen = i-wrapStart-1; + && (bytes[i] != '\r') + && (bytes[i] != '\n')) i++; + int wrapLen = i - wrapStart; + if (i - start < sectionLen) { + i++; + if (bytes[i - 1] == '\r' + && i - start < sectionLen + && bytes[i] == '\n') + i++; + } nameBuf.write(bytes, wrapStart, wrapLen); } else { @@ -167,7 +190,7 @@ } } - entries.computeIfAbsent(new String(nameBuf.toByteArray(), UTF_8), + entries.computeIfAbsent(nameBuf.toString(UTF_8), dummy -> new Entry()) .addSection(new Section(start, sectionLen, sectionLenWithBlank, rawBytes)); @@ -202,6 +225,26 @@ return this; } + /** + * Check if the sections (particularly the last one of usually only one) + * are properly delimited with a trailing blank line so that another + * section can be correctly appended and return {@code true} or return + * {@code false} to indicate that reproduction is not advised and should + * be carried out with a clean "normalized" newly-written manifest. + * + * @see #reproduceRaw + */ + public boolean isProperlyDelimited() { + return sections.stream().allMatch( + Section::isProperlySectionDelimited); + } + + public void reproduceRaw(OutputStream out) throws IOException { + for (Section sec : sections) { + out.write(sec.rawBytes, sec.offset, sec.lengthWithBlankLine); + } + } + public byte[] digest(MessageDigest md) { md.reset(); @@ -242,6 +285,15 @@ this.rawBytes = rawBytes; } + /** + * Returns {@code true} if the raw section is terminated with a blank + * line so that another section can possibly be appended resulting in a + * valid manifest and {@code false} otherwise. + */ + private boolean isProperlySectionDelimited() { + return lengthWithBlankLine > length; + } + private static void doOldStyle(MessageDigest md, byte[] bytes, int offset, @@ -268,10 +320,33 @@ } } + /** + * @see #MF_MAIN_ATTRS + */ + public Entry getMainAttsEntry() { + return mainAttsEntry; + } + + /** + * @see #MF_MAIN_ATTRS + */ + public Entry getMainAttsEntry(boolean oldStyle) { + mainAttsEntry.oldStyle = oldStyle; + return mainAttsEntry; + } + + public Entry get(String name) { + return entries.get(name); + } + public Entry get(String name, boolean oldStyle) { - Entry e = entries.get(name); - if (e != null) + Entry e = get(name); + if (e == null && MF_MAIN_ATTRS.equals(name)) { + e = getMainAttsEntry(); + } + if (e != null) { e.oldStyle = oldStyle; + } return e; }
--- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -72,8 +72,7 @@ private ArrayList<CodeSigner[]> signerCache; private static final String ATTR_DIGEST = - ("-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS).toUpperCase - (Locale.ENGLISH); + "-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS.toUpperCase(Locale.ENGLISH); /** the PKCS7 block for this .DSA/.RSA/.EC file */ private PKCS7 block; @@ -537,8 +536,7 @@ MessageDigest digest = getDigest(algorithm); if (digest != null) { - ManifestDigester.Entry mde = - md.get(ManifestDigester.MF_MAIN_ATTRS, false); + ManifestDigester.Entry mde = md.getMainAttsEntry(false); byte[] computedHash = mde.digest(digest); byte[] expectedHash = Base64.getMimeDecoder().decode((String)se.getValue());
--- a/src/java.base/share/legal/icu.md Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/legal/icu.md Wed Jul 24 12:21:36 2019 +0530 @@ -1,6 +1,7 @@ ## International Components for Unicode (ICU4J) v64.2 ### ICU4J License +``` COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) @@ -387,3 +388,6 @@ # by ICANN or the IETF Trust on the database or the code. Any person # making a contribution to the database or code waives all rights to # future claims in that contribution or in the TZ Database. + +``` +
--- a/src/java.base/share/legal/unicode.md Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/share/legal/unicode.md Wed Jul 24 12:21:36 2019 +0530 @@ -1,6 +1,7 @@ ## The Unicode Standard, Unicode Character Database, Version 12.1.0 ### Unicode Character Database +``` UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE @@ -48,3 +49,6 @@ shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. + +``` +
--- a/src/java.base/unix/native/libjava/childproc.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/unix/native/libjava/childproc.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, 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 @@ -26,6 +26,7 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
--- a/src/java.base/unix/native/libnet/NetworkInterface.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/unix/native/libnet/NetworkInterface.c Wed Jul 24 12:21:36 2019 +0530 @@ -34,6 +34,7 @@ #include <netinet/in6_var.h> #include <sys/ndd_var.h> #include <sys/kinfo.h> +#include <strings.h> #endif #if defined(__solaris__)
--- a/src/java.base/unix/native/libnio/ch/FileChannelImpl.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/unix/native/libnio/ch/FileChannelImpl.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -32,6 +32,7 @@ #if defined(__linux__) || defined(__solaris__) #include <sys/sendfile.h> #elif defined(_AIX) +#include <string.h> #include <sys/socket.h> #elif defined(_ALLBSD_SOURCE) #include <sys/socket.h>
--- a/src/java.base/unix/native/libnio/ch/Net.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.base/unix/native/libnio/ch/Net.c Wed Jul 24 12:21:36 2019 +0530 @@ -42,6 +42,7 @@ #include "nio.h" #ifdef _AIX +#include <stdlib.h> #include <sys/utsname.h> #endif
--- a/src/java.desktop/aix/native/libawt/porting_aix.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.desktop/aix/native/libawt/porting_aix.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 SAP SE. All rights reserved. + * Copyright (c) 2012, 2019 SAP SE. 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 @@ -25,6 +25,7 @@ */ #include <stdio.h> +#include <string.h> #include <sys/ldr.h> #include <errno.h>
--- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, 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 @@ -150,7 +150,7 @@ static jboolean isXCompositeDisplay(Display *display, int screenNumber) { char NET_WM_CM_Sn[25]; - snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d\0", screenNumber); + snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d", screenNumber); Atom managerSelection = XInternAtom(display, NET_WM_CM_Sn, 0); Window owner = XGetSelectionOwner(display, managerSelection);
--- a/src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, 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 @@ -345,7 +345,7 @@ if (krbcredsConstructor == 0) { krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "<init>", - "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V"); + "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V"); if (krbcredsConstructor == 0) { printf("Couldn't find sun.security.krb5.internal.Ticket constructor\n"); break; @@ -359,7 +359,9 @@ krbcredsConstructor, ticket, clientPrincipal, + NULL, targetPrincipal, + NULL, encryptionKey, ticketFlags, authTime,
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, 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 @@ -26,8 +26,6 @@ package javax.security.auth.kerberos; import sun.security.krb5.JavaxSecurityAuthKerberosAccess; -import sun.security.krb5.EncryptionKey; -import sun.security.krb5.PrincipalName; class JavaxSecurityAuthKerberosAccessImpl implements JavaxSecurityAuthKerberosAccess { @@ -35,4 +33,20 @@ KeyTab ktab) { return ktab.takeSnapshot(); } + + public KerberosPrincipal kerberosTicketGetClientAlias(KerberosTicket t) { + return t.clientAlias; + } + + public void kerberosTicketSetClientAlias(KerberosTicket t, KerberosPrincipal a) { + t.clientAlias = a; + } + + public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t) { + return t.serverAlias; + } + + public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a) { + t.serverAlias = a; + } }
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -195,6 +195,10 @@ private transient boolean destroyed = false; + transient KerberosPrincipal clientAlias = null; + + transient KerberosPrincipal serverAlias = null; + /** * Constructs a {@code KerberosTicket} using credentials information that a * client either receives from a KDC or reads from a cache. @@ -591,7 +595,11 @@ try { krb5Creds = new sun.security.krb5.Credentials(asn1Encoding, client.getName(), + (clientAlias != null ? + clientAlias.getName() : null), server.getName(), + (serverAlias != null ? + serverAlias.getName() : null), sessionKey.getEncoded(), sessionKey.getKeyType(), flags,
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -713,14 +713,14 @@ if (subject != null && !subject.isReadOnly()) { /* - * Store the service credentials as - * javax.security.auth.kerberos.KerberosTicket in - * the Subject. We could wait till the context is - * succesfully established; however it is easier - * to do here and there is no harm indoing it here. - */ + * Store the service credentials as + * javax.security.auth.kerberos.KerberosTicket in + * the Subject. We could wait until the context is + * successfully established; however it is easier + * to do it here and there is no harm. + */ final KerberosTicket kt = - Krb5Util.credsToTicket(serviceCreds); + Krb5Util.credsToTicket(serviceCreds); AccessController.doPrivileged ( new java.security.PrivilegedAction<Void>() { public Void run() {
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -59,7 +59,9 @@ private Krb5InitCredential(Krb5NameElement name, byte[] asn1Encoding, KerberosPrincipal client, + KerberosPrincipal clientAlias, KerberosPrincipal server, + KerberosPrincipal serverAlias, byte[] sessionKey, int keyType, boolean[] flags, @@ -80,14 +82,21 @@ endTime, renewTill, clientAddresses); - + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetClientAlias(this, clientAlias); + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetServerAlias(this, serverAlias); this.name = name; try { // Cache this for later use by the sun.security.krb5 package. krb5Credentials = new Credentials(asn1Encoding, client.getName(), + (clientAlias != null ? + clientAlias.getName() : null), server.getName(), + (serverAlias != null ? + serverAlias.getName() : null), sessionKey, keyType, flags, @@ -110,7 +119,9 @@ Credentials delegatedCred, byte[] asn1Encoding, KerberosPrincipal client, + KerberosPrincipal clientAlias, KerberosPrincipal server, + KerberosPrincipal serverAlias, byte[] sessionKey, int keyType, boolean[] flags, @@ -131,7 +142,10 @@ endTime, renewTill, clientAddresses); - + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetClientAlias(this, clientAlias); + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetServerAlias(this, serverAlias); this.name = name; // A delegated cred does not have all fields set. So do not try to // creat new Credentials out of the delegatedCred. @@ -153,10 +167,18 @@ Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); } + KerberosPrincipal clientAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetClientAlias(tgt); + KerberosPrincipal serverAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetServerAlias(tgt); return new Krb5InitCredential(name, tgt.getEncoded(), tgt.getClient(), + clientAlias, tgt.getServer(), + serverAlias, tgt.getSessionKey().getEncoded(), tgt.getSessionKeyType(), tgt.getFlags(), @@ -179,10 +201,14 @@ */ PrincipalName cPrinc = delegatedCred.getClient(); + PrincipalName cAPrinc = delegatedCred.getClientAlias(); PrincipalName sPrinc = delegatedCred.getServer(); + PrincipalName sAPrinc = delegatedCred.getServerAlias(); KerberosPrincipal client = null; + KerberosPrincipal clientAlias = null; KerberosPrincipal server = null; + KerberosPrincipal serverAlias = null; Krb5NameElement credName = null; @@ -193,6 +219,10 @@ client = new KerberosPrincipal(fullName); } + if (cAPrinc != null) { + clientAlias = new KerberosPrincipal(cAPrinc.getName()); + } + // XXX Compare name to credName if (sPrinc != null) { @@ -201,11 +231,17 @@ KerberosPrincipal.KRB_NT_SRV_INST); } + if (sAPrinc != null) { + serverAlias = new KerberosPrincipal(sAPrinc.getName()); + } + return new Krb5InitCredential(credName, delegatedCred, delegatedCred.getEncoded(), client, + clientAlias, server, + serverAlias, sessionKey.getBytes(), sessionKey.getEType(), delegatedCred.getFlags(),
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java Wed Jul 24 12:21:36 2019 +0530 @@ -132,7 +132,7 @@ public static KerberosTicket credsToTicket(Credentials serviceCreds) { EncryptionKey sessionKey = serviceCreds.getSessionKey(); - return new KerberosTicket( + KerberosTicket kt = new KerberosTicket( serviceCreds.getEncoded(), new KerberosPrincipal(serviceCreds.getClient().getName()), new KerberosPrincipal(serviceCreds.getServer().getName(), @@ -145,14 +145,35 @@ serviceCreds.getEndTime(), serviceCreds.getRenewTill(), serviceCreds.getClientAddresses()); + PrincipalName clientAlias = serviceCreds.getClientAlias(); + PrincipalName serverAlias = serviceCreds.getServerAlias(); + if (clientAlias != null) { + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetClientAlias(kt, new KerberosPrincipal( + clientAlias.getName(), clientAlias.getNameType())); + } + if (serverAlias != null) { + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetServerAlias(kt, new KerberosPrincipal( + serverAlias.getName(), serverAlias.getNameType())); + } + return kt; }; public static Credentials ticketToCreds(KerberosTicket kerbTicket) throws KrbException, IOException { + KerberosPrincipal clientAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetClientAlias(kerbTicket); + KerberosPrincipal serverAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetServerAlias(kerbTicket); return new Credentials( kerbTicket.getEncoded(), kerbTicket.getClient().getName(), + (clientAlias != null ? clientAlias.getName() : null), kerbTicket.getServer().getName(), + (serverAlias != null ? serverAlias.getName() : null), kerbTicket.getSessionKey().getEncoded(), kerbTicket.getSessionKeyType(), kerbTicket.getFlags(),
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2019, 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 @@ -25,6 +25,8 @@ package sun.security.jgss.krb5; +import sun.security.krb5.KerberosSecrets; + import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KerberosKey; import javax.security.auth.Subject; @@ -182,24 +184,45 @@ } } else { + KerberosPrincipal serverAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetServerAlias(ticket); if (serverPrincipal == null || - ticket.getServer().getName().equals(serverPrincipal)) { - + ticket.getServer().getName().equals(serverPrincipal) || + (serverAlias != null && + serverPrincipal.equals( + serverAlias.getName()))) { + KerberosPrincipal clientAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetClientAlias(ticket); if (clientPrincipal == null || clientPrincipal.equals( - ticket.getClient().getName())) { + ticket.getClient().getName()) || + (clientAlias != null && + clientPrincipal.equals( + clientAlias.getName()))) { if (oneOnly) { return ticket; } else { // Record names so that tickets will // all belong to same principals if (clientPrincipal == null) { - clientPrincipal = - ticket.getClient().getName(); + if (clientAlias == null) { + clientPrincipal = + ticket.getClient().getName(); + } else { + clientPrincipal = + clientAlias.getName(); + } } if (serverPrincipal == null) { - serverPrincipal = - ticket.getServer().getName(); + if (serverAlias == null) { + serverPrincipal = + ticket.getServer().getName(); + } else { + serverPrincipal = + serverAlias.getName(); + } } answer.add(credClass.cast(ticket)); }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -49,7 +49,9 @@ Ticket ticket; PrincipalName client; + PrincipalName clientAlias; PrincipalName server; + PrincipalName serverAlias; EncryptionKey key; TicketFlags flags; KerberosTime authTime; @@ -69,7 +71,9 @@ public Credentials(Ticket new_ticket, PrincipalName new_client, + PrincipalName new_client_alias, PrincipalName new_server, + PrincipalName new_server_alias, EncryptionKey new_key, TicketFlags new_flags, KerberosTime authTime, @@ -78,14 +82,17 @@ KerberosTime renewTill, HostAddresses cAddr, AuthorizationData authzData) { - this(new_ticket, new_client, new_server, new_key, new_flags, - authTime, new_startTime, new_endTime, renewTill, cAddr); + this(new_ticket, new_client, new_client_alias, new_server, + new_server_alias, new_key, new_flags, authTime, + new_startTime, new_endTime, renewTill, cAddr); this.authzData = authzData; } public Credentials(Ticket new_ticket, PrincipalName new_client, + PrincipalName new_client_alias, PrincipalName new_server, + PrincipalName new_server_alias, EncryptionKey new_key, TicketFlags new_flags, KerberosTime authTime, @@ -95,7 +102,9 @@ HostAddresses cAddr) { ticket = new_ticket; client = new_client; + clientAlias = new_client_alias; server = new_server; + serverAlias = new_server_alias; key = new_key; flags = new_flags; this.authTime = authTime; @@ -107,7 +116,9 @@ public Credentials(byte[] encoding, String client, + String clientAlias, String server, + String serverAlias, byte[] keyBytes, int keyType, boolean[] flags, @@ -118,7 +129,11 @@ InetAddress[] cAddrs) throws KrbException, IOException { this(new Ticket(encoding), new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL), + (clientAlias == null? null : new PrincipalName(clientAlias, + PrincipalName.KRB_NT_PRINCIPAL)), new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST), + (serverAlias == null? null : new PrincipalName(serverAlias, + PrincipalName.KRB_NT_SRV_INST)), new EncryptionKey(keyType, keyBytes), (flags == null? null: new TicketFlags(flags)), (authTime == null? null: new KerberosTime(authTime)), @@ -143,10 +158,18 @@ return client; } + public final PrincipalName getClientAlias() { + return clientAlias; + } + public final PrincipalName getServer() { return server; } + public final PrincipalName getServerAlias() { + return serverAlias; + } + public final EncryptionKey getSessionKey() { return key; } @@ -262,6 +285,7 @@ return new KrbTgsReq(options, this, server, + serverAlias, null, // from null, // till null, // rtime @@ -484,7 +508,11 @@ public static void printDebug(Credentials c) { System.out.println(">>> DEBUG: ----Credentials----"); System.out.println("\tclient: " + c.client.toString()); + if (c.clientAlias != null) + System.out.println("\tclient alias: " + c.clientAlias.toString()); System.out.println("\tserver: " + c.server.toString()); + if (c.serverAlias != null) + System.out.println("\tserver alias: " + c.serverAlias.toString()); System.out.println("\tticket: sname: " + c.ticket.sname.toString()); if (c.startTime != null) { System.out.println("\tstartTime: " + c.startTime.getTime()); @@ -512,7 +540,11 @@ public String toString() { StringBuilder sb = new StringBuilder("Credentials:"); sb.append( "\n client=").append(client); + if (clientAlias != null) + sb.append( "\n clientAlias=").append(clientAlias); sb.append( "\n server=").append(server); + if (serverAlias != null) + sb.append( "\n serverAlias=").append(serverAlias); if (authTime != null) { sb.append("\n authTime=").append(authTime); }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, 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 @@ -25,6 +25,8 @@ package sun.security.krb5; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KeyTab; import sun.security.krb5.EncryptionKey; import sun.security.krb5.PrincipalName; @@ -39,4 +41,12 @@ */ public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot( KeyTab ktab); + + public KerberosPrincipal kerberosTicketGetClientAlias(KerberosTicket t); + + public void kerberosTicketSetClientAlias(KerberosTicket t, KerberosPrincipal a); + + public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t); + + public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a); }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -363,7 +363,9 @@ creds = new Credentials( apReqMessg.ticket, authenticator.cname, + null, apReqMessg.ticket.sname, + null, enc_ticketPart.key, enc_ticketPart.flags, enc_ticketPart.authtime,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java Wed Jul 24 12:21:36 2019 +0530 @@ -118,7 +118,7 @@ "Cannot find key for type/kvno to decrypt AS REP - " + EType.toString(encPartKeyType) + "/" + encPartKvno); } - decrypt(dkey, asReq); + decrypt(dkey, asReq, cname); } /** @@ -136,7 +136,7 @@ password, encPartKeyType, PAData.getSaltAndParams(encPartKeyType, rep.pAData)); - decrypt(dkey, asReq); + decrypt(dkey, asReq, cname); } /** @@ -144,7 +144,8 @@ * @param dkey the decryption key to use * @param asReq the original AS-REQ sent, used to validate AS-REP */ - private void decrypt(EncryptionKey dkey, KrbAsReq asReq) + private void decrypt(EncryptionKey dkey, KrbAsReq asReq, + PrincipalName cname) throws KrbException, Asn1Exception, IOException { byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey, KeyUsage.KU_ENC_AS_REP_PART); @@ -157,10 +158,16 @@ ASReq req = asReq.getMessage(); check(true, req, rep, dkey); + PrincipalName clientAlias = cname; + if (clientAlias.equals(rep.cname)) + clientAlias = null; + creds = new Credentials( rep.ticket, rep.cname, + clientAlias, enc_part.sname, + null, // No server alias expected in a TGT enc_part.key, enc_part.flags, enc_part.authtime,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java Wed Jul 24 12:21:36 2019 +0530 @@ -68,6 +68,7 @@ // Common data for AS-REQ fields private KDCOptions options; private PrincipalName cname; + private PrincipalName refCname; // May be changed by referrals private PrincipalName sname; private KerberosTime from; private KerberosTime till; @@ -100,6 +101,7 @@ private void init(PrincipalName cname) throws KrbException { this.cname = cname; + this.refCname = cname; state = State.INIT; } @@ -284,7 +286,7 @@ } return new KrbAsReq(key, options, - cname, + refCname, sname, from, till, @@ -334,7 +336,7 @@ ReferralsState referralsState = new ReferralsState(); while (true) { if (referralsState.refreshComm()) { - comm = new KdcComm(cname.getRealmAsString()); + comm = new KdcComm(refCname.getRealmAsString()); } try { req = build(pakey, referralsState); @@ -384,7 +386,7 @@ ReferralsState() throws KrbException { if (Config.DISABLE_REFERRALS) { - if (cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) { + if (refCname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) { throw new KrbException("NT-ENTERPRISE principals only allowed" + " when referrals are enabled."); } @@ -402,15 +404,15 @@ if (req.getMessage().reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) && referredRealm != null && referredRealm.toString().length() > 0 && count < Config.MAX_REFERRALS) { - cname = new PrincipalName(cname.getNameType(), - cname.getNameStrings(), referredRealm); + refCname = new PrincipalName(refCname.getNameType(), + refCname.getNameStrings(), referredRealm); refreshComm = true; count++; return true; } } if (count < Config.MAX_REFERRALS && - cname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) { + refCname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) { // Server may raise an error if CANONICALIZE is true. // Try CANONICALIZE false. enabled = false;
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -76,7 +76,7 @@ options.set(KDCOptions.FORWARDABLE, true); KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService, - null, null, null, null, + null, null, null, null, null, null, // No easy way to get addresses right null, null, null); credMessg = createMessage(tgsReq.sendAndGetCreds(), key); @@ -152,7 +152,7 @@ + " endtime=" + endtime + "renewTill=" + renewTill); } - creds = new Credentials(ticket, pname, sname, credInfoKey, + creds = new Credentials(ticket, pname, null, sname, null, credInfoKey, flags, authtime, starttime, endtime, renewTill, caddr); }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java Wed Jul 24 12:21:36 2019 +0530 @@ -86,9 +86,20 @@ check(false, req, rep, tgsReq.tgsReqKey); + PrincipalName serverAlias = tgsReq.getServerAlias(); + if (serverAlias != null) { + PrincipalName repSname = enc_part.sname; + if (serverAlias.equals(repSname) || + isReferralSname(repSname)) { + serverAlias = null; + } + } + this.creds = new Credentials(rep.ticket, rep.cname, + tgsReq.getClientAlias(), enc_part.sname, + serverAlias, enc_part.key, enc_part.flags, enc_part.authtime, @@ -111,4 +122,16 @@ sun.security.krb5.internal.ccache.Credentials setCredentials() { return new sun.security.krb5.internal.ccache.Credentials(rep, secondTicket); } + + private static boolean isReferralSname(PrincipalName sname) { + if (sname != null) { + String[] snameStrings = sname.getNameStrings(); + if (snameStrings.length == 2 && + snameStrings[0].equals( + PrincipalName.TGS_DEFAULT_SRV_NAME)) { + return true; + } + } + return false; + } }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java Wed Jul 24 12:21:36 2019 +0530 @@ -45,7 +45,9 @@ public class KrbTgsReq { private PrincipalName princName; + private PrincipalName clientAlias; private PrincipalName servName; + private PrincipalName serverAlias; private TGSReq tgsReqMessg; private KerberosTime ctime; private Ticket secondTicket = null; @@ -59,13 +61,16 @@ // Used in CredentialsUtil public KrbTgsReq(KDCOptions options, Credentials asCreds, - PrincipalName cname, PrincipalName sname, + PrincipalName cname, PrincipalName clientAlias, + PrincipalName sname, PrincipalName serverAlias, Ticket[] additionalTickets, PAData[] extraPAs) throws KrbException, IOException { this(options, asCreds, cname, + clientAlias, sname, + serverAlias, null, // KerberosTime from null, // KerberosTime till null, // KerberosTime rtime @@ -82,6 +87,7 @@ KDCOptions options, Credentials asCreds, PrincipalName sname, + PrincipalName serverAlias, KerberosTime from, KerberosTime till, KerberosTime rtime, @@ -90,16 +96,18 @@ AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey) throws KrbException, IOException { - this(options, asCreds, asCreds.getClient(), sname, - from, till, rtime, eTypes, addresses, - authorizationData, additionalTickets, subKey, null); + this(options, asCreds, asCreds.getClient(), asCreds.getClientAlias(), + sname, serverAlias, from, till, rtime, eTypes, + addresses, authorizationData, additionalTickets, subKey, null); } private KrbTgsReq( KDCOptions options, Credentials asCreds, PrincipalName cname, + PrincipalName clientAlias, PrincipalName sname, + PrincipalName serverAlias, KerberosTime from, KerberosTime till, KerberosTime rtime, @@ -111,7 +119,9 @@ PAData[] extraPAs) throws KrbException, IOException { princName = cname; + this.clientAlias = clientAlias; servName = sname; + this.serverAlias = serverAlias; ctime = KerberosTime.now(); // check if they are valid arguments. The optional fields @@ -365,6 +375,14 @@ return secondTicket; } + PrincipalName getClientAlias() { + return clientAlias; + } + + PrincipalName getServerAlias() { + return serverAlias; + } + private static void debug(String message) { // System.err.println(">>> KrbTgsReq: " + message); }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java Wed Jul 24 12:21:36 2019 +0530 @@ -564,7 +564,9 @@ for (int i = 0; i < nameStrings.length; i++) { if (i > 0) str.append("/"); - str.append(nameStrings[i]); + String n = nameStrings[i]; + n = n.replace("@", "\\@"); + str.append(n); } str.append("@"); str.append(nameRealm.toString());
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java Wed Jul 24 12:21:36 2019 +0530 @@ -284,8 +284,9 @@ // Try CANONICALIZE false. } } - return serviceCredsSingle(options, asCreds, - cname, sname, additionalTickets, extraPAs); + return serviceCredsSingle(options, asCreds, cname, + asCreds.getClientAlias(), sname, sname, additionalTickets, + extraPAs); } /* @@ -300,26 +301,29 @@ options = new KDCOptions(options.toBooleanArray()); options.set(KDCOptions.CANONICALIZE, true); PrincipalName cSname = sname; + PrincipalName refSname = sname; // May change with referrals Credentials creds = null; boolean isReferral = false; List<String> referrals = new LinkedList<>(); + PrincipalName clientAlias = asCreds.getClientAlias(); while (referrals.size() <= Config.MAX_REFERRALS) { ReferralsCache.ReferralCacheEntry ref = - ReferralsCache.get(sname, cSname.getRealmString()); + ReferralsCache.get(cname, sname, refSname.getRealmString()); String toRealm = null; if (ref == null) { - creds = serviceCredsSingle(options, asCreds, - cname, cSname, additionalTickets, extraPAs); + creds = serviceCredsSingle(options, asCreds, cname, + clientAlias, refSname, cSname, additionalTickets, + extraPAs); PrincipalName server = creds.getServer(); - if (!cSname.equals(server)) { + if (!refSname.equals(server)) { String[] serverNameStrings = server.getNameStrings(); if (serverNameStrings.length == 2 && serverNameStrings[0].equals( PrincipalName.TGS_DEFAULT_SRV_NAME) && - !cSname.getRealmAsString().equals(serverNameStrings[1])) { + !refSname.getRealmAsString().equals(serverNameStrings[1])) { // Server Name (sname) has the following format: // krbtgt/TO-REALM.COM@FROM-REALM.COM - ReferralsCache.put(sname, server.getRealmString(), + ReferralsCache.put(cname, sname, server.getRealmString(), serverNameStrings[1], creds); toRealm = serverNameStrings[1]; isReferral = true; @@ -336,8 +340,8 @@ // Referrals loop detected return null; } - cSname = new PrincipalName(cSname.getNameString(), - cSname.getNameType(), toRealm); + refSname = new PrincipalName(refSname.getNameString(), + refSname.getNameType(), toRealm); referrals.add(toRealm); isReferral = false; continue; @@ -356,14 +360,15 @@ */ private static Credentials serviceCredsSingle( KDCOptions options, Credentials asCreds, - PrincipalName cname, PrincipalName sname, + PrincipalName cname, PrincipalName clientAlias, + PrincipalName refSname, PrincipalName sname, Ticket[] additionalTickets, PAData[] extraPAs) throws KrbException, IOException { Credentials theCreds = null; boolean[] okAsDelegate = new boolean[]{true}; String[] serverAsCredsNames = asCreds.getServer().getNameStrings(); String tgtRealm = serverAsCredsNames[1]; - String serviceRealm = sname.getRealmString(); + String serviceRealm = refSname.getRealmString(); if (!serviceRealm.equals(tgtRealm)) { // This is a cross-realm service request if (DEBUG) { @@ -390,8 +395,8 @@ System.out.println(">>> Credentials serviceCredsSingle:" + " same realm"); } - KrbTgsReq req = new KrbTgsReq(options, asCreds, - cname, sname, additionalTickets, extraPAs); + KrbTgsReq req = new KrbTgsReq(options, asCreds, cname, clientAlias, + refSname, sname, additionalTickets, extraPAs); theCreds = req.sendAndGetCreds(); if (theCreds != null) { if (DEBUG) {
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java Wed Jul 24 12:21:36 2019 +0530 @@ -139,7 +139,7 @@ sTime = new_sTime; suSec = new_suSec; errorCode = new_errorCode; - crealm = new_cname.getRealm(); + crealm = new_cname != null ? new_cname.getRealm() : null; cname = new_cname; sname = new_sname; eText = new_eText; @@ -168,7 +168,7 @@ sTime = new_sTime; suSec = new_suSec; errorCode = new_errorCode; - crealm = new_cname.getRealm(); + crealm = new_cname != null ? new_cname.getRealm() : null; cname = new_cname; sname = new_sname; eText = new_eText;
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java Wed Jul 24 12:21:36 2019 +0530 @@ -45,8 +45,27 @@ */ final class ReferralsCache { - private static Map<PrincipalName, Map<String, ReferralCacheEntry>> referralsMap = - new HashMap<>(); + private static Map<ReferralCacheKey, Map<String, ReferralCacheEntry>> + referralsMap = new HashMap<>(); + + static private final class ReferralCacheKey { + private PrincipalName cname; + private PrincipalName sname; + ReferralCacheKey (PrincipalName cname, PrincipalName sname) { + this.cname = cname; + this.sname = sname; + } + public boolean equals(Object other) { + if (!(other instanceof ReferralCacheKey)) + return false; + ReferralCacheKey that = (ReferralCacheKey)other; + return cname.equals(that.cname) && + sname.equals(that.sname); + } + public int hashCode() { + return cname.hashCode() + sname.hashCode(); + } + } static final class ReferralCacheEntry { private final Credentials creds; @@ -64,8 +83,9 @@ } /* - * Add a new referral entry to the cache, including: service principal, - * source KDC realm, destination KDC realm and referral TGT. + * Add a new referral entry to the cache, including: client principal, + * service principal, source KDC realm, destination KDC realm and + * referral TGT. * * If a loop is generated when adding the new referral, the first hop is * automatically removed. For example, let's assume that adding a @@ -73,16 +93,17 @@ * REALM-1.COM -> REALM-2.COM -> REALM-3.COM -> REALM-1.COM. Then, * REALM-1.COM -> REALM-2.COM referral entry is removed from the cache. */ - static synchronized void put(PrincipalName service, + static synchronized void put(PrincipalName cname, PrincipalName service, String fromRealm, String toRealm, Credentials creds) { - pruneExpired(service); + ReferralCacheKey k = new ReferralCacheKey(cname, service); + pruneExpired(k); if (creds.getEndTime().before(new Date())) { return; } - Map<String, ReferralCacheEntry> entries = referralsMap.get(service); + Map<String, ReferralCacheEntry> entries = referralsMap.get(k); if (entries == null) { entries = new HashMap<String, ReferralCacheEntry>(); - referralsMap.put(service, entries); + referralsMap.put(k, entries); } entries.remove(fromRealm); ReferralCacheEntry newEntry = new ReferralCacheEntry(creds, toRealm); @@ -103,13 +124,14 @@ } /* - * Obtain a referral entry from the cache given a service principal and a - * source KDC realm. + * Obtain a referral entry from the cache given a client principal, + * service principal and a source KDC realm. */ - static synchronized ReferralCacheEntry get(PrincipalName service, - String fromRealm) { - pruneExpired(service); - Map<String, ReferralCacheEntry> entries = referralsMap.get(service); + static synchronized ReferralCacheEntry get(PrincipalName cname, + PrincipalName service, String fromRealm) { + ReferralCacheKey k = new ReferralCacheKey(cname, service); + pruneExpired(k); + Map<String, ReferralCacheEntry> entries = referralsMap.get(k); if (entries != null) { ReferralCacheEntry toRef = entries.get(fromRealm); if (toRef != null) { @@ -122,9 +144,9 @@ /* * Remove referral entries from the cache when referral TGTs expire. */ - private static void pruneExpired(PrincipalName service) { + private static void pruneExpired(ReferralCacheKey k) { Date now = new Date(); - Map<String, ReferralCacheEntry> entries = referralsMap.get(service); + Map<String, ReferralCacheEntry> entries = referralsMap.get(k); if (entries != null) { for (Entry<String, ReferralCacheEntry> mapEntry : entries.entrySet()) {
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java Wed Jul 24 12:21:36 2019 +0530 @@ -180,8 +180,9 @@ // is most likely to be the one in Authenticator in PA-TGS-REQ encoded // in TGS-REQ, therefore only stored with a service ticket. Currently // in Java, we only reads TGTs. - return new sun.security.krb5.Credentials(ticket, - cname, sname, key, flags, authtime, starttime, endtime, renewTill, caddr); + return new sun.security.krb5.Credentials(ticket, cname, null, sname, + null, key, flags, authtime, starttime, endtime, renewTill, + caddr); } public KerberosTime getStartTime() {
--- a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c Mon Jul 22 11:08:27 2019 +0530 +++ b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -406,6 +406,8 @@ "(Lsun/security/krb5/internal/Ticket;" "Lsun/security/krb5/PrincipalName;" "Lsun/security/krb5/PrincipalName;" + "Lsun/security/krb5/PrincipalName;" + "Lsun/security/krb5/PrincipalName;" "Lsun/security/krb5/EncryptionKey;" "Lsun/security/krb5/internal/TicketFlags;" "Lsun/security/krb5/internal/KerberosTime;" @@ -667,7 +669,9 @@ krbcredsConstructor, ticket, clientPrincipal, + NULL, targetPrincipal, + NULL, encryptionKey, ticketFlags, authTime, // mdu
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2019, 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 @@ -109,8 +109,10 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.DCTree; import com.sun.tools.javac.tree.DCTree.DCBlockTag; +import com.sun.tools.javac.tree.DCTree.DCComment; import com.sun.tools.javac.tree.DCTree.DCDocComment; import com.sun.tools.javac.tree.DCTree.DCEndPosTree; +import com.sun.tools.javac.tree.DCTree.DCEntity; import com.sun.tools.javac.tree.DCTree.DCErroneous; import com.sun.tools.javac.tree.DCTree.DCIdentifier; import com.sun.tools.javac.tree.DCTree.DCParam; @@ -302,6 +304,14 @@ return dcComment.comment.getSourcePos(block.pos + block.getTagName().length() + 1); } + case ENTITY: { + DCEntity endEl = (DCEntity) tree; + return dcComment.comment.getSourcePos(endEl.pos + (endEl.name != names.error ? endEl.name.length() : 0) + 2); + } + case COMMENT: { + DCComment endEl = (DCComment) tree; + return dcComment.comment.getSourcePos(endEl.pos + endEl.body.length()); + } default: DocTree last = getLastChild(tree);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java Wed Jul 24 12:21:36 2019 +0530 @@ -930,7 +930,7 @@ skipWhitespace(); if (ch == '>') { nextChar(); - return m.at(p).newEndElementTree(name); + return m.at(p).newEndElementTree(name).setEndPos(bp); } } } else if (ch == '!') {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, 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 @@ -326,7 +326,7 @@ } } - public static class DCEndElement extends DCTree implements EndElementTree { + public static class DCEndElement extends DCEndPosTree<DCStartElement> implements EndElementTree { public final Name name; DCEndElement(Name name) {
--- a/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, 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 @@ -671,26 +671,18 @@ throw new AssertionError(asae); } - PrintStream ps = new PrintStream(os); - ZipOutputStream zos = new ZipOutputStream(ps); + ZipOutputStream zos = new ZipOutputStream(os); Manifest manifest = new Manifest(); - Map<String, Attributes> mfEntries = manifest.getEntries(); - - // The Attributes of manifest before updating - Attributes oldAttr = null; - - boolean mfModified = false; - boolean mfCreated = false; byte[] mfRawBytes = null; // Check if manifest exists - ZipEntry mfFile; - if ((mfFile = getManifestFile(zipFile)) != null) { + ZipEntry mfFile = getManifestFile(zipFile); + boolean mfCreated = mfFile == null; + if (!mfCreated) { // Manifest exists. Read its raw bytes. mfRawBytes = zipFile.getInputStream(mfFile).readAllBytes(); manifest.read(new ByteArrayInputStream(mfRawBytes)); - oldAttr = (Attributes) (manifest.getMainAttributes().clone()); } else { // Create new manifest Attributes mattr = manifest.getMainAttributes(); @@ -701,7 +693,6 @@ mattr.putValue("Created-By", jdkVersion + " (" + javaVendor + ")"); mfFile = new ZipEntry(JarFile.MANIFEST_NAME); - mfCreated = true; } /* @@ -728,8 +719,12 @@ // out first mfFiles.addElement(ze); - if (SignatureFileVerifier.isBlockOrSF( - ze.getName().toUpperCase(Locale.ENGLISH))) { + String zeNameUp = ze.getName().toUpperCase(Locale.ENGLISH); + if (SignatureFileVerifier.isBlockOrSF(zeNameUp) + // no need to preserve binary manifest portions + // if the only existing signature will be replaced + && !zeNameUp.startsWith(SignatureFile + .getBaseSignatureFilesName(signerName))) { wasSigned = true; } @@ -742,55 +737,69 @@ if (manifest.getAttributes(ze.getName()) != null) { // jar entry is contained in manifest, check and // possibly update its digest attributes - if (updateDigests(ze, zipFile, digests, - manifest)) { - mfModified = true; - } + updateDigests(ze, zipFile, digests, manifest); } else if (!ze.isDirectory()) { // Add entry to manifest Attributes attrs = getDigestAttributes(ze, zipFile, digests); - mfEntries.put(ze.getName(), attrs); - mfModified = true; + manifest.getEntries().put(ze.getName(), attrs); } } - // Recalculate the manifest raw bytes if necessary - if (mfModified) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + /* + * Note: + * + * The Attributes object is based on HashMap and can handle + * continuation lines. Therefore, even if the contents are not changed + * (in a Map view), the bytes that it write() may be different from + * the original bytes that it read() from. Since the signature is + * based on raw bytes, we must retain the exact bytes. + */ + boolean mfModified; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (mfCreated || !wasSigned) { + mfModified = true; manifest.write(baos); - if (wasSigned) { - byte[] newBytes = baos.toByteArray(); - if (mfRawBytes != null - && oldAttr.equals(manifest.getMainAttributes())) { + mfRawBytes = baos.toByteArray(); + } else { - /* - * Note: - * - * The Attributes object is based on HashMap and can handle - * continuation columns. Therefore, even if the contents are - * not changed (in a Map view), the bytes that it write() - * may be different from the original bytes that it read() - * from. Since the signature on the main attributes is based - * on raw bytes, we must retain the exact bytes. - */ + // the manifest before updating + Manifest oldManifest = new Manifest( + new ByteArrayInputStream(mfRawBytes)); + mfModified = !oldManifest.equals(manifest); + if (!mfModified) { + // leave whole manifest (mfRawBytes) unmodified + } else { + // reproduce the manifest raw bytes for unmodified sections + manifest.write(baos); + byte[] mfNewRawBytes = baos.toByteArray(); + baos.reset(); + + ManifestDigester oldMd = new ManifestDigester(mfRawBytes); + ManifestDigester newMd = new ManifestDigester(mfNewRawBytes); - int newPos = findHeaderEnd(newBytes); - int oldPos = findHeaderEnd(mfRawBytes); + // main attributes + if (manifest.getMainAttributes().equals( + oldManifest.getMainAttributes()) + && (manifest.getEntries().isEmpty() || + oldMd.getMainAttsEntry().isProperlyDelimited())) { + oldMd.getMainAttsEntry().reproduceRaw(baos); + } else { + newMd.getMainAttsEntry().reproduceRaw(baos); + } - if (newPos == oldPos) { - System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos); + // individual sections + for (Map.Entry<String,Attributes> entry : + manifest.getEntries().entrySet()) { + String sectionName = entry.getKey(); + Attributes entryAtts = entry.getValue(); + if (entryAtts.equals(oldManifest.getAttributes(sectionName)) + && oldMd.get(sectionName).isProperlyDelimited()) { + oldMd.get(sectionName).reproduceRaw(baos); } else { - // cat oldHead newTail > newBytes - byte[] lastBytes = new byte[oldPos + - newBytes.length - newPos]; - System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos); - System.arraycopy(newBytes, newPos, lastBytes, oldPos, - newBytes.length - newPos); - newBytes = lastBytes; + newMd.get(sectionName).reproduceRaw(baos); } } - mfRawBytes = newBytes; - } else { + mfRawBytes = baos.toByteArray(); } } @@ -801,13 +810,12 @@ mfFile = new ZipEntry(JarFile.MANIFEST_NAME); } if (handler != null) { - if (mfCreated) { + if (mfCreated || !mfModified) { handler.accept("adding", mfFile.getName()); - } else if (mfModified) { + } else { handler.accept("updating", mfFile.getName()); } } - zos.putNextEntry(mfFile); zos.write(mfRawBytes); @@ -826,9 +834,8 @@ } signer.initSign(privateKey); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.reset(); sf.write(baos); - byte[] content = baos.toByteArray(); signer.update(content); @@ -889,6 +896,14 @@ if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME) && !ze.getName().equalsIgnoreCase(sfFilename) && !ze.getName().equalsIgnoreCase(bkFilename)) { + if (ze.getName().startsWith(SignatureFile + .getBaseSignatureFilesName(signerName)) + && SignatureFileVerifier.isBlockOrSF(ze.getName())) { + if (handler != null) { + handler.accept("updating", ze.getName()); + } + continue; + } if (handler != null) { if (manifest.getAttributes(ze.getName()) != null) { handler.accept("signing", ze.getName()); @@ -942,11 +957,9 @@ } } - private boolean updateDigests(ZipEntry ze, ZipFile zf, + private void updateDigests(ZipEntry ze, ZipFile zf, MessageDigest[] digests, Manifest mf) throws IOException { - boolean update = false; - Attributes attrs = mf.getAttributes(ze.getName()); String[] base64Digests = getDigests(ze, zf, digests); @@ -976,19 +989,9 @@ if (name == null) { name = digests[i].getAlgorithm() + "-Digest"; - attrs.putValue(name, base64Digests[i]); - update = true; - } else { - // compare digests, and replace the one in the manifest - // if they are different - String mfDigest = attrs.getValue(name); - if (!mfDigest.equalsIgnoreCase(base64Digests[i])) { - attrs.putValue(name, base64Digests[i]); - update = true; - } } + attrs.putValue(name, base64Digests[i]); } - return update; } private Attributes getDigestAttributes( @@ -1051,30 +1054,6 @@ return base64Digests; } - @SuppressWarnings("fallthrough") - private int findHeaderEnd(byte[] bs) { - // Initial state true to deal with empty header - boolean newline = true; // just met a newline - int len = bs.length; - for (int i = 0; i < len; i++) { - switch (bs[i]) { - case '\r': - if (i < len - 1 && bs[i + 1] == '\n') i++; - // fallthrough - case '\n': - if (newline) return i + 1; //+1 to get length - newline = true; - break; - default: - newline = false; - } - } - // If header end is not found, it means the MANIFEST.MF has only - // the main attributes section and it does not end with 2 newlines. - // Returns the whole length so that it can be completely replaced. - return len; - } - /* * Try to load the specified signing mechanism. * The URL class loader is used. @@ -1145,14 +1124,12 @@ } // create digest of the manifest main attributes - ManifestDigester.Entry mde = - md.get(ManifestDigester.MF_MAIN_ATTRS, false); + ManifestDigester.Entry mde = md.getMainAttsEntry(false); if (mde != null) { - for (MessageDigest digest: digests) { - mattr.putValue(digest.getAlgorithm() + - "-Digest-" + ManifestDigester.MF_MAIN_ATTRS, - Base64.getEncoder().encodeToString( - mde.digest(digest))); + for (MessageDigest digest : digests) { + mattr.putValue(digest.getAlgorithm() + "-Digest-" + + ManifestDigester.MF_MAIN_ATTRS, + Base64.getEncoder().encodeToString(mde.digest(digest))); } } else { throw new IllegalStateException @@ -1181,15 +1158,19 @@ sf.write(out); } + private static String getBaseSignatureFilesName(String baseName) { + return "META-INF/" + baseName + "."; + } + // get .SF file name public String getMetaName() { - return "META-INF/" + baseName + ".SF"; + return getBaseSignatureFilesName(baseName) + "SF"; } // get .DSA (or .DSA, .EC) file name public String getBlockName(PrivateKey privateKey) { String keyAlgorithm = privateKey.getAlgorithm(); - return "META-INF/" + baseName + "." + keyAlgorithm; + return getBaseSignatureFilesName(baseName) + keyAlgorithm; } // Generates the PKCS#7 content of block file
--- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Wed Jul 24 12:21:36 2019 +0530 @@ -944,11 +944,10 @@ // Don't read from the newManifest InputStream, as we // might need it below, and we can't re-read the same data // twice. - FileInputStream fis = new FileInputStream(mname); - boolean ambiguous = isAmbiguousMainClass(new Manifest(fis)); - fis.close(); - if (ambiguous) { - return false; + try (FileInputStream fis = new FileInputStream(mname)) { + if (isAmbiguousMainClass(new Manifest(fis))) { + return false; + } } } // Update the manifest.
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java Wed Jul 24 12:21:36 2019 +0530 @@ -409,9 +409,10 @@ } checkLinkCompatibility(elemname, moduleName, path); Item item = new Item(elemname, elempath, relative); - packageItems.computeIfAbsent(moduleName == null ? - DocletConstants.DEFAULT_ELEMENT_NAME : moduleName, k -> new TreeMap<>()) - .put(elemname, item); + packageItems.computeIfAbsent( + moduleName == null ? DocletConstants.DEFAULT_ELEMENT_NAME : moduleName, + k -> new TreeMap<>()) + .putIfAbsent(elemname, item); // first-one-wins semantics } } }
--- a/src/jdk.javadoc/share/legal/pako.md Mon Jul 22 11:08:27 2019 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -## Pako v1.0 - -### Pako License -<pre> -Copyright (C) 2014-2017 by Vitaly Puzrin and Andrei Tuputcyn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -(C) 1995-2013 Jean-loup Gailly and Mark Adler -(C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not -claim that you wrote the original software. If you use this software -in a product, an acknowledgment in the product documentation would be -appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -</pre> - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jcmd/share/classes/sun/tools/common/PrintStreamPrinter.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.tools.common; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.Arrays; + +/** + * A helper class which prints the content of input streams to print streams. + */ +public class PrintStreamPrinter { + + /** + * Reads characters in UTF-8 format from the input stream and prints them + * with the given print stream. Closes the input stream before it returns. + * + * @return The number of printed characters. + */ + public static long drainUTF8(InputStream is, PrintStream ps) throws IOException { + long result = 0; + + try (BufferedInputStream bis = new BufferedInputStream(is); + InputStreamReader isr = new InputStreamReader(bis, "UTF-8")) { + char c[] = new char[256]; + int n; + + do { + n = isr.read(c); + + if (n > 0) { + result += n; + ps.print(n == c.length ? c : Arrays.copyOf(c, n)); + } + } while (n > 0); + } + + return result; + } +}
--- a/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java Wed Jul 24 12:21:36 2019 +0530 @@ -25,13 +25,10 @@ package sun.tools.jcmd; +import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.util.List; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.net.URISyntaxException; @@ -42,6 +39,7 @@ import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; +import sun.tools.common.PrintStreamPrinter; import sun.tools.jstat.JStatLogger; import sun.jvmstat.monitor.Monitor; import sun.jvmstat.monitor.MonitoredHost; @@ -122,23 +120,11 @@ if (line.trim().equals("stop")) { break; } - try (InputStream in = hvm.executeJCmd(line); - InputStreamReader isr = new InputStreamReader(in, "UTF-8")) { - // read to EOF and just print output - char c[] = new char[256]; - int n; - boolean messagePrinted = false; - do { - n = isr.read(c); - if (n > 0) { - String s = new String(c, 0, n); - System.out.print(s); - messagePrinted = true; - } - } while (n > 0); - if (!messagePrinted) { - System.out.println("Command executed successfully"); - } + + InputStream is = hvm.executeJCmd(line); + + if (PrintStreamPrinter.drainUTF8(is, System.out) == 0) { + System.out.println("Command executed successfully"); } } vm.detach();
--- a/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2019, 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 @@ -30,10 +30,10 @@ import java.util.Collection; import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; +import sun.tools.common.PrintStreamPrinter; /* * This class is the main class for the JInfo utility. It parses its arguments @@ -203,17 +203,7 @@ // Read the stream from the target VM until EOF, then detach private static void drain(VirtualMachine vm, InputStream in) throws IOException { - // read to EOF and just print output - byte b[] = new byte[256]; - int n; - do { - n = in.read(b); - if (n > 0) { - String s = new String(b, 0, n, "UTF-8"); - System.out.print(s); - } - } while (n > 0); - in.close(); + PrintStreamPrinter.drainUTF8(in, System.out); vm.detach(); }
--- a/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java Mon Jul 22 11:08:27 2019 +0530 +++ b/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2019, 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 @@ -29,9 +29,9 @@ import java.util.Collection; import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; +import sun.tools.common.PrintStreamPrinter; /* * This class is the main class for the JStack utility. It parses its arguments @@ -128,18 +128,8 @@ // Cast to HotSpotVirtualMachine as this is implementation specific // method. InputStream in = ((HotSpotVirtualMachine)vm).remoteDataDump((Object[])args); - // read to EOF and just print output - byte b[] = new byte[256]; - int n; - do { - n = in.read(b); - if (n > 0) { - String s = new String(b, 0, n, "UTF-8"); - System.out.print(s); - } - } while (n > 0); - in.close(); + PrintStreamPrinter.drainUTF8(in, System.out); vm.detach(); }
--- a/test/hotspot/jtreg/TEST.ROOT Mon Jul 22 11:08:27 2019 +0530 +++ b/test/hotspot/jtreg/TEST.ROOT Wed Jul 24 12:21:36 2019 +0530 @@ -35,7 +35,9 @@ # to determine additional characteristics of the system for use with the @requires tag. # Note: compiled bootlibs code will be located in the folder 'bootClasses' requires.extraPropDefns = ../../jtreg-ext/requires/VMProps.java -requires.extraPropDefns.bootlibs = ../../lib/sun ../../lib/jdk/test/lib/Platform.java +requires.extraPropDefns.bootlibs = ../../lib/sun \ + ../../lib/jdk/test/lib/Platform.java \ + ../../lib/jdk/test/lib/Container.java requires.extraPropDefns.vmOpts = -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:bootClasses requires.properties= \ sun.arch.data.model \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/containers/docker/EventGeneratorLoop.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ +import jdk.jfr.Event; +import jdk.jfr.Description; +import jdk.jfr.Label; + + +// This class generates simple event in a loop +// for a specified time. +// Pass the time in seconds as a parameter. +public class EventGeneratorLoop { + + @Label("SimpleEvent") + @Description("Simple custom event") + static class SimpleEvent extends Event { + @Label("Message") + String msg; + + @Label("Count") + int count; + } + + + public static void main(String[] args) throws Exception { + if ((args.length < 1) || (args[0] == null)) { + throw new IllegalArgumentException("Expecting one argument: time to run (seconds)"); + } + int howLong = Integer.parseInt(args[0]); + + for (int i=0; i < howLong; i++) { + SimpleEvent ev = new SimpleEvent(); + ev.msg = "Hello"; + ev.count = i; + ev.commit(); + + try { Thread.sleep(1000); } catch (InterruptedException e) {} + System.out.print("."); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019, 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 + * 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 + * @summary Test JCMD with side car pattern. + * Sidecar is a common pattern used in the cloud environments for monitoring + * and other uses. In side car pattern the main application/service container + * is paired with a sidecar container by sharing certain aspects of container + * namespace such as PID namespace, specific sub-directories, IPC and more. + * @requires docker.support + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @build EventGeneratorLoop + * @run driver TestJcmdWithSideCar + */ +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.Container; +import jdk.test.lib.Utils; +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; +import jdk.test.lib.process.OutputAnalyzer; + + +public class TestJcmdWithSideCar { + private static final String IMAGE_NAME = Common.imageName("jfr-jcmd"); + private static final int TIME_TO_RUN_MAIN_PROCESS = (int) (30 * Utils.TIMEOUT_FACTOR); // seconds + private static final String MAIN_CONTAINER_NAME = "test-container-main"; + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + DockerTestUtils.buildJdkDockerImage(IMAGE_NAME, "Dockerfile-BasicTest", "jdk-docker"); + + try { + // Start the loop process in the "main" container, then run test cases + // using a sidecar container. + DockerThread t = startMainContainer(); + + waitForMainContainerToStart(500, 10); + t.checkForErrors(); + + OutputAnalyzer jcmdOut = testCase01(); + long mainProcPid = findProcess(jcmdOut, "EventGeneratorLoop"); + + t.assertIsAlive(); + testCase02(mainProcPid); + + // JCMD does not work in sidecar configuration, except for "jcmd -l". + // Including this test case to assist in reproduction of the problem. + // t.assertIsAlive(); + // testCase03(mainProcPid); + + t.join(TIME_TO_RUN_MAIN_PROCESS * 1000); + t.checkForErrors(); + } finally { + DockerTestUtils.removeDockerImage(IMAGE_NAME); + } + } + + + // Run "jcmd -l" in a sidecar container and find a process that runs EventGeneratorLoop + private static OutputAnalyzer testCase01() throws Exception { + return runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l") + .shouldHaveExitValue(0) + .shouldContain("sun.tools.jcmd.JCmd") + .shouldContain("EventGeneratorLoop"); + } + + // run jhsdb jinfo <PID> (jhsdb uses PTRACE) + private static void testCase02(long pid) throws Exception { + runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jhsdb", "jinfo", "--pid", "" + pid) + .shouldHaveExitValue(0) + .shouldContain("Java System Properties") + .shouldContain("VM Flags"); + } + + // test jcmd with some commands (help, start JFR recording) + // JCMD will use signal mechanism and Unix Socket + private static void testCase03(long pid) throws Exception { + runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "help") + .shouldHaveExitValue(0) + .shouldContain("VM.version"); + runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "JFR.start") + .shouldHaveExitValue(0) + .shouldContain("Started recording"); + } + + private static DockerThread startMainContainer() throws Exception { + // start "main" container (the observee) + DockerRunOptions opts = commonDockerOpts("EventGeneratorLoop"); + opts.addDockerOpts("--cap-add=SYS_PTRACE") + .addDockerOpts("--name", MAIN_CONTAINER_NAME) + .addDockerOpts("-v", "/tmp") + .addJavaOpts("-XX:+UsePerfData") + .addClassOptions("" + TIME_TO_RUN_MAIN_PROCESS); + DockerThread t = new DockerThread(opts); + t.start(); + + return t; + } + + private static void waitForMainContainerToStart(int delayMillis, int count) throws Exception { + boolean started = false; + for(int i=0; i < count; i++) { + try { + Thread.sleep(delayMillis); + } catch (InterruptedException e) {} + if (isMainContainerRunning()) { + started = true; + break; + } + } + if (!started) { + throw new RuntimeException("Main container did not start"); + } + } + + private static boolean isMainContainerRunning() throws Exception { + OutputAnalyzer out = + DockerTestUtils.execute(Container.ENGINE_COMMAND, + "ps", "--no-trunc", + "--filter", "name=" + MAIN_CONTAINER_NAME); + return out.getStdout().contains(MAIN_CONTAINER_NAME); + } + + // JCMD relies on the attach mechanism (com.sun.tools.attach), + // which in turn relies on JVMSTAT mechanism, which puts its mapped + // buffers in /tmp directory (hsperfdata_<user>). Thus, in sidecar + // we mount /tmp via --volumes-from from the main container. + private static OutputAnalyzer runSideCar(String MAIN_CONTAINER_NAME, String whatToRun, + String... args) throws Exception { + List<String> cmd = new ArrayList<>(); + String[] command = new String[] { + Container.ENGINE_COMMAND, "run", + "--tty=true", "--rm", + "--cap-add=SYS_PTRACE", "--sig-proxy=true", + "--pid=container:" + MAIN_CONTAINER_NAME, + "--volumes-from", MAIN_CONTAINER_NAME, + IMAGE_NAME, whatToRun + }; + + cmd.addAll(Arrays.asList(command)); + cmd.addAll(Arrays.asList(args)); + return DockerTestUtils.execute(cmd); + } + + private static long findProcess(OutputAnalyzer out, String name) throws Exception { + List<String> l = out.asLines() + .stream() + .filter(s -> s.contains(name)) + .collect(Collectors.toList()); + if (l.isEmpty()) { + throw new RuntimeException("Could not find matching process"); + } + String psInfo = l.get(0); + System.out.println("findProcess(): psInfo: " + psInfo); + String pid = psInfo.substring(0, psInfo.indexOf(' ')); + System.out.println("findProcess(): pid: " + pid); + return Long.parseLong(pid); + } + + private static DockerRunOptions commonDockerOpts(String className) { + return new DockerRunOptions(IMAGE_NAME, "/jdk/bin/java", className) + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addJavaOpts("-cp", "/test-classes/"); + } + + + static class DockerThread extends Thread { + DockerRunOptions runOpts; + Exception exception; + + DockerThread(DockerRunOptions opts) { + runOpts = opts; + } + + public void run() { + try { + DockerTestUtils.dockerRunJava(runOpts); + } catch (Exception e) { + exception = e; + } + } + + public void assertIsAlive() throws Exception { + if (!isAlive()) { + throw new RuntimeException("DockerThread stopped unexpectedly"); + } + } + + public void checkForErrors() throws Exception { + if (exception != null) { + throw new RuntimeException("DockerThread threw exception" + + exception.getMessage()); + } + } + } + +}
--- a/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java Wed Jul 24 12:21:36 2019 +0530 @@ -163,7 +163,6 @@ public static void writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception { fc.position(offset); fc.write(bb); - fc.force(true); } public static FileChannel getFileChannel(File jsaFile) throws Exception { @@ -247,7 +246,6 @@ bbuf.clear(); bytes_written += 4096; } - fc.force(true); if (fc.isOpen()) { fc.close(); }
--- a/test/jdk/ProblemList.txt Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/ProblemList.txt Wed Jul 24 12:21:36 2019 +0530 @@ -652,13 +652,13 @@ sun/security/pkcs11/ec/TestKeyFactory.java 8026976 generic-all sun/security/pkcs11/Secmod/AddTrustedCert.java 8180837 generic-all sun/security/pkcs11/tls/TestKeyMaterial.java 8180837 generic-all -sun/security/pkcs11/tls/tls12/FipsModeTLS12.java 8224954,8225678 windows-all,linux-all sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8161536 generic-all sun/security/tools/keytool/ListKeychainStore.sh 8156889 macosx-all sun/security/tools/keytool/KeyToolTest.java 8224644 solaris-all sun/security/tools/keytool/WeakAlg.java 8224644 solaris-all +sun/security/tools/jarsigner/compatibility/SignTwice.java 8228341 windows-all sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java 8026393 generic-all javax/net/ssl/ServerName/SSLEngineExplorerMatchedSNI.java 8212096 generic-all @@ -673,6 +673,28 @@ security/infra/java/security/cert/CertPathValidator/certification/ActalisCA.java 8224768 generic-all +sun/security/smartcardio/TestChannel.java 8039280 generic-all +sun/security/smartcardio/TestConnect.java 8039280 generic-all +sun/security/smartcardio/TestConnectAgain.java 8039280 generic-all +sun/security/smartcardio/TestControl.java 8039280 generic-all +sun/security/smartcardio/TestDefault.java 8039280 generic-all +sun/security/smartcardio/TestDirect.java 8039280 generic-all +sun/security/smartcardio/TestExclusive.java 8039280 generic-all +sun/security/smartcardio/TestMultiplePresent.java 8039280 generic-all +sun/security/smartcardio/TestPresent.java 8039280 generic-all +sun/security/smartcardio/TestTransmit.java 8039280 generic-all +com/sun/crypto/provider/Cipher/DES/PerformanceTest.java 8039280 generic-all +com/sun/security/auth/callback/TextCallbackHandler/Default.java 8039280 generic-all +com/sun/security/auth/callback/TextCallbackHandler/Password.java 8039280 generic-all +com/sun/security/sasl/gsskerb/AuthOnly.java 8039280 generic-all +com/sun/security/sasl/gsskerb/ConfSecurityLayer.java 8039280 generic-all +com/sun/security/sasl/gsskerb/NoSecurityLayer.java 8039280 generic-all +javax/security/auth/kerberos/KerberosHashEqualsTest.java 8039280 generic-all +javax/security/auth/kerberos/KerberosTixDateTest.java 8039280 generic-all +sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.java 8039280 generic-all +sun/security/provider/PolicyParser/ExtDirsChange.java 8039280 generic-all +sun/security/provider/PolicyParser/PrincipalExpansionError.java 8039280 generic-all + ############################################################################ # jdk_sound
--- a/test/jdk/TEST.ROOT Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/TEST.ROOT Wed Jul 24 12:21:36 2019 +0530 @@ -37,7 +37,9 @@ # to determine additional characteristics of the system for use with the @requires tag. # Note: compiled bootlibs code will be located in the folder 'bootClasses' requires.extraPropDefns = ../jtreg-ext/requires/VMProps.java -requires.extraPropDefns.bootlibs = ../lib/sun ../lib/jdk/test/lib/Platform.java +requires.extraPropDefns.bootlibs = ../lib/sun \ + ../lib/jdk/test/lib/Platform.java \ + ../lib/jdk/test/lib/Container.java requires.extraPropDefns.vmOpts = -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:bootClasses requires.properties= \ sun.arch.data.model \
--- a/test/jdk/java/lang/annotation/loaderLeak/Main.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/java/lang/annotation/loaderLeak/Main.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2019, 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 @@ -56,6 +56,7 @@ if (c.get() == null) throw new AssertionError(); System.gc(); System.gc(); + Reference.reachabilityFence(loader); loader = null; // Might require multiple calls to System.gc() for weak-references
--- a/test/jdk/java/time/tck/java/time/format/TCKOffsetPrinterParser.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/java/time/tck/java/time/format/TCKOffsetPrinterParser.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, 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 @@ -69,6 +69,7 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.TextStyle; +import java.util.Locale; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; @@ -557,20 +558,20 @@ ZonedDateTime zdt = ldt.atZone(offset); DateTimeFormatter f = new DateTimeFormatterBuilder().appendLocalizedOffset(style) - .toFormatter(); + .toFormatter(Locale.US); assertEquals(f.format(odt), expected); assertEquals(f.format(zdt), expected); assertEquals(f.parse(expected, ZoneOffset::from), offset); if (style == TextStyle.FULL) { f = new DateTimeFormatterBuilder().appendPattern("ZZZZ") - .toFormatter(); + .toFormatter(Locale.US); assertEquals(f.format(odt), expected); assertEquals(f.format(zdt), expected); assertEquals(f.parse(expected, ZoneOffset::from), offset); f = new DateTimeFormatterBuilder().appendPattern("OOOO") - .toFormatter(); + .toFormatter(Locale.US); assertEquals(f.format(odt), expected); assertEquals(f.format(zdt), expected); assertEquals(f.parse(expected, ZoneOffset::from), offset); @@ -578,7 +579,7 @@ if (style == TextStyle.SHORT) { f = new DateTimeFormatterBuilder().appendPattern("O") - .toFormatter(); + .toFormatter(Locale.US); assertEquals(f.format(odt), expected); assertEquals(f.format(zdt), expected); assertEquals(f.parse(expected, ZoneOffset::from), offset);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/time/test/java/time/format/TestLocalizedOffsetPrinterParser.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019, 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 + * 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 8154520 + * @summary This test verifies that the localized text for "GMT" from CLDR is + * applied/recognized during printing/parsing timestamps. For example, the + * localized text for "GMT" on some particular locale may be "UTC", and the + * resulting formatted string should have UTC+<offset> (instead of GMT+<offset>). + * Since the test relies on CLDR data, the "expected" text in the test data may + * require to be modified in accordance with changes to CLDR, if any. + * @modules jdk.localedata + */ + +package test.java.time.format; + +import static org.testng.Assert.assertEquals; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; +import java.util.Locale; +import java.util.Objects; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatterBuilder.appendOffset(). + */ +@Test +public class TestLocalizedOffsetPrinterParser { + + private static final LocalDateTime DT_2012_06_30_12_30_40 = LocalDateTime.of(2012, 6, 30, 12, 30, 40); + + private static final Locale LOCALE_GA = new Locale("ga"); + + @DataProvider(name="print_localized_custom_locale") + Object[][] data_print_localized_custom_locale() { + return new Object[][] { + {TextStyle.FULL, DT_2012_06_30_12_30_40, ZoneOffset.UTC, LOCALE_GA, "MAG"}, + {TextStyle.SHORT, DT_2012_06_30_12_30_40, ZoneOffset.ofHours(1), LOCALE_GA, "MAG+1"}, + {TextStyle.FULL, DT_2012_06_30_12_30_40, ZoneOffset.ofHours(-1), LOCALE_GA, "MAG-01:00"} + }; + } + + @Test(dataProvider="print_localized_custom_locale") + public void test_print_localized_custom_locale(TextStyle style, LocalDateTime ldt, ZoneOffset offset, Locale locale, String expected) { + + Objects.requireNonNull(locale, "Locale must not be null"); + + OffsetDateTime odt = OffsetDateTime.of(ldt, offset); + ZonedDateTime zdt = ldt.atZone(offset); + + DateTimeFormatter f = new DateTimeFormatterBuilder().appendLocalizedOffset(style).toFormatter(locale); + assertEquals(f.format(odt), expected); + assertEquals(f.format(zdt), expected); + assertEquals(f.parse(expected, ZoneOffset::from), offset); + + if (style == TextStyle.FULL) { + f = new DateTimeFormatterBuilder().appendPattern("ZZZZ").toFormatter(locale); + assertEquals(f.format(odt), expected); + assertEquals(f.format(zdt), expected); + assertEquals(f.parse(expected, ZoneOffset::from), offset); + + f = new DateTimeFormatterBuilder().appendPattern("OOOO").toFormatter(locale); + assertEquals(f.format(odt), expected); + assertEquals(f.format(zdt), expected); + assertEquals(f.parse(expected, ZoneOffset::from), offset); + } + + if (style == TextStyle.SHORT) { + f = new DateTimeFormatterBuilder().appendPattern("O").toFormatter(locale); + assertEquals(f.format(odt), expected); + assertEquals(f.format(zdt), expected); + assertEquals(f.parse(expected, ZoneOffset::from), offset); + } + + } + +}
--- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java Wed Jul 24 12:21:36 2019 +0530 @@ -22,23 +22,21 @@ */ import java.io.File; +import java.io.PrintWriter; import java.net.InetAddress; -import java.net.NetworkInterface; import java.net.UnknownHostException; -import java.net.SocketException; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; +import java.util.concurrent.CountDownLatch; -import jdk.test.lib.thread.ProcessThread; +import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; /** * @test * @bug 6425769 * @summary Test JMX agent host address binding. Same ports but different - * interfaces to bind to (selecting plain or SSL sockets at random - * @key intermittent + * interfaces to bind to (selecting plain or SSL sockets at random) * * @library /test/lib * @modules java.management.rmi @@ -52,14 +50,9 @@ public static final int STOP_PROCESS_EXIT_VAL = 10; public static final int JMX_PORT_RANGE_LOWER = 9100; public static final int JMX_PORT_RANGE_UPPER = 9200; - public static final int JMX_PORT = getRandomPortInRange(JMX_PORT_RANGE_LOWER, - JMX_PORT_RANGE_UPPER); public static final int JMX_PORT_RANGE_LOWER_SSL = 9201; // 9200 might be RMI Port public static final int JMX_PORT_RANGE_UPPER_SSL = 9300; - public static final int JMX_PORT_SSL = getRandomPortInRange(JMX_PORT_RANGE_LOWER_SSL, - JMX_PORT_RANGE_UPPER_SSL); - public static final int RMI_PORT = JMX_PORT + 1; - public static final int RMI_PORT_SSL = JMX_PORT_SSL + 1; + private static final int MAX_RETRY_ATTEMTS = 10; public static final String READY_MSG = "MainThread: Ready for connections"; public static final String TEST_CLASS = JMXAgentInterfaceBinding.class.getSimpleName(); public static final String KEYSTORE_LOC = System.getProperty("test.src", ".") + @@ -82,98 +75,25 @@ } private void runTests(List<InetAddress> addrs, boolean useSSL) { - List<ProcessThread> jvms = new ArrayList<>(addrs.size()); - int i = 1; + List<TestProcessThread> testThreads = new ArrayList<>(addrs.size()); + CountDownLatch latch = new CountDownLatch(addrs.size()); for (InetAddress addr : addrs) { String address = JMXAgentInterfaceBinding.wrapAddress(addr.getHostAddress()); - System.out.println(); - String msg = String.format("DEBUG: Launching java tester for triplet (HOSTNAME,JMX_PORT,RMI_PORT) == (%s,%d,%d)", - address, - useSSL ? JMX_PORT_SSL : JMX_PORT, - useSSL ? RMI_PORT_SSL : RMI_PORT); - System.out.println(msg); - ProcessThread jvm = runJMXBindingTest(address, useSSL); - jvms.add(jvm); - jvm.start(); - System.out.println("DEBUG: Started " + (i++) + " Process(es)."); - } - int failedProcesses = 0; - for (ProcessThread pt: jvms) { - try { - pt.sendMessage("Exit: " + STOP_PROCESS_EXIT_VAL); - pt.join(); - } catch (Throwable e) { - System.err.println("Failed to stop process: " + pt.getName()); - throw new RuntimeException("Test failed", e); - } - int exitValue = pt.getOutput().getExitValue(); - // If there is a communication error (the case we care about) - // we get a exit code of 1 - if (exitValue == COMMUNICATION_ERROR_EXIT_VAL) { - // Failure case since the java processes should still be - // running. - System.err.println("Test FAILURE on " + pt.getName()); - failedProcesses++; - } else if (exitValue == STOP_PROCESS_EXIT_VAL) { - System.out.println("DEBUG: OK. Spawned java process terminated with expected exit code of " + STOP_PROCESS_EXIT_VAL); - } else { - System.err.println("Test FAILURE on " + pt.getName() + " reason: Unexpected exit code => " + exitValue); - failedProcesses++; - } + TestProcessThread t = new TestProcessThread(address, useSSL, latch); + testThreads.add(t); + t.start(); } - if (failedProcesses > 0) { - throw new RuntimeException("Test FAILED. " + failedProcesses + " out of " + addrs.size() + " process(es) failed to start the JMX agent."); - } - } - - private ProcessThread runJMXBindingTest(String address, boolean useSSL) { - List<String> args = new ArrayList<>(); - args.add("-classpath"); - args.add(TEST_CLASSPATH); - args.add("-Dcom.sun.management.jmxremote.host=" + address); - args.add("-Dcom.sun.management.jmxremote.port=" + (useSSL ? JMX_PORT_SSL : JMX_PORT)); - args.add("-Dcom.sun.management.jmxremote.rmi.port=" + (useSSL ? RMI_PORT_SSL : RMI_PORT)); - args.add("-Dcom.sun.management.jmxremote.authenticate=false"); - args.add("-Dcom.sun.management.jmxremote.ssl=" + Boolean.toString(useSSL)); - // This is needed for testing on loopback - args.add("-Djava.rmi.server.hostname=" + address); - if (useSSL) { - args.add("-Dcom.sun.management.jmxremote.registry.ssl=true"); - args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC); - args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC); - args.add("-Djavax.net.ssl.keyStorePassword=password"); - args.add("-Djavax.net.ssl.trustStorePassword=trustword"); - } - args.add(TEST_CLASS); - args.add(address); - args.add(Integer.toString(useSSL ? JMX_PORT_SSL : JMX_PORT)); - args.add(Integer.toString(useSSL ? RMI_PORT_SSL : RMI_PORT)); - args.add(Boolean.toString(useSSL)); try { - ProcessBuilder builder = ProcessTools.createJavaProcessBuilder(args.toArray(new String[] {})); - System.out.println(ProcessTools.getCommandLine(builder)); - ProcessThread jvm = new ProcessThread("JMX-Tester-" + address, JMXInterfaceBindingTest::isJMXAgentResponseAvailable, builder); - return jvm; - } catch (Exception e) { + latch.await(); + } catch (InterruptedException e) { + System.err.println("Failed to wait for the test threads to complete"); throw new RuntimeException("Test failed", e); } - } - - private static boolean isJMXAgentResponseAvailable(String line) { - if (line.equals(READY_MSG)) { - System.out.println("DEBUG: Found expected READY_MSG."); - return true; - } else if (line.startsWith("Error:")) { - // Allow for a JVM process that exits with - // "Error: JMX connector server communication error: ..." - // to continue as well since we handle that case elsewhere. - // This has the effect that the test does not timeout and - // fails with an exception in the test. - System.err.println("PROBLEM: JMX agent of target JVM did not start as it should."); - return true; - } else { - return false; + long failedProcesses = testThreads.stream().filter(TestProcessThread::isTestFailed).count(); + if (failedProcesses > 0) { + throw new RuntimeException("Test FAILED. " + failedProcesses + " out of " + addrs.size() + + " process(es) failed to start the JMX agent."); } } @@ -215,4 +135,128 @@ throw new RuntimeException("Test failed", e); } } + + private static class TestProcessThread extends Thread { + private final String name; + private final String address; + private final boolean useSSL; + private final CountDownLatch latch; + private volatile boolean testFailed = false; + private OutputAnalyzer output; + + public TestProcessThread(String address, boolean useSSL, CountDownLatch latch) { + this.address = address; + this.useSSL = useSSL; + this.name = "JMX-Tester-" + address; + this.latch = latch; + } + + @Override + public void run() { + int attempts = 0; + boolean needRetry = false; + do { + if (needRetry) { + System.err.println("Retrying the test for " + name); + } + needRetry = runTest(); + } while (needRetry && (attempts++ < MAX_RETRY_ATTEMTS)); + + if (testFailed) { + int exitValue = output.getExitValue(); + if (needRetry) { + System.err.println("Test FAILURE on " + name + " reason: run out of retries to to pick free ports"); + } else if (exitValue == COMMUNICATION_ERROR_EXIT_VAL) { + // Failure case since the java processes should still be + // running. + System.err.println("Test FAILURE on " + name); + } else if (exitValue == STOP_PROCESS_EXIT_VAL) { + System.out.println("Test FAILURE on " + name + " reason: The expected line \"" + READY_MSG + + "\" is not present in the process output"); + } else { + System.err.println("Test FAILURE on " + name + " reason: Unexpected exit code => " + exitValue); + } + output.reportDiagnosticSummary(); + } + latch.countDown(); + } + + public boolean isTestFailed() { + return testFailed; + } + + private int getJMXPort() { + return useSSL ? + getRandomPortInRange(JMX_PORT_RANGE_LOWER_SSL, JMX_PORT_RANGE_UPPER_SSL) : + getRandomPortInRange(JMX_PORT_RANGE_LOWER, JMX_PORT_RANGE_UPPER); + } + + private Process createTestProcess() { + int jmxPort = getJMXPort(); + int rmiPort = jmxPort + 1; + String msg = String.format("DEBUG: Launching java tester for triplet (HOSTNAME,JMX_PORT,RMI_PORT)" + + " == (%s,%d,%d)", address, jmxPort, rmiPort); + System.out.println(msg); + List<String> args = new ArrayList<>(); + args.add("-classpath"); + args.add(TEST_CLASSPATH); + args.add("-Dcom.sun.management.jmxremote.host=" + address); + args.add("-Dcom.sun.management.jmxremote.port=" + jmxPort); + args.add("-Dcom.sun.management.jmxremote.rmi.port=" + rmiPort); + args.add("-Dcom.sun.management.jmxremote.authenticate=false"); + args.add("-Dcom.sun.management.jmxremote.ssl=" + Boolean.toString(useSSL)); + // This is needed for testing on loopback + args.add("-Djava.rmi.server.hostname=" + address); + if (useSSL) { + args.add("-Dcom.sun.management.jmxremote.registry.ssl=true"); + args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC); + args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC); + args.add("-Djavax.net.ssl.keyStorePassword=password"); + args.add("-Djavax.net.ssl.trustStorePassword=trustword"); + } + args.add(TEST_CLASS); + args.add(address); + args.add(Integer.toString(jmxPort)); + args.add(Integer.toString(rmiPort)); + args.add(Boolean.toString(useSSL)); + + try { + ProcessBuilder builder = ProcessTools.createJavaProcessBuilder(args.toArray(new String[]{})); + System.out.println(ProcessTools.getCommandLine(builder)); + Process process = builder.start(); + output = new OutputAnalyzer(process); + return process; + } catch (Exception e) { + throw new RuntimeException("Test failed", e); + } + } + + // Returns true if the test failed due to "Port already in use" error. + private boolean runTest() { + testFailed = true; + Process process = createTestProcess(); + try { + sendMessageToProcess(process, "Exit: " + STOP_PROCESS_EXIT_VAL); + process.waitFor(); + } catch (Throwable e) { + System.err.println("Failed to stop process: " + name); + throw new RuntimeException("Test failed", e); + } + if (output.getExitValue() == STOP_PROCESS_EXIT_VAL && output.getStdout().contains(READY_MSG)) { + testFailed = false; + } else if (output.getStderr().contains("Port already in use")) { + System.out.println("The test attempt for the test " + name +" failed due to the bind error"); + // Need to retry + return true; + } + return false; + } + + private static void sendMessageToProcess(Process process, String message) { + try (PrintWriter pw = new PrintWriter(process.getOutputStream())) { + pw.println(message); + pw.flush(); + } + } + } }
--- a/test/jdk/sun/security/krb5/auto/KDC.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/security/krb5/auto/KDC.java Wed Jul 24 12:21:36 2019 +0530 @@ -808,8 +808,10 @@ PrincipalName cname = null; boolean allowForwardable = true; - + boolean isReferral = false; if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) { + System.out.println(realm + "> verifying referral for " + + body.sname.getNameString()); KDC referral = aliasReferrals.get(body.sname.getNameString()); if (referral != null) { service = new PrincipalName( @@ -817,6 +819,9 @@ PrincipalName.NAME_COMPONENT_SEPARATOR_STR + referral.getRealm(), PrincipalName.KRB_NT_SRV_INST, this.getRealm()); + System.out.println(realm + "> referral to " + + referral.getRealm()); + isReferral = true; } } @@ -918,7 +923,8 @@ if (body.kdcOptions.get(KDCOptions.ALLOW_POSTDATE)) { bFlags[Krb5.TKT_OPTS_MAY_POSTDATE] = true; } - if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT)) { + if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT) && + !isReferral) { if (!options.containsKey(Option.ALLOW_S4U2PROXY)) { // Don't understand CNAME_IN_ADDL_TKT throw new KrbException(Krb5.KDC_ERR_BADOPTION); @@ -1074,8 +1080,7 @@ } int eType = eTypes[0]; - if (body.kdcOptions.get(KDCOptions.CANONICALIZE) && - body.cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) { + if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) { PrincipalName principal = alias2Principals.get( body.cname.getNameString()); if (principal != null) {
--- a/test/jdk/sun/security/krb5/auto/ReferralsTest.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/security/krb5/auto/ReferralsTest.java Wed Jul 24 12:21:36 2019 +0530 @@ -30,9 +30,18 @@ */ import java.io.File; -import sun.security.krb5.Credentials; -import sun.security.krb5.internal.CredentialsUtil; -import sun.security.krb5.KrbAsReqBuilder; +import java.security.Principal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.Subject; + +import org.ietf.jgss.GSSName; + +import sun.security.jgss.GSSUtil; import sun.security.krb5.PrincipalName; public class ReferralsTest { @@ -41,39 +50,32 @@ private static final String realmKDC1 = "RABBIT.HOLE"; private static final String realmKDC2 = "DEV.RABBIT.HOLE"; private static final char[] password = "123qwe@Z".toCharArray(); + + // Names private static final String clientName = "test"; - - private static final String clientAlias = clientName + - PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; - - private static final String clientKDC1QueryName = clientAlias.replaceAll( - PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" + - PrincipalName.NAME_REALM_SEPARATOR_STR) + - PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; - private static PrincipalName clientKDC1QueryPrincipal = null; - static { - try { - clientKDC1QueryPrincipal = new PrincipalName( - clientKDC1QueryName, PrincipalName.KRB_NT_ENTERPRISE, - null); - } catch (Throwable t) {} - } - - private static final String clientKDC2Name = clientName + - PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; - private static final String serviceName = "http" + PrincipalName.NAME_COMPONENT_SEPARATOR_STR + "server.dev.rabbit.hole"; - private static Credentials tgt; - private static Credentials tgs; + // Alias + private static final String clientAlias = clientName + + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; + + // Names + realms + private static final String clientKDC1Name = clientAlias.replaceAll( + PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" + + PrincipalName.NAME_REALM_SEPARATOR_STR) + + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; + private static final String clientKDC2Name = clientName + + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; + private static final String serviceKDC2Name = serviceName + + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; public static void main(String[] args) throws Exception { try { initializeKDCs(); - getTGT(); - getTGS(); + testSubjectCredentials(); + testDelegated(); } finally { cleanup(); } @@ -108,6 +110,11 @@ kdc1.registerAlias(serviceName, kdc2); kdc2.registerAlias(clientAlias, clientKDC2Name); + Map<String,List<String>> mapKDC2 = new HashMap<>(); + mapKDC2.put(serviceName + "@" + realmKDC2, Arrays.asList( + new String[]{serviceName + "@" + realmKDC2})); + kdc2.setOption(KDC.Option.ALLOW_S4U2PROXY, mapKDC2); + KDC.saveConfig(krbConfigName, kdc1, kdc2, "forwardable=true"); System.setProperty("java.security.krb5.conf", krbConfigName); @@ -120,50 +127,123 @@ } } - private static void getTGT() throws Exception { - KrbAsReqBuilder builder = new KrbAsReqBuilder(clientKDC1QueryPrincipal, - password); - tgt = builder.action().getCreds(); - builder.destroy(); + /* + * The client subject (whose principal is + * test@RABBIT.HOLE@RABBIT.HOLE) will obtain a TGT after + * realm referral and name canonicalization (TGT cname + * will be test@DEV.RABBIT.HOLE). With this TGT, the client will request + * a TGS for service http/server.dev.rabbit.hole@RABBIT.HOLE. After + * realm referral, a http/server.dev.rabbit.hole@DEV.RABBIT.HOLE TGS + * will be obtained. + * + * Assert that we get the proper TGT and TGS tickets, and that they are + * associated to the client subject. + * + * Assert that if we request a TGS for the same service again (based on the + * original service name), we don't get a new one but the previous, + * already in the subject credentials. + */ + private static void testSubjectCredentials() throws Exception { + Subject clientSubject = new Subject(); + Context clientContext = Context.fromUserPass(clientSubject, + clientKDC1Name, password, false); + + Set<Principal> clientPrincipals = clientSubject.getPrincipals(); + if (clientPrincipals.size() != 1) { + throw new Exception("Only one client subject principal expected"); + } + Principal clientPrincipal = clientPrincipals.iterator().next(); if (DEBUG) { - System.out.println("TGT"); - System.out.println("----------------------"); - System.out.println(tgt); - System.out.println("----------------------"); + System.out.println("Client subject principal: " + + clientPrincipal.getName()); + } + if (!clientPrincipal.getName().equals(clientKDC1Name)) { + throw new Exception("Unexpected client subject principal."); } - if (tgt == null) { - throw new Exception("TGT is null"); - } - if (!tgt.getClient().getName().equals(clientKDC2Name)) { - throw new Exception("Unexpected TGT client"); + + clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + clientContext.take(new byte[0]); + Set<KerberosTicket> clientTickets = + clientSubject.getPrivateCredentials(KerberosTicket.class); + boolean tgtFound = false; + boolean tgsFound = false; + for (KerberosTicket clientTicket : clientTickets) { + String cname = clientTicket.getClient().getName(); + String sname = clientTicket.getServer().getName(); + if (cname.equals(clientKDC2Name)) { + if (sname.equals(PrincipalName.TGS_DEFAULT_SRV_NAME + + PrincipalName.NAME_COMPONENT_SEPARATOR_STR + + realmKDC2 + PrincipalName.NAME_REALM_SEPARATOR_STR + + realmKDC2)) { + tgtFound = true; + } else if (sname.equals(serviceKDC2Name)) { + tgsFound = true; + } + } + if (DEBUG) { + System.out.println("Client subject KerberosTicket:"); + System.out.println(clientTicket); + } } - String[] tgtServerNames = tgt.getServer().getNameStrings(); - if (tgtServerNames.length != 2 || !tgtServerNames[0].equals( - PrincipalName.TGS_DEFAULT_SRV_NAME) || - !tgtServerNames[1].equals(realmKDC2) || - !tgt.getServer().getRealmString().equals(realmKDC2)) { - throw new Exception("Unexpected TGT server"); + if (!tgtFound || !tgsFound) { + throw new Exception("client subject tickets (TGT/TGS) not found."); + } + int numOfTickets = clientTickets.size(); + clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + clientContext.take(new byte[0]); + clientContext.status(); + int newNumOfTickets = + clientSubject.getPrivateCredentials(KerberosTicket.class).size(); + if (DEBUG) { + System.out.println("client subject number of tickets: " + + numOfTickets); + System.out.println("client subject new number of tickets: " + + newNumOfTickets); + } + if (numOfTickets != newNumOfTickets) { + throw new Exception("Useless client subject TGS request because" + + " TGS was not found in private credentials."); } } - private static void getTGS() throws Exception { - tgs = CredentialsUtil.acquireServiceCreds(serviceName + - PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt); - if (DEBUG) { - System.out.println("TGS"); - System.out.println("----------------------"); - System.out.println(tgs); - System.out.println("----------------------"); + /* + * The server (http/server.dev.rabbit.hole@DEV.RABBIT.HOLE) + * will authenticate on itself on behalf of the client + * (test@DEV.RABBIT.HOLE). Cross-realm referrals will occur + * when requesting different TGTs and TGSs (including the + * request for delegated credentials). + */ + private static void testDelegated() throws Exception { + Context c = Context.fromUserPass(clientKDC2Name, + password, false); + c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + Context s = Context.fromUserPass(serviceKDC2Name, + password, true); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + Context.handshake(c, s); + Context delegatedContext = s.delegated(); + delegatedContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + delegatedContext.x().requestMutualAuth(false); + Context s2 = Context.fromUserPass(serviceKDC2Name, + password, true); + s2.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + + // Test authentication + Context.handshake(delegatedContext, s2); + if (!delegatedContext.x().isEstablished() || !s2.x().isEstablished()) { + throw new Exception("Delegated authentication failed"); } - if (tgs == null) { - throw new Exception("TGS is null"); + + // Test identities + GSSName contextInitiatorName = delegatedContext.x().getSrcName(); + GSSName contextAcceptorName = delegatedContext.x().getTargName(); + if (DEBUG) { + System.out.println("Context initiator: " + contextInitiatorName); + System.out.println("Context acceptor: " + contextAcceptorName); } - if (!tgs.getClient().getName().equals(clientKDC2Name)) { - throw new Exception("Unexpected TGS client"); - } - if (!tgs.getServer().getNameString().equals(serviceName) || - !tgs.getServer().getRealmString().equals(realmKDC2)) { - throw new Exception("Unexpected TGS server"); + if (!contextInitiatorName.toString().equals(clientKDC2Name) || + !contextAcceptorName.toString().equals(serviceName)) { + throw new Exception("Unexpected initiator or acceptor names"); } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2019, 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 + * 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 + * @library /test/lib + * @summary Test that a New Session Ticket will be generated when a + * SSLSessionBindingListener is set (boundValues) + * @run main/othervm ResumptionUpdateBoundValues + */ + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.concurrent.ArrayBlockingQueue; + +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionBindingEvent; +import javax.net.ssl.SSLSessionBindingListener; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Utils; + +public class ResumptionUpdateBoundValues { + + static boolean separateServerThread = true; + + /* + * Where do we find the keystores? + */ + static String pathToStores = "../../../../javax/net/ssl/etc/"; + static String keyStoreFile = "keystore"; + static String trustStoreFile = "truststore"; + static String passwd = "passphrase"; + + /* + * Is the server ready to serve? + */ + volatile static boolean serverReady = false; + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + SSLServerSocketFactory sslssf = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslssf.createServerSocket(serverPort); + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + while (serverReady) { + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + SSLSession sslSession = sslSocket.getSession(); + SBListener sbListener = new SBListener(sslSession); + sslSession.putValue("x", sbListener); + + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + + sslSocket.close(); + } + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + SBListener doClientSide() throws Exception { + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLSocketFactory sslsf = + (SSLSocketFactory) SSLSocketFactory.getDefault(); + + try { + SSLSocket sslSocket = (SSLSocket) + sslsf.createSocket("localhost", serverPort); + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + + SSLSession sslSession = sslSocket.getSession(); + System.out.printf(" sslSession: %s %n %s%n", sslSession, sslSession.getClass()); + SBListener sbListener = new SBListener(sslSession); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + + sslOS.close(); + sslIS.close(); + sslSocket.close(); + + sslOS = null; + sslIS = null; + sslSession = null; + sslSocket = null; + Reference.reachabilityFence(sslOS); + Reference.reachabilityFence(sslIS); + Reference.reachabilityFence(sslSession); + Reference.reachabilityFence(sslSocket); + + return sbListener; + } catch (Exception ex) { + ex.printStackTrace(); + throw ex; + } + } + + /* + * ============================================================= + * The remainder is just support stuff + */ + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + public static void main(String[] args) throws Exception { + + if (args.length == 0) { + System.setProperty("test.java.opts", + "-Dtest.src=" + System.getProperty("test.src") + + " -Dtest.jdk=" + System.getProperty("test.jdk") + + " -Djavax.net.debug=ssl,handshake"); + + System.out.println("test.java.opts: " + + System.getProperty("test.java.opts")); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, + Utils.addTestJavaOpts("ResumptionUpdateBoundValues", "p")); + + OutputAnalyzer output = ProcessTools.executeProcess(pb); + try { + output.shouldContain("trigger new session ticket"); + System.out.println("Found NST in debugging"); + } catch (Exception e) { + throw e; + } finally { + System.out.println("-- BEGIN Stdout:"); + System.out.println(output.getStdout()); + System.out.println("-- END Stdout"); + System.out.println("-- BEGIN Stderr:"); + System.out.println(output.getStderr()); + System.out.println("-- END Stderr"); + } + return; + } + + String keyFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; + String trustFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + trustStoreFile; + System.setProperty("javax.net.ssl.keyStore", keyFilename); + System.setProperty("javax.net.ssl.keyStorePassword", passwd); + System.setProperty("javax.net.ssl.trustStore", trustFilename); + System.setProperty("javax.net.ssl.trustStorePassword", passwd); + + if (debug) + System.setProperty("javax.net.debug", "all"); + + /* + * Start the tests. + */ + + new ResumptionUpdateBoundValues(); + } + + ArrayBlockingQueue<Thread> threads = new ArrayBlockingQueue<Thread>(100); + + ArrayBlockingQueue<SBListener> sbListeners = new ArrayBlockingQueue<>(100); + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + ResumptionUpdateBoundValues() throws Exception { + final int count = 1; + if (separateServerThread) { + startServer(true); + startClients(true, count); + } else { + startClients(true, count); + startServer(true); + } + + /* + * Wait for other side to close down. + */ + Thread t; + while ((t = threads.take()) != Thread.currentThread()) { + System.out.printf(" joining: %s%n", t); + t.join(1000L); + } + serverReady = false; + System.gc(); + System.gc(); + + + SBListener listener = null; + while ((listener = sbListeners.poll()) != null) { + if (!listener.check()) { + System.out.printf(" sbListener not called on finalize: %s%n", + listener); + } + } + + /* + * When we get here, the test is pretty much over. + * + * If the main thread excepted, that propagates back + * immediately. If the other thread threw an exception, we + * should report back. + */ + if (serverException != null) { + System.out.print("Server Exception:"); + throw serverException; + } + if (clientException != null) { + System.out.print("Client Exception:"); + throw clientException; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + Thread t = new Thread("Server") { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..." + e); + serverReady = true; + serverException = e; + } + } + }; + threads.add(t); + t.setDaemon(true); + t.start(); + } else { + doServerSide(); + } + } + + void startClients(boolean newThread, int count) throws Exception { + for (int i = 0; i < count; i++) { + System.out.printf(" newClient: %d%n", i); + startClient(newThread); + } + serverReady = false; + + threads.add(Thread.currentThread()); // add ourselves at the 'end' + } + void startClient(boolean newThread) throws Exception { + if (newThread) { + Thread t = new Thread("Client") { + public void run() { + try { + sbListeners.add(doClientSide()); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..." + e); + clientException = e; + } + } + }; + System.out.printf(" starting: %s%n", t); + threads.add(t); + t.start(); + } else { + sbListeners.add(doClientSide()); + } + } + + + static class SBListener implements SSLSessionBindingListener { + private volatile int unboundNotified; + private final WeakReference<SSLSession> session; + + SBListener(SSLSession session) { + this.unboundNotified = 0; + this.session = new WeakReference<SSLSession>(session); + } + + boolean check() { + System.out.printf(" check: %s%n", this); + return unboundNotified > 0 && session.get() == null; + } + + @Override + public void valueBound(SSLSessionBindingEvent event) { + System.out.printf(" valueBound: %s%n", event.getName()); + } + + @Override + public void valueUnbound(SSLSessionBindingEvent event) { + System.out.printf(" valueUnbound: %s%n", event.getName()); + unboundNotified++; + } + + public String toString() { + return "count: " + unboundNotified + + ", ref: " + session.get(); + } + } +} +
--- a/test/jdk/sun/security/tools/jarsigner/DiffEnd.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/security/tools/jarsigner/DiffEnd.java Wed Jul 24 12:21:36 2019 +0530 @@ -23,10 +23,14 @@ /* * @test - * @bug 6948909 + * @bug 6948909 8217375 * @summary Jarsigner removes MANIFEST.MF info for badly packages jar's * @library /test/lib */ +/* + * See also InsufficientSectionDelimiter.java for similar tests including cases + * without or with different line breaks. + */ import jdk.test.lib.Asserts; import jdk.test.lib.SecurityTools; @@ -44,47 +48,47 @@ public class DiffEnd { static void check() throws Exception { - SecurityTools.jarsigner("-keystore " - + Path.of(System.getProperty("test.src"), "JarSigning.keystore") - .toString() - + " -storepass bbbbbb -digestalg SHA1" - + " -signedjar diffend.new.jar diffend.jar c"); + String ksArgs = "-keystore " + Path.of(System.getProperty("test.src")) + .resolve("JarSigning.keystore") + " -storepass bbbbbb"; - try (JarFile jf = new JarFile("diffend.new.jar")) { + SecurityTools.jarsigner(ksArgs + " -digestalg SHA1 " + + "-signedjar diffend.signed.jar diffend.jar c") + .shouldHaveExitValue(0); + SecurityTools.jarsigner(" -verify " + ksArgs + " -verbose " + + "diffend.signed.jar c") + .stdoutShouldMatch("^smk .* 1$").shouldHaveExitValue(0); + + try (JarFile jf = new JarFile("diffend.signed.jar")) { Asserts.assertTrue(jf.getManifest().getMainAttributes() .containsKey(new Attributes.Name("Today"))); } } public static void main(String[] args) throws Exception { - // A MANIFEST.MF using \n as newlines and no double newlines at the end - byte[] manifest = - ("Manifest-Version: 1.0\n" + byte[] manifest = ("Manifest-Version: 1.0\n" + "Created-By: 1.7.0-internal (Sun Microsystems Inc.)\n" + "Today: Monday\n").getBytes(StandardCharsets.UTF_8); + // Without the fake .RSA file, to trigger the if (wasSigned) else block + try (FileOutputStream fos = new FileOutputStream("diffend.jar"); + ZipOutputStream zos = new ZipOutputStream(fos)) { + zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME)); + zos.write(manifest); + zos.putNextEntry(new ZipEntry("1")); + zos.write(new byte[10]); + } + check(); + // With the fake .RSA file, to trigger the if (wasSigned) block try (FileOutputStream fos = new FileOutputStream("diffend.jar"); ZipOutputStream zos = new ZipOutputStream(fos)) { - zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF")); + zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME)); zos.write(manifest); - zos.putNextEntry(new ZipEntry("META-INF/x.RSA")); + zos.putNextEntry(new ZipEntry("META-INF/x.RSA")); // fake .RSA zos.putNextEntry(new ZipEntry("1")); zos.write(new byte[10]); } - - check(); - - // Without the fake .RSA file, to trigger the else block - try (FileOutputStream fos = new FileOutputStream("diffend.jar"); - ZipOutputStream zos = new ZipOutputStream(fos)) { - zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF")); - zos.write(manifest); - zos.putNextEntry(new ZipEntry("1")); - zos.write(new byte[10]); - } - check(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/DigestDontIgnoreCase.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Map; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.JarEntry; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @run testng DigestDontIgnoreCase + * @summary Check that existing manifest digest entries are taken for valid + * only if they match the actual digest value also taking upper and lower + * case of the base64 encoded form of the digests into account. + */ +/* + * <pre>mfDigest.equalsIgnoreCase(base64Digests[i])</pre> + * previously in JarSigner.java on on line 985 + * @see jdk.security.jarsigner.JarSigner#updateDigests + */ +public class DigestDontIgnoreCase { + + static final String KEYSTORE_FILENAME = "test.jks"; + + static final String DUMMY_FILE1 = "dummy1.txt"; + static final byte[] DUMMY_CONTENTS1 = DUMMY_FILE1.getBytes(UTF_8); + static final String DUMMY_FILE2 = "dummy2.txt"; + static final byte[] DUMMY_CONTENTS2 = DUMMY_FILE2.getBytes(UTF_8); + + byte[] goodSignedManifest; + + @BeforeClass + public void prepareCertificate() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg DSA -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=X").shouldHaveExitValue(0); + } + + void prepareJarFile(String filename, Map<String, byte[]> contents) + throws IOException { + try (OutputStream out = Files.newOutputStream(Path.of(filename)); + JarOutputStream jos = new JarOutputStream(out)) { + for (Map.Entry<String, byte[]> entry : contents.entrySet()) { + JarEntry je = new JarEntry(entry.getKey()); + jos.putNextEntry(je); + jos.write(entry.getValue()); + jos.closeEntry(); + } + } + } + + @BeforeClass(dependsOnMethods = "prepareCertificate") + public void prepareGoodSignedManifest() throws Exception { + String filename = "prepare.jar"; + prepareJarFile(filename, Map.of(DUMMY_FILE1, DUMMY_CONTENTS1)); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + filename + " a") + .shouldHaveExitValue(0); + goodSignedManifest = Utils.readJarManifestBytes(filename); + Utils.echoManifest(goodSignedManifest, + "reference manifest with one file signed"); + } + + void testWithManifest(String filename, byte[] manifestBytes) + throws Exception { + Utils.echoManifest(manifestBytes, + "going to test " + filename + " with manifest"); + prepareJarFile(filename, Map.of( + JarFile.MANIFEST_NAME, manifestBytes, + DUMMY_FILE1, DUMMY_CONTENTS1, // with digest already in manifest + DUMMY_FILE2, DUMMY_CONTENTS2)); // causes manifest update + Utils.echoManifest(Utils.readJarManifestBytes(filename), + filename + " created with manifest"); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + filename + " a") + .shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes(filename), + filename + " signed resulting in manifest"); + SecurityTools.jarsigner("-verify -strict -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -debug -verbose " + + filename + " a").shouldHaveExitValue(0); + } + + @Test + public void verifyDigestGoodCase() throws Exception { + testWithManifest("good.jar", goodSignedManifest); + } + + @Test + public void testDigestHeaderNameCase() throws Exception { + byte[] mfBadHeader = new String(goodSignedManifest, UTF_8). + replace("SHA-256-Digest", "sha-256-dIGEST").getBytes(UTF_8); + testWithManifest("switch-header-name-case.jar", mfBadHeader); + } + + @Test + public void testDigestWrongCase() throws Exception { + byte[] mfBadDigest = switchCase(goodSignedManifest, "Digest"); + testWithManifest("switch-digest-case.jar", mfBadDigest); + } + + byte[] switchCase(byte[] manifest, String attrName) { + byte[] wrongCase = Arrays.copyOf(manifest, manifest.length); + byte[] name = (attrName + ":").getBytes(UTF_8); + int matched = 0; // number of bytes before position i matching attrName + for (int i = 0; i < wrongCase.length; i++) { + if (wrongCase[i] == '\r' && + (i == wrongCase.length - 1 || wrongCase[i + 1] == '\n')) { + continue; + } else if ((wrongCase[i] == '\r' || wrongCase[i] == '\n') + && (i == wrongCase.length - 1 || wrongCase[i + 1] != ' ')) { + matched = 0; + } else if (matched == name.length) { + wrongCase[i] = switchCase(wrongCase[i]); + } else if (name[matched] == wrongCase[i]) { + matched++; + } else { + matched = 0; + } + } + return wrongCase; + } + + byte switchCase(byte c) { + if (c >= 'A' && c <= 'Z') { + return (byte) ('a' + (c - 'A')); + } else if (c >= 'a' && c <= 'z') { + return (byte) ('A' + (c - 'a')); + } else { + return c; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/EmptyIndividualSectionName.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; + +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; + +import static org.testng.Assert.*; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @modules java.base/java.util.jar:+open + * @run testng/othervm EmptyIndividualSectionName + * @summary Check that an individual section with an empty name is digested + * and signed. + * <p> + * See also + * jdk/test/jdk/sun/security/util/ManifestDigester/FindSections.java + * for much more detailed api level tests + */ +public class EmptyIndividualSectionName { + + static final String KEYSTORE_FILENAME = "test.jks"; + + @BeforeClass + public void prepareCertificate() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg EC -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit " + + "-alias a -dname CN=X").shouldHaveExitValue(0); + } + + /** + * Adds an additional section with name {@code sectionName} to the manifest + * of a JAR before signing it with {@code signOpts}. + * @return signature file {@code META-INF/A.SF} for further assertions + */ + Manifest test(String sectionName, String signOpts) throws Exception { + Manifest mf = new Manifest(); + mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); + mf.getEntries().put(sectionName, new Attributes()); + String jarFilename = "test" + sectionName + + (signOpts != null ? signOpts : "") + ".jar"; + JarUtils.createJarFile(Path.of(jarFilename), mf, Path.of(".")); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + + (signOpts != null ? signOpts + " " : "") + jarFilename + " a") + .shouldHaveExitValue(0); + SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilename + " a") + .shouldHaveExitValue(0); + + byte[] mfBytes = Utils.readJarManifestBytes(jarFilename); + Utils.echoManifest(mfBytes, "manifest"); + mf = new Manifest(new ByteArrayInputStream(mfBytes)); + assertNotNull(mf.getAttributes(sectionName)); + byte[] sfBytes = Utils.readJarEntryBytes(jarFilename, "META-INF/A.SF"); + Utils.echoManifest(sfBytes, "signature file META-INF/A.SF"); + return new Manifest(new ByteArrayInputStream(sfBytes)); + } + + /** + * Verifies that it makes a difference if the name is empty or not + * by running the same test as {@link #testNameEmpty} with only a different + * section name. + */ + @Test + public void testNameNotEmpty() throws Exception { + String sectionName = "X"; + assertNotNull(test(sectionName, null).getAttributes(sectionName)); + } + + /** + * Verifies that individual sections are digested and signed also if the + * name of such a section is empty. + * An empty name of an individual section cannot be tested by adding a file + * with an empty name to a JAR because such a file name is invalid and + * cannot be used to add a file because it cannot be created or added to + * the JAR file in the first place. However, an individual section with an + * empty name can be added to the manifest. + * Expected is a corresponding digest in the signature file which was not + * present or produced before resolution of bug 8217375. + */ + @Test + public void testNameEmpty() throws Exception { + String sectionName = ""; + assertNotNull(test(sectionName, null).getAttributes(sectionName)); + } + + /** + * Similar to {@link #testNameEmpty} but tries to show a real difference + * rather than just some internals in a {@code .SF} file, but TODO + */ + @Test(enabled = false, description = "TODO") + public void testNameEmptyTrusted() throws Exception { + String sectionName = ""; + test(sectionName, "-sectionsonly"); + String jarFilename = "test" + sectionName + "-sectionsonly.jar"; + try (JarFile jar = new JarFile(jarFilename, true)) { + Manifest m = jar.getManifest(); + Method getTrustedAttributes = m.getClass() + .getDeclaredMethod("getTrustedAttributes", String.class); + getTrustedAttributes.setAccessible(true); + assertThrows(SecurityException.class, () -> + getTrustedAttributes.invoke(m, sectionName)); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/EmptyJar.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.nio.file.Path; +import java.util.jar.Manifest; +import java.util.jar.Attributes.Name; + +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8217375 + * @modules java.base/sun.security.util + * @library /test/lib /lib/testlibrary + * @run testng EmptyJar + * @summary Checks that signing an empty jar file does not result in an NPE or + * other error condition. + */ +public class EmptyJar { + + static final String KEYSTORE_FILENAME = "test.jks"; + + @BeforeClass + public void prepareKeyStore() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg EC -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=A").shouldHaveExitValue(0); + } + + @Test + public void test() throws Exception { + String jarFilename = "test.jar"; + JarUtils.createJarFile(Path.of(jarFilename), (Manifest) null, + Path.of(".")); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename + " a") + .shouldHaveExitValue(0); + + // verify that jarsigner has added a default manifest + byte[] mfBytes = Utils.readJarManifestBytes(jarFilename); + Utils.echoManifest(mfBytes, "manifest"); + assertTrue(new String(mfBytes, UTF_8).startsWith( + Name.MANIFEST_VERSION + ": 1.0\r\nCreated-By: ")); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/FindHeaderEndVsManifestDigesterFindFirstSection.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; +import sun.security.util.ManifestDigester; + +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8217375 + * @modules java.base/sun.security.util + * @run testng FindHeaderEndVsManifestDigesterFindFirstSection + * @summary Checks that {@link JarSigner#findHeaderEnd} (moved to now + * {@link #findHeaderEnd} in this test) can be replaced with + * {@link ManifestDigester#findSection} + * (first invocation will identify main attributes) + * without making a difference. + */ +/* + * Note to future maintainer: + * While it might look at first glance like this test ensures backwards- + * compatibility between JarSigner.findHeaderEnd and + * ManifestDigester.findSection's first invocation that find the main + * attributes section, at the time of that change, this test continues to + * verify main attributes digestion now with ManifestDigester.findSection as + * opposed to previous implementation in JarSigner.findHeaderEnd. + * Before completely removing this test, make sure that main attributes + * digestion is covered appropriately with tests. After JarSigner.findHeaderEnd + * has been removed digests should still continue to match. + * + * See also + * - jdk/test/jdk/sun/security/tools/jarsigner/PreserveRawManifestEntryAndDigest.java + * for some end-to-end tests utilizing the jarsigner tool, + * - jdk/test/jdk/sun/security/util/ManifestDigester/FindSection.java and + * - jdk/test/jdk/sun/security/util/ManifestDigester/DigestInput.java + * for much more detailed tests at api level + * + * Both test mentioned above, however, originally were created when removing + * confusion of "Manifest-Main-Attributes" individual section with actual main + * attributes whereas the test here is about changes related to raw manifest + * reproduction and in the end test pretty much the same behavior. + */ +public class FindHeaderEndVsManifestDigesterFindFirstSection { + + static final boolean FIXED_8217375 = true; // FIXME + + /** + * from former {@link JarSigner#findHeaderEnd}, subject to verification if + * it can be replaced with {@link ManifestDigester#findSection} + */ + @SuppressWarnings("fallthrough") + private int findHeaderEnd(byte[] bs) { + // Initial state true to deal with empty header + boolean newline = true; // just met a newline + int len = bs.length; + for (int i = 0; i < len; i++) { + switch (bs[i]) { + case '\r': + if (i < len - 1 && bs[i + 1] == '\n') i++; + // fallthrough + case '\n': + if (newline) return i + 1; //+1 to get length + newline = true; + break; + default: + newline = false; + } + } + // If header end is not found, it means the MANIFEST.MF has only + // the main attributes section and it does not end with 2 newlines. + // Returns the whole length so that it can be completely replaced. + return len; + } + + @DataProvider(name = "parameters") + public static Object[][] parameters() { + List<Object[]> tests = new ArrayList<>(); + for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) { + if ("\r".equals(lineBreak) && !FIXED_8217375) continue; + for (int numLBs = 0; numLBs <= 3; numLBs++) { + for (String addSection : new String[] { null, "Ignore" }) { + tests.add(new Object[] { lineBreak, numLBs, addSection }); + } + } + } + return tests.toArray(new Object[tests.size()][]); + } + + @Factory(dataProvider = "parameters") + public static Object[] createTests(String lineBreak, int numLineBreaks, + String individualSectionName) { + return new Object[]{new FindHeaderEndVsManifestDigesterFindFirstSection( + lineBreak, numLineBreaks, individualSectionName + )}; + } + + final String lineBreak; + final int numLineBreaks; // number of line breaks after main attributes + final String individualSectionName; // null means only main attributes + final byte[] rawBytes; + + FindHeaderEndVsManifestDigesterFindFirstSection(String lineBreak, + int numLineBreaks, String individualSectionName) { + this.lineBreak = lineBreak; + this.numLineBreaks = numLineBreaks; + this.individualSectionName = individualSectionName; + + rawBytes = ( + "oldStyle: trailing space " + lineBreak + + "newStyle: no trailing space" + lineBreak.repeat(numLineBreaks) + + // numLineBreaks < 2 will not properly delimit individual section + // but it does not hurt to test that anyway + (individualSectionName != null ? + "Name: " + individualSectionName + lineBreak + + "Ignore: nothing here" + lineBreak + + lineBreak + : "") + ).getBytes(UTF_8); + } + + @BeforeMethod + public void verbose() { + System.out.println("lineBreak = " + stringToIntList(lineBreak)); + System.out.println("numLineBreaks = " + numLineBreaks); + System.out.println("individualSectionName = " + individualSectionName); + } + + @FunctionalInterface + interface Callable { + void call() throws Exception; + } + + void catchNoLineBreakAfterMainAttributes(Callable test) throws Exception { + // manifests cannot be parsed and digested if the main attributes do + // not end in a blank line (double line break) or one line break + // immediately before eof. + boolean failureExpected = numLineBreaks == 0 + && individualSectionName == null; + try { + test.call(); + if (failureExpected) fail("expected an exception"); + } catch (NullPointerException | IllegalStateException e) { + if (!failureExpected) fail("unexpected " + e.getMessage(), e); + } + } + + /** + * Checks that the beginning of the manifest until position<ol> + * <li>{@code Jarsigner.findHeaderEnd} in the previous version + * and</li> + * <li>{@code ManifestDigester.getMainAttsEntry().sections[0]. + * lengthWithBlankLine} in the new version</li> + * </ol>produce the same offset (TODO: or the same error). + * The beginning of the manifest until that offset (range + * <pre>0 .. (offset - 1)</pre>) will be reproduced if the manifest has + * not changed. + * <p> + * Getting {@code startOfNext} of {@link ManifestDigester#findSection}'s + * first invokation returned {@link ManifestDigester.Position} which + * identifies the end offset of the main attributes is difficulted by + * {@link ManifestDigester#findSection} being private and therefore not + * directly accessible. + */ + @Test + public void startOfNextLengthWithBlankLine() throws Exception { + catchNoLineBreakAfterMainAttributes(() -> + assertEquals(lengthWithBlankLine(), findHeaderEnd(rawBytes)) + ); + } + + /** + * Due to its private visibility, + * {@link ManifestDigester.Section#lengthWithBlankLine} is not directly + * accessible. However, calling {@link ManifestDigester.Entry#digest} + * reveals {@code lengthWithBlankLine} as third parameter in + * <pre>md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);</pre> + * on line ManifestDigester.java:212. + * <p> + * This value is the same as {@code startOfNext} of + * {@link ManifestDigester#findSection}'s first invocation returned + * {@link ManifestDigester.Position} identifying the end offset of the + * main attributes because<ol> + * <li>the end offset of the main attributes is assigned to + * {@code startOfNext} in + * <pre>pos.startOfNext = i+1;</pre> in ManifestDigester.java:98</li> + * <li>which is then passed on as the third parameter to the constructor + * of a new {@link ManifestDigester.Section#Section} by + * <pre>new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));</pre> + * in in ManifestDigester.java:128</li> + * <li>where it is assigned to + * {@link ManifestDigester.Section#lengthWithBlankLine} by + * <pre>this.lengthWithBlankLine = lengthWithBlankLine;</pre> + * in ManifestDigester.java:241</li> + * <li>from where it is picked up by {@link ManifestDigester.Entry#digest} + * in + * <pre>md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);</pre> + * in ManifestDigester.java:212</li> + * </ol> + * all of which without any modification. + */ + int lengthWithBlankLine() { + int[] lengthWithBlankLine = new int[] { 0 }; + new ManifestDigester(rawBytes).get(ManifestDigester.MF_MAIN_ATTRS, + false).digest(new MessageDigest("lengthWithBlankLine") { + @Override protected void engineReset() { + lengthWithBlankLine[0] = 0; + } + @Override protected void engineUpdate(byte b) { + lengthWithBlankLine[0]++; + } + @Override protected void engineUpdate(byte[] b, int o, int l) { + lengthWithBlankLine[0] += l; + } + @Override protected byte[] engineDigest() { + return null; + } + }); + return lengthWithBlankLine[0]; + } + + /** + * Checks that the replacement of {@link JarSigner#findHeaderEnd} is + * actually used to reproduce manifest main attributes. + * <p> + * {@link #startOfNextLengthWithBlankLine} demonstrates that + * {@link JarSigner#findHeaderEnd} has been replaced successfully with + * {@link ManifestDigester#findSection} but does not also show that the + * main attributes are reproduced with the same offset as before. + * {@link #startOfNextLengthWithBlankLine} uses + * {@link ManifestDigester.Entry#digest} to demonstrate an equal offset + * calculated but {@link ManifestDigester.Entry#digest} is not necessarily + * the same as reproducing, especially when considering + * {@link ManifestDigester.Entry#oldStyle}. + */ + @Test(enabled = FIXED_8217375) + public void reproduceMainAttributes() throws Exception { + catchNoLineBreakAfterMainAttributes(() -> { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + ManifestDigester md = new ManifestDigester(rawBytes); + // without 8217375 fixed the following line will not even compile + // so just remove it and skip the test for regression + md.getMainAttsEntry().reproduceRaw(buf); // FIXME + + assertEquals(buf.size(), findHeaderEnd(rawBytes)); + }); + } + + static List<Integer> stringToIntList(String string) { + byte[] bytes = string.getBytes(UTF_8); + List<Integer> list = new ArrayList<>(); + for (int i = 0; i < bytes.length; i++) { + list.add((int) bytes[i]); + } + return list; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/InsufficientSectionDelimiter.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.stream.Stream; +import java.util.jar.Attributes.Name; +import java.util.jar.Manifest; +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @run testng InsufficientSectionDelimiter + * @summary Checks some cases signing a jar the manifest of which has no or + * only one line break at the end and no proper delimiting blank line does not + * result in an invalid signed jar without jarsigner noticing and failing. + * + * <p>See also<ul> + * <li>{@link PreserveRawManifestEntryAndDigest} with an update of a signed + * jar with a different signer whereas this test just signs with one signer + * </li> + * <li>{@link WasSignedByOtherSigner} for a test that detects if + * {@code wasSigned} in {@link jdk.security.jarsigner.JarSigner#sign0} was set + * correctly determining whether or not to re-write the manifest, and</li> + * <li>{@code diffend.sh} for another similar test</li></ul> + */ +public class InsufficientSectionDelimiter { + + static final String KEYSTORE_FILENAME = "test.jks"; + + @BeforeTest + public void prepareCertificate() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg EC -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=A").shouldHaveExitValue(0); + } + + @BeforeTest + public void prepareFakeSfFile() throws IOException { + new File("META-INF").mkdir(); + Files.write(Path.of("META-INF/.SF"), ( + Name.SIGNATURE_VERSION + ": 1.0\r\n" + + "-Digest-Manifest: \r\n\r\n").getBytes(UTF_8)); + } + + @DataProvider(name = "parameters") + public static Object[][] parameters() { + return new String[][] { { "" }, { "\n" }, { "\r" }, { "\r\n" } }; + } + + @Factory(dataProvider = "parameters") + public static Object[] createTests(String lineBreak) { + return new Object[] { new InsufficientSectionDelimiter(lineBreak) }; + } + + final String lineBreak; + final String jarFilenameSuffix; + + InsufficientSectionDelimiter(String lineBreak) { + this.lineBreak = lineBreak; + jarFilenameSuffix = Utils.escapeStringWithNumbers(lineBreak); + } + + @BeforeMethod + public void verbose() { + System.out.println("lineBreak = " + + Utils.escapeStringWithNumbers(lineBreak)); + } + + void test(String jarFilenamePrefix, String... files) throws Exception { + String jarFilename = jarFilenamePrefix + jarFilenameSuffix + ".jar"; + JarUtils.createJarFile(Path.of(jarFilename), new Manifest() { + @Override public void write(OutputStream out) throws IOException { + out.write((Name.MANIFEST_VERSION + ": 1.0" + + lineBreak).getBytes(UTF_8)); + } + }, Path.of("."), Stream.of(files).map(Path::of).toArray(Path[]::new) + ); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename), "unsigned jar"); + try { + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename + + " a").shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename), "signed jar"); + } catch (Exception e) { + if (lineBreak.isEmpty()) { + return; // invalid manifest without trailing line break + } + throw e; + } + + // remove META-INF/.SF from signed jar which would not validate + // (not added in all the test cases) + JarUtils.updateJar(jarFilename, "verify-" + jarFilename, + Map.of("META-INF/.SF", false)); + SecurityTools.jarsigner("-verify -strict -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -debug -verbose " + + "verify-" + jarFilename + " a").shouldHaveExitValue(0); + } + + /** + * Test that signing a jar which has never been signed yet and contains + * no signature related files with a manifest that ends immediately after + * the last main attributes value byte or only one line break and no blank + * line produces a valid signed jar or an error if the manifest ends + * without line break. + */ + @Test + public void testOnlyMainAttrs() throws Exception { + test("testOnlyMainAttrs"); + } + + /** + * Test that signing a jar with a manifest that ends immediately after + * the last main attributes value byte or with too few line break + * characters to properly delimit an individual section and has a fake + * signing related file to trigger a signature update or more specifically + * wasSigned in JarSigner.sign0 to become true produces a valid signed jar + * or an error if the manifest ends without line break. + * <p> + * Only one line break and hence no blank line ('\r', '\n', or '\r\n') + * after last main attributes value byte is too little to delimit an + * individual section to hold a file's digest but acceptable if no + * individual section has to be added because no contained file has to be + * signed as is the case in this test. + * + * @see #testMainAttrsWasSignedAddFile + */ + @Test + public void testMainAttrsWasSigned() throws Exception { + test("testMainAttrsWasSigned", "META-INF/.SF"); + } + + /** + * Test that signing a jar with a manifest that ends immediately after + * the last main attributes value byte or with too few line break + * characters to properly delimit an individual section and has a fake + * signing related file to trigger a signature update or more specifically + * wasSigned in JarSigner.sign0 to become true produces no invalid signed + * jar or an error if the manifest ends without line break. + * <p> + * Only one line break and hence no blank line ('\r', '\n', or '\r\n') + * after the last main attributes value byte is too little to delimit an + * individual section which would be required here to save the digest of a + * contained file to be signed. + * <p> + * Changing the delimiters after the main attributes changes the main + * attributes digest but + * {@link SignatureFileVerifier#verifyManifestMainAttrs} and + * {@link ManifestDigester#digestWorkaround} work around it. + */ + @Test + public void testMainAttrsWasSignedAddFile() throws Exception { + Files.write(Path.of("test.txt"), "test.txt".getBytes(UTF_8)); + test("testMainAttrsWasSignedAddFile", "META-INF/.SF", "test.txt"); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/MainAttributesConfused.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import sun.security.util.ManifestDigester; +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8217375 + * @modules java.base/sun.security.util + * @library /test/lib /lib/testlibrary + * @run testng MainAttributesConfused + * @summary Check that manifest individual section "Manifest-Main-Attributes" + * does not interfere and is not confused with ManifestDigester internals. + * + * See also + * jdk/test/jdk/sun/security/util/ManifestDigester/ManifestMainAttributes.java + * for much more detailed api level tests + */ +public class MainAttributesConfused { + + static final String KEYSTORE_FILENAME = "test.jks"; + static final String MAIN_ATTRIBUTES_MARKER = null; + + @BeforeClass + void prepareKeyStore() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg EC -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=X").shouldHaveExitValue(0); + } + + void testAddManifestSection(String sectionName) throws Exception { + // create a signed jar + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); + String testFile = "test-" + sectionName; + Files.write(Path.of(testFile), testFile.getBytes(UTF_8)); + String jarFilename = sectionName + ".jar"; + JarUtils.createJarFile(Path.of(jarFilename), manifest, + Path.of("."), Path.of(testFile)); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename + " a") + .shouldHaveExitValue(0); + + // get the manifest of the signed jar with the signature digests, add + // a new individual section, and write it back + try (JarFile jar = new JarFile(jarFilename)) { + manifest = jar.getManifest(); + } + Attributes attrs = sectionName == MAIN_ATTRIBUTES_MARKER + ? manifest.getMainAttributes() + : manifest.getEntries().computeIfAbsent(sectionName, + n -> new Attributes()); + attrs.put(new Name("Some-Key"), "Some-Value"); + String jarFilenameAttrs = sectionName + "-attrs.jar"; + JarUtils.updateManifest(jarFilename, jarFilenameAttrs, manifest); + + // having just added another manifest entry (individual section) not + // modifying existing digests or main attributes should not invalidate + // the existing signature. + SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilenameAttrs + + " a").shouldHaveExitValue(0); + } + + @Test + public void testAddOtherThanManifestMainAttributes() throws Exception { + // any value but "Manifest-Main-Attributes", even lower case works + testAddManifestSection("manifest-main-attributes"); + } + + @Test + public void testAddMainAttributesHeader() throws Exception { + // adding or changing existing attributes of the main section, however, + // will invalidate the signature + assertThrows(() -> testAddManifestSection(MAIN_ATTRIBUTES_MARKER)); + } + + @Test + public void testAddManifestMainAttributesSection() throws Exception { + testAddManifestSection(ManifestDigester.MF_MAIN_ATTRS); + } + +}
--- a/test/jdk/sun/security/tools/jarsigner/OldSig.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/security/tools/jarsigner/OldSig.java Wed Jul 24 12:21:36 2019 +0530 @@ -23,10 +23,15 @@ /* * @test - * @bug 6543940 6868865 + * @bug 6543940 6868865 8217375 * @summary Exception thrown when signing a jarfile in java 1.5 * @library /test/lib */ +/* + * See also PreserveRawManifestEntryAndDigest.java for tests with arbitrarily + * formatted individual sections in addition the the main attributes tested + * here. + */ import jdk.test.lib.SecurityTools; import jdk.test.lib.util.JarUtils; @@ -44,8 +49,11 @@ JarUtils.updateJarFile(Path.of("B.jar"), Path.of("."), Path.of("B.class")); - SecurityTools.jarsigner("-keystore " + src.resolve("JarSigning.keystore") - + " -storepass bbbbbb -digestalg SHA1 B.jar c"); - SecurityTools.jarsigner("-verify B.jar"); + String ksArgs = "-keystore " + src.resolve("JarSigning.keystore") + + " -storepass bbbbbb"; + SecurityTools.jarsigner(ksArgs + " -digestalg SHA1 B.jar c"); + SecurityTools.jarsigner("-verify B.jar").shouldHaveExitValue(0); + SecurityTools.jarsigner("-verify " + ksArgs + " -verbose B.jar c") + .stdoutShouldMatch("^smk .* B[.]class$").shouldHaveExitValue(0); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/PreserveRawManifestEntryAndDigest.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,1016 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.function.Function; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.Manifest; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.SecurityTools; +import jdk.test.lib.util.JarUtils; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @modules jdk.jartool/sun.security.tools.jarsigner + * @run testng/timeout=1200 PreserveRawManifestEntryAndDigest + * @summary Verifies that JarSigner does not change manifest file entries + * in a binary view if its decoded map view does not change so that an + * unchanged (individual section) entry continues to produce the same digest. + * The same manifest (in terms of {@link Manifest#equals}) could be encoded + * with different line breaks ("{@code \r}", "{@code \n}", or "{@code \r\n}") + * or with arbitrary line break positions (as is also the case with the change + * of the default line width in JDK 11, bug 6372077) resulting in a different + * digest for manifest entries with identical values. + * + * <p>See also:<ul> + * <li>{@code oldsig.sh} and {@code diffend.sh} in + * {@code /test/jdk/sun/security/tools/jarsigner/}</li> + * <li>{@code Compatibility.java} in + * {@code /test/jdk/sun/security/tools/jarsigner/compatibility}</li> + * <li>{@link ReproduceRaw} testing relevant + * {@sun.security.util.ManifestDigester} api in much more detail</li> + * </ul> + */ +/* + * debug with "run testng" += "/othervm -Djava.security.debug=jar" + */ +public class PreserveRawManifestEntryAndDigest { + + static final String KEYSTORE_FILENAME = "test.jks"; + static final String FILENAME_INITIAL_CONTENTS = "initial-contents"; + static final String FILENAME_UPDATED_CONTENTS = "updated-contents"; + + /** + * @see sun.security.tools.jarsigner.Main#run + */ + static final int NOTSIGNEDBYALIASORALIASNOTINSTORE = 32; + + @BeforeTest + public void prepareContentFiles() throws IOException { + Files.write(Path.of(FILENAME_INITIAL_CONTENTS), + FILENAME_INITIAL_CONTENTS.getBytes(UTF_8)); + Files.write(Path.of(FILENAME_UPDATED_CONTENTS), + FILENAME_UPDATED_CONTENTS.getBytes(UTF_8)); + } + + @BeforeTest + public void prepareCertificates() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg DSA -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=A").shouldHaveExitValue(0); + SecurityTools.keytool("-genkeypair -keyalg DSA -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias b -dname CN=B").shouldHaveExitValue(0); + } + + static class TeeOutputStream extends FilterOutputStream { + final OutputStream tee; // don't flush or close + + public TeeOutputStream(OutputStream out, OutputStream tee) { + super(out); + this.tee = tee; + } + + @Override + public void write(int b) throws IOException { + super.write(b); + tee.write(b); + } + } + + /** + * runs jarsigner in its own child process and captures exit code and the + * output of stdout and stderr, as opposed to {@link #karsignerMain} + */ + OutputAnalyzer jarsignerProc(String args) throws Exception { + long start = System.currentTimeMillis(); + try { + return SecurityTools.jarsigner(args); + } finally { + long end = System.currentTimeMillis(); + System.out.println("jarsignerProc duration [ms]: " + (end - start)); + } + } + + /** + * assume non-zero exit code would call System.exit but is faster than + * {@link #jarsignerProc} + */ + void jarsignerMain(String args) throws Exception { + long start = System.currentTimeMillis(); + try { + new sun.security.tools.jarsigner.Main().run(args.split("\\s+")); + } finally { + long end = System.currentTimeMillis(); + System.out.println("jarsignerMain duration [ms]: " + (end - start)); + } + } + + void createSignedJarA(String jarFilename, Manifest manifest, + String additionalJarsignerOptions, String dummyContentsFilename) + throws Exception { + JarUtils.createJarFile(Path.of(jarFilename), manifest, Path.of("."), + dummyContentsFilename == null ? new Path[]{} : + new Path[] { Path.of(dummyContentsFilename) }); + jarsignerMain("-keystore " + KEYSTORE_FILENAME + " -storepass changeit" + + (additionalJarsignerOptions == null ? "" : + " " + additionalJarsignerOptions) + + " -verbose -debug " + jarFilename + " a"); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename), "original signed jar by signer a"); + // check assumption that jar is valid at this point + jarsignerMain("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename + " a"); + } + + void manipulateManifestSignAgainA(String srcJarFilename, String tmpFilename, + String dstJarFilename, String additionalJarsignerOptions, + Function<Manifest, byte[]> manifestManipulation) throws Exception { + Manifest mf; + try (JarFile jar = new JarFile(srcJarFilename)) { + mf = jar.getManifest(); + } + byte[] manipulatedManifest = manifestManipulation.apply(mf); + Utils.echoManifest(manipulatedManifest, "manipulated manifest"); + JarUtils.updateJar(srcJarFilename, tmpFilename, Map.of( + JarFile.MANIFEST_NAME, manipulatedManifest, + // add a fake sig-related file to trigger wasSigned in JarSigner + "META-INF/.SF", Name.SIGNATURE_VERSION + ": 1.0\r\n")); + jarsignerMain("-keystore " + KEYSTORE_FILENAME + " -storepass changeit" + + (additionalJarsignerOptions == null ? "" : + " " + additionalJarsignerOptions) + + " -verbose -debug " + tmpFilename + " a"); + // remove META-INF/.SF from signed jar again which would not validate + JarUtils.updateJar(tmpFilename, dstJarFilename, + Map.of("META-INF/.SF", false)); + + Utils.echoManifest(Utils.readJarManifestBytes( + dstJarFilename), "manipulated jar signed again with a"); + // check assumption that jar is valid at this point + jarsignerMain("-verify -keystore " + KEYSTORE_FILENAME + " " + + "-storepass changeit -verbose -debug " + dstJarFilename + " a"); + } + + OutputAnalyzer signB(String jarFilename, String additionalJarsignerOptions, + int updateExitCodeVerifyA) throws Exception { + jarsignerMain("-keystore " + KEYSTORE_FILENAME + " -storepass changeit" + + (additionalJarsignerOptions == null ? "" : + " " + additionalJarsignerOptions) + + " -verbose -debug " + jarFilename + " b"); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename), "signed again with signer b"); + // check assumption that jar is valid at this point with any alias + jarsignerMain("-verify -strict -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilename); + // check assumption that jar is valid at this point with b just signed + jarsignerMain("-verify -strict -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilename + " b"); + // return result of verification of signature by a before update + return jarsignerProc("-verify -strict " + "-keystore " + + KEYSTORE_FILENAME + " -storepass changeit " + "-debug " + + "-verbose " + jarFilename + " a") + .shouldHaveExitValue(updateExitCodeVerifyA); + } + + String[] fromFirstToSecondEmptyLine(String[] lines) { + int from = 0; + for (int i = 0; i < lines.length; i++) { + if ("".equals(lines[i])) { + from = i + 1; + break; + } + } + + int to = lines.length - 1; + for (int i = from; i < lines.length; i++) { + if ("".equals(lines[i])) { + to = i - 1; + break; + } + } + + return Arrays.copyOfRange(lines, from, to + 1); + } + + /** + * @see "concise_jarsigner.sh" + */ + String[] getExpectedJarSignerOutputUpdatedContentNotValidatedBySignerA( + String jarFilename, String digestalg, + String firstAddedFilename, String secondAddedFilename) { + final String TS = ".{28,29}"; // matches a timestamp + List<String> expLines = new ArrayList<>(); + expLines.add("s k *\\d+ " + TS + " META-INF/MANIFEST[.]MF"); + expLines.add(" *\\d+ " + TS + " META-INF/B[.]SF"); + expLines.add(" *\\d+ " + TS + " META-INF/B[.]DSA"); + expLines.add(" *\\d+ " + TS + " META-INF/A[.]SF"); + expLines.add(" *\\d+ " + TS + " META-INF/A[.]DSA"); + if (firstAddedFilename != null) { + expLines.add("smk *\\d+ " + TS + " " + firstAddedFilename); + } + if (secondAddedFilename != null) { + expLines.add("smkX *\\d+ " + TS + " " + secondAddedFilename); + } + return expLines.toArray(new String[expLines.size()]); + } + + void assertMatchByLines(String[] actLines, String[] expLines) { + for (int i = 0; i < actLines.length && i < expLines.length; i++) { + String actLine = actLines[i]; + String expLine = expLines[i]; + assertTrue(actLine.matches("^" + expLine + "$"), + "\"" + actLine + "\" should have matched \"" + expLine + "\""); + } + assertEquals(actLines.length, expLines.length); + } + + String test(String name, Function<Manifest, byte[]> mm) throws Exception { + return test(name, FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS, + mm); + } + + String test(String name, + String firstAddedFilename, String secondAddedFilename, + Function<Manifest, byte[]> mm) throws Exception { + return test(name, firstAddedFilename, secondAddedFilename, mm, null, + true, true); + } + + /** + * Essentially, creates a first signed JAR file with a single contained + * file or without and a manipulation applied to its manifest signed by + * signer a and then signes it again with a different signer b. + * The jar file is signed twice with signer a in order to make the digests + * available to the manipulation function that might use it. + * + * @param name Prefix for the JAR filenames used throughout the test. + * @param firstAddedFilename Name of a file to add before the first + * signature by signer a or null. The name will also become the contents + * if not null. + * @param secondAddedFilename Name of a file to add after the first + * signature by signer a and before the second signature by signer b or + * null. The name will also become the contents if not null. + * @param manifestManipulation A callback hook to manipulate the manifest + * after the first signature by signer a and before the second signature by + * signer b. + * @param digestalg The digest algorithm name to be used or null for + * default. + * @param assertMainAttrsDigestsUnchanged Assert that the + * manifest main attributes digests have not changed. In any case the test + * also checks that the digests are still valid whether changed or not + * by {@code jarsigner -verify} which might use + * {@link ManifestDigester.Entry#digestWorkaround} + * @param assertFirstAddedFileDigestsUnchanged Assert that the + * digest of the file firstAddedFilename has not changed with the second + * signature. In any case the test checks that the digests are valid whether + * changed or not by {@code jarsigner -verify} which might use + * {@link ManifestDigester.Entry#digestWorkaround} + * @return The name of the resulting JAR file that has passed the common + * assertions ready for further examination + */ + String test(String name, + String firstAddedFilename, String secondAddedFilename, + Function<Manifest, byte[]> manifestManipulation, + String digestalg, boolean assertMainAttrsDigestsUnchanged, + boolean assertFirstAddedFileDigestsUnchanged) + throws Exception { + String digOpts = (digestalg != null ? "-digestalg " + digestalg : ""); + String jarFilename1 = "test-" + name + "-step1.jar"; + createSignedJarA(jarFilename1, + /* no manifest will let jarsigner create a default one */ null, + digOpts, firstAddedFilename); + + // manipulate the manifest, write it back, and sign the jar again with + // the same certificate a as before overwriting the first signature + String jarFilename2 = "test-" + name + "-step2.jar"; + String jarFilename3 = "test-" + name + "-step3.jar"; + manipulateManifestSignAgainA(jarFilename1, jarFilename2, jarFilename3, + digOpts, manifestManipulation); + + // add another file, sign it with the other certificate, and verify it + String jarFilename4 = "test-" + name + "-step4.jar"; + JarUtils.updateJar(jarFilename3, jarFilename4, + secondAddedFilename != null ? + Map.of(secondAddedFilename, secondAddedFilename) + : Collections.EMPTY_MAP); + OutputAnalyzer o = signB(jarFilename4, digOpts, + secondAddedFilename != null ? NOTSIGNEDBYALIASORALIASNOTINSTORE : 0); + // check that secondAddedFilename is the only entry which is not signed + // by signer with alias "a" unless secondAddedFilename is null + assertMatchByLines( + fromFirstToSecondEmptyLine(o.getStdout().split("\\R")), + getExpectedJarSignerOutputUpdatedContentNotValidatedBySignerA( + jarFilename4, digestalg, + firstAddedFilename, secondAddedFilename)); + + // double-check reading the files with a verifying JarFile + try (JarFile jar = new JarFile(jarFilename4, true)) { + if (firstAddedFilename != null) { + JarEntry je1 = jar.getJarEntry(firstAddedFilename); + jar.getInputStream(je1).readAllBytes(); + assertTrue(je1.getCodeSigners().length > 0); + } + if (secondAddedFilename != null) { + JarEntry je2 = jar.getJarEntry(secondAddedFilename); + jar.getInputStream(je2).readAllBytes(); + assertTrue(je2.getCodeSigners().length > 0); + } + } + + // assert that the signature of firstAddedFilename signed by signer + // with alias "a" is not lost and its digest remains the same + try (ZipFile zip = new ZipFile(jarFilename4)) { + ZipEntry ea = zip.getEntry("META-INF/A.SF"); + Manifest sfa = new Manifest(zip.getInputStream(ea)); + ZipEntry eb = zip.getEntry("META-INF/B.SF"); + Manifest sfb = new Manifest(zip.getInputStream(eb)); + if (assertMainAttrsDigestsUnchanged) { + String mainAttrsDigKey = + (digestalg != null ? digestalg : "SHA-256") + + "-Digest-Manifest-Main-Attributes"; + assertEquals(sfa.getMainAttributes().getValue(mainAttrsDigKey), + sfb.getMainAttributes().getValue(mainAttrsDigKey)); + } + if (assertFirstAddedFileDigestsUnchanged) { + assertEquals(sfa.getAttributes(firstAddedFilename), + sfb.getAttributes(firstAddedFilename)); + } + } + + return jarFilename4; + } + + /** + * Test that signing a jar with manifest entries with arbitrary line break + * positions in individual section headers does not destroy an existing + * signature<ol> + * <li>create two self-signed certificates</li> + * <li>sign a jar with at least one non-META-INF file in it with a JDK + * before 11 or place line breaks not at 72 bytes in an individual section + * header</li> + * <li>add a new file to the jar</li> + * <li>sign the jar with a JDK 11, 12, or 13 with bug 8217375 not yet + * resolved with a different signer</li> + * </ol>→ first signature will not validate anymore even though it + * should. + */ + @Test + public void arbitraryLineBreaksSectionName() throws Exception { + test("arbitraryLineBreaksSectionName", m -> { + return ( + Name.MANIFEST_VERSION + ": 1.0\r\n" + + "Created-By: " + + m.getMainAttributes().getValue("Created-By") + "\r\n" + + "\r\n" + + "Name: Test\r\n" + + " -\r\n" + + " Section\r\n" + + "Key: Value \r\n" + + "\r\n" + + "Name: " + FILENAME_INITIAL_CONTENTS.substring(0, 1) + "\r\n" + + " " + FILENAME_INITIAL_CONTENTS.substring(1, 8) + "\r\n" + + " " + FILENAME_INITIAL_CONTENTS.substring(8) + "\r\n" + + "SHA-256-Digest: " + m.getAttributes(FILENAME_INITIAL_CONTENTS) + .getValue("SHA-256-Digest") + "\r\n" + + "\r\n" + ).getBytes(UTF_8); + }); + } + + /** + * Test that signing a jar with manifest entries with arbitrary line break + * positions in individual section headers does not destroy an existing + * signature<ol> + * <li>create two self-signed certificates</li> + * <li>sign a jar with at least one non-META-INF file in it with a JDK + * before 11 or place line breaks not at 72 bytes in an individual section + * header</li> + * <li>add a new file to the jar</li> + * <li>sign the jar with a JDK 11 or 12 with a different signer</li> + * </ol>→ first signature will not validate anymore even though it + * should. + */ + @Test + public void arbitraryLineBreaksHeader() throws Exception { + test("arbitraryLineBreaksHeader", m -> { + String digest = m.getAttributes(FILENAME_INITIAL_CONTENTS) + .getValue("SHA-256-Digest"); + return ( + Name.MANIFEST_VERSION + ": 1.0\r\n" + + "Created-By: " + + m.getMainAttributes().getValue("Created-By") + "\r\n" + + "\r\n" + + "Name: Test-Section\r\n" + + "Key: Value \r\n" + + " with\r\n" + + " strange \r\n" + + " line breaks.\r\n" + + "\r\n" + + "Name: " + FILENAME_INITIAL_CONTENTS + "\r\n" + + "SHA-256-Digest: " + digest.substring(0, 11) + "\r\n" + + " " + digest.substring(11) + "\r\n" + + "\r\n" + ).getBytes(UTF_8); + }); + } + + /** + * Breaks {@code line} at 70 bytes even though the name says 72 but when + * also counting the line delimiter ("{@code \r\n}") the line totals to 72 + * bytes. + * Borrowed from {@link Manifest#make72Safe} before JDK 11 + * + * @see Manifest#make72Safe + */ + static void make72Safe(StringBuffer line) { + int length = line.length(); + if (length > 72) { + int index = 70; + while (index < length - 2) { + line.insert(index, "\r\n "); + index += 72; + length += 3; + } + } + return; + } + + /** + * Test that signing a jar with manifest entries with line breaks at + * position where Manifest would not place them now anymore (72 instead of + * 70 bytes after line starts) does not destroy an existing signature<ol> + * <li>create two self-signed certificates</li> + * <li>simulate a manifest as it would have been written by a JDK before 11 + * by re-positioning line breaks at 70 bytes (which makes a difference by + * digests that grow headers longer than 70 characters such as SHA-512 as + * opposed to default SHA-256, long file names, or manual editing)</li> + * <li>add a new file to the jar</li> + * <li>sign the jar with a JDK 11 or 12 with a different signer</li> + * </ol><p>→ + * The first signature will not validate anymore even though it should. + */ + public void lineWidth70(String name, String digestalg) throws Exception { + Files.write(Path.of(name), name.getBytes(UTF_8)); + test(name, name, FILENAME_UPDATED_CONTENTS, m -> { + // force a line break with a header exceeding line width limit + m.getEntries().put("Test-Section", new Attributes()); + m.getAttributes("Test-Section").put( + Name.IMPLEMENTATION_VERSION, "1" + "0".repeat(100)); + + StringBuilder sb = new StringBuilder(); + StringBuffer[] buf = new StringBuffer[] { null }; + manifestToString(m).lines().forEach(line -> { + if (line.startsWith(" ")) { + buf[0].append(line.substring(1)); + } else { + if (buf[0] != null) { + make72Safe(buf[0]); + sb.append(buf[0].toString()); + sb.append("\r\n"); + } + buf[0] = new StringBuffer(); + buf[0].append(line); + } + }); + make72Safe(buf[0]); + sb.append(buf[0].toString()); + sb.append("\r\n"); + return sb.toString().getBytes(UTF_8); + }, digestalg, false, false); + } + + @Test + public void lineWidth70Filename() throws Exception { + lineWidth70( + "lineWidth70".repeat(6) /* 73 chars total with "Name: " */, null); + } + + @Test + public void lineWidth70Digest() throws Exception { + lineWidth70("lineWidth70digest", "SHA-512"); + } + + /** + * Test that signing a jar with a manifest with line delimiter other than + * "{@code \r\n}" does not destroy an existing signature<ol> + * <li>create two self-signed certificates</li> + * <li>sign a jar with at least one non-META-INF file in it</li> + * <li>extract the manifest, and change its line delimiters + * (for example dos2unix)</li> + * <li>update the jar with the updated manifest</li> + * <li>sign it again with the same signer as before</li> + * <li>add a new file to the jar</li> + * <li>sign the jar with a JDK before 13 with a different signer<li> + * </ol><p>→ + * The first signature will not validate anymore even though it should. + */ + public void lineBreak(String lineBreak) throws Exception { + test("lineBreak" + byteArrayToIntList(lineBreak.getBytes(UTF_8)).stream + ().map(i -> "" + i).collect(Collectors.joining("")), m -> { + StringBuilder sb = new StringBuilder(); + manifestToString(m).lines().forEach(l -> { + sb.append(l); + sb.append(lineBreak); + }); + return sb.toString().getBytes(UTF_8); + }); + } + + @Test + public void lineBreakCr() throws Exception { + lineBreak("\r"); + } + + @Test + public void lineBreakLf() throws Exception { + lineBreak("\n"); + } + + @Test + public void lineBreakCrLf() throws Exception { + lineBreak("\r\n"); + } + + @Test + public void testAdjacentRepeatedSection() throws Exception { + test("adjacent", m -> { + return (manifestToString(m) + + "Name: " + FILENAME_INITIAL_CONTENTS + "\r\n" + + "Foo: Bar\r\n" + + "\r\n" + ).getBytes(UTF_8); + }); + } + + @Test + public void testIntermittentRepeatedSection() throws Exception { + test("intermittent", m -> { + return (manifestToString(m) + + "Name: don't know\r\n" + + "Foo: Bar\r\n" + + "\r\n" + + "Name: " + FILENAME_INITIAL_CONTENTS + "\r\n" + + "Foo: Bar\r\n" + + "\r\n" + ).getBytes(UTF_8); + }); + } + + @Test + public void testNameImmediatelyContinued() throws Exception { + test("testNameImmediatelyContinued", m -> { + // places a continuation line break and space at the first allowed + // position after ": " and before the first character of the value + return (manifestToString(m).replaceAll(FILENAME_INITIAL_CONTENTS, + "\r\n " + FILENAME_INITIAL_CONTENTS + "\r\nFoo: Bar") + ).getBytes(UTF_8); + }); + } + + /* + * "malicious" '\r' after continuation line continued + */ + @Test + public void testNameContinuedContinuedWithCr() throws Exception { + test("testNameContinuedContinuedWithCr", m -> { + return (manifestToString(m).replaceAll(FILENAME_INITIAL_CONTENTS, + FILENAME_INITIAL_CONTENTS.substring(0, 1) + "\r\n " + + FILENAME_INITIAL_CONTENTS.substring(1, 4) + "\r " + + FILENAME_INITIAL_CONTENTS.substring(4) + "\r\n" + + "Foo: Bar") + ).getBytes(UTF_8); + }); + } + + /* + * "malicious" '\r' after continued continuation line + */ + @Test + public void testNameContinuedContinuedEndingWithCr() throws Exception { + test("testNameContinuedContinuedEndingWithCr", m -> { + return (manifestToString(m).replaceAll(FILENAME_INITIAL_CONTENTS, + FILENAME_INITIAL_CONTENTS.substring(0, 1) + "\r\n " + + FILENAME_INITIAL_CONTENTS.substring(1, 4) + "\r\n " + + FILENAME_INITIAL_CONTENTS.substring(4) + "\r" + // no '\n' + "Foo: Bar") + ).getBytes(UTF_8); + }); + } + + @DataProvider(name = "trailingSeqParams", parallel = true) + public static Object[][] trailingSeqParams() { + return new Object[][] { + {""}, + {"\r"}, + {"\n"}, + {"\r\n"}, + {"\r\r"}, + {"\n\n"}, + {"\n\r"}, + {"\r\r\r"}, + {"\r\r\n"}, + {"\r\n\r"}, + {"\r\n\n"}, + {"\n\r\r"}, + {"\n\r\n"}, + {"\n\n\r"}, + {"\n\n\n"}, + {"\r\r\r\n"}, + {"\r\r\n\r"}, + {"\r\r\n\n"}, + {"\r\n\r\r"}, + {"\r\n\r\n"}, + {"\r\n\n\r"}, + {"\r\n\n\n"}, + {"\n\r\r\n"}, + {"\n\r\n\r"}, + {"\n\r\n\n"}, + {"\n\n\r\n"}, + {"\r\r\n\r\n"}, + {"\r\n\r\r\n"}, + {"\r\n\r\n\r"}, + {"\r\n\r\n\n"}, + {"\r\n\n\r\n"}, + {"\n\r\n\r\n"}, + {"\r\n\r\n\r\n"}, + {"\r\n\r\n\r\n\r\n"} + }; + } + + boolean isSufficientSectionDelimiter(String trailingSeq) { + if (trailingSeq.length() < 2) return false; + if (trailingSeq.startsWith("\r\n")) { + trailingSeq = trailingSeq.substring(2); + } else if (trailingSeq.startsWith("\r") || + trailingSeq.startsWith("\n")) { + trailingSeq = trailingSeq.substring(1); + } else { + return false; + } + if (trailingSeq.startsWith("\r\n")) { + return true; + } else if (trailingSeq.startsWith("\r") || + trailingSeq.startsWith("\n")) { + return true; + } + return false; + } + + Function<Manifest, byte[]> replaceTrailingLineBreaksManipulation( + String trailingSeq) { + return m -> { + StringBuilder sb = new StringBuilder(manifestToString(m)); + // cut off default trailing line break characters + while ("\r\n".contains(sb.substring(sb.length() - 1))) { + sb.deleteCharAt(sb.length() - 1); + } + // and instead add another trailing sequence + sb.append(trailingSeq); + return sb.toString().getBytes(UTF_8); + }; + } + + boolean abSigFilesEqual(String jarFilename, + Function<Manifest,Object> getter) throws IOException { + try (ZipFile zip = new ZipFile(jarFilename)) { + ZipEntry ea = zip.getEntry("META-INF/A.SF"); + Manifest sfa = new Manifest(zip.getInputStream(ea)); + ZipEntry eb = zip.getEntry("META-INF/B.SF"); + Manifest sfb = new Manifest(zip.getInputStream(eb)); + return getter.apply(sfa).equals(getter.apply(sfb)); + } + } + + /** + * Create a signed JAR file with a strange sequence of line breaks after + * the main attributes and no individual section and hence no file contained + * within the JAR file in order not to produce an individual section, + * then add no other file and sign it with a different signer. + * The manifest is not expected to be changed during the second signature. + */ + @Test(dataProvider = "trailingSeqParams") + public void emptyJarTrailingSeq(String trailingSeq) throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + if (trailingSeq.isEmpty()) { + return; // invalid manifest without trailing line break + } + + test("emptyJarTrailingSeq" + trailingSeqEscaped, null, null, + replaceTrailingLineBreaksManipulation(trailingSeq)); + + // test called above already asserts by default that the main attributes + // digests have not changed. + } + + /** + * Create a signed JAR file with a strange sequence of line breaks after + * the main attributes and no individual section and hence no file contained + * within the JAR file in order not to produce an individual section, + * then add another file and sign it with a different signer so that the + * originally trailing sequence after the main attributes might have to be + * completed to a full section delimiter or reproduced only partially + * before the new individual section with the added file digest can be + * appended. The main attributes digests are expected to change if the + * first signed trailing sequence did not contain a blank line and are not + * expected to change if superfluous parts of the trailing sequence were + * not reproduced. All digests are expected to validate either with digest + * or with digestWorkaround. + */ + @Test(dataProvider = "trailingSeqParams") + public void emptyJarTrailingSeqAddFile(String trailingSeq) throws Exception{ + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + if (!isSufficientSectionDelimiter(trailingSeq)) { + return; // invalid manifest without trailing blank line + } + boolean expectUnchangedDigests = + isSufficientSectionDelimiter(trailingSeq); + System.out.println("expectUnchangedDigests = " + expectUnchangedDigests); + String jarFilename = test("emptyJarTrailingSeqAddFile" + + trailingSeqEscaped, null, FILENAME_UPDATED_CONTENTS, + replaceTrailingLineBreaksManipulation(trailingSeq), + null, expectUnchangedDigests, false); + + // Check that the digests have changed only if another line break had + // to be added before a new individual section. That both also are valid + // with either digest or digestWorkaround has been checked by test + // before. + assertEquals(abSigFilesEqual(jarFilename, sf -> sf.getMainAttributes() + .getValue("SHA-256-Digest-Manifest-Main-Attributes")), + expectUnchangedDigests); + } + + /** + * Create a signed JAR file with a strange sequence of line breaks after + * the only individual section holding the digest of the only file contained + * within the JAR file, + * then add no other file and sign it with a different signer. + * The manifest is expected to be changed during the second signature only + * by removing superfluous line break characters which are not digested + * and the manifest entry digest is expected not to change. + * The individual section is expected to be reproduced without additional + * line breaks even if the trailing sequence does not properly delimit + * another section. + */ + @Test(dataProvider = "trailingSeqParams") + public void singleIndividualSectionTrailingSeq(String trailingSeq) + throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + if (trailingSeq.isEmpty()) { + return; // invalid manifest without trailing line break + } + String jarFilename = test("singleIndividualSectionTrailingSeq" + + trailingSeqEscaped, FILENAME_INITIAL_CONTENTS, null, + replaceTrailingLineBreaksManipulation(trailingSeq)); + + assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes( + FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest"))); + } + + /** + * Create a signed JAR file with a strange sequence of line breaks after + * the first individual section holding the digest of the only file + * contained within the JAR file and a second individual section with the + * same name to be both digested into the same entry digest, + * then add no other file and sign it with a different signer. + * The manifest is expected to be changed during the second signature + * by removing superfluous line break characters which are not digested + * anyway or if the trailingSeq is not a sufficient delimiter that both + * intially provided sections are treated as only one which is maybe not + * perfect but does at least not result in an invalid signed jar file. + */ + @Test(dataProvider = "trailingSeqParams") + public void firstIndividualSectionTrailingSeq(String trailingSeq) + throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + String jarFilename; + jarFilename = test("firstIndividualSectionTrailingSeq" + + trailingSeqEscaped, FILENAME_INITIAL_CONTENTS, null, m -> { + StringBuilder sb = new StringBuilder(manifestToString(m)); + // cut off default trailing line break characters + while ("\r\n".contains(sb.substring(sb.length() - 1))) { + sb.deleteCharAt(sb.length() - 1); + } + // and instead add another trailing sequence + sb.append(trailingSeq); + // now add another section with the same name assuming sb + // already contains one entry for FILENAME_INITIAL_CONTENTS + sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n"); + sb.append("Foo: Bar\r\n"); + sb.append("\r\n"); + return sb.toString().getBytes(UTF_8); + }); + + assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes( + FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest"))); + } + + /** + * Create a signed JAR file with two individual sections for the same + * contained file (corresponding by name) the first of which properly + * delimited and the second of which followed by a strange sequence of + * line breaks both digested into the same entry digest, + * then add no other file and sign it with a different signer. + * The manifest is expected to be changed during the second signature + * by removing superfluous line break characters which are not digested + * anyway. + */ + @Test(dataProvider = "trailingSeqParams") + public void secondIndividualSectionTrailingSeq(String trailingSeq) + throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + String jarFilename = test("secondIndividualSectionTrailingSeq" + + trailingSeqEscaped, FILENAME_INITIAL_CONTENTS, null, m -> { + StringBuilder sb = new StringBuilder(manifestToString(m)); + sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n"); + sb.append("Foo: Bar"); + sb.append(trailingSeq); + return sb.toString().getBytes(UTF_8); + }); + + assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes( + FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest"))); + } + + /** + * Create a signed JAR file with a strange sequence of line breaks after + * the only individual section holding the digest of the only file contained + * within the JAR file, + * then add another file and sign it with a different signer. + * The manifest is expected to be changed during the second signature by + * removing superfluous line break characters which are not digested + * anyway or adding another line break to complete to a proper section + * delimiter blank line. + * The first file entry digest is expected to change only if another + * line break has been added. + */ + @Test(dataProvider = "trailingSeqParams") + public void singleIndividualSectionTrailingSeqAddFile(String trailingSeq) + throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + if (!isSufficientSectionDelimiter(trailingSeq)) { + return; // invalid manifest without trailing blank line + } + String jarFilename = test("singleIndividualSectionTrailingSeqAddFile" + + trailingSeqEscaped, + FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS, + replaceTrailingLineBreaksManipulation(trailingSeq), + null, true, true); + + assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes( + FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest"))); + } + + /** + * Create a signed JAR file with a strange sequence of line breaks after + * the first individual section holding the digest of the only file + * contained within the JAR file and a second individual section with the + * same name to be both digested into the same entry digest, + * then add another file and sign it with a different signer. + * The manifest is expected to be changed during the second signature + * by removing superfluous line break characters which are not digested + * anyway or if the trailingSeq is not a sufficient delimiter that both + * intially provided sections are treated as only one which is maybe not + * perfect but does at least not result in an invalid signed jar file. + */ + @Test(dataProvider = "trailingSeqParams") + public void firstIndividualSectionTrailingSeqAddFile(String trailingSeq) + throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + String jarFilename = test("firstIndividualSectionTrailingSeqAddFile" + + trailingSeqEscaped, + FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS, m -> { + StringBuilder sb = new StringBuilder(manifestToString(m)); + // cut off default trailing line break characters + while ("\r\n".contains(sb.substring(sb.length() - 1))) { + sb.deleteCharAt(sb.length() - 1); + } + // and instead add another trailing sequence + sb.append(trailingSeq); + // now add another section with the same name assuming sb + // already contains one entry for FILENAME_INITIAL_CONTENTS + sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n"); + sb.append("Foo: Bar\r\n"); + sb.append("\r\n"); + return sb.toString().getBytes(UTF_8); + }); + + assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes( + FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest"))); + } + + /** + * Create a signed JAR file with two individual sections for the same + * contained file (corresponding by name) the first of which properly + * delimited and the second of which followed by a strange sequence of + * line breaks both digested into the same entry digest, + * then add another file and sign it with a different signer. + * The manifest is expected to be changed during the second signature + * by removing superfluous line break characters which are not digested + * anyway or by adding a proper section delimiter. + * The digests are expected to be changed only if another line break is + * added to properly delimit the next section both digests of which are + * expected to validate with either digest or digestWorkaround. + */ + @Test(dataProvider = "trailingSeqParams") + public void secondIndividualSectionTrailingSeqAddFile(String trailingSeq) + throws Exception { + String trailingSeqEscaped = byteArrayToIntList(trailingSeq.getBytes( + UTF_8)).stream().map(i -> "" + i).collect(Collectors.joining("")); + System.out.println("trailingSeq = " + trailingSeqEscaped); + if (!isSufficientSectionDelimiter(trailingSeq)) { + return; // invalid manifest without trailing blank line + } + String jarFilename = test("secondIndividualSectionTrailingSeqAddFile" + + trailingSeqEscaped, + FILENAME_INITIAL_CONTENTS, FILENAME_UPDATED_CONTENTS, m -> { + StringBuilder sb = new StringBuilder(manifestToString(m)); + sb.append("Name: " + FILENAME_INITIAL_CONTENTS + "\r\n"); + sb.append("Foo: Bar"); + sb.append(trailingSeq); + return sb.toString().getBytes(UTF_8); + }, null, true, true); + + assertTrue(abSigFilesEqual(jarFilename, sf -> sf.getAttributes( + FILENAME_INITIAL_CONTENTS).getValue("SHA-256-Digest"))); + } + + String manifestToString(Manifest mf) { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + mf.write(out); + return new String(out.toByteArray(), UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static List<Integer> byteArrayToIntList(byte[] bytes) { + List<Integer> list = new ArrayList<>(); + for (int i = 0; i < bytes.length; i++) { + list.add((int) bytes[i]); + } + return list; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/RemoveDifferentKeyAlgBlockFile.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.nio.file.Path; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes.Name; +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @run testng RemoveDifferentKeyAlgBlockFile + * @summary Checks that if a signed jar file is signed again with the same + * signer name and a different algorithm that the signature block file for + * the previous signature is removed. Example: the jar had META-INF/A.SF and + * META-INF/A.RSA files and is now signed with DSA. So it should contain + * an updated META-INF/A.SF and META-INF/A.DSA and the META-INF/A.RSA should + * be removed because not valid any longer. + */ +public class RemoveDifferentKeyAlgBlockFile { + + static final String KEYSTORE_FILENAME = "test.jks"; + + @BeforeClass + public void prepareCertificates() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg RSA -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias RSA -dname CN=RSA").shouldHaveExitValue(0); + SecurityTools.keytool("-genkeypair -keyalg DSA -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias DSA -dname CN=DSA").shouldHaveExitValue(0); + } + + @Test + public void testOtherAlgSigBlockFileRemoved() throws Exception { + String jarFilename = "test.jar"; + JarUtils.createJarFile(Path.of(jarFilename), (Manifest) null, + Path.of(".")); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug -sigfile A " + + jarFilename + " RSA").shouldHaveExitValue(0); + + // change the jar file to invalidate the first signature with RSA + String jarFilenameModified = "modified.jar"; + try (JarFile jar = new JarFile(jarFilename)) { + Manifest manifest = jar.getManifest(); + manifest.getMainAttributes().put( + new Name("Some-Key"), "Some-Value"); + JarUtils.updateManifest(jarFilename, jarFilenameModified, manifest); + } + + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug -sigfile A " + + jarFilenameModified + " DSA").shouldHaveExitValue(0); + SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilenameModified) + .shouldHaveExitValue(0); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/SectionNameContinuedVsLineBreak.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.function.Function; +import java.util.jar.Manifest; +import java.util.jar.JarFile; +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @run testng SectionNameContinuedVsLineBreak + * @summary Checks some specific line break character sequences in section name + * continuation line breaks. + */ +public class SectionNameContinuedVsLineBreak { + + static final String KEYSTORE_FILENAME = "test.jks"; + + @BeforeTest + public void prepareCertificate() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg EC -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=A").shouldHaveExitValue(0); + } + + void manipulateManifestSignAgainA( + String srcJarFilename, String dstJarFilename, + Function<Manifest, byte[]> manifestManipulation) throws Exception { + byte[] manipulatedManifest = manifestManipulation.apply( + new Manifest(new ByteArrayInputStream( + Utils.readJarManifestBytes(srcJarFilename)))); + Utils.echoManifest(manipulatedManifest, "manipulated manifest"); + JarUtils.updateJar(srcJarFilename, dstJarFilename, Map.of( + JarFile.MANIFEST_NAME, manipulatedManifest)); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + dstJarFilename + " a") + .shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes( + dstJarFilename), "manipulated jar signed again with a"); + // check assumption that jar is valid at this point + SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + dstJarFilename + " a") + .shouldHaveExitValue(0); + } + + void test(String name, Function<Manifest, byte[]> manifestManipulation, + String jarContentFilename) throws Exception { + String jarFilename1 = "test-" + name + "-step1.jar"; + Files.write(Path.of(jarContentFilename), + jarContentFilename.getBytes(UTF_8)); + JarUtils.createJarFile(Path.of(jarFilename1), (Manifest) + /* no manifest will let jarsigner create a default one */ null, + Path.of("."), Path.of(jarContentFilename)); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename1 + + " a").shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename1), "signed jar"); + String jarFilename2 = "test-" + name + "-step2.jar"; + manipulateManifestSignAgainA(jarFilename1, jarFilename2, + manifestManipulation); + + SecurityTools.jarsigner("-verify -strict -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -debug -verbose " + + jarFilename2 + " a").shouldHaveExitValue(0); + } + + /** + * Test signing a jar with a manifest that has an entry the name of + * which continued on a continuation line with '\r' as line break before + * the continuation line space ' ' on the line the name starts. + */ + @Test + public void testContinueNameAfterCr() throws Exception { + String filename = "abc"; + test("testContinueNameAfterCr", m -> { + String digest = m.getAttributes("abc").getValue("SHA-256-Digest"); + m.getEntries().remove("abc"); + return (manifestToString(m) + + "Name: a\r" + + " bc\r\n" + + "SHA-256-Digest: " + digest + "\r\n" + + "\r\n").getBytes(UTF_8); + }, filename); + } + + /** + * Test signing a jar with a manifest that has an entry the name of + * which continued on a continuation line with '\r' as line break before + * the continuation line space ' ' after a first continuation. + */ + @Test + public void testContinueNameAfterCrOnContinuationLine() throws Exception { + String filename = "abc"; + test("testContinueNameAfterCr", m -> { + String digest = m.getAttributes("abc").getValue("SHA-256-Digest"); + m.getEntries().remove("abc"); + return (manifestToString(m) + + "Name: a\r\n" + + " b\r" + + " c\r\n" + + "SHA-256-Digest: " + digest + "\r\n" + + "\r\n").getBytes(UTF_8); + }, filename); + } + + /** + * Test signing a jar with a manifest that has an entry the name of + * which continued on a continuation line and terminated with '\r' as line + * break after the name. + */ + @Test + public void testEndNameWithCrOnContinuationLine() throws Exception { + String filename = "abc"; + test("testContinueNameAfterCr", m -> { + String digest = m.getAttributes("abc").getValue("SHA-256-Digest"); + m.getEntries().remove("abc"); + return (manifestToString(m) + + "Name: a\r\n" + + " bc\r" + + "SHA-256-Digest: " + digest + "\r\n" + + "\r\n").getBytes(UTF_8); + }, filename); + } + + String manifestToString(Manifest mf) { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + mf.write(out); + return out.toString(UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +}
--- a/test/jdk/sun/security/tools/jarsigner/Utils.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/security/tools/jarsigner/Utils.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, 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 @@ -21,8 +21,15 @@ * questions. */ +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.InputStream; import java.io.IOException; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipFile; + +import static java.nio.charset.StandardCharsets.UTF_8; /** * Helper class. @@ -35,5 +42,66 @@ } } + static void printNonPrintableCharactersEscaped(byte[] manifest) + throws IOException { + // keep byte sequences encoding multi-byte UTF-8 encoded and composite + // characters together as much as possible before decoding into a String + ByteArrayOutputStream lineBuf = new ByteArrayOutputStream(); + for (int i = 0; i < manifest.length; i++) { + switch (manifest[i]) { + case '\t': + lineBuf.write("\\t".getBytes(UTF_8)); + break; + case '\r': + lineBuf.write("\\r".getBytes(UTF_8)); + if (i + 1 >= manifest.length || manifest[i + 1] != '\n') { + System.out.println(lineBuf.toString(UTF_8)); + lineBuf.reset(); + } + break; + case '\n': + lineBuf.write("\\n".getBytes(UTF_8)); + System.out.println(lineBuf.toString(UTF_8)); + lineBuf.reset(); + break; + default: + lineBuf.write(manifest[i]); + } + } + if (lineBuf.size() > 0) { + System.out.println(lineBuf.toString(UTF_8)); + } + } + + static void echoManifest(byte[] manifest, String msg) throws IOException { + System.out.println("-".repeat(72)); + System.out.println(msg); + System.out.println("-".repeat(72)); + printNonPrintableCharactersEscaped(manifest); + System.out.println("-".repeat(72)); + } + + static byte[] readJarManifestBytes(String jarFilename) throws IOException { + return readJarEntryBytes(jarFilename, JarFile.MANIFEST_NAME); + } + + static byte[] readJarEntryBytes(String jarFilename, String jarEntryname) + throws IOException { + try ( + ZipFile jar = new ZipFile(jarFilename); + InputStream is = jar.getInputStream(jar.getEntry(jarEntryname)); + ) { + return is.readAllBytes(); + } + } + + static String escapeStringWithNumbers(String lineBreak) { + String escaped = ""; + byte[] bytes = lineBreak.getBytes(UTF_8); + for (int i = 0; i < bytes.length; i++) { + escaped += bytes[i]; + } + return escaped; + } + } -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/jarsigner/WasSignedByOtherSigner.java Wed Jul 24 12:21:36 2019 +0530 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Path; +import java.util.Map; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes.Name; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import jdk.test.lib.util.JarUtils; +import jdk.test.lib.SecurityTools; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8217375 + * @library /test/lib + * @run testng WasSignedByOtherSigner + * @summary Checks that {@code wasSigned} in + * {@link jdk.security.jarsigner.JarSigner#sign0} is set true if the jar to sign + * contains a signature that will not be overwritten with the current one. + */ +public class WasSignedByOtherSigner { + + static final String KEYSTORE_FILENAME = "test.jks"; + + @BeforeClass + public void prepareKeyStore() throws Exception { + SecurityTools.keytool("-genkeypair -keyalg EC -keystore " + + KEYSTORE_FILENAME + " -storepass changeit -keypass changeit" + + " -alias a -dname CN=A").shouldHaveExitValue(0); + } + + void test(String secondSigner, boolean expRrewritten) throws Exception { + String jarFilename1 = "test" + secondSigner + "-1.jar"; + JarUtils.createJarFile(Path.of(jarFilename1), (Manifest) null, + Path.of(".")); + // TODO: use jarsigner here only to create a default manifest... + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename1 + " a") + .shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename1), "initial manifest"); + + // replace manifest with a non-standard one that can later be checked + String jarFilename2 = "test" + secondSigner + "-2.jar"; + JarUtils.updateJar(jarFilename1, jarFilename2, Map.of( + // add a fake sig-related file to trigger wasSigned in JarSigner + "META-INF/.SF", Name.SIGNATURE_VERSION + ": 1.0\r\n")); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename2), "with fake META-INF.SF file"); + String jarFilename3 = "test" + secondSigner + "-3.jar"; + JarUtils.updateManifest(jarFilename2, jarFilename3, new Manifest() { + @Override public void write(OutputStream out) throws IOException { + // no trailing blank line + out.write((Name.MANIFEST_VERSION + ": 1.0\r\n").getBytes(UTF_8)); + } + }); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename3), "with manifest manipulated"); + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug " + jarFilename3 + " a") + .shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename3), "signed"); + String jarFilename4 = "test" + secondSigner + "-4.jar"; + JarUtils.updateJar(jarFilename3, jarFilename4, + Map.of("META-INF/.SF", false)); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename4), "with fake META-INF.SF file removed"); + + // re-sign the jar with signer named secondSigner (same or different) + SecurityTools.jarsigner("-keystore " + KEYSTORE_FILENAME + + " -storepass changeit -verbose -debug -sigfile " + + secondSigner + " " + jarFilename4 + " a") + .shouldHaveExitValue(0); + Utils.echoManifest(Utils.readJarManifestBytes( + jarFilename4), "signed again"); + // remove META-INF/.SF from signed jar again which would not validate + + // in any case verify that the resulting jar file is valid + SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilename4) + .shouldHaveExitValue(0); + SecurityTools.jarsigner("-verify -keystore " + KEYSTORE_FILENAME + + " -storepass changeit -debug -verbose " + jarFilename4 + + " a").shouldHaveExitValue(0); + + // if wasSigned was true in JarSigner#sign0 the manifest (only main + // attributes present and tested here but same consideration applies + // to individual sections just the same) should be reproduced with + // unchanged binary form. Otherwise, if there were no previous + // signatures or only one being replaced, the manifest is kind of + // "normalized" by re-writing it thereby replacing all line breaks + // (from cr or lf to crlf) and replacing all line breaks onto + // continuation lines and also writing all section delimiting blank + // lines. + // if that "normalization" has took place the test here can conclude + // whether wasSigned was true or was not. + try (ZipFile jar = new ZipFile(jarFilename4)) { + ZipEntry ze = jar.getEntry(JarFile.MANIFEST_NAME); + byte[] manifestBytes = jar.getInputStream(ze).readAllBytes(); + Utils.echoManifest(manifestBytes, "manifest"); + String manifestString = new String(manifestBytes, UTF_8); + boolean actRewritten = manifestString.endsWith("\r\n\r\n"); + assertEquals(actRewritten, expRrewritten); + } + } + + @Test + public void reSignSameSigner() throws Exception { + test("A", true); + } + + @Test + public void reSignOtherSigner() throws Exception { + test("B", false); + } + +}
--- a/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java Mon Jul 22 11:08:27 2019 +0530 +++ b/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java Wed Jul 24 12:21:36 2019 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, 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 @@ -23,55 +23,59 @@ /* * @test - * @summary This test is used to verify the compatibility on jarsigner cross + * @bug 8217375 + * @summary This test is used to verify the compatibility of jarsigner across * different JDK releases. It also can be used to check jar signing (w/ - * and w/o TSA) and verifying on some specific key algorithms and digest - * algorithms. - * Note that, this is a manual test. For more details about the test and - * its usages, please look through README. + * and w/o TSA) and to verify some specific signing and digest algorithms. + * Note that this is a manual test. For more details about the test and + * its usages, please look through the README. * - * @modules java.base/sun.security.pkcs - * java.base/sun.security.timestamp - * java.base/sun.security.tools.keytool - * java.base/sun.security.util - * java.base/sun.security.x509 - * @library /test/lib /lib/testlibrary ../warnings + * @library /test/lib ../warnings * @compile -source 1.7 -target 1.7 JdkUtils.java * @run main/manual/othervm Compatibility */ import java.io.BufferedReader; import java.io.File; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintStream; +import java.nio.file.Path; +import java.nio.file.Files; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.jar.Manifest; +import java.util.jar.Attributes.Name; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import java.util.stream.Collectors; +import java.util.stream.IntStream; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; -public class Compatibility { +import static java.nio.charset.StandardCharsets.UTF_8; - private static final String TEST_JAR_NAME = "test.jar"; +public class Compatibility { private static final String TEST_SRC = System.getProperty("test.src"); private static final String TEST_CLASSES = System.getProperty("test.classes"); private static final String TEST_JDK = System.getProperty("test.jdk"); - private static final String TEST_JARSIGNER = jarsignerPath(TEST_JDK); + private static JdkInfo TEST_JDK_INFO; private static final String PROXY_HOST = System.getProperty("proxyHost"); private static final String PROXY_PORT = System.getProperty("proxyPort", "80"); @@ -84,29 +88,43 @@ "javaSecurityFile", TEST_SRC + "/java.security"); private static final String PASSWORD = "testpass"; - private static final String KEYSTORE = "testKeystore"; + private static final String KEYSTORE = "testKeystore.jks"; private static final String RSA = "RSA"; private static final String DSA = "DSA"; private static final String EC = "EC"; - private static final String[] KEY_ALGORITHMS = new String[] { + private static String[] KEY_ALGORITHMS; + private static final String[] DEFAUL