fix(ateompath): validate ateom socket path length (#92)

## Symptom

When `<namespace>:<pod-name>` exceeds 107 bytes, `ateom-gvisor` exits
with `bind: invalid argument` and the controller surfaces a generic
`DeadlineExceeded`.

## Cause

Linux limits unix-socket paths to 107 bytes (`sockaddr_un.sun_path`). A
pod's ReplicaSet hash can jiggle between 8 and 10 chars across rollouts,
so a demo that fits at 8 silently breaks at 10 with no clear error.

## Fix

Add `ValidateAteomSocketPath` (with unit tests) and call it from
`ateom-gvisor` before the bind, so the operator sees the real cause in
the pod log instead of a kernel EINVAL.
This commit is contained in:
Adrien Delorme
2026-05-27 18:58:47 -07:00
committed by GitHub
parent 2dc9e6f023
commit 27f239dcf5
3 changed files with 99 additions and 0 deletions
+6
View File
@@ -94,6 +94,12 @@ func do(ctx context.Context) error {
go reap.ReapChildren(nil, nil, nil, &reapLock)
slog.InfoContext(ctx, "Child process reaper launched")
// Validate before opening the socket so the operator sees a clear
// message rather than the kernel's cryptic "bind: invalid argument".
if err := ateompath.ValidateAteomSocketPath(*podNamespace, *podName); err != nil {
return err
}
// Clean up any old socket.
sockPath := ateompath.AteomSocketPath(*podNamespace, *podName)
if err := os.RemoveAll(sockPath); err != nil {
+22
View File
@@ -16,9 +16,15 @@
package ateompath
import (
"fmt"
"path/filepath"
)
// MaxUnixSocketPathLen is the practical Linux limit for unix-domain socket
// paths. The kernel's sockaddr_un.sun_path is 108 bytes including the trailing
// NUL, leaving 107 usable bytes. Bind fails with EINVAL above this.
const MaxUnixSocketPathLen = 107
const (
// The base path. This is both the path of the root shared folder on the
// host filesystem, and when it is mounted into ateom and atelet containers.
@@ -49,6 +55,22 @@ func AteomSocketPath(ateomNamespace, ateomName string) string {
)
}
// ValidateAteomSocketPath returns a descriptive error when the socket path
// derived from ateomNamespace and ateomName would exceed Linux's unix-socket
// limit. Calling net.Listen("unix", ...) with an over-limit path otherwise
// fails with the cryptic "bind: invalid argument".
func ValidateAteomSocketPath(ateomNamespace, ateomName string) error {
p := AteomSocketPath(ateomNamespace, ateomName)
if len(p) > MaxUnixSocketPathLen {
return fmt.Errorf(
"ateom socket path %q is %d bytes, exceeds Linux unix-socket limit of %d: shorten the namespace or pod name (%d + %d = %d chars used for namespace + name)",
p, len(p), MaxUnixSocketPathLen,
len(ateomNamespace), len(ateomName), len(ateomNamespace)+len(ateomName),
)
}
return nil
}
func AteomNetNSName(ateomNamespace, ateomName string) string {
return "ateom:" + ateomNamespace + ":" + ateomName
}
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ateompath
import (
"strings"
"testing"
)
func TestValidateAteomSocketPath(t *testing.T) {
tests := []struct {
name string
namespace string
podName string
wantErr bool
}{
{
name: "short names well under the limit",
namespace: "ate-demo-counter",
podName: "counter-deployment-abcd1234-xyzw1",
wantErr: false,
},
{
name: "exactly at the limit",
namespace: strings.Repeat("a", 25),
podName: strings.Repeat("b", 45),
wantErr: false,
},
{
name: "one byte over the limit",
namespace: strings.Repeat("a", 25),
podName: strings.Repeat("b", 46),
wantErr: true,
},
{
name: "the reproducer from the original bug report",
namespace: "ate-demo-lovable-sandbox",
podName: "lovable-sandbox-pool-deployment-5797879cd7-2n7wb",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateAteomSocketPath(tt.namespace, tt.podName)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateAteomSocketPath(%q, %q) err=%v, wantErr=%v",
tt.namespace, tt.podName, err, tt.wantErr)
}
if tt.wantErr && err != nil {
// Error message should mention the limit so an operator can
// figure out by how much to shorten.
msg := err.Error()
if !strings.Contains(msg, "107") {
t.Errorf("error message %q does not reference the limit (107)", msg)
}
}
})
}
}