Francis Perron created BEANUTILS-580:
----------------------------------------

             Summary: PropertyUtilsBean/WrapDynaClass descriptor caches can 
re-expose the 'class' property after introspectors are changed  at runtime
                 Key: BEANUTILS-580
                 URL: https://issues.apache.org/jira/browse/BEANUTILS-580
             Project: Commons BeanUtils
          Issue Type: Improvement
          Components: Bean / Property Utils
    Affects Versions: 1.11.0
         Environment: Apache Commons BeanUtils 1.x (verified against 
1.11.1-SNAPSHOT, commit cd7afd9). JDK/OS independent, not platform-specific.
            Reporter: Francis Perron
         Attachments: beanutils.patch

Summary

After a bean class has been introspected, calling 
PropertyUtilsBean.addBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS)
 does not invalidate the already-cached property descriptors in 
PropertyUtilsBean's WeakFastHashMap cache or in WrapDynaClass's derived 
descriptorsMap. As a result, the class property can remain reachable even after 
an application (re-)installs the suppressor, and a concurrent variant of the 
same issue can expose a stale descriptor during a startup race between 
introspection and introspector-list changes.

This does not affect the out-of-the-box default: SUPPRESS_CLASS has been 
registered automatically since 1.9.4, so the class property is already 
suppressed from first introspection. The issue only surfaces if an application 
removes the suppressor, introspects, and later re-adds it (or resets it) 
expecting the cache to reflect the new state — a "mutate a shared bean's 
introspectors at runtime" pattern the project's own guidance already 
discourages in favor of configuring introspectors once at startup.


Steps to reproduce

1. Introspect a bean class through PropertyUtilsBean (or WrapDynaClass) with 
SUPPRESS_CLASS removed.
2. Call addBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS) 
(or resetBeanIntrospectors()).
3. Access the class property on the already-introspected bean — the cached 
descriptor is served unchanged and class is still reachable.
4. A companion timing test exercises the same stale-read window when 
introspection on one thread races with an introspector-list change on another.


Proposed fix

A patch is attached that invalidates the derived descriptor caches whenever the 
introspector list changes, and makes the currency check plus cache lookup 
atomic in WrapDynaClass (synchronized on this). Patch size: +840/‑6 (main 
changes to PropertyUtilsBean.java, WeakFastHashMap.java, WrapDynaClass.java), 
plus four new JUnit test classes covering both the direct-bypass and the 
concurrent-race cases. It targets the 1.X branch and applies cleanly to 
origin/1.X.


Impact assessment

This is being filed as a hardening improvement rather than a security bug: it 
requires an application to first opt out of the default suppressor and later 
attempt to restore it, which is not the shipped default posture. We'd 
appreciate maintainer input on:
- Whether resetBeanIntrospectors() / re-adding a suppressor is intended to also 
restore suppression for classes already in the descriptor cache (if so, the 
current behavior contradicts that intent and this may warrant a stronger fix).
- Whether the concurrent-race variant (introspection racing with 
introspector-list mutation) is an accepted risk of "configure once at startup," 
or worth hardening regardless.


Happy to open this as a PR against 1.X if that's preferred to an attached patch.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to