Radek Kraus created AMQ-7466:
--------------------------------
Summary: JmsSecurityException is not propagated back to client
Key: AMQ-7466
URL: https://issues.apache.org/jira/browse/AMQ-7466
Project: ActiveMQ
Issue Type: Bug
Components: Broker
Affects Versions: 5.15.9
Reporter: Radek Kraus
It seems there is a "condition race", which caused, that
{{JmsSecurityException}} exception is not sent back to the client side.
I use a custom authentication plugin (based on {{SimpleAuthenticationPlugin}})
on broker side. My test usually works as expected I got
{{JmsSecurityException}}, when
user/password is incorrect and all is OK, when user/password is correct.
It is very hard to simulated it (and I have no test to reproduce it, because I
didn't find a way how write it). So I can only describe, what probably happens
in wrong scenario:
* I use vm transport (but I think it does not matter)
* and now we expect following threads timing (see simplified pieces of current
sources bellow):
** Thread 1 enters into {{TransportConnection.start()}} method (invoked from
{{TransportAcceptListener}} at {{TransportConnector}} by "taskRunnerFactory"
from "brokerService")
** Thread 1 successfully starts the transport, but it is suspended on the
beginning of finally section (before {{if}} condition, but outside of
synchronization section)
** Thread 2 enters into
{{TransportConnection.processAddConnection(ConnectionInfo)}} (triggered by
client on create session, send {{ConnectionInfo}} -
{{ActiveMQConnection.ensureConnectionInfoSent()}})
** Thread 2 goes into catch block (because {{SecurityException}} is thrown -
wrong user/password combination)
** Thread 2 enters into {{TransportConnection.delayedStop(int, String,
Throwable)}} method, *where status is set to {{PENDING_STOP}}*
** Thread 1 is back now and continues with processing if condition - {{if
(!status.compareAndSet(STARTING, STARTED))}}
** Thread 1 *stops the transport, because status is {{PENDING_STOP}}*
* {{JmsSecurityException}} is not sent back to the client side :(
{code:java}
public void start() throws Exception {
if (status.compareAndSet(NEW, STARTING)) {
try {
synchronized (this) {
...
}
} catch (Exception e) {
// Force clean up on an error starting up.
status.set(PENDING_STOP);
throw e;
} finally {
if (!status.compareAndSet(STARTING, STARTED)) {
LOG.debug("Calling the delayed stop() after start() {}", this);
stop();
}
}
}
}
{code}
{code:java}
public Response processAddConnection(ConnectionInfo info) throws Exception {
...
try {
broker.addConnection(context, info);
} catch (Exception e) {
synchronized (brokerConnectionStates) {
brokerConnectionStates.remove(info.getConnectionId());
}
unregisterConnectionState(info.getConnectionId());
LOG.warn("Failed to add Connection id={}, clientId={} due to {}",
info.getConnectionId(), clientId, e);
//AMQ-6561 - stop for all exceptions on addConnection
// close this down - in case the peer of this transport doesn't play nice
delayedStop(2000, "Failed with SecurityException: " +
e.getLocalizedMessage(), e);
throw e;
}
...
}
{code}
{code:java}
public void delayedStop(final int waitTime, final String reason, Throwable
cause) {
if (waitTime > 0) {
status.compareAndSet(STARTING, PENDING_STOP);
...
}
}
{code}
Unfortunately I can simulate it only in debugger, I am very sorry. But I think
it is a problem. Or I am completely wrong?
In a reality I have no such deep knowledge to prepare some fix, but I think,
that the solution can be to set status to {{STARTING}} inside of
synchronization section, something like this, but I am not really sure about
this solution:
{code:java}
public void start() throws Exception {
if (status.compareAndSet(NEW, STARTING)) {
boolean isStarted = false;
try {
synchronized (this) {
try {
...
} catch (Exception e) {
// Force clean up on an error starting up.
status.set(PENDING_STOP);
throw e;
} finally {
isStarted = status.compareAndSet(STARTING, STARTED);
}
}
} finally {
// stop() can be called from within the above block,
// but we want to be sure start() completes before
// stop() runs, so queue the stop until right now:
if (!isStarted) {
LOG.debug("Calling the delayed stop() after start() {}", this);
stop();
}
}
}
}
{code}
--
This message was sent by Atlassian Jira
(v8.3.4#803005)