Skip to content

Guard against infinite recursion (StackOverflowError) in BranchJobProperty.decorateACL#574

Open
y-c-s wants to merge 1 commit into
jenkinsci:masterfrom
y-c-s:fix-branchjobproperty-acl-recursion
Open

Guard against infinite recursion (StackOverflowError) in BranchJobProperty.decorateACL#574
y-c-s wants to merge 1 commit into
jenkinsci:masterfrom
y-c-s:fix-branchjobproperty-acl-recursion

Conversation

@y-c-s

@y-c-s y-c-s commented Jun 20, 2026

Copy link
Copy Markdown

Problem

BranchJobProperty.decorateACL wraps the delegate ACL and, in its final else branch, calls acl.hasPermission2(a, permission). Under certain configurations the delegate ACL is cyclic: the cloudbees-folder child-ACL composition can produce a base ACL that re-enters this very same decorator. When that happens hasPermission2 recurses into itself indefinitely and throws StackOverflowError.

This is the JENKINS-44809 class of bug. It is sensitive to scale — it shows up on controllers with many branches/children where the folder ACL composition becomes cyclic.

Symptom

The StackOverflowError propagates out of permission checks performed while rendering API responses, so the affected endpoints return HTTP 500:

  • /queue/api/json
  • the full job REST APIs (.../api/json for multibranch jobs)

Because /queue/api/json is used to drive build triggering, the practical effect is that build triggering breaks entirely for the instance.

Fix

Add a thread-local re-entrancy guard around the delegate call. On first entry the guard is set and the delegate ACL is consulted as before. If the delegate re-enters the same decorator on the same thread, the guard short-circuits and answers from the instance-wide terminal root ACL:

Jenkins.get().getAuthorizationStrategy().getRootACL().hasPermission2(a, permission)

The root ACL is the non-decorating terminal of the strategy, so it yields the same permission decision the strategy intends for that authentication/permission — without looping. The guard is reset in a finally block so it never leaks across requests on a pooled thread.

The change is scoped to the single else branch; the existing SYSTEM2, CONFIGURE, and DELETE short-circuits are untouched.

Verification

We ran the patched plugin on a production controller that was reliably reproducing the failure. After deploying it:

  • /queue/api/json and the previously-failing job REST endpoints returned 200 instead of 500.
  • No StackOverflowError / recursion appeared in the controller logs.
  • Permission decisions were unchanged for normal (non-cyclic) cases.

Notes

  • Related issue: JENKINS-44809.
  • If maintainers would prefer a dedicated JIRA ticket to track this fix, happy to file one — let me know.

…perty.decorateACL

When the delegate ACL re-enters the same decorator (a cyclic base ACL
produced by the cloudbees-folder child-ACL composition under many
branches, the JENKINS-44809 class of bug), hasPermission2 recursed
forever and threw StackOverflowError. This surfaced as HTTP 500 on
/queue/api/json and the full job REST APIs, which broke build
triggering.

This adds a thread-local re-entrancy guard. On re-entry the decorator
answers from the instance-wide terminal root ACL
(Jenkins.get().getAuthorizationStrategy().getRootACL()), which yields
the same permission decision the strategy intends without looping.

Related: JENKINS-44809
Signed-off-by: Yann Sommer <ysommer@gmail.com>
@y-c-s y-c-s requested a review from a team as a code owner June 20, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant