diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java index 7338e0b8355c190a21b7b991064efe59d6ab447a..6a12295791f0205988ce74e19280a63e13dd47ba 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java @@ -27,6 +27,7 @@ public class InboundMessageState { private int _lastFragment; private long _receiveBegin; private int _completeSize; + private boolean _released; /** expire after 10s */ private static final long MAX_RECEIVE_TIME = 10*1000; @@ -156,9 +157,15 @@ public class InboundMessageState { for (int i = 0; i < _fragments.length; i++) _fragmentCache.release(_fragments[i]); //_fragments = null; + _released = true; } public ByteArray[] getFragments() { + if (_released) { + RuntimeException e = new RuntimeException("Use after free: " + toString()); + _log.error("SSU IMS", e); + throw e; + } return _fragments; } public int getFragmentCount() { return _lastFragment+1; } diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java index f4afa45c632c55bd3f7be872d0b69dcc3ca6f28d..1a9a9c590c311abe0bf00682d684c3a2b68ab06d 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java @@ -1,6 +1,7 @@ package net.i2p.router.transport.udp; import java.util.Arrays; +import java.util.Date; import net.i2p.I2PAppContext; import net.i2p.data.Base64; @@ -33,6 +34,9 @@ public class OutboundMessageState { private int _pushCount; private short _maxSends; // private int _nextSendFragment; + /** for tracking use-after-free bugs */ + private boolean _released; + private Exception _releasedBy; public static final int MAX_MSG_SIZE = 32 * 1024; /** is this enough for a high-bandwidth router? */ @@ -116,13 +120,22 @@ public class OutboundMessageState { } catch (IllegalStateException ise) { _cache.release(_messageBuf); _messageBuf = null; + _released = true; return false; } } - public void releaseResources() { - if (_messageBuf != null) + /** + * This is synchronized with writeFragment(), + * so we do not release (probably due to an ack) while we are retransmitting. + * Also prevent double-free + */ + public synchronized void releaseResources() { + if (_messageBuf != null && !_released) { _cache.release(_messageBuf); + _released = true; + _releasedBy = new Exception ("Released on " + new Date() + " by:"); + } //_messageBuf = null; } @@ -267,16 +280,50 @@ public class OutboundMessageState { /** * Write a part of the the message onto the specified buffer. + * See releaseResources() above for synchhronization information. * * @param out target to write * @param outOffset into outOffset to begin writing * @param fragmentNum fragment to write (0 indexed) * @return bytesWritten */ - public int writeFragment(byte out[], int outOffset, int fragmentNum) { + public synchronized int writeFragment(byte out[], int outOffset, int fragmentNum) { + if (_messageBuf == null) return -1; + if (_released) { + /****** + Solved by synchronization with releaseResources() and simply returning -1. + Previous output: + + 23:50:57.013 ERROR [acket pusher] sport.udp.OutboundMessageState: SSU OMS Use after free + java.lang.Exception: Released on Wed Dec 23 23:50:57 GMT 2009 by: + at net.i2p.router.transport.udp.OutboundMessageState.releaseResources(OutboundMessageState.java:133) + at net.i2p.router.transport.udp.PeerState.acked(PeerState.java:1391) + at net.i2p.router.transport.udp.OutboundMessageFragments.acked(OutboundMessageFragments.java:404) + at net.i2p.router.transport.udp.InboundMessageFragments.receiveACKs(InboundMessageFragments.java:191) + at net.i2p.router.transport.udp.InboundMessageFragments.receiveData(InboundMessageFragments.java:77) + at net.i2p.router.transport.udp.PacketHandler$Handler.handlePacket(PacketHandler.java:485) + at net.i2p.router.transport.udp.PacketHandler$Handler.receivePacket(PacketHandler.java:282) + at net.i2p.router.transport.udp.PacketHandler$Handler.handlePacket(PacketHandler.java:231) + at net.i2p.router.transport.udp.PacketHandler$Handler.run(PacketHandler.java:136) + at java.lang.Thread.run(Thread.java:619) + at net.i2p.util.I2PThread.run(I2PThread.java:71) + 23:50:57.014 ERROR [acket pusher] ter.transport.udp.PacketPusher: SSU Output Queue Error + java.lang.RuntimeException: SSU OMS Use after free: Message 2381821417 with 4 fragments of size 0 volleys: 2 lifetime: 1258 pending fragments: 0 1 2 3 + at net.i2p.router.transport.udp.OutboundMessageState.writeFragment(OutboundMessageState.java:298) + at net.i2p.router.transport.udp.PacketBuilder.buildPacket(PacketBuilder.java:170) + at net.i2p.router.transport.udp.OutboundMessageFragments.preparePackets(OutboundMessageFragments.java:332) + at net.i2p.router.transport.udp.OutboundMessageFragments.getNextVolley(OutboundMessageFragments.java:297) + at net.i2p.router.transport.udp.PacketPusher.run(PacketPusher.java:38) + at java.lang.Thread.run(Thread.java:619) + at net.i2p.util.I2PThread.run(I2PThread.java:71) + *******/ + if (_log.shouldLog(Log.WARN)) + _log.log(Log.WARN, "SSU OMS Use after free: " + toString(), _releasedBy); + return -1; + //throw new RuntimeException("SSU OMS Use after free: " + toString()); + } int start = _fragmentSize * fragmentNum; int end = start + fragmentSize(fragmentNum); - if (_messageBuf == null) return -1; int toSend = end - start; byte buf[] = _messageBuf.getData(); if ( (buf != null) && (start + toSend < buf.length) && (out != null) && (outOffset + toSend < out.length) ) { diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 5fe177764f394b79f63e5b5103e816ee35229eee..75258791e7b7f0e1df3472d1c925632d07e5017c 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -169,8 +169,14 @@ public class PacketBuilder { int sizeWritten = state.writeFragment(data, off, fragment); if (sizeWritten != size) { - _log.error("Size written: " + sizeWritten + " but size: " + size - + " for fragment " + fragment + " of " + state.getMessageId()); + if (sizeWritten < 0) { + // probably already freed from OutboundMessageState + if (_log.shouldLog(Log.WARN)) + _log.warn("Write failed for fragment " + fragment + " of " + state.getMessageId()); + } else { + _log.error("Size written: " + sizeWritten + " but size: " + size + + " for fragment " + fragment + " of " + state.getMessageId()); + } packet.release(); return null; } else if (_log.shouldLog(Log.DEBUG)) diff --git a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java index cb31376b4ff4a411f615e50e146533e7d232582b..582eed1c37d1e6f3f7e80d4c83362918caae4c73 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java @@ -43,7 +43,7 @@ public class PacketPusher implements Runnable { } } } catch (Exception e) { - _log.log(Log.CRIT, "Error pushing", e); + _log.error("SSU Output Queue Error", e); } } }