Skip to content

fix: add Snapshot method to prevent InFlight map race condition#7026

Open
n3integration wants to merge 2 commits intoprojectdiscovery:devfrom
n3integration:fix/executor-concurrency-improvement
Open

fix: add Snapshot method to prevent InFlight map race condition#7026
n3integration wants to merge 2 commits intoprojectdiscovery:devfrom
n3integration:fix/executor-concurrency-improvement

Conversation

@n3integration
Copy link

@n3integration n3integration commented Feb 25, 2026

Proposed changes

There appears to be a race condition accessing the InFlight map within the ResumeInfo struct that can lead to a runtime panic. This change wraps access to the struct's map using RLock()/RUnlock() calls to serialize read/write access across goroutines.

Proof

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • Refactor
    • Consolidated multiple internal state checks into a single atomic snapshot operation to streamline concurrent state access, reduce redundant probing, and improve efficiency.
    • Preserved existing behavior, logging, and decision outcomes while simplifying control flow for more reliable execution under concurrency.

@auto-assign auto-assign bot requested a review from Mzack9999 February 25, 2026 00:12
@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Feb 25, 2026

Neo - PR Security Review

No security issues found

Highlights

  • Adds Snapshot method to ResumeInfo struct for thread-safe access to resume state during template execution
  • Wraps InFlight map access with read locks to prevent concurrent access issues

Comment @neo help for available commands. · Open in Neo

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4a1d48 and 2d0076e.

📒 Files selected for processing (1)
  • pkg/types/resume.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/types/resume.go

Walkthrough

Replaces multiple per-field resume checks with a new ResumeInfo.Snapshot(index) method that returns (completed, skipUnder, doAbove, isInFlight) under a read lock; executor code now calls this single snapshot instead of reading fields individually.

Changes

Cohort / File(s) Summary
Resume Snapshot Method
pkg/types/resume.go
Added Snapshot(index uint32) (completed bool, skipUnder uint32, doAbove uint32, isInFlight bool) to ResumeInfo. It reads Completed, SkipUnder, DoAbove, and InFlight[index] under a read lock. Note: implementation currently calls RLock() twice before a single RUnlock()—review for locking bug.
Executor Logic Refactoring
pkg/core/executors.go
Replaced individual checks of resumeFromInfo.Completed, SkipUnder, DoAbove, and probing InFlight[index] with a single resumeFromInfo.Snapshot(index) call; conditional logic preserved but now uses snapshot return values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I peeked at the state with one gentle hop,

Four truths in a breath—no more scattered stop.
A tiny nudge to locks, a careful new rhyme,
Snapshotting moments, one hop at a time. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: adding a Snapshot method to resolve a race condition with InFlight map access.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/types/resume.go (1)

101-126: ⚠️ Potential issue | 🟡 Minor

Compile() writes resumeInfo fields without holding resumeInfo's own lock — inconsistent with Snapshot()'s locking contract.

Compile() acquires only resumeCfg.Lock() (the outer struct lock) and writes directly to resumeInfo.SkipUnder, resumeInfo.DoAbove, resumeInfo.InFlight, and resumeInfo.Completed. Snapshot() acquires resumeInfo.RLock() for its reads. Because these are orthogonal mutexes, a concurrent Compile() and Snapshot() call on the same ResumeInfo instance are not synchronized with each other, which is a data race.

Today this is safe only because Compile() is called before execution begins. However, the locking discipline is inconsistent: anyone who introduces an execution path where Compile() overlaps with active scanning will trigger a race that Snapshot()'s RLock cannot prevent.

Fix Compile() to acquire each resumeInfo's own write lock when mutating its fields:

