[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[FD] Apache Struts2 Spring Plugin Uses a Previously Closed Resource
- To: "fulldisclosure@xxxxxxxxxxxx" <fulldisclosure@xxxxxxxxxxxx>
- Subject: [FD] Apache Struts2 Spring Plugin Uses a Previously Closed Resource
- From: Daniel Owens via Fulldisclosure <fulldisclosure@xxxxxxxxxxxx>
- Date: Sat, 31 Oct 2026 19:46:10 +0000
Description
The Apache Struts2 Spring Plugin opens a Spring `ApplicationContex` via a
try-with-resources block and, outside of said block, then continues to use the
closed resource, resulting in an uncaught `IllegalStateException`. This
particular issue prevents proper functionality and indicates poor code quality
and code review standards and processes, while not being, itself, otherwise
excessively meaningful for a production environment (as developer mode should
not be enabled in such an environment), although it would hamper staging, test,
and developer environments that are configured in accordance with
Apache-recommended hot-reload configurations for such environments
(https://struts.apache.org/plugins/spring/#class-reloading).
Impact
Anyone who follows Struts' documented configuration for this plugin -- enabling
`struts.devMode`, setting a non-blank
`struts.objectFactory.spring.class.reloading.watchList`, and declaring
`ClassReloadingXMLWebApplicationContext` as the root context class in web.xml
-- will trigger this pathway on every application startup. Since close() call
`null`s the context's internal bean factory immediately after `refresh()`
succeeds, `SpringObjectFactory.setApplicationContext()`call to
`getBeanFactory()` throws an uncaught `IllegalStateException`, aborting the
Struts-Spring integration startup process before it can ever finish. Even if
the exception were caught, `close()` has already stopped the
`FilesystemAlterationMonitor` watchdog thread, so the live class-reloading
capability would never fire a single reload. As such, the hot-reload feature
cannot and does not work with this Apache-recommended configuration.
Examples
Sample Code
The code below clearly shows the issue when in developer, a watch list, and
hot-reloading are enabled. The try-with-resources calls `.close()` when
exiting, against `reloadingContext`, which is merely a pointer to `appContext`,
thereby closing `appContext`. Immediately after exiting the try-with-resources
and wrapping conditional, `this. setApplicationContext` is called, passing in
the now closed `appContext`. Importantly, `
ClassReloadingXMLWebApplicationContext`, to which `appContext` was cast during
the initialisation of the try-with-resources and against which the `finally`
that this construct generates (try-with-resources effectively being syntactic
sugar for a `try{...} finally{reloadingContext.close()}`), has a `close()`
defined that calls to `super.close()` before removing and stopping the listener.
```java
String watchList = container.getInstance(String.class,
SpringConstants.STRUTS_OBJECTFACTORY_SPRING_CLASS_RELOADING_WATCH_LIST);
String acceptClasses = container.getInstance(String.class,
SpringConstants.STRUTS_OBJECTFACTORY_SPRING_CLASS_RELOADING_ACCEPT_CLASSES);
String reloadConfig = container.getInstance(String.class,
SpringConstants.STRUTS_OBJECTFACTORY_SPRING_CLASS_RELOADING_RELOAD_CONFIG);
if ("true".equals(devMode)
&& StringUtils.isNotBlank(watchList)
&& appContext instanceof
ClassReloadingXMLWebApplicationContext) {
//prevent class caching
useClassCache = false;
try (ClassReloadingXMLWebApplicationContext reloadingContext =
(ClassReloadingXMLWebApplicationContext) appContext) {
reloadingContext.setupReloading(watchList.split(","),
acceptClasses, servletContext,
"true".equals(reloadConfig));
LOG.info("Class reloading is enabled. Make sure this is not
used on a production environment!\n{}",
watchList);
setClassLoader(reloadingContext.getReloadingClassLoader());
// we need to reload the context, so our isntance of the
factory is picked up
reloadingContext.refresh();
}
}
this.setApplicationContext(appContext);
```
Code snippet from `struts2-spring-plugin`'s
plugins/spring/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java,
lines 97-120
```java
public void close() {
super.close();
if (filesystemAlterationMonitor != null) {
filesystemAlterationMonitor.removeListener(this);
filesystemAlterationMonitor.stop();
}
}
```
Snippet from `struts2-spring-plugin`'s
plugins/spring/src/main/java/org/apache/struts2/spring/ClassReloadingXMLWebApplicationContext.java,
lines 157-164, showing the close() override invoked by the implicit
try-with-resources call
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/