Nil pointer dereference in waitForPluginLoad closes #4752 (#4896)

* Add test for #4752: Nil pointer dereference in waitForPluginLoad

This test demonstrates the bug where waitForPluginLoad() panics with a nil
pointer dereference when a plugin fails during startup before the reattach
config is set.

The test creates a runningPlugin with reattach=nil and closes the failed
channel, simulating a plugin that fails in startPluginProcess before
initializePlugin is called.

* Fix #4752: Add nil check for reattach in waitForPluginLoad

Adds a nil pointer check before accessing p.reattach.Pid when a plugin
fails during startup. If the plugin fails before the reattach config is
set (e.g., in startPluginProcess), reattach will be nil and the code
would previously panic when trying to log the PID.

The fix checks if reattach is nil and logs an appropriate message in
each case, preventing the panic while still providing useful debugging
information.
This commit is contained in:
Nathan Wallace
2025-11-16 15:53:59 -05:00
committed by GitHub
parent a9fa56ba02
commit 754c7e6832
2 changed files with 49 additions and 1 deletions

View File

@@ -701,7 +701,12 @@ func (m *PluginManager) waitForPluginLoad(p *runningPlugin, req *pb.GetRequest)
case <-p.initialized:
log.Printf("[TRACE] plugin initialized: pid %d (%p)", p.reattach.Pid, req)
case <-p.failed:
log.Printf("[TRACE] plugin pid %d failed %s (%p)", p.reattach.Pid, p.error.Error(), req)
// reattach may be nil if plugin failed before it was set
if p.reattach != nil {
log.Printf("[TRACE] plugin pid %d failed %s (%p)", p.reattach.Pid, p.error.Error(), req)
} else {
log.Printf("[TRACE] plugin %s failed before reattach was set: %s (%p)", p.pluginInstance, p.error.Error(), req)
}
// get error from running plugin
return p.error
}

View File

@@ -773,3 +773,46 @@ func TestPluginManager_Shutdown_NoPlugins(t *testing.T) {
t.Error("Shutdown returned nil response")
}
}
// TestWaitForPluginLoadWithNilReattach tests that waitForPluginLoad handles
// the case where a plugin fails before reattach is set.
// This reproduces bug #4752 - a nil pointer panic when trying to log p.reattach.Pid
// after the plugin fails during startup before the reattach config is set.
func TestWaitForPluginLoadWithNilReattach(t *testing.T) {
pm := newTestPluginManager(t)
// Add plugin config required by waitForPluginLoad with a reasonable timeout
timeout := 30 // Set timeout to 30 seconds so test doesn't time out immediately
pm.plugins["test-instance"] = &plugin.Plugin{
Plugin: "test-plugin",
Instance: "test-instance",
StartTimeout: &timeout,
}
// Create a runningPlugin that simulates a plugin that failed before reattach was set
rp := &runningPlugin{
pluginInstance: "test-instance",
initialized: make(chan struct{}),
failed: make(chan struct{}),
error: fmt.Errorf("plugin startup failed"),
reattach: nil, // Explicitly nil - this is the bug condition
}
// Simulate plugin failure by closing the failed channel in a goroutine
go func() {
time.Sleep(10 * time.Millisecond)
close(rp.failed)
}()
// Create a dummy request
req := &pb.GetRequest{
Connections: []string{"test-conn"},
}
// This should panic with nil pointer dereference when trying to log p.reattach.Pid
err := pm.waitForPluginLoad(rp, req)
// We expect an error (the plugin failed), but we should NOT panic
assert.Error(t, err)
assert.Contains(t, err.Error(), "plugin startup failed")
}