Skip to content

Commit 3d4ec3a

Browse files
committed
build: fix segfault with nil refs in multi-platform target dependencies
When building multi-platform images with bake where target contexts reference other targets, a segmentation fault occurred when calling ToState() on nil references in the Refs map. This occurs with platform-independent builds (e.g., "FROM scratch") where BuildKit optimizes by not materializing separate references for identical content across platforms, resulting in nil entries in the Refs map. The fix adds a nil check to skip nil references, consistent with how BuildKit's own code handles such cases. Fixes #3508 Signed-off-by: GitHub Copilot <noreply@github.com> Signed-off-by: Orgad Shaneh <orgad.shaneh@audiocodes.com>
1 parent 606e9d1 commit 3d4ec3a

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

build/build.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,13 @@ func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *c
10741074
for _, v := range contexts {
10751075
if len(rr.Refs) > 0 {
10761076
for platform, r := range rr.Refs {
1077+
if r == nil {
1078+
// Skip nil references. This can occur when BuildKit determines
1079+
// that the build result is identical across platforms (e.g., with
1080+
// "FROM scratch" or other platform-independent content). In such
1081+
// cases, BuildKit may not materialize separate refs for each platform.
1082+
continue
1083+
}
10771084
st, err := r.ToState()
10781085
if err != nil {
10791086
return err

build/build_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package build
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/docker/buildx/util/waitmap"
8+
"github.com/moby/buildkit/client"
9+
"github.com/moby/buildkit/client/llb"
10+
gateway "github.com/moby/buildkit/frontend/gateway/client"
11+
"github.com/stretchr/testify/require"
12+
fstypes "github.com/tonistiigi/fsutil/types"
13+
)
14+
15+
type mockReference struct{}
16+
17+
func (m *mockReference) ToState() (llb.State, error) { return llb.Scratch(), nil }
18+
func (m *mockReference) Evaluate(ctx context.Context) error { return nil }
19+
func (m *mockReference) ReadFile(ctx context.Context, req gateway.ReadRequest) ([]byte, error) {
20+
return nil, nil
21+
}
22+
func (m *mockReference) StatFile(ctx context.Context, req gateway.StatRequest) (*fstypes.Stat, error) {
23+
return nil, nil
24+
}
25+
func (m *mockReference) ReadDir(ctx context.Context, req gateway.ReadDirRequest) ([]*fstypes.Stat, error) {
26+
return nil, nil
27+
}
28+
29+
// TestWaitContextDepsWithNilRefs reproduces issue #3508 where nil refs in multi-platform
30+
// builds (e.g., FROM scratch) caused a segmentation fault.
31+
func TestWaitContextDepsWithNilRefs(t *testing.T) {
32+
ctx := context.Background()
33+
results := waitmap.New()
34+
35+
result := &gateway.Result{
36+
Refs: map[string]gateway.Reference{
37+
"linux/amd64": &mockReference{},
38+
"linux/arm64": nil, // Nil ref should not panic
39+
},
40+
}
41+
results.Set("0-base", result)
42+
43+
so := &client.SolveOpt{
44+
FrontendAttrs: map[string]string{
45+
"context:base": "target:base",
46+
},
47+
}
48+
49+
err := waitContextDeps(ctx, 0, results, so)
50+
require.NoError(t, err)
51+
52+
// Only non-nil platform should be set
53+
require.Contains(t, so.FrontendAttrs, "context:base::linux/amd64")
54+
require.NotContains(t, so.FrontendAttrs, "context:base::linux/arm64")
55+
}
56+
57+
// TestWaitContextDepsNormal verifies normal multi-platform operation.
58+
func TestWaitContextDepsNormal(t *testing.T) {
59+
ctx := context.Background()
60+
results := waitmap.New()
61+
62+
result := &gateway.Result{
63+
Refs: map[string]gateway.Reference{
64+
"linux/amd64": &mockReference{},
65+
"linux/arm64": &mockReference{},
66+
},
67+
}
68+
results.Set("0-base", result)
69+
70+
so := &client.SolveOpt{
71+
FrontendAttrs: map[string]string{
72+
"context:base": "target:base",
73+
},
74+
}
75+
76+
err := waitContextDeps(ctx, 0, results, so)
77+
require.NoError(t, err)
78+
require.Contains(t, so.FrontendAttrs, "context:base::linux/amd64")
79+
require.Contains(t, so.FrontendAttrs, "context:base::linux/arm64")
80+
}

0 commit comments

Comments
 (0)