mirror of
https://github.com/skylot/jadx.git
synced 2024-11-27 14:40:54 +00:00
fix: use variable length encoding instead short for offsets (can overflow) (#1489)
This commit is contained in:
parent
e07332d49a
commit
6448f0e32b
@ -206,7 +206,6 @@ public final class JavaClass implements JavaNode {
|
||||
// ignore declarations and offset annotations
|
||||
continue;
|
||||
}
|
||||
// ignore declarations
|
||||
JavaNode annNode = rootDec.getJavaNodeByCodeAnnotation(codeInfo, ann);
|
||||
if (annNode == null && LOG.isDebugEnabled()) {
|
||||
LOG.debug("Failed to resolve code annotation, cls: {}, pos: {}, ann: {}", this, entry.getKey(), ann);
|
||||
|
@ -25,6 +25,8 @@ import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.utils.codecache.disk.adapters.CodeAnnotationAdapter;
|
||||
|
||||
import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.readUVInt;
|
||||
import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.writeUVInt;
|
||||
import static java.nio.file.StandardOpenOption.CREATE;
|
||||
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
|
||||
import static java.nio.file.StandardOpenOption.WRITE;
|
||||
@ -68,8 +70,8 @@ public class CodeMetadataAdapter {
|
||||
private void writeLines(DataOutput out, Map<Integer, Integer> lines) throws IOException {
|
||||
out.writeInt(lines.size());
|
||||
for (Map.Entry<Integer, Integer> entry : lines.entrySet()) {
|
||||
out.writeShort(entry.getKey());
|
||||
out.writeShort(entry.getValue());
|
||||
writeUVInt(out, entry.getKey());
|
||||
writeUVInt(out, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,8 +82,8 @@ public class CodeMetadataAdapter {
|
||||
}
|
||||
Map<Integer, Integer> lines = new HashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
int key = in.readShort();
|
||||
int value = in.readShort();
|
||||
int key = readUVInt(in);
|
||||
int value = readUVInt(in);
|
||||
lines.put(key, value);
|
||||
}
|
||||
return lines;
|
||||
@ -90,7 +92,7 @@ public class CodeMetadataAdapter {
|
||||
private void writeAnnotations(DataOutputStream out, Map<Integer, ICodeAnnotation> annotations) throws IOException {
|
||||
out.writeInt(annotations.size());
|
||||
for (Map.Entry<Integer, ICodeAnnotation> entry : annotations.entrySet()) {
|
||||
out.writeInt(entry.getKey());
|
||||
writeUVInt(out, entry.getKey());
|
||||
codeAnnotationAdapter.write(out, entry.getValue());
|
||||
}
|
||||
}
|
||||
@ -102,7 +104,7 @@ public class CodeMetadataAdapter {
|
||||
}
|
||||
Map<Integer, ICodeAnnotation> map = new HashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
int pos = in.readInt();
|
||||
int pos = readUVInt(in);
|
||||
ICodeAnnotation ann = codeAnnotationAdapter.read(in);
|
||||
if (ann != null) {
|
||||
map.put(pos, ann);
|
||||
|
@ -37,7 +37,7 @@ import jadx.core.utils.files.FileUtils;
|
||||
public class DiskCodeCache implements ICodeCache {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DiskCodeCache.class);
|
||||
|
||||
private static final int DATA_FORMAT_VERSION = 8;
|
||||
private static final int DATA_FORMAT_VERSION = 9;
|
||||
|
||||
private final Path srcDir;
|
||||
private final Path metaDir;
|
||||
|
@ -1,26 +0,0 @@
|
||||
package jadx.gui.utils.codecache.disk.adapters;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class BaseDataAdapter<T> implements DataAdapter<T> {
|
||||
|
||||
public void writeNullableUTF(DataOutput out, @Nullable String str) throws IOException {
|
||||
if (str == null) {
|
||||
out.writeByte(0);
|
||||
} else {
|
||||
out.writeByte(1);
|
||||
out.writeUTF(str);
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable String readNullableUTF(DataInput in) throws IOException {
|
||||
if (in.readByte() == 0) {
|
||||
return null;
|
||||
}
|
||||
return in.readUTF();
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package jadx.gui.utils.codecache.disk.adapters;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class DataAdapterHelper {
|
||||
|
||||
public static void writeNullableUTF(DataOutput out, @Nullable String str) throws IOException {
|
||||
if (str == null) {
|
||||
out.writeByte(0);
|
||||
} else {
|
||||
out.writeByte(1);
|
||||
out.writeUTF(str);
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable String readNullableUTF(DataInput in) throws IOException {
|
||||
if (in.readByte() == 0) {
|
||||
return null;
|
||||
}
|
||||
return in.readUTF();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write unsigned variable length integer (ULEB128 encoding)
|
||||
*/
|
||||
public static void writeUVInt(DataOutput out, int val) throws IOException {
|
||||
if (val < 0) {
|
||||
throw new IllegalArgumentException("Expect value >= 0, got: " + val);
|
||||
}
|
||||
int current = val;
|
||||
int next = val;
|
||||
while (true) {
|
||||
next >>>= 7;
|
||||
if (next == 0) {
|
||||
// last byte
|
||||
out.writeByte(current & 0x7f);
|
||||
return;
|
||||
}
|
||||
out.writeByte((current & 0x7f) | 0x80);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read unsigned variable length integer (ULEB128 encoding)
|
||||
*/
|
||||
public static int readUVInt(DataInput in) throws IOException {
|
||||
int result = 0;
|
||||
int shift = 0;
|
||||
while (true) {
|
||||
byte v = in.readByte();
|
||||
result |= (v & (byte) 0x7f) << shift;
|
||||
shift += 7;
|
||||
if ((v & 0x80) != 0x80) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,11 +12,11 @@ public class InsnCodeOffsetAdapter implements DataAdapter<InsnCodeOffset> {
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out, InsnCodeOffset value) throws IOException {
|
||||
out.writeShort(value.getOffset());
|
||||
DataAdapterHelper.writeUVInt(out, value.getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsnCodeOffset read(DataInput in) throws IOException {
|
||||
return new InsnCodeOffset(in.readShort());
|
||||
return new InsnCodeOffset(DataAdapterHelper.readUVInt(in));
|
||||
}
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ public class NodeDeclareRefAdapter implements DataAdapter<NodeDeclareRef> {
|
||||
throw new RuntimeException("Null node in NodeDeclareRef");
|
||||
}
|
||||
refAdapter.write(out, node);
|
||||
out.writeShort(value.getDefPos());
|
||||
DataAdapterHelper.writeUVInt(out, value.getDefPos());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeDeclareRef read(DataInput in) throws IOException {
|
||||
ICodeNodeRef ref = (ICodeNodeRef) refAdapter.read(in);
|
||||
int defPos = in.readShort();
|
||||
int defPos = DataAdapterHelper.readUVInt(in);
|
||||
NodeDeclareRef nodeDeclareRef = new NodeDeclareRef(ref);
|
||||
nodeDeclareRef.setDefPos(defPos);
|
||||
// restore def position if loading metadata without actual decompilation
|
||||
|
@ -8,7 +8,12 @@ import jadx.api.metadata.annotations.VarNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public class VarNodeAdapter extends BaseDataAdapter<VarNode> {
|
||||
import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.readNullableUTF;
|
||||
import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.readUVInt;
|
||||
import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.writeNullableUTF;
|
||||
import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.writeUVInt;
|
||||
|
||||
public class VarNodeAdapter implements DataAdapter<VarNode> {
|
||||
private final MethodNodeAdapter mthAdapter;
|
||||
|
||||
public VarNodeAdapter(MethodNodeAdapter mthAdapter) {
|
||||
@ -18,8 +23,8 @@ public class VarNodeAdapter extends BaseDataAdapter<VarNode> {
|
||||
@Override
|
||||
public void write(DataOutput out, VarNode value) throws IOException {
|
||||
mthAdapter.write(out, value.getMth());
|
||||
out.writeShort(value.getReg());
|
||||
out.writeShort(value.getSsa());
|
||||
writeUVInt(out, value.getReg());
|
||||
writeUVInt(out, value.getSsa());
|
||||
ArgTypeAdapter.INSTANCE.write(out, value.getType());
|
||||
writeNullableUTF(out, value.getName());
|
||||
}
|
||||
@ -27,8 +32,8 @@ public class VarNodeAdapter extends BaseDataAdapter<VarNode> {
|
||||
@Override
|
||||
public VarNode read(DataInput in) throws IOException {
|
||||
MethodNode mth = mthAdapter.read(in);
|
||||
int reg = in.readShort();
|
||||
int ssa = in.readShort();
|
||||
int reg = readUVInt(in);
|
||||
int ssa = readUVInt(in);
|
||||
ArgType type = ArgTypeAdapter.INSTANCE.read(in);
|
||||
String name = readNullableUTF(in);
|
||||
return new VarNode(mth, reg, ssa, type, name);
|
||||
|
@ -6,21 +6,19 @@ import java.io.IOException;
|
||||
|
||||
import jadx.api.metadata.annotations.VarRef;
|
||||
|
||||
public class VarRefAdapter extends BaseDataAdapter<VarRef> {
|
||||
public class VarRefAdapter implements DataAdapter<VarRef> {
|
||||
|
||||
public static final VarRefAdapter INSTANCE = new VarRefAdapter();
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out, VarRef value) throws IOException {
|
||||
int refPos = value.getRefPos();
|
||||
if (refPos <= 0) {
|
||||
throw new RuntimeException("Variable refPos is invalid: " + value);
|
||||
}
|
||||
out.writeInt(refPos);
|
||||
DataAdapterHelper.writeUVInt(out, refPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarRef read(DataInput in) throws IOException {
|
||||
return VarRef.fromPos(in.readInt());
|
||||
int refPos = DataAdapterHelper.readUVInt(in);
|
||||
return VarRef.fromPos(refPos);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
package jadx.gui.utils.codecache.disk.adapters;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DataAdapterHelperTest {
|
||||
|
||||
@Test
|
||||
void uvInt() throws IOException {
|
||||
checkUVIntFor(0);
|
||||
checkUVIntFor(7);
|
||||
checkUVIntFor(0x7f);
|
||||
checkUVIntFor(0x80);
|
||||
checkUVIntFor(0x256);
|
||||
checkUVIntFor(Byte.MAX_VALUE);
|
||||
checkUVIntFor(Short.MAX_VALUE);
|
||||
checkUVIntFor(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
private void checkUVIntFor(int val) throws IOException {
|
||||
assertThat(writeReadUVInt(val)).isEqualTo(val);
|
||||
}
|
||||
|
||||
private int writeReadUVInt(int val) throws IOException {
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(byteOut);
|
||||
DataAdapterHelper.writeUVInt(out, val);
|
||||
|
||||
DataInput in = new DataInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
|
||||
return DataAdapterHelper.readUVInt(in);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user