GP-6886: Fix OOM in ObjectStorageStreamAdapter

This commit is contained in:
Dan
2026-06-29 12:48:07 +00:00
parent 6fd250d041
commit 9767a16bde
2 changed files with 402 additions and 310 deletions

View File

@@ -28,6 +28,7 @@ import org.apache.logging.log4j.Logger;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.util.Msg;
/** /**
* {@link GhidraObjectInputFilter} provides global serial input filter for use with Ghidra server * {@link GhidraObjectInputFilter} provides global serial input filter for use with Ghidra server
@@ -64,11 +65,24 @@ public class GhidraObjectInputFilter implements ObjectInputFilter {
private static final String MAXDEPTH = "maxdepth"; private static final String MAXDEPTH = "maxdepth";
private static final String MAXBYTES = "maxbytes"; private static final String MAXBYTES = "maxbytes";
private static int getMaxArrayFromProperties() {
String limitStr = System.getProperty("ghidra.serial.array.limit", "200000");
try {
return Integer.parseInt(limitStr);
}
catch (Exception e) {
Msg.error(GhidraObjectInputFilter.class,
"Could not parse ghidra.serial.array.limit: %s. A decimal integer is required"
.formatted(limitStr));
}
return 200_000;
}
// NOTE: Be sure to update serialFilterREADME.md if values are updated. // NOTE: Be sure to update serialFilterREADME.md if values are updated.
private int MAXARRAY_DEFAULT = 200_000; public static final int MAXARRAY_DEFAULT = getMaxArrayFromProperties();
private int MAXREFS_DEFAULT = 10_000; public static final int MAXREFS_DEFAULT = 10_000;
private int MAXDEPTH_DEFAULT = 50; public static final int MAXDEPTH_DEFAULT = 50;
private int MAXBYTES_DEFAULT = 32 * 1024 * 1024; // 32MB public static final int MAXBYTES_DEFAULT = 32 * 1024 * 1024; // 32MB
private long maxArray; private long maxArray;
private long maxRefs; private long maxRefs;

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,376 +18,454 @@ package ghidra.util;
import java.io.*; import java.io.*;
/** /**
* Implementation for ObjectStorage to save and restore Strings and * Implementation for ObjectStorage to save and restore Strings and Java primitives using an
* Java primitives using an ObjectOutputStream and ObjectInputStream, * ObjectOutputStream and ObjectInputStream, respectively.
* respectively.
* *
* *
*/ */
public class ObjectStorageStreamAdapter implements ObjectStorage { public class ObjectStorageStreamAdapter implements ObjectStorage {
private static int getMaxArrayFromProperties() {
String limitStr = System.getProperty("ghidra.serial.array.limit", "200000");
try {
return Integer.parseInt(limitStr);
}
catch (Exception e) {
Msg.error(ObjectStorageStreamAdapter.class,
"Could not parse ghidra.serial.array.limit: %s. A decimal integer is required"
.formatted(limitStr));
}
return 200_000;
}
public static final int MAXARRAY = getMaxArrayFromProperties();
ObjectOutputStream out; ObjectOutputStream out;
ObjectInputStream in; ObjectInputStream in;
/**
* Constructor for ObjectStorageStreamAdapter. /**
* @param out output stream to write to * Constructor for ObjectStorageStreamAdapter.
*/ *
public ObjectStorageStreamAdapter(ObjectOutputStream out) { * @param out output stream to write to
this.out = out; */
} public ObjectStorageStreamAdapter(ObjectOutputStream out) {
/** this.out = out;
* Constructor for new ObjectStorageStreamAdapter }
* @param in input stream to read from
*/ /**
public ObjectStorageStreamAdapter(ObjectInputStream in) { * Constructor for new ObjectStorageStreamAdapter
this.in = in; *
} * @param in input stream to read from
*/
public ObjectStorageStreamAdapter(ObjectInputStream in) {
this.in = in;
}
@Override @Override
public void putInt(int value) { public void putInt(int value) {
try { try {
out.writeInt(value); out.writeInt(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putByte(byte value) { public void putByte(byte value) {
try { try {
out.writeByte(value); out.writeByte(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putShort(short value) { public void putShort(short value) {
try { try {
out.writeShort(value); out.writeShort(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putLong(long value) { public void putLong(long value) {
try { try {
out.writeLong(value); out.writeLong(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putString(String value) { public void putString(String value) {
try { try {
out.writeObject(value); out.writeObject(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putBoolean(boolean value) { public void putBoolean(boolean value) {
try { try {
out.writeBoolean(value); out.writeBoolean(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putFloat(float value) { public void putFloat(float value) {
try { try {
out.writeFloat(value); out.writeFloat(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putDouble(double value) { public void putDouble(double value) {
try { try {
out.writeDouble(value); out.writeDouble(value);
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public int getInt() { public int getInt() {
try { try {
return in.readInt(); return in.readInt();
} catch (IOException e) { }
return 0; catch (IOException e) {
} return 0;
} }
}
@Override @Override
public byte getByte() { public byte getByte() {
try { try {
return in.readByte(); return in.readByte();
} catch (IOException e) { }
return (byte)0; catch (IOException e) {
} return (byte) 0;
} }
}
@Override @Override
public short getShort() { public short getShort() {
try { try {
return in.readShort(); return in.readShort();
} catch (IOException e) { }
return (short)0; catch (IOException e) {
} return (short) 0;
} }
}
@Override @Override
public long getLong() { public long getLong() {
try { try {
return in.readLong(); return in.readLong();
} catch (IOException e) { }
return 0; catch (IOException e) {
} return 0;
} }
}
@Override @Override
public boolean getBoolean() { public boolean getBoolean() {
try { try {
return in.readBoolean(); return in.readBoolean();
} catch (IOException e) { }
return false; catch (IOException e) {
} return false;
} }
}
@Override @Override
public String getString() { public String getString() {
try { try {
return (String)in.readObject(); return (String) in.readObject();
}catch(Exception e) { }
return null; catch (Exception e) {
} return null;
} }
}
@Override @Override
public float getFloat() { public float getFloat() {
try { try {
return in.readFloat(); return in.readFloat();
} catch (IOException e) { }
return 0; catch (IOException e) {
} return 0;
} }
}
@Override @Override
public double getDouble() { public double getDouble() {
try { try {
return in.readDouble(); return in.readDouble();
} catch (IOException e) { }
return 0.0; catch (IOException e) {
} return 0.0;
} }
}
protected void checkWriteArrayLength(int n) throws IOException {
if (n > MAXARRAY) {
throw new IOException("Array size %d exceeds max of %d".formatted(n, MAXARRAY));
}
out.writeInt(n);
}
@Override @Override
public void putInts(int[] value) { public void putInts(int[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeInt(value[i]); out.writeInt(value[i]);
} }
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putBytes(byte[] value) { public void putBytes(byte[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeByte(value[i]); out.writeByte(value[i]);
} }
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putShorts(short[] value) { public void putShorts(short[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeShort(value[i]); out.writeShort(value[i]);
} }
} catch (IOException e) {} }
catch (IOException e) {
}
} }
@Override @Override
public void putLongs(long[] value) { public void putLongs(long[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeLong(value[i]); out.writeLong(value[i]);
} }
} catch (IOException e) {} }
catch (IOException e) {
}
} }
@Override @Override
public void putFloats(float[] value) { public void putFloats(float[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeFloat(value[i]); out.writeFloat(value[i]);
} }
} catch (IOException e) {} }
catch (IOException e) {
} }
}
@Override @Override
public void putDoubles(double[] value) { public void putDoubles(double[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeDouble(value[i]); out.writeDouble(value[i]);
} }
} catch (IOException e) {} }
} catch (IOException e) {
}
}
@Override @Override
public void putStrings(String[] value) { public void putStrings(String[] value) {
try { try {
if (value == null) { if (value == null) {
out.writeInt(-1); out.writeInt(-1);
return; return;
} }
out.writeInt(value.length); checkWriteArrayLength(value.length);
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
out.writeObject(value[i]); out.writeObject(value[i]);
} }
} catch (IOException e) {} }
} catch (IOException e) {
}
}
protected int checkReadArrayLength() throws IOException {
int n = in.readInt();
if (n > MAXARRAY) {
throw new IOException("Array size %d exceeds max of %d".formatted(n, MAXARRAY));
}
return n;
}
@Override @Override
public int[] getInts() { public int[] getInts() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
int[] r = new int[n]; int[] r = new int[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = in.readInt(); r[i] = in.readInt();
} }
return r; return r;
} catch (IOException e) { }
return new int[0]; catch (IOException e) {
} return new int[0];
} }
}
@Override @Override
public byte[] getBytes() { public byte[] getBytes() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
byte[] r = new byte[n]; byte[] r = new byte[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = in.readByte(); r[i] = in.readByte();
} }
return r; return r;
} catch (IOException e) { }
return new byte[0]; catch (IOException e) {
} return new byte[0];
} }
}
@Override @Override
public short[] getShorts() { public short[] getShorts() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
short[] r = new short[n]; short[] r = new short[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = in.readShort(); r[i] = in.readShort();
} }
return r; return r;
} catch (IOException e) { }
return new short[0]; catch (IOException e) {
} return new short[0];
} }
}
@Override @Override
public long[] getLongs() { public long[] getLongs() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
long[] r = new long[n]; long[] r = new long[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = in.readLong(); r[i] = in.readLong();
} }
return r; return r;
} catch (IOException e) { }
return new long[0]; catch (IOException e) {
} return new long[0];
} }
}
@Override @Override
public float[] getFloats() { public float[] getFloats() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
float[] r = new float[n]; float[] r = new float[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = in.readFloat(); r[i] = in.readFloat();
} }
return r; return r;
} catch (IOException e) { }
return new float[0]; catch (IOException e) {
} return new float[0];
} }
}
@Override @Override
public double[] getDoubles() { public double[] getDoubles() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
double[] r = new double[n]; double[] r = new double[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = in.readDouble(); r[i] = in.readDouble();
} }
return r; return r;
} catch (IOException e) { }
return new double[0]; catch (IOException e) {
} return new double[0];
} }
}
@Override @Override
public String[] getStrings() { public String[] getStrings() {
try { try {
int n = in.readInt(); int n = checkReadArrayLength();
if (n < 0) { if (n < 0) {
return null; return null;
} }
String[] r = new String[n]; String[] r = new String[n];
for(int i=0;i<n;i++) { for (int i = 0; i < n; i++) {
r[i] = (String)in.readObject(); r[i] = (String) in.readObject();
} }
return r; return r;
} catch (Exception e) { }
return new String[0]; catch (Exception e) {
} return new String[0];
} }
}
} }