From b5356a59c6637d549f932a6148d89f1cf1540aa5 Mon Sep 17 00:00:00 2001 From: jiangli Date: Tue, 12 Aug 2014 17:46:16 -0400 Subject: [PATCH] 8044269: Analysis of archive files. Summary: Add checksum verification. Reviewed-by: iklam, dholmes, mschoene --- src/share/vm/classfile/classLoader.cpp | 11 ++++++- src/share/vm/classfile/classLoader.hpp | 1 + src/share/vm/memory/filemap.cpp | 40 +++++++++++++++++++++++-- src/share/vm/memory/filemap.hpp | 9 ++++-- src/share/vm/memory/metaspaceShared.cpp | 5 ++++ src/share/vm/runtime/arguments.cpp | 5 ++++ src/share/vm/runtime/globals.hpp | 4 +++ 7 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/share/vm/classfile/classLoader.cpp b/src/share/vm/classfile/classLoader.cpp index 8366c8fc4..85a0564dd 100644 --- a/src/share/vm/classfile/classLoader.cpp +++ b/src/share/vm/classfile/classLoader.cpp @@ -84,6 +84,7 @@ typedef jzentry* (JNICALL *FindEntry_t)(jzfile *zip, const char *name, jint *siz typedef jboolean (JNICALL *ReadEntry_t)(jzfile *zip, jzentry *entry, unsigned char *buf, char *namebuf); typedef jboolean (JNICALL *ReadMappedEntry_t)(jzfile *zip, jzentry *entry, unsigned char **buf, char *namebuf); typedef jzentry* (JNICALL *GetNextEntry_t)(jzfile *zip, jint n); +typedef jint (JNICALL *Crc32_t)(jint crc, const jbyte *buf, jint len); static ZipOpen_t ZipOpen = NULL; static ZipClose_t ZipClose = NULL; @@ -92,6 +93,7 @@ static ReadEntry_t ReadEntry = NULL; static ReadMappedEntry_t ReadMappedEntry = NULL; static GetNextEntry_t GetNextEntry = NULL; static canonicalize_fn_t CanonicalizeEntry = NULL; +static Crc32_t Crc32 = NULL; // Globals @@ -632,9 +634,11 @@ void ClassLoader::load_zip_library() { ReadEntry = CAST_TO_FN_PTR(ReadEntry_t, os::dll_lookup(handle, "ZIP_ReadEntry")); ReadMappedEntry = CAST_TO_FN_PTR(ReadMappedEntry_t, os::dll_lookup(handle, "ZIP_ReadMappedEntry")); GetNextEntry = CAST_TO_FN_PTR(GetNextEntry_t, os::dll_lookup(handle, "ZIP_GetNextEntry")); + Crc32 = CAST_TO_FN_PTR(Crc32_t, os::dll_lookup(handle, "ZIP_CRC32")); // ZIP_Close is not exported on Windows in JDK5.0 so don't abort if ZIP_Close is NULL - if (ZipOpen == NULL || FindEntry == NULL || ReadEntry == NULL || GetNextEntry == NULL) { + if (ZipOpen == NULL || FindEntry == NULL || ReadEntry == NULL || + GetNextEntry == NULL || Crc32 == NULL) { vm_exit_during_initialization("Corrupted ZIP library", path); } @@ -644,6 +648,11 @@ void ClassLoader::load_zip_library() { // This lookup only works on 1.3. Do not check for non-null here } +int ClassLoader::crc32(int crc, const char* buf, int len) { + assert(Crc32 != NULL, "ZIP_CRC32 is not found"); + return (*Crc32)(crc, (const jbyte*)buf, len); +} + // PackageInfo data exists in order to support the java.lang.Package // class. A Package object provides information about a java package // (version, vendor, etc.) which originates in the manifest of the jar diff --git a/src/share/vm/classfile/classLoader.hpp b/src/share/vm/classfile/classLoader.hpp index e03cfad1b..a0dd03605 100644 --- a/src/share/vm/classfile/classLoader.hpp +++ b/src/share/vm/classfile/classLoader.hpp @@ -215,6 +215,7 @@ class ClassLoader: AllStatic { // to avoid confusing the zip library static bool get_canonical_path(char* orig, char* out, int len); public: + static int crc32(int crc, const char* buf, int len); // Used by the kernel jvm. static void update_class_path_entry_list(char *path, bool check_for_duplicates); diff --git a/src/share/vm/memory/filemap.cpp b/src/share/vm/memory/filemap.cpp index 2c0f8de80..e026d920f 100644 --- a/src/share/vm/memory/filemap.cpp +++ b/src/share/vm/memory/filemap.cpp @@ -177,7 +177,14 @@ bool FileMapInfo::init_from_file(int fd) { fail_continue("The shared archive file has the wrong version."); return false; } - _file_offset = (long)n; + size_t len = lseek(fd, 0, SEEK_END); + struct FileMapInfo::FileMapHeader::space_info* si = + &_header._space[MetaspaceShared::mc]; + if (si->_file_offset >= len || len - si->_file_offset < si->_used) { + fail_continue("The shared archive file has been truncated."); + return false; + } + _file_offset = n; return true; } @@ -268,6 +275,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size, si->_capacity = capacity; si->_read_only = read_only; si->_allow_exec = allow_exec; + si->_crc = ClassLoader::crc32(0, base, (jint)size); write_bytes_aligned(base, (int)size); } @@ -292,14 +300,15 @@ void FileMapInfo::write_bytes(const void* buffer, int nbytes) { // Align file position to an allocation unit boundary. void FileMapInfo::align_file_position() { - long new_file_offset = align_size_up(_file_offset, os::vm_allocation_granularity()); + size_t new_file_offset = align_size_up(_file_offset, + os::vm_allocation_granularity()); if (new_file_offset != _file_offset) { _file_offset = new_file_offset; if (_file_open) { // Seek one byte back from the target and write a byte to insure // that the written file is the correct length. _file_offset -= 1; - if (lseek(_fd, _file_offset, SEEK_SET) < 0) { + if (lseek(_fd, (long)_file_offset, SEEK_SET) < 0) { fail_stop("Unable to seek.", NULL); } char zero = 0; @@ -406,6 +415,19 @@ char* FileMapInfo::map_region(int i) { return base; } +bool FileMapInfo::verify_region_checksum(int i) { + if (!VerifySharedSpaces) { + return true; + } + const char* buf = _header._space[i]._base; + size_t sz = _header._space[i]._used; + int crc = ClassLoader::crc32(0, buf, (jint)sz); + if (crc != _header._space[i]._crc) { + fail_continue("Checksum verification failed."); + return false; + } + return true; +} // Unmap a memory region in the address space. @@ -457,8 +479,20 @@ bool FileMapInfo::initialize() { return true; } +int FileMapInfo::compute_header_crc() { + char* header = (char*)&_header; + // start computing from the field after _crc + char* buf = (char*)&_header._crc + sizeof(int); + size_t sz = sizeof(FileMapInfo::FileMapHeader) - (buf - header); + int crc = ClassLoader::crc32(0, buf, (jint)sz); + return crc; +} bool FileMapInfo::validate() { + if (VerifySharedSpaces && compute_header_crc() != _header._crc) { + fail_continue("Header checksum verification failed."); + return false; + } if (_header._version != current_version()) { fail_continue("The shared archive file is the wrong version."); return false; diff --git a/src/share/vm/memory/filemap.hpp b/src/share/vm/memory/filemap.hpp index 0c93c8799..65ce02119 100644 --- a/src/share/vm/memory/filemap.hpp +++ b/src/share/vm/memory/filemap.hpp @@ -54,7 +54,7 @@ private: bool _file_open; int _fd; - long _file_offset; + size_t _file_offset; // FileMapHeader describes the shared space data in the file to be // mapped. This structure gets written to a file. It is not a class, so @@ -62,12 +62,14 @@ private: struct FileMapHeader { int _magic; // identify file type. + int _crc; // header crc checksum. int _version; // (from enum, above.) size_t _alignment; // how shared archive should be aligned int _obj_alignment; // value of ObjectAlignmentInBytes struct space_info { - int _file_offset; // sizeof(this) rounded to vm page size + int _crc; // crc checksum of the current space + size_t _file_offset; // sizeof(this) rounded to vm page size char* _base; // copy-on-write base address size_t _capacity; // for validity checking size_t _used; // for setting space top on read @@ -104,6 +106,8 @@ public: } static int current_version() { return _current_version; } + int compute_header_crc(); + void set_header_crc(int crc) { _header._crc = crc; } void populate_header(size_t alignment); bool validate(); void invalidate(); @@ -136,6 +140,7 @@ public: void write_bytes_aligned(const void* buffer, int count); char* map_region(int i); void unmap_region(int i); + bool verify_region_checksum(int i); void close(); bool is_open() { return _file_open; } ReservedSpace reserve_shared_memory(); diff --git a/src/share/vm/memory/metaspaceShared.cpp b/src/share/vm/memory/metaspaceShared.cpp index d0a875494..06e2e66a0 100644 --- a/src/share/vm/memory/metaspaceShared.cpp +++ b/src/share/vm/memory/metaspaceShared.cpp @@ -585,6 +585,7 @@ void VM_PopulateDumpSharedSpace::doit() { // Pass 2 - write data. mapinfo->open_for_write(); + mapinfo->set_header_crc(mapinfo->compute_header_crc()); mapinfo->write_header(); mapinfo->write_space(MetaspaceShared::ro, _loader_data->ro_metaspace(), true); mapinfo->write_space(MetaspaceShared::rw, _loader_data->rw_metaspace(), false); @@ -863,9 +864,13 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { // Map each shared region if ((_ro_base = mapinfo->map_region(ro)) != NULL && + mapinfo->verify_region_checksum(ro) && (_rw_base = mapinfo->map_region(rw)) != NULL && + mapinfo->verify_region_checksum(rw) && (_md_base = mapinfo->map_region(md)) != NULL && + mapinfo->verify_region_checksum(md) && (_mc_base = mapinfo->map_region(mc)) != NULL && + mapinfo->verify_region_checksum(mc) && (image_alignment == (size_t)max_alignment())) { // Success (no need to do anything) return true; diff --git a/src/share/vm/runtime/arguments.cpp b/src/share/vm/runtime/arguments.cpp index f66d8530e..31effa1ce 100644 --- a/src/share/vm/runtime/arguments.cpp +++ b/src/share/vm/runtime/arguments.cpp @@ -3581,6 +3581,11 @@ jint Arguments::parse(const JavaVMInitArgs* args) { return JNI_ENOMEM; } + // Set up VerifySharedSpaces + if (FLAG_IS_DEFAULT(VerifySharedSpaces) && SharedArchiveFile != NULL) { + VerifySharedSpaces = true; + } + // Delay warning until here so that we've had a chance to process // the -XX:-PrintWarnings flag if (needs_hotspotrc_warning) { diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index 5341c9cb4..63222afe7 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -3745,6 +3745,10 @@ class CommandLineFlags { product(bool, UseSharedSpaces, true, \ "Use shared spaces for metadata") \ \ + product(bool, VerifySharedSpaces, false, \ + "Verify shared spaces (false for default archive, true for " \ + "archive specified by -XX:SharedArchiveFile)") \ + \ product(bool, RequireSharedSpaces, false, \ "Require shared spaces for metadata") \ \ -- GitLab