From d91e9432d5c43624ad5f8080904b288bb7bc7299 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Fri, 22 Nov 2024 21:51:13 +0000 Subject: [PATCH] fix(destination): avoid panic on missing managed fields timestamp We received a report of a panic: runtime error: invalid memory address or nil pointer dereference panic({0x1edb860?, 0x37a6050?} /usr/local/go/src/runtime/panic.go:785 +0x132 github.com/linkerd/linkerd2/controller/api/destination/watcher.latestUpdated({0xc0006b2d80?, 0xc00051a540?, 0xc0008fa008?}) /linkerd-build/vendor/github.com/linkerd/linkerd2/controller/api/destination/watcher/endpoints_watcher.go:1612 +0x125 github.com/linkerd/linkerd2/controller/api/destination/watcher.(*OpaquePortsWatcher).updateService(0xc0007d5480, {0x21fd160?, 0xc000d71688?}, {0x21fd160, 0xc000d71688}) /linkerd-build/vendor/github.com/linkerd/linkerd2/controller/api/destination/watcher/opaque_ports_watcher.go:141 +0x68 The `latestUpdated` function does not properly handle the case where a atime is omitted from a `ManagedFieldsEntry`. type ManagedFieldsEntry struct { // Time is the timestamp of when the ManagedFields entry was added. The // timestamp will also be updated if a field is added, the manager // changes any of the owned fields value or removes a field. The // timestamp does not update when a field is removed from the entry // because another manager took it over. // +optional Time *Time `json:"time,omitempty" protobuf:"bytes,4,opt,name=time"` This change adds a check to avoid the nil dereference. --- controller/api/destination/watcher/endpoints_watcher.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/controller/api/destination/watcher/endpoints_watcher.go b/controller/api/destination/watcher/endpoints_watcher.go index 800035beaa315..0a7276a36cbc5 100644 --- a/controller/api/destination/watcher/endpoints_watcher.go +++ b/controller/api/destination/watcher/endpoints_watcher.go @@ -1607,6 +1607,9 @@ func SetToServerProtocolExternalWorkload(k8sAPI *k8s.API, address *Address) erro func latestUpdated(managedFields []metav1.ManagedFieldsEntry) time.Time { var latest time.Time for _, field := range managedFields { + if field.Time == nil { + continue + } if field.Operation == metav1.ManagedFieldsOperationUpdate { if latest.IsZero() || field.Time.After(latest) { latest = field.Time.Time