# How the Scrub Package Securely Zeroes Out Memory Buffers in Go

> Learn how the Go scrub package securely zeroes memory buffers with a compiler-resistant loop that optimizes to memset and prevents dead-code elimination.

- Repository: [Aperture Robotics/util](https://github.com/aperturerobotics/util)
- Tags: internals
- Published: 2026-02-25

---

**The `scrub` package provides a single exported function `Scrub` that overwrites byte slices with zeros using a compiler-resistant loop that optimizes to `memset` while preventing dead-code elimination.**

Secure memory management is critical when handling sensitive data like cryptographic keys, authentication tokens, or passwords in Go applications. The `aperturerobotics/util` repository provides a dedicated `scrub` package designed specifically to securely zero out memory buffers before they are released back to the runtime or garbage collector.

## How the Scrub Function Works

The core implementation resides in [`scrub/scrub.go`](https://github.com/aperturerobotics/util/blob/main/scrub/scrub.go) and consists of a straightforward yet carefully constructed loop:

```go
func Scrub(buf []byte) {
	// compiler optimizes this to memset
	for i := range buf {
		buf[i] = 0
	}
}

```

This function accepts a byte slice and iterates through every index, explicitly setting each element to zero. While the implementation appears simple, the specific loop construction is engineered to defeat compiler optimizations that might otherwise eliminate the zeroing operation as "dead code."

## Security Mechanisms Behind Secure Memory Zeroing

### Explicit Write Loop

The `for i := range buf` construct forces the Go compiler to generate a write to every element of the slice. Because each write is observed—the slice's underlying array is mutated—the compiler cannot legally eliminate the loop as dead-code, even under aggressive optimizations. This ensures the zeroing operation actually executes at runtime rather than being optimized away.

### Compiler Optimization to memset

Despite preventing elimination, the comment in the source notes that the compiler translates the loop into a call to the runtime's `memclrNoHeapPointers`. On supported architectures, this is implemented as a highly efficient `memset`-style memory clear. This ensures that the clearing operation performs quickly while still guaranteeing that every byte is overwritten.

### Safety Guarantees

By zero-filling the buffer, any previously stored secrets are removed from the process's address space. This reduces the risk of accidental exposure through memory dumps, core dumps, or after the buffer is returned to the Go runtime's garbage collector. The deterministic behavior works across all platforms supported by the repository.

## Practical Implementation Examples

### Basic Usage

Import the package from `github.com/aperturerobotics/util/scrub` and call `Scrub` when finished with sensitive data:

```go
package main

import (
	"fmt"
	"github.com/aperturerobotics/util/scrub"
)

func main() {
	// Allocate a buffer and fill it with sensitive data.
	secret := []byte("my-super-secret-key")
	fmt.Printf("Before scrub: %s\n", secret)

	// Use the secret for whatever operation is needed.
	// ...

	// Securely erase the buffer.
	scrub.Scrub(secret)
	fmt.Printf("After scrub: %v\n", secret) // prints a slice of zeros
}

```

### Guaranteed Cleanup with Defer

For production code, call `scrub.Scrub` in a `defer` statement immediately after creating the buffer. This ensures zeroing occurs regardless of early returns or panics:

```go
func doSomethingSecure() {
	buf := make([]byte, 64)
	// fill buf with a key …
	defer scrub.Scrub(buf) // guaranteed cleanup
	// ... use buf ...
}

```

## Summary

- The `scrub` package in `aperturerobotics/util` provides a single function `Scrub` located in [`scrub/scrub.go`](https://github.com/aperturerobotics/util/blob/main/scrub/scrub.go) to securely zero out memory buffers.
- The implementation uses a `for i := range buf` loop that prevents compiler elimination while allowing optimization to efficient `memset`-style operations via `memclrNoHeapPointers`.
- Secure memory zeroing protects against exposure of sensitive data in memory dumps and during garbage collection.
- Best practice involves using `defer scrub.Scrub(buf)` immediately after buffer allocation to ensure cleanup regardless of control flow.

## Frequently Asked Questions

### Why not just use `memset` directly?

The Go compiler does not expose `memset` directly to user code in a way that guarantees the operation won't be optimized away. The explicit loop in `scrub.Scrub` ensures the compiler recognizes the side effects of writing to the slice, preventing dead-code elimination while still allowing the backend to optimize the loop into an efficient `memclrNoHeapPointers` call.

### Can the Go compiler optimize away the scrub loop?

No. The specific construction `for i := range buf { buf[i] = 0 }` creates observable side effects by mutating the slice's underlying array. Since the compiler cannot prove that the buffer won't be read after scrubbing, it must emit the writes. This pattern is recognized by the Go compiler and lowered to efficient runtime intrinsics rather than eliminated.

### When should I use `scrub.Scrub` instead of letting garbage collection handle memory?

Use `scrub.Scrub` when the buffer contains sensitive cryptographic material, passwords, authentication tokens, or any data that could compromise security if extracted from a memory dump or core file. While Go's garbage collector eventually reclaims memory, it does not zero the memory first, leaving sensitive data potentially visible in RAM until that memory page is reused by the operating system.

### Does scrub work with strings or other data types?

The `scrub.Scrub` function specifically accepts `[]byte` slices. To scrub strings, you must first convert them to byte slices, though this requires care since strings are immutable in Go and the converted slice may point to read-only memory. For secure handling of string data, it is recommended to work with `[]byte` throughout the sensitive operation lifecycle and scrub the buffer before conversion or disposal.