🔒 Proposed fix for `Compile()`
 func (resumeCfg *ResumeCfg) Compile() {
 	resumeCfg.Lock()
 	defer resumeCfg.Unlock()
 
 	for _, resumeInfo := range resumeCfg.ResumeFrom {
+		resumeInfo.Lock()
 		if resumeInfo.Completed && len(resumeInfo.InFlight) > 0 {
 			resumeInfo.InFlight = make(map[uint32]struct{})
 		}
 		min := uint32(math.MaxUint32)
 		max := uint32(0)
 		for index := range resumeInfo.InFlight {
 			if index < min {
 				min = index
 			}
 			if index > max {
 				max = index
 			}
 		}
 		resumeInfo.Repeat = map[uint32]struct{}{}
 		for index := range resumeInfo.InFlight {
 			resumeInfo.Repeat[index] = struct{}{}
 		}
 		resumeInfo.SkipUnder = min
 		resumeInfo.DoAbove = max
+		resumeInfo.Unlock()
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/types/resume.go` around lines 101 - 126, Compile() currently mutates
ResumeInfo fields under only resumeCfg's lock, causing races with Snapshot()
which uses resumeInfo.RLock(); fix by acquiring the per-item write lock around
all mutations: inside ResumeCfg.Compile(), for each resumeInfo in
resumeCfg.ResumeFrom call resumeInfo.Lock() before changing resumeInfo.InFlight,
resumeInfo.Repeat, resumeInfo.SkipUnder, resumeInfo.DoAbove,
resumeInfo.Completed and call resumeInfo.Unlock() immediately after (do not use
defer inside the loop); keep the outer resumeCfg.Lock() if desired but ensure
the per-resumeInfo Lock/Unlock brackets all direct reads/writes to that
resumeInfo to prevent data races.
🧹 Nitpick comments (1)
pkg/types/resume.go (1)

39-47: Consider named return values for Snapshot to self-document the return order.

The four-position unnamed signature (bool, uint32, uint32, bool) is opaque at call sites; callers (and future maintainers) must consult the implementation to know that the two bools mean completed and isInFlight, not some other ordering.

♻️ Proposed naming improvement
-func (resumeInfo *ResumeInfo) Snapshot(index uint32) (bool, uint32, uint32, bool) {
+func (resumeInfo *ResumeInfo) Snapshot(index uint32) (completed bool, skipUnder uint32, doAbove uint32, isInFlight bool) {
 	resumeInfo.RLock()
 	defer resumeInfo.RUnlock()
-	completed := resumeInfo.Completed
-	skipUnder := resumeInfo.SkipUnder
-	doAbove := resumeInfo.DoAbove
-	_, isInFlight := resumeInfo.InFlight[index]
-	return completed, skipUnder, doAbove, isInFlight
+	completed = resumeInfo.Completed
+	skipUnder = resumeInfo.SkipUnder
+	doAbove = resumeInfo.DoAbove
+	_, isInFlight = resumeInfo.InFlight[index]
+	return
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/types/resume.go` around lines 39 - 47, The Snapshot function's unnamed
four-value return makes call sites unclear; change its signature to use named
return values (e.g., completed bool, skipUnder uint32, doAbove uint32,
isInFlight bool) and update the function to assign those names from
ResumeInfo.Completed, ResumeInfo.SkipUnder, ResumeInfo.DoAbove and the InFlight
check, then use a bare return; reference the Snapshot method and the ResumeInfo
fields Completed, SkipUnder, DoAbove and InFlight when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@pkg/types/resume.go`:
- Around line 101-126: Compile() currently mutates ResumeInfo fields under only
resumeCfg's lock, causing races with Snapshot() which uses resumeInfo.RLock();
fix by acquiring the per-item write lock around all mutations: inside
ResumeCfg.Compile(), for each resumeInfo in resumeCfg.ResumeFrom call
resumeInfo.Lock() before changing resumeInfo.InFlight, resumeInfo.Repeat,
resumeInfo.SkipUnder, resumeInfo.DoAbove, resumeInfo.Completed and call
resumeInfo.Unlock() immediately after (do not use defer inside the loop); keep
the outer resumeCfg.Lock() if desired but ensure the per-resumeInfo Lock/Unlock
brackets all direct reads/writes to that resumeInfo to prevent data races.

---

Nitpick comments:
In `@pkg/types/resume.go`:
- Around line 39-47: The Snapshot function's unnamed four-value return makes
call sites unclear; change its signature to use named return values (e.g.,
completed bool, skipUnder uint32, doAbove uint32, isInFlight bool) and update
the function to assign those names from ResumeInfo.Completed,
ResumeInfo.SkipUnder, ResumeInfo.DoAbove and the InFlight check, then use a bare
return; reference the Snapshot method and the ResumeInfo fields Completed,
SkipUnder, DoAbove and InFlight when making the change.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d771daa and e4a1d48.

📒 Files selected for processing (2)
  • pkg/core/executors.go
  • pkg/types/resume.go

@n3integration n3integration changed the title fix: executor concurrency improvements fix: add Snapshot method to prevent InFlight map race condition Feb 25, 2026
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