grpc/grpc-go · critical

Cannot slice freed buffer

Error message

Cannot slice freed buffer

What it means

buffer.Slice(start, end) (mem/buffers.go:187) returns a new view into the buffer's data and panics at line 188-190 if b.rootBuf == nil (buffer already freed). Slicing freed memory would point at pooled/recycled bytes, so it is rejected. A successful Slice also takes a Ref() on the root (line 205) so the view keeps the data alive.

Source

Thrown at mem/buffers.go:189

		}
		b.origData = nil
	} else {
		// This buffer doesn't own the data slice, decrement a ref on the root
		// buffer.
		b.rootBuf.Free()
	}

	b.rootBuf = nil
	bufferObjectPool.Put(b)
}

func (b *buffer) Len() int {
	return len(b.ReadOnlyData())
}

func (b *buffer) Slice(start, end int) Buffer {
	if b.rootBuf == nil {
		panic("Cannot slice freed buffer")
	}

	data := b.data[start:end] // access the data to check slice bounds

	if len(data) == 0 {
		return emptyBuffer{}
	}
	if len(data) == len(b.data) {
		b.Ref()
		return b
	}
	// We are creating a new reference (view) to a portion of the root buffer's
	// data. Therefore, we must increment the reference count of the root buffer
	// to ensure the underlying data is not freed while this view is still in
	// use.
	b.rootBuf.Ref()
	s := newBuffer()
	s.data = data

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Slice the buffer only while it is alive (before Free, or on a Ref()'d copy).
  2. If you need the sliced view beyond the current scope, take a Ref() first and Free() when done.
  3. Treat the input BufferSlice in CodecV2 as freed once Unmarshal returns; copy or Ref() anything you retain.

Example fix

// before
b.Free()
sub := b.Slice(0, 4) // panic: freed

// after
sub := b.Slice(0, 4) // slice while alive (Ref taken on root)
b.Free()             // free original ref; sub keeps data alive
// later: sub.Free()
Defensive patterns

Strategy: validation

Validate before calling

// Slice only while alive; take a Ref first if lifetime is uncertain.
func safeSlice(b mem.Buffer, start, end int) mem.Buffer {
    if b.Len() == 0 || start < 0 || end > b.Len() || start > end {
        return nil
    }
    return b.Slice(start, end)
}

Prevention

When it happens

Trigger: Calling buf.Slice(a, b) after buf.Free(); slicing a buffer whose data was freed by another goroutine; slicing within a CodecV2 after the input BufferSlice was freed.

Common situations: Custom CodecV2/interceptor that slices input buffers after they were freed by the framework; concurrent stream processing where one path freed the buffer; reusing a buffer variable across iterations after freeing it.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/67520ddf4f8b226c. Report an issue: GitHub.