runtime, cmd/compile: rename memclr -> memclrNoHeapPointers
Since barrier-less memclr is only safe in very narrow circumstances,
this commit renames memclr to avoid accidentally calling memclr on
typed memory. This can cause subtle, non-deterministic bugs, so it's
worth some effort to prevent. In the near term, this will also prevent
bugs creeping in from any concurrent CLs that add calls to memclr; if
this happens, whichever patch hits master second will fail to compile.
This also adds the other new memclr variants to the compiler's
builtin.go to minimize the churn on that binary blob. We'll use these
in future commits.
Updates #17503.
Change-Id: I00eead049f5bd35ca107ea525966831f3d1ed9ca
Reviewed-on: https://go-review.googlesource.com/31369
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
diff --git a/src/runtime/alg.go b/src/runtime/alg.go
index 80f205c..5c378c6 100644
--- a/src/runtime/alg.go
+++ b/src/runtime/alg.go
@@ -275,12 +275,6 @@
return algarray[alg_INTER].hash(noescape(unsafe.Pointer(&i)), seed)
}
-// Testing adapter for memclr
-func memclrBytes(b []byte) {
- s := (*slice)(unsafe.Pointer(&b))
- memclr(s.array, uintptr(s.len))
-}
-
const hashRandomBytes = sys.PtrSize / 4 * 64
// used in asm_{386,amd64}.s to seed the hash function
diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s
index 60613b1..ab73508 100644
--- a/src/runtime/asm_amd64p32.s
+++ b/src/runtime/asm_amd64p32.s
@@ -484,7 +484,7 @@
MOVL 0, AX
RET
-TEXT runtime·memclr(SB),NOSPLIT,$0-8
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
MOVL ptr+0(FP), DI
MOVL n+4(FP), CX
MOVQ CX, BX
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 5510a27..d83b3b0 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -155,7 +155,11 @@
var Int64Hash = int64Hash
var EfaceHash = efaceHash
var IfaceHash = ifaceHash
-var MemclrBytes = memclrBytes
+
+func MemclrBytes(b []byte) {
+ s := (*slice)(unsafe.Pointer(&b))
+ memclrNoHeapPointers(s.array, uintptr(s.len))
+}
var HashLoad = &hashLoad
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 86d3b37..086d374 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -1090,7 +1090,7 @@
if t.bucket.kind&kindNoPointers == 0 {
memclrHasPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
} else {
- memclr(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
+ memclrNoHeapPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
}
}
}
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 8cdccb8..6039417 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -631,7 +631,7 @@
s.ensureSwept()
}
}
- memclr(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
+ memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
dumpparams()
dumpitabs()
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index c5f6fac..7cdca03 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -682,7 +682,7 @@
}
x = unsafe.Pointer(v)
if needzero && span.needzero != 0 {
- memclr(unsafe.Pointer(v), size)
+ memclrNoHeapPointers(unsafe.Pointer(v), size)
}
}
} else {
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 1a7bef4..a8766c7 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -333,14 +333,17 @@
}
// typedmemclr clears the typed memory at ptr with type typ. The
-// memory at ptr must already be type-safe.
+// memory at ptr must already be initialized (and hence in type-safe
+// state). If the memory is being initialized for the first time, see
+// memclrNoHeapPointers.
//
// If the caller knows that typ has pointers, it can alternatively
// call memclrHasPointers.
//
//go:nosplit
func typedmemclr(typ *_type, ptr unsafe.Pointer) {
- memclr(ptr, typ.size)
+ // TODO(austin): Call the hybrid barrier.
+ memclrNoHeapPointers(ptr, typ.size)
}
// memclrHasPointers clears n bytes of typed memory starting at ptr.
@@ -350,5 +353,6 @@
//
//go:nosplit
func memclrHasPointers(ptr unsafe.Pointer, n uintptr) {
- memclr(ptr, n)
+ // TODO(austin): Call the hybrid barrier.
+ memclrNoHeapPointers(ptr, n)
}
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index f1f9158..be52bfa 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -742,7 +742,7 @@
}
return
}
- memclr(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
+ memclrNoHeapPointers(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
}
// initCheckmarkSpan initializes a span for being checkmarked.
@@ -1433,7 +1433,7 @@
}
endProg := unsafe.Pointer(subtractb(h.bitp, (totalBits+3)/4))
endAlloc := unsafe.Pointer(subtractb(h.bitp, allocSize/heapBitmapScale))
- memclr(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
+ memclrNoHeapPointers(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
}
// progToPointerMask returns the 1-bit pointer mask output by the GC program prog.
diff --git a/src/runtime/mem_plan9.go b/src/runtime/mem_plan9.go
index 3d82a98..98bfc7f 100644
--- a/src/runtime/mem_plan9.go
+++ b/src/runtime/mem_plan9.go
@@ -38,7 +38,7 @@
p.size -= n
p = (*memHdr)(add(unsafe.Pointer(p), p.size))
}
- memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
+ *p = memHdr{}
return unsafe.Pointer(p)
}
prevp = p
@@ -48,7 +48,7 @@
func memFree(ap unsafe.Pointer, n uintptr) {
n = memRound(n)
- memclr(ap, n)
+ memclrNoHeapPointers(ap, n)
bp := (*memHdr)(ap)
bp.size = n
bpn := uintptr(ap)
@@ -63,7 +63,7 @@
if bpn+bp.size == uintptr(unsafe.Pointer(p)) {
bp.size += p.size
bp.next = p.next
- memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
+ *p = memHdr{}
} else {
bp.next.set(p)
}
@@ -77,14 +77,14 @@
if bpn+bp.size == uintptr(unsafe.Pointer(p.next)) {
bp.size += p.next.ptr().size
bp.next = p.next.ptr().next
- memclr(unsafe.Pointer(p.next), unsafe.Sizeof(memHdr{}))
+ *p.next.ptr() = memHdr{}
} else {
bp.next = p.next
}
if uintptr(unsafe.Pointer(p))+p.size == bpn {
p.size += bp.size
p.next = bp.next
- memclr(unsafe.Pointer(bp), unsafe.Sizeof(memHdr{}))
+ *bp = memHdr{}
} else {
p.next.set(bp)
}
diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s
index ce962f3..ef6e602 100644
--- a/src/runtime/memclr_386.s
+++ b/src/runtime/memclr_386.s
@@ -8,8 +8,8 @@
// NOTE: Windows externalthreadhandler expects memclr to preserve DX.
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-8
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-8
MOVL ptr+0(FP), DI
MOVL n+4(FP), BX
XORL AX, AX
diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s
index 6f30eca..244f5b4 100644
--- a/src/runtime/memclr_amd64.s
+++ b/src/runtime/memclr_amd64.s
@@ -8,8 +8,8 @@
// NOTE: Windows externalthreadhandler expects memclr to preserve DX.
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-16
MOVQ ptr+0(FP), DI
MOVQ n+8(FP), BX
XORQ AX, AX
diff --git a/src/runtime/memclr_arm.s b/src/runtime/memclr_arm.s
index 6ad70fb..eb37674 100644
--- a/src/runtime/memclr_arm.s
+++ b/src/runtime/memclr_arm.s
@@ -30,7 +30,7 @@
#define N R12
#define TMP R12 /* N and TMP don't overlap */
-TEXT runtime·memclr(SB),NOSPLIT,$0-8
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
MOVW ptr+0(FP), TO
MOVW n+4(FP), N
MOVW $0, R0
diff --git a/src/runtime/memclr_arm64.s b/src/runtime/memclr_arm64.s
index 47c6b73..9d756bc 100644
--- a/src/runtime/memclr_arm64.s
+++ b/src/runtime/memclr_arm64.s
@@ -4,8 +4,8 @@
#include "textflag.h"
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
MOVD ptr+0(FP), R3
MOVD n+8(FP), R4
// TODO(mwhudson): this is written this way to avoid tickling
diff --git a/src/runtime/memclr_mips64x.s b/src/runtime/memclr_mips64x.s
index 30a4af3..5018d43 100644
--- a/src/runtime/memclr_mips64x.s
+++ b/src/runtime/memclr_mips64x.s
@@ -6,8 +6,8 @@
#include "textflag.h"
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
MOVV ptr+0(FP), R1
MOVV n+8(FP), R2
ADDV R1, R2, R4
diff --git a/src/runtime/memclr_plan9_386.s b/src/runtime/memclr_plan9_386.s
index 4707ab2..c3d92a9 100644
--- a/src/runtime/memclr_plan9_386.s
+++ b/src/runtime/memclr_plan9_386.s
@@ -4,8 +4,8 @@
#include "textflag.h"
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-8
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-8
MOVL ptr+0(FP), DI
MOVL n+4(FP), BX
XORL AX, AX
diff --git a/src/runtime/memclr_plan9_amd64.s b/src/runtime/memclr_plan9_amd64.s
index 37e61df..d4d1a3a 100644
--- a/src/runtime/memclr_plan9_amd64.s
+++ b/src/runtime/memclr_plan9_amd64.s
@@ -4,8 +4,8 @@
#include "textflag.h"
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
MOVQ ptr+0(FP), DI
MOVQ n+8(FP), CX
MOVQ CX, BX
diff --git a/src/runtime/memclr_ppc64x.s b/src/runtime/memclr_ppc64x.s
index f7375db..e3a4673 100644
--- a/src/runtime/memclr_ppc64x.s
+++ b/src/runtime/memclr_ppc64x.s
@@ -6,8 +6,8 @@
#include "textflag.h"
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT|NOFRAME, $0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT|NOFRAME, $0-16
MOVD ptr+0(FP), R3
MOVD n+8(FP), R4
diff --git a/src/runtime/memclr_s390x.s b/src/runtime/memclr_s390x.s
index 846131e..43da10d 100644
--- a/src/runtime/memclr_s390x.s
+++ b/src/runtime/memclr_s390x.s
@@ -4,8 +4,8 @@
#include "textflag.h"
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT|NOFRAME,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT|NOFRAME,$0-16
MOVD ptr+0(FP), R4
MOVD n+8(FP), R5
diff --git a/src/runtime/mfixalloc.go b/src/runtime/mfixalloc.go
index 0d3d895..fe4b0fc 100644
--- a/src/runtime/mfixalloc.go
+++ b/src/runtime/mfixalloc.go
@@ -72,7 +72,7 @@
f.list = f.list.next
f.inuse += f.size
if f.zero {
- memclr(v, f.size)
+ memclrNoHeapPointers(v, f.size)
}
return v
}
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index e81e410..a0f5599 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -629,7 +629,7 @@
if s != nil {
if needzero && s.needzero != 0 {
- memclr(unsafe.Pointer(s.base()), s.npages<<_PageShift)
+ memclrNoHeapPointers(unsafe.Pointer(s.base()), s.npages<<_PageShift)
}
s.needzero = 0
}
@@ -1418,7 +1418,7 @@
} else {
result = gcBitsArenas.free
gcBitsArenas.free = gcBitsArenas.free.next
- memclr(unsafe.Pointer(result), gcBitsChunkBytes)
+ memclrNoHeapPointers(unsafe.Pointer(result), gcBitsChunkBytes)
}
result.next = nil
// If result.bits is not 8 byte aligned adjust index so
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index d6def7b..067fb3b 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -317,7 +317,7 @@
// here because it could cause a deadlock.
_g_.m.libcall.fn = uintptr(unsafe.Pointer(&libc_malloc))
_g_.m.libcall.n = 1
- memclr(unsafe.Pointer(&_g_.m.scratch), uintptr(len(_g_.m.scratch.v)))
+ _g_.m.scratch = mscratch{}
_g_.m.scratch.v[0] = unsafe.Sizeof(*sem)
_g_.m.libcall.args = uintptr(unsafe.Pointer(&_g_.m.scratch))
asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&_g_.m.libcall))
@@ -337,7 +337,7 @@
_m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_reltimedwait_np))
_m_.libcall.n = 2
- memclr(unsafe.Pointer(&_m_.scratch), uintptr(len(_m_.scratch.v)))
+ _m_.scratch = mscratch{}
_m_.scratch.v[0] = _m_.waitsema
_m_.scratch.v[1] = uintptr(unsafe.Pointer(&_m_.ts))
_m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch))
@@ -353,7 +353,7 @@
for {
_m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_wait))
_m_.libcall.n = 1
- memclr(unsafe.Pointer(&_m_.scratch), uintptr(len(_m_.scratch.v)))
+ _m_.scratch = mscratch{}
_m_.scratch.v[0] = _m_.waitsema
_m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch))
asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&_m_.libcall))
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 8b57514..af11101 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -2812,7 +2812,7 @@
}
memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
- memclr(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
+ memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
newg.sched.sp = sp
newg.stktopsp = sp
newg.sched.pc = funcPC(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index 7f4de45..0f49df1 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -141,7 +141,7 @@
memmove(p, old.array, lenmem)
// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
// Only clear the part that will not be overwritten.
- memclr(add(p, newlenmem), capmem-newlenmem)
+ memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
} else {
// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
p = mallocgc(capmem, et, true)
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index dfc71b4..ea9a69a 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -436,7 +436,7 @@
}
if stackDebug >= 1 {
println("stackfree", v, n)
- memclr(v, n) // for testing, clobber stack data
+ memclrNoHeapPointers(v, n) // for testing, clobber stack data
}
if debug.efence != 0 || stackFromSystem != 0 {
if debug.efence != 0 || stackFaultOnFree != 0 {
diff --git a/src/runtime/string.go b/src/runtime/string.go
index 4cf165b..0752823 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -249,7 +249,7 @@
cap := roundupsize(uintptr(size))
p := mallocgc(cap, nil, false)
if cap != uintptr(size) {
- memclr(add(p, uintptr(size)), cap-uintptr(size))
+ memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
}
*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
@@ -264,7 +264,7 @@
mem := roundupsize(uintptr(size) * 4)
p := mallocgc(mem, nil, false)
if mem != uintptr(size)*4 {
- memclr(add(p, uintptr(size)*4), mem-uintptr(size)*4)
+ memclrNoHeapPointers(add(p, uintptr(size)*4), mem-uintptr(size)*4)
}
*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(mem / 4)}
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index 693a344..7384c78 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -60,20 +60,24 @@
throw("systemstack called from unexpected goroutine")
}
-// memclr clears n bytes starting at ptr.
+// memclrNoHeapPointers clears n bytes starting at ptr.
//
-// Usually you should use typedmemclr. memclr should be used only when
-// the caller knows that *ptr contains no heap pointers or to
-// initialize memory to a type-safe state when allocation reuses dead
-// memory.
+// Usually you should use typedmemclr. memclrNoHeapPointers should be
+// used only when the caller knows that *ptr contains no heap pointers
+// because either:
+//
+// 1. *ptr is initialized memory and its type is pointer-free.
+//
+// 2. *ptr is uninitialized memory (e.g., memory that's being reused
+// for a new allocation) and hence contains only "junk".
//
// in memclr_*.s
//go:noescape
-func memclr(ptr unsafe.Pointer, n uintptr)
+func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
-//go:linkname reflect_memclr reflect.memclr
-func reflect_memclr(ptr unsafe.Pointer, n uintptr) {
- memclr(ptr, n)
+//go:linkname reflect_memclrNoHeapPointers reflect.memclrNoHeapPointers
+func reflect_memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) {
+ memclrNoHeapPointers(ptr, n)
}
// memmove copies n bytes from "from" to "to".
diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s
index 60302e0..bd5de33 100644
--- a/src/runtime/sys_windows_386.s
+++ b/src/runtime/sys_windows_386.s
@@ -192,7 +192,7 @@
SUBL $m__size, SP // space for M
MOVL SP, 0(SP)
MOVL $m__size, 4(SP)
- CALL runtime·memclr(SB) // smashes AX,BX,CX
+ CALL runtime·memclrNoHeapPointers(SB) // smashes AX,BX,CX
LEAL m_tls(SP), CX
MOVL CX, 0x14(FS)
@@ -203,7 +203,7 @@
MOVL SP, 0(SP)
MOVL $g__size, 4(SP)
- CALL runtime·memclr(SB) // smashes AX,BX,CX
+ CALL runtime·memclrNoHeapPointers(SB) // smashes AX,BX,CX
LEAL g__size(SP), BX
MOVL BX, g_m(SP)
diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
index 9679099..c61b79d 100644
--- a/src/runtime/sys_windows_amd64.s
+++ b/src/runtime/sys_windows_amd64.s
@@ -236,7 +236,7 @@
SUBQ $m__size, SP // space for M
MOVQ SP, 0(SP)
MOVQ $m__size, 8(SP)
- CALL runtime·memclr(SB) // smashes AX,BX,CX, maybe BP
+ CALL runtime·memclrNoHeapPointers(SB) // smashes AX,BX,CX, maybe BP
LEAQ m_tls(SP), CX
MOVQ CX, 0x28(GS)
@@ -247,7 +247,7 @@
MOVQ SP, 0(SP)
MOVQ $g__size, 8(SP)
- CALL runtime·memclr(SB) // smashes AX,BX,CX, maybe BP
+ CALL runtime·memclrNoHeapPointers(SB) // smashes AX,BX,CX, maybe BP
LEAQ g__size(SP), BX
MOVQ BX, g_m(SP)
diff --git a/src/runtime/write_err_android.go b/src/runtime/write_err_android.go
index 4411a14..748dec6 100644
--- a/src/runtime/write_err_android.go
+++ b/src/runtime/write_err_android.go
@@ -75,7 +75,9 @@
if v == '\n' || writePos == len(dst)-1 {
dst[writePos] = 0
write(writeFD, unsafe.Pointer(&writeBuf[0]), int32(hlen+writePos))
- memclrBytes(dst)
+ for i := range dst {
+ dst[i] = 0
+ }
writePos = 0
}
}