From d9235d53d4f88bd6701aeb156afced0a6c1045dd Mon Sep 17 00:00:00 2001 From: Pranit More Date: Thu, 23 Jul 2026 13:44:47 +0530 Subject: [PATCH] copy operands in Bytes.Add to avoid aliasing the receiver Bytes values are immutable, but Add grew the receiver with append. When the receiver's backing array had spare capacity the second operand was written into it and the returned slice aliased the receiver, so two concatenations sharing a receiver corrupted each other. Allocate a fresh slice and copy both operands, matching list concatenation. --- common/types/bytes.go | 5 ++++- common/types/bytes_test.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/common/types/bytes.go b/common/types/bytes.go index 88da05315..2eefb5d7f 100644 --- a/common/types/bytes.go +++ b/common/types/bytes.go @@ -44,7 +44,10 @@ func (b Bytes) Add(other ref.Val) ref.Val { if !ok { return ValOrErr(other, "no such overload") } - return append(b, otherBytes...) + sum := make([]byte, 0, len(b)+len(otherBytes)) + sum = append(sum, b...) + sum = append(sum, otherBytes...) + return Bytes(sum) } // Compare implements traits.Comparer interface method by lexicographic ordering. diff --git a/common/types/bytes_test.go b/common/types/bytes_test.go index 326f18091..0349edca0 100644 --- a/common/types/bytes_test.go +++ b/common/types/bytes_test.go @@ -36,6 +36,27 @@ func TestBytesAdd(t *testing.T) { } } +func TestBytesAddNoAlias(t *testing.T) { + // A bytes value whose backing array has spare capacity, as happens when a + // host binds a []byte that is a sub-slice of a larger buffer. + backing := make([]byte, 3, 16) + copy(backing, "abc") + b := Bytes(backing) + + first := b.Add(Bytes("111")).(Bytes) + second := b.Add(Bytes("222")).(Bytes) + + if !bytes.Equal(first, []byte("abc111")) { + t.Errorf("first concatenation aliased: got %q, wanted %q", first, "abc111") + } + if !bytes.Equal(second, []byte("abc222")) { + t.Errorf("second concatenation wrong: got %q, wanted %q", second, "abc222") + } + if !bytes.Equal(b, []byte("abc")) { + t.Errorf("receiver mutated: got %q, wanted %q", b, "abc") + } +} + func TestBytesCompare(t *testing.T) { if !Bytes("1234").Compare(Bytes("2345")).Equal(IntNegOne).(Bool) { t.Error("Comparison did not yield -1")