Fix uk locale key parity, capture claim race and approval window reuse

This commit is contained in:
Viktor Liu
2026-09-22 19:21:28 +02:00
parent 31a27e575f
commit 5cf52e7e12
4 changed files with 47 additions and 34 deletions
+23 -16
View File
@@ -118,13 +118,6 @@ func (s *Server) StartCapture(req *proto.StartCaptureRequest, stream proto.Daemo
return err
}
if err := engine.SetCapture(sess); err != nil {
s.releaseCapture(sess)
sess.Stop()
pw.Close()
return status.Errorf(codes.Internal, "set capture: %v", err)
}
// Send an empty initial message to signal that the capture was accepted.
// The client waits for this before printing the banner, so it must arrive
// before any packet data.
@@ -305,20 +298,18 @@ func (s *Server) cleanupBundleCapture() {
s.bundleCapture = nil
}
// claimCapture reserves the engine's capture slot for sess. If another
// capture is already running it is evicted: a previous streaming session
// whose gRPC client died and never freed the slot stays stuck otherwise,
// and a bundle capture is just informational state.
// claimCapture reserves the engine's capture slot for sess and installs sess on
// the engine. If another capture is already running it is evicted: a previous
// streaming session whose gRPC client died and never freed the slot stays stuck
// otherwise, and a bundle capture is just informational state. The returned
// engine already has sess installed; the caller must not install it again.
func (s *Server) claimCapture(sess *capture.Session, cancel func()) (*internal.Engine, error) {
s.mutex.Lock()
stopEvicted := s.evictActiveCaptureLocked()
engine, err := s.getCaptureEngineLocked()
if err == nil {
s.activeCapture = sess
s.activeCaptureCancel = cancel
}
engine, err := s.installCaptureLocked(sess, cancel)
s.mutex.Unlock()
// Waits for the evicted session's writer, so it must run outside s.mutex.
stopEvicted()
if err != nil {
@@ -327,6 +318,22 @@ func (s *Server) claimCapture(sess *capture.Session, cancel func()) (*internal.E
return engine, nil
}
// installCaptureLocked installs sess on the engine and records it as the slot's
// owner, so no other claim can interleave between the two. On failure the slot
// is left unowned. Caller must hold mutex.
func (s *Server) installCaptureLocked(sess *capture.Session, cancel func()) (*internal.Engine, error) {
engine, err := s.getCaptureEngineLocked()
if err != nil {
return nil, err
}
if err := engine.SetCapture(sess); err != nil {
return nil, status.Errorf(codes.Internal, "set capture: %v", err)
}
s.activeCapture = sess
s.activeCaptureCancel = cancel
return engine, nil
}
// evictActiveCaptureLocked releases the engine's capture slot from whatever
// capture currently owns it so a fresh claim can succeed, and returns the rest
// of that teardown as a function. The returned function is never nil, is safe
+2 -10
View File
@@ -77,18 +77,10 @@ func TestClaimCapture_EvictionDoesNotHoldMutex(t *testing.T) {
t.Fatal("claimCapture blocked evicting a stalled capture")
}
locked := make(chan struct{})
go func() {
s.mutex.Lock()
s.mutex.Unlock()
close(locked)
}()
select {
case <-locked:
case <-time.After(5 * time.Second):
if !s.mutex.TryLock() {
t.Fatal("s.mutex still held after evicting a stalled capture")
}
s.mutex.Unlock()
select {
case <-sess.Done():