3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-07 23:03:04 +00:00

Catch out-of-bound reads and rethrow as ProtocolExceptions.

This commit is contained in:
Matt Corallo 2013-05-28 17:17:05 +02:00 committed by Mike Hearn
parent dbf7728b4f
commit a7987585b8
8 changed files with 97 additions and 57 deletions

View File

@ -148,7 +148,7 @@ public class Block extends Message {
hash = null; hash = null;
} }
private void parseHeader() { private void parseHeader() throws ProtocolException {
if (headerParsed) if (headerParsed)
return; return;
@ -230,7 +230,7 @@ public class Block extends Message {
/* /*
* Block uses some special handling for lazy parsing and retention of cached bytes. Parsing and serializing the * Block uses some special handling for lazy parsing and retention of cached bytes. Parsing and serializing the
* block header and the transaction list are both non-trivial so there are good efficiency gains to be had by * block header and the transaction list are both non-trivial so there are good efficiency gains to be had by
* separating them. There are many cases where a user may need access to access or change one or the other but not both. * separating them. There are many cases where a user may need to access or change one or the other but not both.
* *
* With this in mind we ignore the inherited checkParse() and unCache() methods and implement a separate version * With this in mind we ignore the inherited checkParse() and unCache() methods and implement a separate version
* of them for both header and transactions. * of them for both header and transactions.
@ -242,9 +242,15 @@ public class Block extends Message {
private void maybeParseHeader() { private void maybeParseHeader() {
if (headerParsed || bytes == null) if (headerParsed || bytes == null)
return; return;
try {
parseHeader(); parseHeader();
if (!(headerBytesValid || transactionBytesValid)) if (!(headerBytesValid || transactionBytesValid))
bytes = null; bytes = null;
} catch (ProtocolException e) {
throw new LazyParseException(
"ProtocolException caught during lazy parse. For safe access to fields call ensureParsed before attempting read or write access",
e);
}
} }
private void maybeParseTransactions() { private void maybeParseTransactions() {

View File

@ -402,13 +402,18 @@ public abstract class Message implements Serializable {
return length; return length;
} }
long readUint32() { long readUint32() throws ProtocolException {
try {
long u = Utils.readUint32(bytes, cursor); long u = Utils.readUint32(bytes, cursor);
cursor += 4; cursor += 4;
return u; return u;
} catch (ArrayIndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
Sha256Hash readHash() { Sha256Hash readHash() throws ProtocolException {
try {
byte[] hash = new byte[32]; byte[] hash = new byte[32];
System.arraycopy(bytes, cursor, hash, 0, 32); System.arraycopy(bytes, cursor, hash, 0, 32);
// We have to flip it around, as it's been read off the wire in little endian. // We have to flip it around, as it's been read off the wire in little endian.
@ -416,47 +421,67 @@ public abstract class Message implements Serializable {
hash = Utils.reverseBytes(hash); hash = Utils.reverseBytes(hash);
cursor += 32; cursor += 32;
return new Sha256Hash(hash); return new Sha256Hash(hash);
} catch (IndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
long readInt64() { long readInt64() throws ProtocolException {
try {
long u = Utils.readInt64(bytes, cursor); long u = Utils.readInt64(bytes, cursor);
cursor += 8; cursor += 8;
return u; return u;
} catch (ArrayIndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
BigInteger readUint64() { BigInteger readUint64() throws ProtocolException {
try {
// Java does not have an unsigned 64 bit type. So scrape it off the wire then flip. // Java does not have an unsigned 64 bit type. So scrape it off the wire then flip.
byte[] valbytes = new byte[8]; byte[] valbytes = new byte[8];
System.arraycopy(bytes, cursor, valbytes, 0, 8); System.arraycopy(bytes, cursor, valbytes, 0, 8);
valbytes = Utils.reverseBytes(valbytes); valbytes = Utils.reverseBytes(valbytes);
cursor += valbytes.length; cursor += valbytes.length;
return new BigInteger(valbytes); return new BigInteger(valbytes);
} catch (IndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
long readVarInt() { long readVarInt() throws ProtocolException {
return readVarInt(0); return readVarInt(0);
} }
long readVarInt(int offset) { long readVarInt(int offset) throws ProtocolException {
try {
VarInt varint = new VarInt(bytes, cursor + offset); VarInt varint = new VarInt(bytes, cursor + offset);
cursor += offset + varint.getOriginalSizeInBytes(); cursor += offset + varint.getOriginalSizeInBytes();
return varint.value; return varint.value;
} catch (ArrayIndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
byte[] readBytes(int length) { byte[] readBytes(int length) throws ProtocolException {
try {
byte[] b = new byte[length]; byte[] b = new byte[length];
System.arraycopy(bytes, cursor, b, 0, length); System.arraycopy(bytes, cursor, b, 0, length);
cursor += length; cursor += length;
return b; return b;
} catch (IndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
byte[] readByteArray() { byte[] readByteArray() throws ProtocolException {
long len = readVarInt(); long len = readVarInt();
return readBytes((int)len); return readBytes((int)len);
} }
String readStr() { String readStr() throws ProtocolException {
try {
VarInt varInt = new VarInt(bytes, cursor); VarInt varInt = new VarInt(bytes, cursor);
if (varInt.value == 0) { if (varInt.value == 0) {
cursor += 1; cursor += 1;
@ -471,6 +496,11 @@ public abstract class Message implements Serializable {
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // Cannot happen, UTF-8 is always supported. throw new RuntimeException(e); // Cannot happen, UTF-8 is always supported.
} }
} catch (ArrayIndexOutOfBoundsException e) {
throw new ProtocolException(e);
} catch (IndexOutOfBoundsException e) {
throw new ProtocolException(e);
}
} }
boolean hasMoreBytes() { boolean hasMoreBytes() {

View File

@ -129,7 +129,7 @@ public class PeerAddress extends ChildMessage {
} }
@Override @Override
protected void parse() { protected void parse() throws ProtocolException {
// Format of a serialized address: // Format of a serialized address:
// uint32 timestamp // uint32 timestamp
// uint64 services (flags determining what the node can do) // uint64 services (flags determining what the node can do)

View File

@ -54,7 +54,7 @@ public class Ping extends Message {
try { try {
nonce = readInt64(); nonce = readInt64();
hasNonce = true; hasNonce = true;
} catch(ArrayIndexOutOfBoundsException e) { } catch(ProtocolException e) {
hasNonce = false; hasNonce = false;
} }
length = hasNonce ? 8 : 0; length = hasNonce ? 8 : 0;

View File

@ -17,7 +17,7 @@
package com.google.bitcoin.core; package com.google.bitcoin.core;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class ProtocolException extends Exception { public class ProtocolException extends VerificationException {
public ProtocolException(String msg) { public ProtocolException(String msg) {
super(msg); super(msg);

View File

@ -121,7 +121,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
this.parentTransaction = parentTransaction; this.parentTransaction = parentTransaction;
} }
protected void parseLite() { protected void parseLite() throws ProtocolException {
int curs = cursor; int curs = cursor;
int scriptLen = (int) readVarInt(36); int scriptLen = (int) readVarInt(36);
length = cursor - offset + scriptLen + 4; length = cursor - offset + scriptLen + 4;

View File

@ -121,7 +121,7 @@ public class TransactionOutput extends ChildMessage implements Serializable {
return scriptPubKey; return scriptPubKey;
} }
protected void parseLite() { protected void parseLite() throws ProtocolException {
// TODO: There is no reason to use BigInteger for values, they are always smaller than 21 million * COIN // TODO: There is no reason to use BigInteger for values, they are always smaller than 21 million * COIN
// The only reason to use BigInteger would be to properly read values from the reference implementation, however // The only reason to use BigInteger would be to properly read values from the reference implementation, however
// the reference implementation uses signed 64-bit integers for its values as well (though it probably shouldn't) // the reference implementation uses signed 64-bit integers for its values as well (though it probably shouldn't)

View File

@ -22,6 +22,10 @@ public class VerificationException extends Exception {
super(msg); super(msg);
} }
public VerificationException(Exception e) {
super(e);
}
public VerificationException(String msg, Throwable t) { public VerificationException(String msg, Throwable t) {
super(msg, t); super(msg, t);
} }