io.github.dmlloyd.classfile.impl.verifier.VerificationTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdk-classfile-backport Show documentation
Show all versions of jdk-classfile-backport Show documentation
An unofficial backport of the JDK Classfile API to Java 17
/*
* Copyright (c) 2022, 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 io.github.dmlloyd.classfile.impl.verifier;
import static io.github.dmlloyd.classfile.impl.verifier.VerificationType.ITEM_Object;
import static io.github.dmlloyd.classfile.impl.verifier.VerificationType.ITEM_Uninitialized;
import static io.github.dmlloyd.classfile.impl.verifier.VerificationType.ITEM_UninitializedThis;
/**
* @see hotspot/share/classfile/stackMapTable.hpp
* @see hotspot/share/classfile/stackMapTable.cpp
*/
class VerificationTable {
private final int _code_length;
private final int _frame_count;
private final VerificationFrame[] _frame_array;
private final VerifierImpl _verifier;
int get_frame_count() {
return _frame_count;
}
int get_offset(int index) {
return _frame_array[index].offset();
}
static class StackMapStream {
private final byte[] _data;
private int _index;
private final VerifierImpl _verifier;
StackMapStream(byte[] ah, VerifierImpl context) {
_data = ah;
_index = 0;
_verifier = context;
}
int get_u1() {
if (_data == null || _index >= _data.length) {
_verifier.classError("access beyond the end of attribute");
}
return _data[_index++] & 0xff;
}
int get_u2() {
int res = get_u1() << 8;
return res | get_u1();
}
boolean at_end() {
return (_data == null) || (_index == _data.length);
}
}
VerificationTable(byte[] stackmap_data, VerificationFrame init_frame, int max_locals, int max_stack, byte[] code_data, int code_len,
VerificationWrapper.ConstantPoolWrapper cp, VerifierImpl v) {
_verifier = v;
var reader = new StackMapReader(stackmap_data, code_data, code_len, cp, v);
_code_length = code_len;
_frame_count = reader.get_frame_count();
_frame_array = new VerificationFrame[_frame_count];
if (_frame_count > 0) {
VerificationFrame pre_frame = init_frame;
for (int i = 0; i < _frame_count; i++) {
VerificationFrame frame = reader.next(pre_frame, i == 0, max_locals, max_stack);
_frame_array[i] = frame;
int offset = frame.offset();
if (offset >= code_len || code_data[offset] == 0) {
_verifier.verifyError("StackMapTable error: bad offset");
}
pre_frame = frame;
}
}
reader.check_end();
}
int get_index_from_offset(int offset) {
int i = 0;
for (; i < _frame_count; i++) {
if (_frame_array[i].offset() == offset) {
return i;
}
}
return i;
}
boolean match_stackmap(VerificationFrame frame, int target, boolean match, boolean update) {
int index = get_index_from_offset(target);
return match_stackmap(frame, target, index, match, update);
}
boolean match_stackmap(VerificationFrame frame, int target, int frame_index, boolean match, boolean update) {
if (frame_index < 0 || frame_index >= _frame_count) {
_verifier.verifyError(String.format("Expecting a stackmap frame at branch target %d", target));
}
VerificationFrame stackmap_frame = _frame_array[frame_index];
boolean result = true;
if (match) {
result = frame.is_assignable_to(stackmap_frame);
}
if (update) {
int lsize = stackmap_frame.locals_size();
int ssize = stackmap_frame.stack_size();
if (frame.locals_size() > lsize || frame.stack_size() > ssize) {
frame.reset();
}
frame.set_locals_size(lsize);
frame.copy_locals(stackmap_frame);
frame.set_stack_size(ssize);
frame.copy_stack(stackmap_frame);
frame.set_flags(stackmap_frame.flags());
}
return result;
}
void check_jump_target(VerificationFrame frame, int target) {
boolean match = match_stackmap(frame, target, true, false);
if (!match || (target < 0 || target >= _code_length)) {
_verifier.verifyError(String.format("Inconsistent stackmap frames at branch target %d", target));
}
}
static class StackMapReader {
private final VerificationWrapper.ConstantPoolWrapper _cp;
private final StackMapStream _stream;
private final byte[] _code_data;
private final int _code_length;
private final int _frame_count;
void check_verification_type_array_size(int size, int max_size) {
if (size < 0 || size > max_size) {
_verifier.classError("StackMapTable format error: bad type array size");
}
}
private static final int
SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
SAME_EXTENDED = 251,
FULL = 255;
public int get_frame_count() {
return _frame_count;
}
public void check_end() {
if (!_stream.at_end()) {
_verifier.classError("wrong attribute size");
}
}
private final VerifierImpl _verifier;
public StackMapReader(byte[] stackmapData, byte[] code_data, int code_len, VerificationWrapper.ConstantPoolWrapper cp, VerifierImpl context) {
this._verifier = context;
_stream = new StackMapStream(stackmapData, _verifier);
if (stackmapData != null) {
_frame_count = _stream.get_u2();
} else {
_frame_count = 0;
}
_code_data = code_data;
_code_length = code_len;
_cp = cp;
}
int chop(VerificationType[] locals, int length, int chops) {
if (locals == null) return -1;
int pos = length - 1;
for (int i=0; i= nconstants || _cp.tagAt(class_index) != VerifierImpl.JVM_CONSTANT_Class) {
_verifier.classError("bad class index");
}
return VerificationType.reference_type(_cp.classNameAt(class_index));
}
if (tag == ITEM_UninitializedThis) {
if (flags != null) {
flags[0] |= VerificationFrame.FLAG_THIS_UNINIT;
}
return VerificationType.uninitialized_this_type;
}
if (tag == ITEM_Uninitialized) {
int offset = _stream.get_u2();
if (offset >= _code_length || _code_data[offset] != VerifierImpl.NEW_OFFSET) {
_verifier.classError("StackMapTable format error: bad offset for Uninitialized");
}
return VerificationType.uninitialized_type(offset);
}
_verifier.classError("bad verification type");
return VerificationType.bogus_type;
}
public VerificationFrame next(VerificationFrame pre_frame, boolean first, int max_locals, int max_stack) {
VerificationFrame frame;
int offset;
VerificationType[] locals = null;
int frame_type = _stream.get_u1();
if (frame_type < 64) {
if (first) {
offset = frame_type;
if (pre_frame.locals_size() > 0) {
locals = new VerificationType[pre_frame.locals_size()];
}
} else {
offset = pre_frame.offset() + frame_type + 1;
locals = pre_frame.locals();
}
frame = new VerificationFrame(offset, pre_frame.flags(), pre_frame.locals_size(), 0, max_locals, max_stack, locals, null, _verifier);
if (first && locals != null) {
frame.copy_locals(pre_frame);
}
return frame;
}
if (frame_type < 128) {
if (first) {
offset = frame_type - 64;
if (pre_frame.locals_size() > 0) {
locals = new VerificationType[pre_frame.locals_size()];
}
} else {
offset = pre_frame.offset() + frame_type - 63;
locals = pre_frame.locals();
}
VerificationType[] stack = new VerificationType[2];
int stack_size = 1;
stack[0] = parse_verification_type(null);
if (stack[0].is_category2()) {
stack[1] = stack[0].to_category2_2nd(_verifier);
stack_size = 2;
}
check_verification_type_array_size(stack_size, max_stack);
frame = new VerificationFrame(offset, pre_frame.flags(), pre_frame.locals_size(), stack_size, max_locals, max_stack, locals, stack, _verifier);
if (first && locals != null) {
frame.copy_locals(pre_frame);
}
return frame;
}
int offset_delta = _stream.get_u2();
if (frame_type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
_verifier.classError("reserved frame type");
}
if (frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
if (first) {
offset = offset_delta;
if (pre_frame.locals_size() > 0) {
locals = new VerificationType[pre_frame.locals_size()];
}
} else {
offset = pre_frame.offset() + offset_delta + 1;
locals = pre_frame.locals();
}
VerificationType[] stack = new VerificationType[2];
int stack_size = 1;
stack[0] = parse_verification_type(null);
if (stack[0].is_category2()) {
stack[1] = stack[0].to_category2_2nd(_verifier);
stack_size = 2;
}
check_verification_type_array_size(stack_size, max_stack);
frame = new VerificationFrame(offset, pre_frame.flags(), pre_frame.locals_size(), stack_size, max_locals, max_stack, locals, stack, _verifier);
if (first && locals != null) {
frame.copy_locals(pre_frame);
}
return frame;
}
if (frame_type <= SAME_EXTENDED) {
locals = pre_frame.locals();
int length = pre_frame.locals_size();
int chops = SAME_EXTENDED - frame_type;
int new_length = length;
int flags = pre_frame.flags();
if (chops != 0) {
new_length = chop(locals, length, chops);
check_verification_type_array_size(new_length, max_locals);
flags = 0;
for (int i=0; i 0) {
locals = new VerificationType[new_length];
} else {
locals = null;
}
} else {
offset = pre_frame.offset() + offset_delta + 1;
}
frame = new VerificationFrame(offset, flags, new_length, 0, max_locals, max_stack, locals, null, _verifier);
if (first && locals != null) {
frame.copy_locals(pre_frame);
}
return frame;
} else if (frame_type < SAME_EXTENDED + 4) {
int appends = frame_type - SAME_EXTENDED;
int real_length = pre_frame.locals_size();
int new_length = real_length + appends*2;
locals = new VerificationType[new_length];
VerificationType[] pre_locals = pre_frame.locals();
int i;
for (i=0; i 0) {
locals = new VerificationType[locals_size*2];
}
int i;
for (i=0; i 0) {
stack = new VerificationType[stack_size*2];
}
for (i=0; i
© 2015 - 2024 Weber Informatics LLC | Privacy Policy