- Prevent release of TunnelDataMessage cached ByteArray,
as it may be reused if retried in another transport; a nasty bug causing corrupt messages - Lots of code to detect improper reuse of cached objects after release
This commit is contained in:
@ -33,7 +33,7 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
/** if we can't deliver a tunnel message in 10s, fuck it */
|
||||
private static final int EXPIRATION_PERIOD = 10*1000;
|
||||
|
||||
private static final ByteCache _cache = ByteCache.getInstance(512, DATA_SIZE);
|
||||
private static final ByteCache _cache;
|
||||
/**
|
||||
* When true, it means this tunnelDataMessage is being used as part of a tunnel
|
||||
* processing pipeline, where the byte array is acquired during the TunnelDataMessage's
|
||||
@ -42,9 +42,63 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
* handler's cache, etc), until it is finally released back into the cache when written
|
||||
* to the next peer (or explicitly by the fragment handler's completion).
|
||||
* Setting this to false just increases memory churn
|
||||
*
|
||||
* Well, this is tricky to get right and avoid data corruption,
|
||||
* here's an example after checks were put in:
|
||||
*
|
||||
*
|
||||
10:57:05.197 CRIT [NTCP read 1 ] 2p.data.i2np.TunnelDataMessage: TDM boom
|
||||
net.i2p.data.i2np.I2NPMessageException: TDM data buf use after free
|
||||
at net.i2p.data.i2np.TunnelDataMessage.writeMessageBody(TunnelDataMessage.java:124)
|
||||
at net.i2p.data.i2np.I2NPMessageImpl.toByteArray(I2NPMessageImpl.java:217)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection.bufferedPrepare(NTCPConnection.java:678)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection.send(NTCPConnection.java:293)
|
||||
at net.i2p.router.transport.ntcp.NTCPTransport.outboundMessageReady(NTCPTransport.java:185)
|
||||
at net.i2p.router.transport.TransportImpl.send(TransportImpl.java:357)
|
||||
at net.i2p.router.transport.GetBidsJob.getBids(GetBidsJob.java:80)
|
||||
at net.i2p.router.transport.CommSystemFacadeImpl.processMessage(CommSystemFacadeImpl.java:129)
|
||||
at net.i2p.router.OutNetMessagePool.add(OutNetMessagePool.java:61)
|
||||
at net.i2p.router.transport.TransportImpl.afterSend(TransportImpl.java:252)
|
||||
at net.i2p.router.transport.TransportImpl.afterSend(TransportImpl.java:163)
|
||||
at net.i2p.router.transport.udp.UDPTransport.failed(UDPTransport.java:1314)
|
||||
at net.i2p.router.transport.udp.PeerState.add(PeerState.java:1064)
|
||||
at net.i2p.router.transport.udp.OutboundMessageFragments.add(OutboundMessageFragments.java:146)
|
||||
at net.i2p.router.transport.udp.UDPTransport.send(UDPTransport.java:1098)
|
||||
at net.i2p.router.transport.GetBidsJob.getBids(GetBidsJob.java:80)
|
||||
at net.i2p.router.transport.CommSystemFacadeImpl.processMessage(CommSystemFacadeImpl.java:129)
|
||||
at net.i2p.router.OutNetMessagePool.add(OutNetMessagePool.java:61)
|
||||
at net.i2p.router.tunnel.TunnelParticipant.send(TunnelParticipant.java:172)
|
||||
at net.i2p.router.tunnel.TunnelParticipant.dispatch(TunnelParticipant.java:86)
|
||||
at net.i2p.router.tunnel.TunnelDispatcher.dispatch(TunnelDispatcher.java:351)
|
||||
at net.i2p.router.InNetMessagePool.doShortCircuitTunnelData(InNetMessagePool.java:306)
|
||||
at net.i2p.router.InNetMessagePool.shortCircuitTunnelData(InNetMessagePool.java:291)
|
||||
at net.i2p.router.InNetMessagePool.add(InNetMessagePool.java:160)
|
||||
at net.i2p.router.transport.TransportManager.messageReceived(TransportManager.java:462)
|
||||
at net.i2p.router.transport.TransportImpl.messageReceived(TransportImpl.java:416)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection$ReadState.receiveLastBlock(NTCPConnection.java:1285)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection$ReadState.receiveSubsequent(NTCPConnection.java:1248)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection$ReadState.receiveBlock(NTCPConnection.java:1205)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection.recvUnencryptedI2NP(NTCPConnection.java:1035)
|
||||
at net.i2p.router.transport.ntcp.NTCPConnection.recvEncryptedI2NP(NTCPConnection.java:1018)
|
||||
at net.i2p.router.transport.ntcp.Reader.processRead(Reader.java:167)
|
||||
at net.i2p.router.transport.ntcp.Reader.access$400(Reader.java:17)
|
||||
at net.i2p.router.transport.ntcp.Reader$Runner.run(Reader.java:106)
|
||||
at java.lang.Thread.run(Thread.java:619)
|
||||
at net.i2p.util.I2PThread.run(I2PThread.java:71)
|
||||
*
|
||||
*/
|
||||
private static final boolean PIPELINED_CACHE = true;
|
||||
|
||||
static {
|
||||
if (PIPELINED_CACHE)
|
||||
_cache = ByteCache.getInstance(512, DATA_SIZE);
|
||||
else
|
||||
_cache = null;
|
||||
}
|
||||
|
||||
/** For use-after-free checks. Always false if PIPELINED_CACHE is false. */
|
||||
private boolean _hadCache;
|
||||
|
||||
public TunnelDataMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(TunnelDataMessage.class);
|
||||
@ -63,7 +117,15 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
_tunnelId = id.getTunnelId();
|
||||
}
|
||||
|
||||
public byte[] getData() { return _data; }
|
||||
public byte[] getData() {
|
||||
if (_hadCache && _dataBuf == null) {
|
||||
RuntimeException e = new RuntimeException("TDM data buf use after free");
|
||||
_log.error("TDM boom", e);
|
||||
throw e;
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
public void setData(byte data[]) {
|
||||
if ( (data == null) || (data.length <= 0) )
|
||||
throw new IllegalArgumentException("Empty tunnel payload?");
|
||||
@ -86,6 +148,7 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
if (PIPELINED_CACHE) {
|
||||
_dataBuf = _cache.acquire();
|
||||
_data = _dataBuf.getData();
|
||||
_hadCache = true;
|
||||
} else {
|
||||
_data = new byte[DATA_SIZE];
|
||||
}
|
||||
@ -101,12 +164,24 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
if (_data.length <= 0)
|
||||
throw new I2NPMessageException("Not enough data to write out (data.length=" + _data.length + ")");
|
||||
|
||||
if (_hadCache && _dataBuf == null) {
|
||||
I2NPMessageException e = new I2NPMessageException("TDM data buf use after free");
|
||||
_log.error("TDM boom", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
DataHelper.toLong(out, curIndex, 4, _tunnelId);
|
||||
curIndex += 4;
|
||||
System.arraycopy(_data, 0, out, curIndex, DATA_SIZE);
|
||||
curIndex += _data.length;
|
||||
if (PIPELINED_CACHE)
|
||||
_cache.release(_dataBuf);
|
||||
|
||||
// We can use from the cache, we just can't release to the cache, due to the bug
|
||||
// noted above. In effect, this means that transmitted TDMs don't get their
|
||||
// dataBufs released - but received TDMs do (via FragmentHandler)
|
||||
//if (_hadCache) {
|
||||
// _cache.release(_dataBuf);
|
||||
// _dataBuf = null;
|
||||
//}
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user