From c6fe788fc3848b85b0f5670e402916fa23f57bac Mon Sep 17 00:00:00 2001 From: shimengwang Date: Sun, 26 Jan 2025 16:11:05 +0800 Subject: [PATCH] Fix crash due to shared mutable state in EnvoyFilter #53594 (#830) * Fix crash due to shared mutable state in EnvoyFilter #53594 https://github.com/istio/istio/issues/53590 * update patch file --- patch/README.md | 1 + .../istio/1.21/20241224-fix-proto-panic.patch | 469 ++++++++++++++++++ 2 files changed, 470 insertions(+) create mode 100644 patch/istio/1.21/20241224-fix-proto-panic.patch diff --git a/patch/README.md b/patch/README.md index 92f8e062..567bd88e 100644 --- a/patch/README.md +++ b/patch/README.md @@ -15,3 +15,4 @@ This list documents each patch: * 20240823-server-side-filter.patch: Add server-side filters to filter istio CRD. * 20240903-dynamic-configs.patch: Add DynamicConfig CRD. * 20240912-optimize-xds-generation.patch: Avoid unnecessary xDS generation for our CRD. + * 20241224-fix-proto-panic.patch: Fix crash due to shared mutable state in EnvoyFilter [#53594](https://github.com/istio/istio/issues/53590) diff --git a/patch/istio/1.21/20241224-fix-proto-panic.patch b/patch/istio/1.21/20241224-fix-proto-panic.patch new file mode 100644 index 00000000..ea13b300 --- /dev/null +++ b/patch/istio/1.21/20241224-fix-proto-panic.patch @@ -0,0 +1,469 @@ +diff --git a/pilot/pkg/model/authorization_test.go b/pilot/pkg/model/authorization_test.go +index 3df18ce43b..469e83e61e 100644 +--- a/pilot/pkg/model/authorization_test.go ++++ b/pilot/pkg/model/authorization_test.go +@@ -19,8 +19,6 @@ import ( + "reflect" + "testing" + +- "google.golang.org/protobuf/proto" +- + meshconfig "istio.io/api/mesh/v1alpha1" + authpb "istio.io/api/security/v1beta1" + selectorpb "istio.io/api/type/v1beta1" +@@ -30,6 +28,7 @@ import ( + "istio.io/istio/pkg/config/mesh" + "istio.io/istio/pkg/config/schema/collection" + "istio.io/istio/pkg/config/schema/gvk" ++ "istio.io/istio/pkg/util/protomarshal" + ) + + func TestAuthorizationPolicies_ListAuthorizationPolicies(t *testing.T) { +@@ -53,14 +52,14 @@ func TestAuthorizationPolicies_ListAuthorizationPolicies(t *testing.T) { + }, + }, + } +- policyWithSelector := proto.Clone(policy).(*authpb.AuthorizationPolicy) ++ policyWithSelector := protomarshal.Clone(policy) + policyWithSelector.Selector = &selectorpb.WorkloadSelector{ + MatchLabels: labels.Instance{ + "app": "httpbin", + "version": "v1", + }, + } +- policyWithTargetRef := proto.Clone(policy).(*authpb.AuthorizationPolicy) ++ policyWithTargetRef := protomarshal.Clone(policy) + policyWithTargetRef.TargetRef = &selectorpb.PolicyTargetReference{ + Group: gvk.KubernetesGateway.Group, + Kind: gvk.KubernetesGateway.Kind, +@@ -68,13 +67,13 @@ func TestAuthorizationPolicies_ListAuthorizationPolicies(t *testing.T) { + Namespace: "bar", + } + +- denyPolicy := proto.Clone(policy).(*authpb.AuthorizationPolicy) ++ denyPolicy := protomarshal.Clone(policy) + denyPolicy.Action = authpb.AuthorizationPolicy_DENY + +- auditPolicy := proto.Clone(policy).(*authpb.AuthorizationPolicy) ++ auditPolicy := protomarshal.Clone(policy) + auditPolicy.Action = authpb.AuthorizationPolicy_AUDIT + +- customPolicy := proto.Clone(policy).(*authpb.AuthorizationPolicy) ++ customPolicy := protomarshal.Clone(policy) + customPolicy.Action = authpb.AuthorizationPolicy_CUSTOM + + cases := []struct { +diff --git a/pilot/pkg/model/service.go b/pilot/pkg/model/service.go +index c93bc91d9a..7d35833555 100644 +--- a/pilot/pkg/model/service.go ++++ b/pilot/pkg/model/service.go +@@ -35,7 +35,6 @@ import ( + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/mitchellh/copystructure" +- "google.golang.org/protobuf/proto" + + "istio.io/api/label" + "istio.io/istio/pilot/pkg/features" +@@ -49,6 +48,7 @@ import ( + "istio.io/istio/pkg/maps" + "istio.io/istio/pkg/network" + "istio.io/istio/pkg/slices" ++ "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/util/sets" + "istio.io/istio/pkg/workloadapi" + "istio.io/istio/pkg/workloadapi/security" +@@ -975,7 +975,7 @@ func workloadResourceName(w *workloadapi.Workload) string { + + func (i *WorkloadInfo) Clone() *WorkloadInfo { + return &WorkloadInfo{ +- Workload: proto.Clone(i).(*workloadapi.Workload), ++ Workload: protomarshal.Clone(i.Workload), + Labels: maps.Clone(i.Labels), + Source: i.Source, + CreationTime: i.CreationTime, +diff --git a/pilot/pkg/networking/core/v1alpha3/cluster_builder.go b/pilot/pkg/networking/core/v1alpha3/cluster_builder.go +index 5e7ee9e212..1148ba2620 100644 +--- a/pilot/pkg/networking/core/v1alpha3/cluster_builder.go ++++ b/pilot/pkg/networking/core/v1alpha3/cluster_builder.go +@@ -23,7 +23,6 @@ import ( + endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" + discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" +- "google.golang.org/protobuf/proto" + anypb "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" +@@ -42,6 +41,7 @@ import ( + "istio.io/istio/pkg/config/host" + "istio.io/istio/pkg/log" + "istio.io/istio/pkg/security" ++ "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/util/sets" + ) + +@@ -307,7 +307,7 @@ func (cb *ClusterBuilder) buildCluster(name string, discoveryType cluster.Cluste + c.DnsLookupFamily = cluster.Cluster_V4_ONLY + } + } +- c.DnsRefreshRate = cb.req.Push.Mesh.DnsRefreshRate ++ c.DnsRefreshRate = protomarshal.ShallowClone(cb.req.Push.Mesh.DnsRefreshRate) + c.RespectDnsTtl = true + fallthrough + case cluster.Cluster_STATIC: +@@ -480,7 +480,7 @@ func (cb *ClusterBuilder) buildBlackHoleCluster() *cluster.Cluster { + c := &cluster.Cluster{ + Name: util.BlackHoleCluster, + ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}, +- ConnectTimeout: proto.Clone(cb.req.Push.Mesh.ConnectTimeout).(*durationpb.Duration), ++ ConnectTimeout: protomarshal.ShallowClone(cb.req.Push.Mesh.ConnectTimeout), + LbPolicy: cluster.Cluster_ROUND_ROBIN, + } + return c +@@ -492,7 +492,7 @@ func (cb *ClusterBuilder) buildDefaultPassthroughCluster() *cluster.Cluster { + cluster := &cluster.Cluster{ + Name: util.PassthroughCluster, + ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST}, +- ConnectTimeout: proto.Clone(cb.req.Push.Mesh.ConnectTimeout).(*durationpb.Duration), ++ ConnectTimeout: protomarshal.ShallowClone(cb.req.Push.Mesh.ConnectTimeout), + LbPolicy: cluster.Cluster_CLUSTER_PROVIDED, + TypedExtensionProtocolOptions: map[string]*anypb.Any{ + v3.HttpProtocolOptionsType: passthroughHttpProtocolOptions, +@@ -698,7 +698,7 @@ func (cb *ClusterBuilder) buildExternalSDSCluster(addr string) *cluster.Cluster + c := &cluster.Cluster{ + Name: security.SDSExternalClusterName, + ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}, +- ConnectTimeout: proto.Clone(cb.req.Push.Mesh.ConnectTimeout).(*durationpb.Duration), ++ ConnectTimeout: protomarshal.ShallowClone(cb.req.Push.Mesh.ConnectTimeout), + LoadAssignment: &endpoint.ClusterLoadAssignment{ + ClusterName: security.SDSExternalClusterName, + Endpoints: []*endpoint.LocalityLbEndpoints{ +diff --git a/pilot/pkg/networking/core/v1alpha3/cluster_traffic_policy.go b/pilot/pkg/networking/core/v1alpha3/cluster_traffic_policy.go +index 5291aa6c70..3d2133e975 100644 +--- a/pilot/pkg/networking/core/v1alpha3/cluster_traffic_policy.go ++++ b/pilot/pkg/networking/core/v1alpha3/cluster_traffic_policy.go +@@ -24,7 +24,6 @@ import ( + xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" + "github.com/golang/protobuf/ptypes/duration" + "github.com/golang/protobuf/ptypes/wrappers" +- "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/wrapperspb" + +@@ -36,6 +35,7 @@ import ( + "istio.io/istio/pilot/pkg/util/protoconv" + "istio.io/istio/pkg/config/protocol" + "istio.io/istio/pkg/log" ++ "istio.io/istio/pkg/util/protomarshal" + ) + + // applyTrafficPolicy applies the trafficPolicy defined within destinationRule, +@@ -226,7 +226,7 @@ func shouldH2Upgrade(clusterName string, port *model.Port, mesh *meshconfig.Mesh + } + + func (cb *ClusterBuilder) applyDefaultConnectionPool(cluster *cluster.Cluster) { +- cluster.ConnectTimeout = proto.Clone(cb.req.Push.Mesh.ConnectTimeout).(*durationpb.Duration) ++ cluster.ConnectTimeout = protomarshal.Clone(cb.req.Push.Mesh.ConnectTimeout) + } + + func applyLoadBalancer(c *cluster.Cluster, lb *networking.LoadBalancerSettings, port *model.Port, +diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go +index ce74e5c174..f1d1b35fce 100644 +--- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go ++++ b/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go +@@ -27,6 +27,7 @@ import ( + "istio.io/istio/pkg/log" + "istio.io/istio/pkg/proto/merge" + "istio.io/istio/pkg/slices" ++ "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/util/sets" + ) + +@@ -388,5 +389,5 @@ func routeMatch(httpRoute *route.Route, rp *model.EnvoyFilterConfigPatchWrapper) + } + + func cloneVhostRouteByRouteIndex(virtualHost *route.VirtualHost, routeIndex int) { +- virtualHost.Routes[routeIndex] = proto.Clone(virtualHost.Routes[routeIndex]).(*route.Route) ++ virtualHost.Routes[routeIndex] = protomarshal.Clone(virtualHost.Routes[routeIndex]) + } +diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go +index d75e997c0d..48127e5533 100644 +--- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go ++++ b/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go +@@ -19,13 +19,13 @@ import ( + + route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + "github.com/google/go-cmp/cmp" +- "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + + networking "istio.io/api/networking/v1alpha3" + "istio.io/istio/pilot/pkg/model" + "istio.io/istio/pilot/pkg/serviceregistry/memory" + "istio.io/istio/pkg/config/xds" ++ "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/util/sets" + ) + +@@ -1104,7 +1104,7 @@ func TestPatchHTTPRoute(t *testing.T) { + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { +- savedSharedVHost := proto.Clone(tt.args.sharedRoutesVHost).(*route.VirtualHost) ++ savedSharedVHost := protomarshal.Clone(tt.args.sharedRoutesVHost) + patchHTTPRoute(tt.args.patchContext, tt.args.patches, tt.args.routeConfiguration, + tt.args.virtualHost, tt.args.routeIndex, tt.args.routesRemoved, tt.args.portMap, &tt.args.clonedVhostRoutes) + if diff := cmp.Diff(tt.want, tt.args.virtualHost, protocmp.Transform()); diff != "" { +diff --git a/pilot/pkg/security/authn/policy_applier_test.go b/pilot/pkg/security/authn/policy_applier_test.go +index 416c17015b..381b2bee81 100644 +--- a/pilot/pkg/security/authn/policy_applier_test.go ++++ b/pilot/pkg/security/authn/policy_applier_test.go +@@ -26,7 +26,6 @@ import ( + hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + "github.com/google/go-cmp/cmp" +- "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/emptypb" +@@ -45,6 +44,7 @@ import ( + protovalue "istio.io/istio/pkg/proto" + istiotest "istio.io/istio/pkg/test" + "istio.io/istio/pkg/test/util/assert" ++ "istio.io/istio/pkg/util/protomarshal" + ) + + func TestJwtFilter(t *testing.T) { +@@ -1569,7 +1569,7 @@ func TestInboundMTLSSettings(t *testing.T) { + }, + RequireClientCertificate: protovalue.BoolTrue, + } +- tlsContextHTTP := proto.Clone(tlsContext).(*tls.DownstreamTlsContext) ++ tlsContextHTTP := protomarshal.Clone(tlsContext) + tlsContextHTTP.CommonTlsContext.AlpnProtocols = []string{"h2", "http/1.1"} + + expectedStrict := MTLSSettings{ +diff --git a/pilot/pkg/serviceregistry/serviceentry/controller.go b/pilot/pkg/serviceregistry/serviceentry/controller.go +index 1438e70303..ae8cfe7467 100644 +--- a/pilot/pkg/serviceregistry/serviceentry/controller.go ++++ b/pilot/pkg/serviceregistry/serviceentry/controller.go +@@ -193,8 +193,7 @@ func ConvertServiceEntry(cfg config.Config) *networking.ServiceEntry { + } + + // shallow copy +- copied := &networking.ServiceEntry{} +- protomarshal.ShallowCopy(copied, se) ++ copied := protomarshal.ShallowClone(se) + return copied + } + +@@ -208,8 +207,7 @@ func ConvertWorkloadEntry(cfg config.Config) *networking.WorkloadEntry { + // we will merge labels from metadata with spec, with precedence to the metadata + labels := maps.MergeCopy(wle.Labels, cfg.Labels) + // shallow copy +- copied := &networking.WorkloadEntry{} +- protomarshal.ShallowCopy(copied, wle) ++ copied := protomarshal.ShallowClone(wle) + copied.Labels = labels + return copied + } +diff --git a/pilot/pkg/xds/endpoints/ep_filters.go b/pilot/pkg/xds/endpoints/ep_filters.go +index f8518c0e58..b6b3f2c7b5 100644 +--- a/pilot/pkg/xds/endpoints/ep_filters.go ++++ b/pilot/pkg/xds/endpoints/ep_filters.go +@@ -19,7 +19,6 @@ import ( + + core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" +- "google.golang.org/protobuf/proto" + wrappers "google.golang.org/protobuf/types/known/wrapperspb" + + "istio.io/istio/pilot/pkg/model" +@@ -29,6 +28,7 @@ import ( + "istio.io/istio/pkg/config/labels" + "istio.io/istio/pkg/maps" + "istio.io/istio/pkg/network" ++ "istio.io/istio/pkg/util/protomarshal" + ) + + // EndpointsByNetworkFilter is a network filter function to support Split Horizon EDS - filter the endpoints based on the network +@@ -80,7 +80,7 @@ func (b *EndpointBuilder) EndpointsByNetworkFilter(endpoints []*LocalityEndpoint + // result at the maximum value for uint32. + weight := b.scaleEndpointLBWeight(lbEp, scaleFactor) + if lbEp.GetLoadBalancingWeight().GetValue() != weight { +- lbEp = proto.Clone(lbEp).(*endpoint.LbEndpoint) ++ lbEp = protomarshal.Clone(lbEp) + lbEp.LoadBalancingWeight = &wrappers.UInt32Value{ + Value: weight, + } +diff --git a/pkg/config/mesh/mesh.go b/pkg/config/mesh/mesh.go +index 8f5de00aa4..ac2a9f99dc 100644 +--- a/pkg/config/mesh/mesh.go ++++ b/pkg/config/mesh/mesh.go +@@ -20,7 +20,6 @@ import ( + "time" + + "github.com/hashicorp/go-multierror" +- "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + wrappers "google.golang.org/protobuf/types/known/wrapperspb" + "sigs.k8s.io/yaml" +@@ -140,7 +139,7 @@ func DefaultMeshConfig() *meshconfig.MeshConfig { + // ApplyProxyConfig applies the give proxy config yaml to a mesh config object. The passed in mesh config + // will not be modified. + func ApplyProxyConfig(yaml string, meshConfig *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { +- mc := proto.Clone(meshConfig).(*meshconfig.MeshConfig) ++ mc := protomarshal.Clone(meshConfig) + pc, err := MergeProxyConfig(yaml, mc.DefaultConfig) + if err != nil { + return nil, err +diff --git a/pkg/config/model.go b/pkg/config/model.go +index b1edfaf50b..73a5a22fd0 100644 +--- a/pkg/config/model.go ++++ b/pkg/config/model.go +@@ -283,7 +283,7 @@ func DeepCopy(s any) any { + // but also not used by Istio at all. + if _, ok := s.(protoreflect.ProtoMessage); ok { + if pb, ok := s.(proto.Message); ok { +- return proto.Clone(pb) ++ return protomarshal.Clone(pb) + } + } + +diff --git a/pkg/config/validation/validation.go b/pkg/config/validation/validation.go +index f8835de511..e6141ab20d 100644 +--- a/pkg/config/validation/validation.go ++++ b/pkg/config/validation/validation.go +@@ -2747,8 +2747,7 @@ func asJSON(data any) string { + switch mr := data.(type) { + case *networking.HTTPMatchRequest: + if mr != nil && mr.Name != "" { +- cl := &networking.HTTPMatchRequest{} +- protomarshal.ShallowCopy(cl, mr) ++ cl := protomarshal.ShallowClone(mr) + cl.Name = "" + data = cl + } +diff --git a/pkg/config/validation/validation_test.go b/pkg/config/validation/validation_test.go +index abdedcf33a..363265ecf8 100644 +--- a/pkg/config/validation/validation_test.go ++++ b/pkg/config/validation/validation_test.go +@@ -40,6 +40,7 @@ import ( + "istio.io/istio/pkg/config/constants" + "istio.io/istio/pkg/config/schema/gvk" + "istio.io/istio/pkg/test/util/assert" ++ protomarshal "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/wellknown" + ) + +@@ -502,7 +503,7 @@ func TestValidateMeshConfigProxyConfig(t *testing.T) { + } + + modify := func(config *meshconfig.ProxyConfig, fieldSetter func(*meshconfig.ProxyConfig)) *meshconfig.ProxyConfig { +- clone := proto.Clone(config).(*meshconfig.ProxyConfig) ++ clone := protomarshal.Clone(config) + fieldSetter(clone) + return clone + } +diff --git a/pkg/dns/client/dns_test.go b/pkg/dns/client/dns_test.go +index 2783b75171..c87184839f 100644 +--- a/pkg/dns/client/dns_test.go ++++ b/pkg/dns/client/dns_test.go +@@ -24,10 +24,10 @@ import ( + + "github.com/miekg/dns" + "go.uber.org/atomic" +- "google.golang.org/protobuf/proto" + + dnsProto "istio.io/istio/pkg/dns/proto" + "istio.io/istio/pkg/test" ++ "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/util/sets" + ) + +@@ -94,7 +94,7 @@ func TestBuildAlternateHosts(t *testing.T) { + } + + nt := d.NameTable() +- nt = proto.Clone(nt).(*dnsProto.NameTable) ++ nt = protomarshal.Clone(nt) + d.BuildAlternateHosts(nt, func(althosts map[string]struct{}, ipv4 []netip.Addr, ipv6 []netip.Addr, _ []string) { + for host := range althosts { + if _, exists := nt.Table[host]; !exists { +diff --git a/pkg/istio-agent/agent.go b/pkg/istio-agent/agent.go +index 36d7d09d67..a8c78ed33a 100644 +--- a/pkg/istio-agent/agent.go ++++ b/pkg/istio-agent/agent.go +@@ -29,7 +29,6 @@ import ( + "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +- "google.golang.org/protobuf/proto" + + mesh "istio.io/api/mesh/v1alpha1" + "istio.io/istio/pilot/cmd/pilot-agent/config" +@@ -47,6 +46,7 @@ import ( + "istio.io/istio/pkg/istio-agent/grpcxds" + "istio.io/istio/pkg/log" + "istio.io/istio/pkg/security" ++ "istio.io/istio/pkg/util/protomarshal" + "istio.io/istio/pkg/wasm" + "istio.io/istio/security/pkg/nodeagent/cache" + "istio.io/istio/security/pkg/nodeagent/caclient" +@@ -528,7 +528,7 @@ func (a *Agent) Check() (err error) { + func (a *Agent) GetDNSTable() *dnsProto.NameTable { + if a.localDNSServer != nil && a.localDNSServer.NameTable() != nil { + nt := a.localDNSServer.NameTable() +- nt = proto.Clone(nt).(*dnsProto.NameTable) ++ nt = protomarshal.Clone(nt) + a.localDNSServer.BuildAlternateHosts(nt, func(althosts map[string]struct{}, ipv4 []netip.Addr, ipv6 []netip.Addr, _ []string) { + for host := range althosts { + if _, exists := nt.Table[host]; !exists { +diff --git a/pkg/kube/inject/template.go b/pkg/kube/inject/template.go +index 47f6016a87..3ea303bc12 100644 +--- a/pkg/kube/inject/template.go ++++ b/pkg/kube/inject/template.go +@@ -289,7 +289,7 @@ func cleanProxyConfig(msg proto.Message) proto.Message { + if !ok || originalProxyConfig == nil { + return msg + } +- pc := proto.Clone(originalProxyConfig).(*meshconfig.ProxyConfig) ++ pc := protomarshal.Clone(originalProxyConfig) + defaults := mesh.DefaultProxyConfig() + if pc.ConfigPath == defaults.ConfigPath { + pc.ConfigPath = "" +diff --git a/pkg/util/protomarshal/protomarshal.go b/pkg/util/protomarshal/protomarshal.go +index 8b5795e6eb..b16a120a55 100644 +--- a/pkg/util/protomarshal/protomarshal.go ++++ b/pkg/util/protomarshal/protomarshal.go +@@ -196,7 +196,18 @@ func ApplyYAMLStrict(yml string, pb proto.Message) error { + return ApplyJSONStrict(string(js), pb) + } + +-func ShallowCopy(dst, src proto.Message) { ++type ComparableMessage interface { ++ comparable ++ proto.Message ++} ++ ++// ShallowClone performs a shallow clone of the object. For a deep clone, use Clone. ++func ShallowClone[T ComparableMessage](src T) T { ++ var empty T ++ if src == empty { ++ return empty ++ } ++ dst := src.ProtoReflect().New().Interface().(T) + dm := dst.ProtoReflect() + sm := src.ProtoReflect() + if dm.Type() != sm.Type() { +@@ -207,4 +218,10 @@ func ShallowCopy(dst, src proto.Message) { + dm.Set(fd, v) + return true + }) ++ return dst ++} ++ ++// Clone is a small wrapper that handles the upstream function not returning a typed message ++func Clone[T proto.Message](obj T) T { ++ return proto.Clone(obj).(T) + }