VYPR
High severity7.5NVD Advisory· Published Mar 22, 2024· Updated Apr 15, 2026

CVE-2023-5685

CVE-2023-5685

Description

A flaw was found in XNIO. The XNIO NotifierState that can cause a Stack Overflow Exception when the chain of notifier states becomes problematically large can lead to uncontrolled resource management and a possible denial of service (DoS).

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.jboss.xnio:xnio-apiMaven
< 3.8.14.Final3.8.14.Final

Patches

1
ffabdcdda508

[XNIO-423] CVE-2023-5685 Move the recursion in NotifierState to a iteration, preventing a stack overflow exception when the chain of notifier states becomes problematically big

https://github.com/xnio/xnioFlavia RainoneOct 14, 2022via ghsa
1 file changed · +187 51
  • api/src/main/java/org/xnio/AbstractIoFuture.java+187 51 modified
    @@ -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() {
    @@ -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;
             }
    @@ -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;
             }
         }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

14

News mentions

0

No linked articles in our index yet.