Skip to content

fix(monitor): return locked snapshot from ListContainers to prevent race with Update - #2311

Closed
Aamod007 wants to merge 2 commits into
Project-HAMi:masterfrom
Aamod007:fix-container-lister-race
Closed

fix(monitor): return locked snapshot from ListContainers to prevent race with Update#2311
Aamod007 wants to merge 2 commits into
Project-HAMi:masterfrom
Aamod007:fix-container-lister-race

Conversation

@Aamod007

@Aamod007 Aamod007 commented Aug 3, 2026

Copy link
Copy Markdown

What type of PR is this?

/kind bug

What this PR does / why we need it

ListContainers() returned the internal containers map without holding the lister mutex, while Update() — called every 5s from the feedback loop — mutates that map and Munmaps removed entries under the lock. The metrics collector and Observe() iterate the same map concurrently, which is:

  1. a data race (concurrent map read/write, fails -race), and
  2. a potential use-after-munmap SIGBUS when a collector dereferences c.Info for an entry whose shared-memory region Update() just unmapped — crashing the entire vGPUmonitor DaemonSet pod.

This PR makes ListContainers() copy the map under the mutex and return the snapshot. All existing callers only range over the result, so the snapshot is a drop-in replacement.

It also hardens Update() against container directory names without an underscore: previously strings.Split(entry.Name(), "_")[1] panicked with index out of range on any stray directory in the hook path; now such entries are skipped with a warning.

Note: the snapshot shrinks the munmap race window to entries removed mid-scrape; fully eliminating it would require refcounting the mmap lifetime, which I left as a possible follow-up given the current behavior is always racy.

Which issue(s) this PR fixes

Fixes #2309

Tests

  • Test_ListContainers_snapshot — snapshot is isolated from subsequent internal-map mutation
  • Test_ListContainers_concurrentUpdate — drives Update() and ListContainers() concurrently; fails under -race on the previous implementation
  • Test_ContainerLister_Update — new subtest: dirname without underscore is skipped, not panicked on

AI assistance disclosure

Per CONTRIBUTING.md: AI tooling was used to help draft this change; I reviewed and verified the code, race analysis, and tests.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented returned NVIDIA container listings from modifying monitored state.
    • Improved safety when container updates and listings occur concurrently.
    • Improved reliability of container monitoring during active updates.
  • Tests

    • Added regression coverage to verify listing snapshots remain independent from internal monitoring state.
    • Added concurrent-access tests to validate safe iteration while container data is updated and cached resources are cleaned up.

@hami-robot hami-robot Bot added kind/bug Something isn't working dco-signoff: no labels Aug 3, 2026
@hami-robot
hami-robot Bot requested review from archlitchi and lengrongfu August 3, 2026 15:25
@hami-robot hami-robot Bot added the size/M label Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 36961023-78d0-46a4-9767-772795fc5c38

📥 Commits

Reviewing files that changed from the base of the PR and between da47cef and d4c84f7.

📒 Files selected for processing (2)
  • pkg/monitor/nvidia/cudevshr.go
  • pkg/monitor/nvidia/cudevshr_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/monitor/nvidia/cudevshr.go
  • pkg/monitor/nvidia/cudevshr_test.go

📝 Walkthrough

Walkthrough

ContainerLister.ListContainers now returns a locked copy of container state. Tests cover snapshot isolation and concurrent listing during updates.

Changes

Container snapshot safety

Layer / File(s) Summary
Synchronized container snapshots
pkg/monitor/nvidia/cudevshr.go, pkg/monitor/nvidia/cudevshr_test.go
ListContainers locks the container state and returns an independent map. Tests cover snapshot isolation and concurrent iteration during Update.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: archlitchi

Poem

A rabbit copies each map with care,
While locked updates move through shared data there.
Snapshots stay still,
Race tests check the drill,
And mapped memory receives proper repair.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The changes address snapshot isolation and concurrent access, but the provided summary does not show the required malformed-directory hardening in Update for issue #2309. Add SplitN-based validation in Update, skip directory names without an underscore, log a warning, and add a regression test.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: returning a locked snapshot from ListContainers to prevent races with Update.
Out of Scope Changes check ✅ Passed The implementation and regression tests focus on ContainerLister race prevention and related behavior described in issue #2309.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@archlitchi

Copy link
Copy Markdown
Member

please sign-off your commit

@Aamod007

Aamod007 commented Aug 4, 2026

Copy link
Copy Markdown
Author

@archlitchi Done — amended the commit with sign-off and force-pushed. Thanks for the review!

