fix: use variable length encoding instead short for offsets (can overflow) (#1489)

This commit is contained in:
Skylot 2022-05-22 14:21:25 +01:00
parent e07332d49a
commit 6448f0e32b
No known key found for this signature in database
GPG Key ID: 1E23F5B52567AA39
10 changed files with 130 additions and 49 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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;
}
}
}
}

View File

@ -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));
}
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}
}