diff --git a/backport-CVE-2023-5685.patch b/backport-CVE-2023-5685.patch new file mode 100644 index 0000000000000000000000000000000000000000..b280d4c7575f36f2487c4a79753e98df023918ca --- /dev/null +++ b/backport-CVE-2023-5685.patch @@ -0,0 +1,364 @@ +From a2fce38d48cca947ea42ee3bead08dd138b42c01 Mon Sep 17 00:00:00 2001 +From: Flavia Rainone +Date: Fri, 14 Oct 2022 17:00:46 -0300 +Subject: [PATCH] [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 + +Signed-off-by: Flavia Rainone + +Origin: +https://github.com/xnio/xnio/commit/a2fce38d48cca947ea42ee3bead08dd138b42c01 +--- + .../main/java/org/xnio/AbstractIoFuture.java | 238 ++++++++++++++---- + 1 file changed, 187 insertions(+), 51 deletions(-) + +diff --git a/api/src/main/java/org/xnio/AbstractIoFuture.java b/api/src/main/java/org/xnio/AbstractIoFuture.java +index 6118a0c7e3..030336b918 100644 +--- a/api/src/main/java/org/xnio/AbstractIoFuture.java ++++ b/api/src/main/java/org/xnio/AbstractIoFuture.java +@@ -74,6 +74,141 @@ IOException getException() { + } + } + ++ private static abstract class NestedState extends State { ++ private final State 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 future, T result); ++ ++ @Override ++ public void notifyDone(AbstractIoFuture future, T result) { ++ doNotifyDone(future, result); ++ if (next instanceof NestedState) { ++ NestedState current = this; ++ do { ++ current = (NestedState) 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 future, IOException exception); ++ ++ @Override ++ public void notifyFailed(AbstractIoFuture future, IOException exception) { ++ doNotifyFailed(future, exception); ++ if (next instanceof NestedState) { ++ NestedState current = this; ++ do { ++ current = (NestedState) 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 future); ++ ++ ++ ++ @Override ++ public void notifyCancelled(AbstractIoFuture future) { ++ doNotifyCancelled(future); ++ if (next instanceof NestedState) { ++ NestedState current = this; ++ do { ++ current = (NestedState) 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 current = this; ++ do { ++ current = (NestedState) 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 current = this; ++ do { ++ current = (NestedState) current.next; ++ if (current.isCancelRequested()) { ++ return true; ++ } ++ } while (current.next instanceof NestedState); ++ return current.next.cancelRequested(); ++ } else { ++ return next.cancelRequested(); ++ } ++ } ++ } ++ + static final class InitialState extends State { + + Status getStatus() { +@@ -229,13 +364,12 @@ boolean cancelRequested() { + } + } + +- static final class NotifierState extends State { +- final State next; ++ static final class NotifierState extends NestedState { + final Notifier notifier; + final A attachment; + + NotifierState(final State next, final Notifier notifier, final A attachment) { +- this.next = next; ++ super(next); + this.notifier = notifier; + this.attachment = attachment; + } +@@ -244,40 +378,41 @@ Status getStatus() { + return Status.WAITING; + } + +- void notifyDone(final AbstractIoFuture future, final T result) { ++ @Override ++ protected void doNotifyDone(final AbstractIoFuture future, final T result) { + doNotify(future); +- next.notifyDone(future, result); + } + +- void notifyFailed(final AbstractIoFuture future, final IOException exception) { ++ @Override ++ protected void doNotifyFailed(final AbstractIoFuture future, final IOException exception) { + doNotify(future); +- next.notifyFailed(future, exception); + } + +- void notifyCancelled(final AbstractIoFuture future) { ++ @Override ++ protected void doNotifyCancelled(final AbstractIoFuture future) { + doNotify(future); +- next.notifyCancelled(future); + } + +- void cancel() { +- next.cancel(); ++ ++ @Override ++ protected void doCancel() { + } + + private void doNotify(final AbstractIoFuture future) { + future.runNotifier(new NotifierRunnable(notifier, future, attachment)); + } + +- boolean cancelRequested() { +- return next.cancelRequested(); ++ @Override ++ protected boolean isCancelRequested() { ++ return false; + } + } + +- static final class WaiterState extends State { +- final State next; ++ static final class WaiterState extends NestedState { + final Thread waiter; + + WaiterState(final State next, final Thread waiter) { +- this.next = next; ++ super(next); + this.waiter = waiter; + } + +@@ -285,36 +420,35 @@ Status getStatus() { + return Status.WAITING; + } + +- void notifyDone(final AbstractIoFuture future, final T result) { ++ @Override ++ protected void doNotifyDone(final AbstractIoFuture future, final T result) { + LockSupport.unpark(waiter); +- next.notifyDone(future, result); + } + +- void notifyFailed(final AbstractIoFuture future, final IOException exception) { ++ @Override ++ protected void doNotifyFailed(final AbstractIoFuture future, final IOException exception) { + LockSupport.unpark(waiter); +- next.notifyFailed(future, exception); + } + +- void notifyCancelled(final AbstractIoFuture future) { ++ @Override ++ protected void doNotifyCancelled(final AbstractIoFuture 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 extends State { +- final State next; ++ static final class CancellableState extends NestedState { + final Cancellable cancellable; + + CancellableState(final State next, final Cancellable cancellable) { +- this.next = next; ++ super(next); + this.cancellable = cancellable; + } + +@@ -322,58 +456,60 @@ Status getStatus() { + return Status.WAITING; + } + +- void notifyDone(final AbstractIoFuture future, final T result) { +- next.notifyDone(future, result); ++ @Override ++ protected void doNotifyDone(final AbstractIoFuture future, final T result) { + } + +- void notifyFailed(final AbstractIoFuture future, final IOException exception) { +- next.notifyFailed(future, exception); ++ @Override ++ protected void doNotifyFailed(final AbstractIoFuture future, final IOException exception) { + } + +- void notifyCancelled(final AbstractIoFuture future) { +- next.notifyCancelled(future); ++ @Override ++ protected void doNotifyCancelled(final AbstractIoFuture 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 extends State { +- final State next; ++ static final class CancelRequestedState extends NestedState { + + CancelRequestedState(final State next) { +- this.next = next; ++ super(next); + } + + Status getStatus() { + return Status.WAITING; + } + +- void notifyDone(final AbstractIoFuture future, final T result) { +- next.notifyDone(future, result); ++ @Override ++ protected void doNotifyDone(final AbstractIoFuture future, final T result) { + } + +- void notifyFailed(final AbstractIoFuture future, final IOException exception) { +- next.notifyFailed(future, exception); ++ @Override ++ protected void doNotifyFailed(final AbstractIoFuture future, final IOException exception) { + } + +- void notifyCancelled(final AbstractIoFuture future) { +- next.notifyCancelled(future); ++ @Override ++ protected void doNotifyCancelled(final AbstractIoFuture future) { + } + +- void cancel() { ++ @Override ++ protected void doCancel() { + // terminate + } + +- boolean cancelRequested() { ++ @Override ++ protected boolean isCancelRequested() { + return true; + } + } diff --git a/xnio.spec b/xnio.spec index 87429cbfb4de75a0a98ca62022f9c5d47ef569e7..de7626ffb80ce29cfaf81abbb8cff319a1f09487 100644 --- a/xnio.spec +++ b/xnio.spec @@ -1,6 +1,6 @@ Name: xnio Version: 3.4.0 -Release: 10 +Release: 11 Summary: A simplified low-level I/O layer License: ASL 2.0 and LGPLv2+ URL: http://www.jboss.org/xnio @@ -18,6 +18,9 @@ BuildRequires: mvn(org.wildfly.common:wildfly-common) Patch0001: 0001-Disable-tests-use-TLSv1-protocol.patch Patch0002: 0002-skip-connect-timeout.patch Patch0003: 0003-skip-Future-cancel-test-case.patch + +Patch3000: backport-CVE-2023-5685.patch + %description XNIO is a simplified low-level I/O layer which can be used anywhere you are using NIO today. It frees you from the hassle of dealing with Selectors and the lack of NIO support for @@ -67,6 +70,9 @@ rm api/src/test/java/org/xnio/racecondition/ResumeReadsOnHandlingReadableChannel %files help -f .mfiles-javadoc %changelog +* Wed Nov 06 2024 yaoxin - 3.4.0-11 +- Fix CVE-2023-5685 + * Tue Aug 23 2022 liyanan - 3.4.0-10 - skip cancelAcceptStreamConnection test case