8139390: Very long classname in jimage causes SIGSEGV
Summary: Correct issues with ImageNativeSubstrate and JImageReadTest
Reviewed-by: mchung
/*
* Copyright (c) 2015, 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.
*/
#include <string.h>
#include "jni.h"
#include "endian.hpp"
#include "imageDecompressor.hpp"
#include "imageFile.hpp"
#include "inttypes.hpp"
#include "jimage.hpp"
#include "osSupport.hpp"
#include "jdk_internal_jimage_ImageNativeSubstrate.h"
extern bool MemoryMapImage;
/////////////////////////////////////////////////////////////////////////////
// Static function for primitive throw since libjimage is not linked with libjava
static void JNICALL ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
jclass cls = (env)->FindClass(name);
if (cls != 0) /* Otherwise an exception has already been thrown */
(env)->ThrowNew(cls, msg);
}
// jdk.internal.jimage /////////////////////////////////////////////////////////
// Java entry to open an image file for sharing.
static jlong JIMAGE_Open(JNIEnv *env, const char *nativePath, jboolean big_endian) {
// Open image file for reading.
ImageFileReader* reader = ImageFileReader::open(nativePath, big_endian != JNI_FALSE);
// Return image ID as a jlong.
return ImageFileReader::readerToID(reader);
}
// Java entry for closing a shared image file.
static void JIMAGE_Close(JNIEnv *env, jlong id) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// If valid reader the close.
if (reader != NULL) {
ImageFileReader::close(reader);
}
}
// Java entry for accessing the base address of the image index.
static jlong JIMAGE_GetIndexAddress(JNIEnv *env, jlong id) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// If valid reader return index base address (as jlong) else zero.
return reader != NULL ? (jlong) reader->get_index_address() : 0L;
}
// Java entry for accessing the base address of the image data.
static jlong JIMAGE_GetDataAddress(JNIEnv *env, jlong id) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// If valid reader return data base address (as jlong) else zero.
return MemoryMapImage && reader != NULL ? (jlong) reader->get_data_address() : 0L;
}
// Java entry for reading an uncompressed resource from the image.
static jboolean JIMAGE_Read(JNIEnv *env, jlong id, jlong offset,
unsigned char* uncompressedAddress, jlong uncompressed_size) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);\
// If not a valid reader the fail the read.
if (reader == NULL) return false;
// Get the file offset of resource data.
u8 file_offset = reader->get_index_size() + offset;
// Check validity of arguments.
if (offset < 0 ||
uncompressed_size < 0 ||
file_offset > reader->file_size() - uncompressed_size) {
return false;
}
// Read file content into buffer.
return (jboolean) reader->read_at((u1*) uncompressedAddress, uncompressed_size,
file_offset);
}
// Java entry for reading a compressed resource from the image.
static jboolean JIMAGE_ReadCompressed(JNIEnv *env,
jlong id, jlong offset,
unsigned char* compressedAddress, jlong compressed_size,
unsigned char* uncompressedAddress, jlong uncompressed_size) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// If not a valid reader the fail the read.
if (reader == NULL) return false;
// Get the file offset of resource data.
u8 file_offset = reader->get_index_size() + offset;
// Check validity of arguments.
if (offset < 0 ||
compressed_size < 0 ||
uncompressed_size < 0 ||
file_offset > reader->file_size() - compressed_size) {
return false;
}
// Read file content into buffer.
bool is_read = reader->read_at(compressedAddress, compressed_size,
file_offset);
// If successfully read then decompress.
if (is_read) {
const ImageStrings strings = reader->get_strings();
ImageDecompressor::decompress_resource(compressedAddress, uncompressedAddress,
(u4) uncompressed_size, &strings);
}
return (jboolean) is_read;
}
// Java entry for retrieving UTF-8 bytes from image string table.
static const char* JIMAGE_GetStringBytes(JNIEnv *env, jlong id, jint offset) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// Fail if not valid reader.
if (reader == NULL) return NULL;
// Manage image string table.
ImageStrings strings = reader->get_strings();
// Retrieve string adrress from table.
const char* data = strings.get(offset);
return data;
}
// Utility function to copy location information into a jlong array.
// WARNING: This function is experimental and temporary during JDK 9 development
// cycle. It will not be supported in the eventual JDK 9 release.
static void image_expand_location(JNIEnv *env, jlong* rawAttributes, ImageLocation& location) {
// Copy attributes from location.
for (int kind = ImageLocation::ATTRIBUTE_END + 1;
kind < ImageLocation::ATTRIBUTE_COUNT;
kind++) {
rawAttributes[kind] = location.get_attribute(kind);
}
}
// Java entry for retrieving location attributes for attribute offset.
static jlong* JIMAGE_GetAttributes(JNIEnv *env, jlong* rawAttributes, jlong id, jint offset) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// Fail if not valid reader.
if (reader == NULL) return NULL;
// Retrieve first byte address of resource's location attribute stream.
u1* data = reader->get_location_offset_data(offset);
// Fail if not valid offset.
if (data == NULL) return NULL;
// Expand stream into array.
ImageLocation location(data);
image_expand_location(env, rawAttributes, location);
return rawAttributes;
}
// Java entry for retrieving location attributes count for attribute offset.
static jsize JIMAGE_GetAttributesCount(JNIEnv *env) {
return ImageLocation::ATTRIBUTE_COUNT;
}
// Java entry for retrieving location attributes for named resource.
static jlong* JIMAGE_FindAttributes(JNIEnv *env, jlong* rawAttributes, jbyte* rawBytes, jsize size, jlong id) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// Fail if not valid reader.
if (reader == NULL) return NULL;
// Convert byte array to a cstring.
char* path = new char[size + 1];
memcpy(path, rawBytes, size);
path[size] = '\0';
// Locate resource location data.
ImageLocation location;
bool found = reader->find_location(path, location);
delete path;
// Resource not found.
if (!found) return NULL;
// Expand stream into array.
image_expand_location(env, rawAttributes, location);
return rawAttributes;
}
// Java entry for retrieving all the attribute stream offsets from an image.
static jint* JIMAGE_AttributeOffsets(JNIEnv *env, jint* rawOffsets, unsigned int length, jlong id) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// Fail if not valid reader.
if (reader == NULL) return NULL;
// Determine endian for reader.
Endian* endian = reader->endian();
// Get base address of attribute stream offsets table.
u4* offsets_table = reader->offsets_table();
// Allocate int array result.
// Copy values to result (converting endian.)
for (u4 i = 0; i < length; i++) {
rawOffsets[i] = endian->get(offsets_table[i]);
}
return rawOffsets;
}
// Java entry for retrieving all the attribute stream offsets length from an image.
static unsigned int JIMAGE_AttributeOffsetsLength(JNIEnv *env, jlong id) {
// Convert image ID to image reader structure.
ImageFileReader* reader = ImageFileReader::idToReader(id);
// Fail if not valid reader.
if (reader == NULL) return 0;
// Get perfect hash table length.
u4 length = reader->table_length();
return (jint) length;
}
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK) {
return JNI_EVERSION; /* JNI version not supported */
}
return JNI_VERSION_1_2;
}
JNIEXPORT jlong JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_openImage(JNIEnv *env,
jclass cls, jstring path, jboolean big_endian) {
const char *nativePath;
jlong ret;
nativePath = env->GetStringUTFChars(path, NULL);
ret = JIMAGE_Open(env, nativePath, big_endian);
env->ReleaseStringUTFChars(path, nativePath);
return ret;
}
JNIEXPORT void JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage(JNIEnv *env,
jclass cls, jlong id) {
JIMAGE_Close(env, id);
}
JNIEXPORT jlong JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress(JNIEnv *env,
jclass cls, jlong id) {
return JIMAGE_GetIndexAddress(env, id);
}
JNIEXPORT jlong JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress(JNIEnv *env,
jclass cls, jlong id) {
return JIMAGE_GetDataAddress(env, id);
}
JNIEXPORT jboolean JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_read(JNIEnv *env,
jclass cls, jlong id, jlong offset,
jobject uncompressedBuffer, jlong uncompressed_size) {
unsigned char* uncompressedAddress;
uncompressedAddress = (unsigned char*) env->GetDirectBufferAddress(uncompressedBuffer);
if (uncompressedAddress == NULL) {
return JNI_FALSE;
}
return JIMAGE_Read(env, id, offset, uncompressedAddress, uncompressed_size);
}
JNIEXPORT jboolean JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed(JNIEnv *env,
jclass cls, jlong id, jlong offset,
jobject compressedBuffer, jlong compressed_size,
jobject uncompressedBuffer, jlong uncompressed_size) {
// Get address of read direct buffer.
unsigned char* compressedAddress;
unsigned char* uncompressedAddress;
compressedAddress = (unsigned char*) env->GetDirectBufferAddress(compressedBuffer);
// Get address of decompression direct buffer.
uncompressedAddress = (unsigned char*) env->GetDirectBufferAddress(uncompressedBuffer);
if (compressedAddress == NULL || uncompressedAddress == NULL) {
return JNI_FALSE;
}
return JIMAGE_ReadCompressed(env, id, offset, compressedAddress, compressed_size,
uncompressedAddress, uncompressed_size);
}
JNIEXPORT jbyteArray JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes(JNIEnv *env,
jclass cls, jlong id, jint offset) {
const char* data;
size_t size;
jbyteArray byteArray;
jbyte* rawBytes;
data = JIMAGE_GetStringBytes(env, id, offset);
// Determine String length.
size = strlen(data);
// Allocate byte array.
byteArray = env->NewByteArray((jsize) size);
if (byteArray == NULL) {
return NULL;
}
// Get array base address.
rawBytes = env->GetByteArrayElements(byteArray, NULL);
// Copy bytes from image string table.
memcpy(rawBytes, data, size);
// Release byte array base address.
env->ReleaseByteArrayElements(byteArray, rawBytes, 0);
return byteArray;
}
JNIEXPORT jlongArray JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes(JNIEnv *env,
jclass cls, jlong id, jint offset) {
// Allocate a jlong large enough for all location attributes.
jlongArray attributes;
jlong* rawAttributes;
jlong* ret;
attributes = env->NewLongArray(JIMAGE_GetAttributesCount(env));
if (attributes == NULL) {
return NULL;
}
// Get base address for jlong array.
rawAttributes = env->GetLongArrayElements(attributes, NULL);
ret = JIMAGE_GetAttributes(env, rawAttributes, id, offset);
// Release jlong array base address.
env->ReleaseLongArrayElements(attributes, rawAttributes, 0);
return ret == NULL ? NULL : attributes;
}
JNIEXPORT jlongArray JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes(JNIEnv *env,
jclass cls, jlong id, jbyteArray utf8) {
// Allocate a jlong large enough for all location attributes.
jsize count;
jlongArray attributes;
jlong* rawAttributes;
jsize size;
jbyte* rawBytes;
jlong* ret;
count = JIMAGE_GetAttributesCount(env);
attributes = env->NewLongArray(JIMAGE_GetAttributesCount(env));
if (attributes == NULL) {
return NULL;
}
// Get base address for jlong array.
rawAttributes = env->GetLongArrayElements(attributes, NULL);
size = env->GetArrayLength(utf8);
rawBytes = env->GetByteArrayElements(utf8, NULL);
ret = JIMAGE_FindAttributes(env, rawAttributes, rawBytes, size, id);
env->ReleaseByteArrayElements(utf8, rawBytes, 0);
// Release jlong array base address.
env->ReleaseLongArrayElements(attributes, rawAttributes, 0);
return ret == NULL ? NULL : attributes;
}
JNIEXPORT jintArray JNICALL
Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets(JNIEnv *env,
jclass cls, jlong id) {
unsigned int length;
jintArray offsets;
jint* rawOffsets;
jint* ret;
length = JIMAGE_AttributeOffsetsLength(env, id);
offsets = env->NewIntArray(length);
if (offsets == NULL) {
return NULL;
}
// Get base address of result.
rawOffsets = env->GetIntArrayElements(offsets, NULL);
ret = JIMAGE_AttributeOffsets(env, rawOffsets, length, id);
if (length == 0) {
return NULL;
}
// Release result base address.
env->ReleaseIntArrayElements(offsets, rawOffsets, 0);
return ret == NULL ? NULL : offsets;
}
/*
* Class: jdk_internal_jimage_ImageNativeSubstrate
* Method: JIMAGE_open
* Signature: (Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Open
(JNIEnv *env, jclass, jstring path) {
const char *nativePath = env->GetStringUTFChars(path, NULL);
if (nativePath == NULL)
return 0; // Exception already thrown
jint error;
jlong ret = (jlong) JIMAGE_Open(nativePath, &error);
env->ReleaseStringUTFChars(path, nativePath);
return ret;
}
/*
* Class: jdk_internal_jimage_ImageNativeSubstrate
* Method: JIMAGE_Close
* Signature: (J)J
*/
JNIEXPORT void JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Close
(JNIEnv *env, jclass, jlong jimageHandle) {
JIMAGE_Close((JImageFile*) jimageHandle);
}
/*
* Class: jdk_internal_jimage_ImageNativeSubstrate
* Method: JIMAGE_FindResource
* Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[J)J
*/
JNIEXPORT jlong JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1FindResource
(JNIEnv *env, jclass, jlong jimageHandle, jstring moduleName,
jstring version, jstring path, jlongArray output_size) {
const char *native_module = NULL;
const char *native_version = NULL;
const char *native_path = NULL;
jlong * native_array = NULL;
jlong size = 0;
jlong ret = 0;
if (moduleName == NULL) {
ThrowByName(env, "java/lang/NullPointerException", "moduleName");
return 0;
}
if (version == NULL) {
ThrowByName(env, "java/lang/NullPointerException", "version");
return 0;
}
if (path == NULL) {
ThrowByName(env, "java/lang/NullPointerException", "path");
return 0;
}
if (output_size == NULL) {
ThrowByName(env, "java/lang/NullPointerException", "size");
return 0;
}
do {
native_module = env->GetStringUTFChars(moduleName, NULL);
if (native_module == NULL)
break;
native_version = env->GetStringUTFChars(version, NULL);
if (native_version == NULL)
break;
native_path = env->GetStringUTFChars(path, NULL);
if (native_path == NULL)
break;
if (env->GetArrayLength(output_size) < 1)
break;
// Get base address for jlong array.
native_array = env->GetLongArrayElements(output_size, NULL);
if (native_array == NULL)
break;
ret = (jlong) JIMAGE_FindResource((JImageFile *) jimageHandle,
native_module, native_version, native_path, &size);
if (ret != 0)
*native_array = size;
} while (0);
if (native_array != NULL)
env->ReleaseLongArrayElements(output_size, native_array, 0);
if (native_path != NULL)
env->ReleaseStringUTFChars(path, native_path);
if (native_version != NULL)
env->ReleaseStringUTFChars(path, native_version);
if (native_module != NULL)
env->ReleaseStringUTFChars(path, native_module);
return ret;
}
/*
* Class: jdk_internal_jimage_ImageNativeSubstrate
* Method: JIMAGE_GetResource
* Signature: (JJ[BJ)J
*/
JNIEXPORT jlong JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1GetResource
(JNIEnv *env, jclass, jlong jimageHandle, jlong jlocationHandle, jbyteArray buffer, jlong size) {
jbyte * native_buffer = NULL;
jlong actual_size = 0;
do {
if (env->GetArrayLength(buffer) < size)
break;
native_buffer = env->GetByteArrayElements(buffer, NULL);
if (native_buffer == NULL)
break;
actual_size = JIMAGE_GetResource((JImageFile*) jimageHandle,
(JImageLocationRef) jlocationHandle,
(char *) native_buffer, size);
} while (0);
// Release byte array
if (native_buffer != NULL)
env->ReleaseByteArrayElements(buffer, native_buffer, 0);
return actual_size;
}
// Structure passed from iterator to a visitor to accumulate the results
struct VisitorData {
JNIEnv *env;
int size; // current number of strings
int max; // Maximum number of strings
jobjectArray array; // String array to store the strings
};
// Visitor to accumulate fully qualified resource names
static bool resourceVisitor(JImageFile* image,
const char* module, const char* version, const char* package,
const char* name, const char* extension, void* arg) {
struct VisitorData *vdata = (struct VisitorData *) arg;
JNIEnv* env = vdata->env;
if (vdata->size < vdata->max) {
// Store if there is room in the array
// Concatenate to get full path
char fullpath[IMAGE_MAX_PATH];
size_t moduleLen = strlen(module);
size_t packageLen = strlen(package);
size_t nameLen = strlen(name);
size_t extLen = strlen(extension);
size_t index;
if (1 + moduleLen + 1 + packageLen + 1 + nameLen + 1 + extLen + 1 > IMAGE_MAX_PATH) {
ThrowByName(env, "java/lang/InternalError", "concatenated name too long");
return true;
}
index = 0;
if (moduleLen > 0) {
fullpath[index++] = '/';
memcpy(&fullpath[index], module, moduleLen);
index += moduleLen;
fullpath[index++] = '/';
}
if (packageLen > 0) {
memcpy(&fullpath[index], package, packageLen);
index += packageLen;
fullpath[index++] = '/';
}
memcpy(&fullpath[index], name, nameLen);
index += nameLen;
if (extLen > 0) {
fullpath[index++] = '.';
memcpy(&fullpath[index], extension, extLen);
index += extLen;
}
fullpath[index++] = '\0';
jobject str = env->NewStringUTF(fullpath);
if (env->ExceptionCheck()) {
return true;
}
env->SetObjectArrayElement(vdata->array, vdata->size, str);
if (env->ExceptionCheck()) {
return true;
}
}
vdata->size++; // always count so the total size is returned
return true;
}
/*
* Class: jdk_internal_jimage_ImageNativeSubstrate
* Method: JIMAGE_Resources
* Signature: (J)V
*/
JNIEXPORT jint JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Resources
(JNIEnv *env, jclass, jlong jimageHandle,
jobjectArray outputNames) {
struct VisitorData vdata;
vdata.env = env;
vdata.max = 0;
vdata.size = 0;
vdata.array = outputNames;
vdata.max = (outputNames != NULL) ? env->GetArrayLength(outputNames) : 0;
JIMAGE_ResourceIterator((JImageFile*) jimageHandle, &resourceVisitor, &vdata);
return vdata.size;
}
/*
* Class: jdk_internal_jimage_ImageNativeSubstrate
* Method: JIMAGE_PackageToModule
* Signature: (JLjava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1PackageToModule
(JNIEnv *env, jclass, jlong jimageHandle, jstring package_name) {
const char *native_package = NULL;
const char *native_module = NULL;
jstring module = NULL;
native_package = env->GetStringUTFChars(package_name, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
native_module = JIMAGE_PackageToModule((JImageFile*) jimageHandle, native_package);
if (native_module != NULL) {
module = env->NewStringUTF(native_module);
}
env->ReleaseStringUTFChars(package_name, native_package);
return module;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
ImageDecompressor::image_decompressor_close();
}