Follow-up of #792 (item 2 of its Proposed fixes), deliberately left out of #795.
What
AssuredReplicationServerTest allocates its four replication server ports once, in
@BeforeClass (opendj-server-legacy/src/test/java/org/opends/server/replication/server/AssuredReplicationServerTest.java:208,
TestCaseUtils.findFreePorts(4)), and its 360 invocations re-create the real replication
servers on those same fixed numbers for ~20 minutes.
TestCaseUtils.findFreePorts() binds a probe socket, closes it and returns the number
(TestCaseUtils.java:826-842), so every later reuse of that number carries a TOCTOU window:
anything in the JVM — an ephemeral local port, a socket still being closed by the previous
invocation, a self-connect — can hold it at the exact moment the next replication server binds
it. That is how #792 happened.
Scope
Nine replication test classes call TestCaseUtils.findFreePorts():
- pinned for the whole class in
@BeforeClass: AssuredReplicationServerTest,
GenerationIdTest;
- allocated in a class level helper (
initTest() / findFreePorts()) and reused by the test
methods which call it: TopologyViewTest, ReplicationServerFailoverTest,
GroupIdHandshakeTest, ReplicationServerLoadBalancingTest;
- allocated per test method:
ReplicationServerTest, ReplicationDomainTest,
ReplicationServerDynamicConfTest.
The first group is the one which re-binds the same numbers hundreds of times.
Why it is only robustness now
#795 made the transient case self-healing and the durable case loud: ReplicationServer
retries the bind five times, 200 ms apart, and throws a ConfigException instead of silently
starting without a listener. So a momentarily occupied port no longer fails the suite, and when
it does fail it says so where it happens.
What to do
Allocate the port right before the replication server which uses it is created, or retry with a
fresh port on EADDRINUSE.
Follow-up of #792 (item 2 of its Proposed fixes), deliberately left out of #795.
What
AssuredReplicationServerTestallocates its four replication server ports once, in@BeforeClass(opendj-server-legacy/src/test/java/org/opends/server/replication/server/AssuredReplicationServerTest.java:208,TestCaseUtils.findFreePorts(4)), and its 360 invocations re-create the real replicationservers on those same fixed numbers for ~20 minutes.
TestCaseUtils.findFreePorts()binds a probe socket, closes it and returns the number(
TestCaseUtils.java:826-842), so every later reuse of that number carries a TOCTOU window:anything in the JVM — an ephemeral local port, a socket still being closed by the previous
invocation, a self-connect — can hold it at the exact moment the next replication server binds
it. That is how #792 happened.
Scope
Nine replication test classes call
TestCaseUtils.findFreePorts():@BeforeClass:AssuredReplicationServerTest,GenerationIdTest;initTest()/findFreePorts()) and reused by the testmethods which call it:
TopologyViewTest,ReplicationServerFailoverTest,GroupIdHandshakeTest,ReplicationServerLoadBalancingTest;ReplicationServerTest,ReplicationDomainTest,ReplicationServerDynamicConfTest.The first group is the one which re-binds the same numbers hundreds of times.
Why it is only robustness now
#795 made the transient case self-healing and the durable case loud:
ReplicationServerretries the bind five times, 200 ms apart, and throws a
ConfigExceptioninstead of silentlystarting without a listener. So a momentarily occupied port no longer fails the suite, and when
it does fail it says so where it happens.
What to do
Allocate the port right before the replication server which uses it is created, or retry with a
fresh port on
EADDRINUSE.