@Aamod007
Aamod007 force-pushed the fix-container-lister-race branch from 57d6a0f to 4cfb3a6 Compare August 4, 2026 11:09
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
unittests 61.03% <100.00%> (+0.26%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/monitor/nvidia/cudevshr.go 87.05% <100.00%> (+0.63%) ⬆️

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@archlitchi

Copy link
Copy Markdown
Member

please resolve these conflicts, we've dealt with unavailable directory name in #2371

@archlitchi

Copy link
Copy Markdown
Member

/assign

…ace with Update

ListContainers returned the internal containers map without holding the
mutex, while Update() concurrently mutates the map and munmaps entries
under the lock (called every 5s from the feedback loop). The metrics
collector and Observe() iterate that map at the same time, which is a
data race and can dereference a shared-memory region after Munmap.

Fix ListContainers to copy the map under the lock and return the
snapshot. Also harden Update() against container directory names
without an underscore, which previously panicked with index out of
range when deriving the container name.

Signed-off-by: Aamod007 <aamodkumar2006@gmail.com>
Signed-off-by: Aamod007 <aamodkumar2006@gmail.com>
@Aamod007
Aamod007 force-pushed the fix-container-lister-race branch from 4cfb3a6 to d4c84f7 Compare August 6, 2026 06:16
@hami-robot

hami-robot Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Aamod007
Once this PR has been reviewed and has the lgtm label, please ask for approval from archlitchi. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@imantaba

imantaba commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

The TestDescribeCollectSync 10m hang is a real deadlock from this change.
'ListContainers()' now locks l.mutex, but the metrics collector already holds that lock across the scrape (metrics.go:380-383, from #2194 ). sync.Mutex is not reentrant -> locks twice -> hangs. Master alone passes: only shows up rebased on current master.

Fix :

// Containers returns the live internal map without locking; caller must already hold Lock().
func (l *ContainerLister) Containers() map[string]*ContainerUsage {
	return l.containers
}

Collector then calls .Containers() instead of .ListContainers()

@imantaba

imantaba commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fix : apply this git apply fix.diff

diff --git a/cmd/vGPUmonitor/metrics.go b/cmd/vGPUmonitor/metrics.go
index f943a63..317dfc6 100644
--- a/cmd/vGPUmonitor/metrics.go
+++ b/cmd/vGPUmonitor/metrics.go
@@ -380,7 +380,9 @@ func (cc ClusterManagerCollector) collectPodAndContainerInfo(ch chan<- prometheu
 	cc.ClusterManager.containerLister.Lock()
 	defer cc.ClusterManager.containerLister.UnLock()
 
-	containers := cc.ClusterManager.containerLister.ListContainers()
+	// We already hold the lister lock above, so use the non-locking accessor;
+	// ListContainers() would re-lock the non-reentrant mutex and deadlock.
+	containers := cc.ClusterManager.containerLister.Containers()
 	containerMap := make(map[string][]*nvidia.ContainerUsage) // podUID -> containers
 	for _, c := range containers {
 		if c.Info != nil && c.PodUID != "" {
diff --git a/pkg/monitor/nvidia/cudevshr.go b/pkg/monitor/nvidia/cudevshr.go
index 9b5b461..b6e567c 100644
--- a/pkg/monitor/nvidia/cudevshr.go
+++ b/pkg/monitor/nvidia/cudevshr.go
@@ -165,6 +165,14 @@ func (l *ContainerLister) ListContainers() map[string]*ContainerUsage {
 	return snapshot
 }
 
+// Containers returns the live internal map without locking. The caller must
+// already hold Lock(); the metrics collector does so to keep the mmap'd
+// ContainerUsage.Info alive across a scrape, so it cannot use the locking
+// ListContainers() above without deadlocking on the non-reentrant mutex.
+func (l *ContainerLister) Containers() map[string]*ContainerUsage {
+	return l.containers
+}
+
 func (l *ContainerLister) Clientset() *kubernetes.Clientset {
 	return l.clientset
 }

@FouoF

FouoF commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for investigating the container-map race. The current implementation introduces a deterministic deadlock: the metrics collector already holds this non-reentrant mutex when it calls ListContainers(), and the new code attempts to acquire the same mutex again. The unit-test failure reflects this regression. The unrelated directory fix included in the branch has already been merged through #2371. We are closing this implementation. If the underlying mmap lifetime race still exists, please open a new, focused proposal that defines lock ownership and includes a regression test for both concurrent update safety and scrape completion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(vGPUmonitor): ListContainers races with Update — unlocked map iteration and possible use-after-munmap

4 participants