Skip to content

Commit

Permalink
[XNIO-423] CVE-2023-5685 Move the recursion in NotifierState to a ite…
Browse files Browse the repository at this point in the history
…ration, preventing a stack overflow exception when the chain of notifier states becomes problematically big

Signed-off-by: Flavia Rainone <frainone@redhat.com>
  • Loading branch information
fl4via committed Apr 19, 2024
1 parent de55d0a commit ffabdcd
Showing 1 changed file with 187 additions and 51 deletions.
238 changes: 187 additions & 51 deletions api/src/main/java/org/xnio/AbstractIoFuture.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,141 @@ IOException getException() {
}
}

private static abstract class NestedState<T> extends State<T> {
private final State<T> next;

public NestedState(final State next) {
this.next = next;
}

/**
* Perform any actions that need to be executed when future is done, delegation of done notification to next is
* taken care of by invoker.
*
* @param future the future
* @param result the result
*/
protected abstract void doNotifyDone(AbstractIoFuture<T> future, T result);

@Override
public void notifyDone(AbstractIoFuture<T> future, T result) {
doNotifyDone(future, result);
if (next instanceof NestedState) {
NestedState<T> current = this;
do {
current = (NestedState<T>) current.next;
current.doNotifyDone(future, result);
} while (current.next instanceof NestedState);
current.next.notifyDone(future, result);
} else {
next.notifyDone(future, result);
}

}

/**
* Perform any actions that need to be done at this state for handling failure, delegation of failure
* notification to next is taken care of by invoker
*
* @param future the future
* @param exception the failure
*/
protected abstract void doNotifyFailed(AbstractIoFuture<T> future, IOException exception);

@Override
public void notifyFailed(AbstractIoFuture<T> future, IOException exception) {
doNotifyFailed(future, exception);
if (next instanceof NestedState) {
NestedState<T> current = this;
do {
current = (NestedState<T>) current.next;
current.doNotifyFailed(future, exception);
} while (current.next instanceof NestedState);
current.next.notifyFailed(future, exception);
} else {
next.notifyFailed(future, exception);
}
}

/**
* Perform any actions that need to be done at this state for handling cancellation, delegation of cancellation
* notification to next is taken care of by invoker
*
* @param future the future
*/
protected abstract void doNotifyCancelled(AbstractIoFuture<T> future);



@Override
public void notifyCancelled(AbstractIoFuture<T> future) {
doNotifyCancelled(future);
if (next instanceof NestedState) {
NestedState<T> current = this;
do {
current = (NestedState<T>) current.next;
current.doNotifyCancelled(future);
} while (current.next instanceof NestedState);
current.next.notifyCancelled(future);
} else {
next.notifyCancelled(future);
}
}

/**
* Perform any actions that need to be done at this state for cancellation. Delegation of cancellation to next
* is taken care of by invoker.
*/
protected abstract void doCancel();

/**
* Just delegate cancel() to first next state in the nested chain that is not a NestedState.
*/
@Override
public void cancel() {
doCancel();
if (next instanceof NestedState) {
NestedState<T> current = this;
do {
current = (NestedState<T>) current.next;
current.doCancel();
} while (current.next instanceof NestedState);
current.next.cancel();
} else {
next.cancel();
}
}

/**
* Return {@code true} to indicate that, at this state, cancel is requested. If returns false, invoker
* will check for inner next states in the chain until it finds a positive result or the final state in the
* chain.
*
* @return {@code true} to indicate that cancel is requested; {@code false} to delegate response to nested
* state.
*/
protected abstract boolean isCancelRequested();

@Override
public boolean cancelRequested() {
if (isCancelRequested()) {
return true;
}
if (next instanceof NestedState) {
NestedState<T> current = this;
do {
current = (NestedState<T>) current.next;
if (current.isCancelRequested()) {
return true;
}
} while (current.next instanceof NestedState);
return current.next.cancelRequested();
} else {
return next.cancelRequested();
}
}
}

