Skip to content

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

Open
n3integration wants to merge 1 commit intoprojectdiscovery:devfrom
n3integration:fix/executor-concurrency-improvement
Open

fix: add Snapshot method to prevent InFlight map race condition#7026
n3integration wants to merge 1 commit 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
    • Improved concurrent state access patterns by consolidating multiple individual state checks into a unified snapshot operation, enhancing thread safety and efficiency.

@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 sync.RWMutex to ResumeInfo struct to protect concurrent access to the InFlight map and related fields
  • Introduces Snapshot() method that provides atomic read access to Completed, SkipUnder, DoAbove, and InFlight fields under RLock
  • Refactors executor code to use the new Snapshot() method instead of direct field access, preventing concurrent map read/write panics

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

The changes introduce a Snapshot() method on the ResumeInfo type that atomically retrieves four resume state fields (Completed, SkipUnder, DoAbove, and InFlight status) via a single locked operation. The executor logic is updated to call this method instead of individually accessing each field multiple times.

Changes

Cohort / File(s) Summary
Resume Snapshot Method
pkg/types/resume.go
Added new Snapshot(index uint32) method to ResumeInfo that safely captures Completed, SkipUnder, DoAbove, and in-flight status with read-lock protection.
Executor Logic Refactoring
pkg/core/executors.go
Replaced individual resume field checks (Completed, SkipUnder, DoAbove, InFlight) with a single call to resumeFromInfo.Snapshot(index), consolidating four separate operations into one locked access.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A snapshot in time, so neat and so clean,
Four checks now become one—how efficient we've been!
With locks held just once, the code flows with grace,
Consolidation and care keep the bugs far from this place. 🎯✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title 'fix: add Snapshot method to prevent InFlight map race condition' accurately describes the main change—a new Snapshot method added to prevent a race condition when accessing the InFlight map, which is the core purpose of this pull request.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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