[
https://issues.apache.org/jira/browse/RANGER-5403?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18039090#comment-18039090
]
Xuze Yang commented on RANGER-5403:
-----------------------------------
h3. Solution 1 — Synchronized lazy initialization (single synchronized block)
{*}Proposed change{*}: Replace the existing unsynchronized lazy initialization
with a synchronized block:
{code:java}
synchronized (this) {
if (valueWithoutSeparator == null && value != null) {
valueWithoutSeparator = getStringToCompare(value);
valueWithSeparator = valueWithoutSeparator +
Character.toString(levelSeparatorChar);
}
} {code}
*Pros:*
•Prevents two requests from performing initialization concurrently
•Eliminates the “half-initialized state” where only one of the two fields is
assigned
•Easy to implement and minimally intrusive
*Cons:*
•Does not guarantee safe visibility under the Java Memory Model
•Adds synchronization to the isMatch() hot path, which may negatively affect
HDFS authorization performance under high concurrency
h3. Solution 2 — Conditional lazy initialization based on valueWithSeparator ==
null
{*}Proposed change{*}: Modify the initialization logic to use
valueWithSeparator as the indicator of whether initialization is complete:
{code:java}
if (valueWithSeparator == null && value != null) {
valueWithoutSeparator = getStringToCompare(value);
valueWithSeparator = valueWithoutSeparator +
Character.toString(levelSeparatorChar);
} {code}
*Pros:*
•Very small code change
•No synchronization overhead
*Cons:*
•Still not thread-safe under concurrent access
•can be executed more than once
*Solution 3 — Eager initialization in constructor (selected solution)*
*Proposed change:* Perform all initialization in the constructor and make the
fields final:
{code:java}
public CaseSensitiveRecursiveMatcher(String value, char levelSeparatorChar) {
super(value);
this.levelSeparatorChar = levelSeparatorChar;
if (!getNeedsDynamicEval() && value != null) {
String noSep = getStringToCompare(value);
this.valueWithoutSeparator = noSep;
this.valueWithSeparator = noSep +
Character.toString(levelSeparatorChar);
} else {
this.valueWithoutSeparator = null;
this.valueWithSeparator = null;
}
} {code}
*Pros:*
•Fully thread-safe under the Java Memory Model
•Safe publication of final fields
•No stale reads
•Zero risk of performance regressions
*Cons:*
•Performs initialization at construction time rather than lazily
(However, the work performed is trivial and occurs once per matcher, so the
cost is negligible.)
> Intermittent HDFS authorization failures caused by unsafe lazy initialization
> in CaseSensitiveRecursiveMatcher
> --------------------------------------------------------------------------------------------------------------
>
> Key: RANGER-5403
> URL: https://issues.apache.org/jira/browse/RANGER-5403
> Project: Ranger
> Issue Type: Bug
> Components: plugins
> Affects Versions: 2.1.0
> Reporter: Xuze Yang
> Assignee: Xuze Yang
> Priority: Major
>
> HDFS plugin may intermittently return incorrect authorization results when
> evaluating path-based policies.
> This issue occurs under concurrent access due to unsafe lazy initialization
> inside RangerPathResourceMatcher$CaseSensitiveRecursiveMatcher.
>
> Symptoms
> •Sporadic access denied errors for valid HDFS paths
> •The issue appears shortly after policy refresh
>
> Root Cause
>
> valueWithoutSeparator and valueWithSeparator are lazily initialized inside
> isMatch() without proper synchronization or safe publication.
>
> For example:
> 1.Request R1 starts initializing only valueWithoutSeparator
> 2.Before R1 assigns valueWithSeparator, Request R2 skips initialization
> (because valueWithoutSeparator != null)
> 3.R2 reads valueWithSeparator == null
> 4.startsWith() check fails → false negative authorization
>
> This has been reproduced in stress tests of HDFS authorization in our
> environment.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)