static final class InitialState<T> extends State<T> {

Status getStatus() {
Expand Down Expand Up @@ -229,13 +364,12 @@ boolean cancelRequested() {
}
}

static final class NotifierState<T, A> extends State<T> {
final State<T> next;
static final class NotifierState<T, A> extends NestedState<T> {
final Notifier<? super T, A> notifier;
final A attachment;

NotifierState(final State<T> next, final Notifier<? super T, A> notifier, final A attachment) {
this.next = next;
super(next);
this.notifier = notifier;
this.attachment = attachment;
}
Expand All @@ -244,136 +378,138 @@ Status getStatus() {
return Status.WAITING;
}

void notifyDone(final AbstractIoFuture<T> future, final T result) {
@Override
protected void doNotifyDone(final AbstractIoFuture<T> future, final T result) {
doNotify(future);
next.notifyDone(future, result);
}

void notifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
@Override
protected void doNotifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
doNotify(future);
next.notifyFailed(future, exception);
}

void notifyCancelled(final AbstractIoFuture<T> future) {
@Override
protected void doNotifyCancelled(final AbstractIoFuture<T> future) {
doNotify(future);
next.notifyCancelled(future);
}

void cancel() {
next.cancel();

@Override
protected void doCancel() {
}

private void doNotify(final AbstractIoFuture<T> future) {
future.runNotifier(new NotifierRunnable<T, A>(notifier, future, attachment));
}

boolean cancelRequested() {
return next.cancelRequested();
@Override
protected boolean isCancelRequested() {
return false;
}
}

static final class WaiterState<T> extends State<T> {
final State<T> next;
static final class WaiterState<T> extends NestedState<T> {
final Thread waiter;

WaiterState(final State<T> next, final Thread waiter) {
this.next = next;
super(next);
this.waiter = waiter;
}

Status getStatus() {
return Status.WAITING;
}

void notifyDone(final AbstractIoFuture<T> future, final T result) {
@Override
protected void doNotifyDone(final AbstractIoFuture<T> future, final T result) {
LockSupport.unpark(waiter);
next.notifyDone(future, result);
}

void notifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
@Override
protected void doNotifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
LockSupport.unpark(waiter);
next.notifyFailed(future, exception);
}

void notifyCancelled(final AbstractIoFuture<T> future) {
@Override
protected void doNotifyCancelled(final AbstractIoFuture<T> future) {
LockSupport.unpark(waiter);
next.notifyCancelled(future);
}

void cancel() {
next.cancel();
}
@Override
protected void doCancel() {}

boolean cancelRequested() {
return next.cancelRequested();
@Override
protected boolean isCancelRequested() {
return false;
}
}

static final class CancellableState<T> extends State<T> {
final State<T> next;
static final class CancellableState<T> extends NestedState<T> {
final Cancellable cancellable;

CancellableState(final State<T> next, final Cancellable cancellable) {
this.next = next;
super(next);
this.cancellable = cancellable;
}

Status getStatus() {
return Status.WAITING;
}

void notifyDone(final AbstractIoFuture<T> future, final T result) {
next.notifyDone(future, result);
@Override
protected void doNotifyDone(final AbstractIoFuture<T> future, final T result) {
}

void notifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
next.notifyFailed(future, exception);
@Override
protected void doNotifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
}

void notifyCancelled(final AbstractIoFuture<T> future) {
next.notifyCancelled(future);
@Override
protected void doNotifyCancelled(final AbstractIoFuture<T> future) {
}

void cancel() {
@Override
protected void doCancel() {
try {
cancellable.cancel();
} catch (Throwable ignored) {}
next.cancel();
}

boolean cancelRequested() {
return next.cancelRequested();
@Override
protected boolean isCancelRequested() {
return false;
}
}

static final class CancelRequestedState<T> extends State<T> {
final State<T> next;
static final class CancelRequestedState<T> extends NestedState<T> {

CancelRequestedState(final State<T> next) {
this.next = next;
super(next);
}

Status getStatus() {
return Status.WAITING;
}

void notifyDone(final AbstractIoFuture<T> future, final T result) {
next.notifyDone(future, result);
@Override
protected void doNotifyDone(final AbstractIoFuture<T> future, final T result) {
}

void notifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
next.notifyFailed(future, exception);
@Override
protected void doNotifyFailed(final AbstractIoFuture<T> future, final IOException exception) {
}

void notifyCancelled(final AbstractIoFuture<T> future) {
next.notifyCancelled(future);
@Override
protected void doNotifyCancelled(final AbstractIoFuture<T> future) {
}

void cancel() {
@Override
protected void doCancel() {
// terminate
}

boolean cancelRequested() {
@Override
protected boolean isCancelRequested() {
return true;
}
}
Expand Down

0 comments on commit ffabdcd

Please sign in to comment.