microsoft/FASTER · critical · FasterException

Attempting to return an already-free block

Error message

Attempting to return an already-free block

What it means

SectorAlignedBufferPool blocks carry a free bit (kFreeBitMask) in their level field. The Free property setter validates transitions: marking a block free when it is already free throws "Attempting to return an already-free block" (a double-free), and marking an allocated block as allocated throws the symmetric error. This indicates the pool's block bookkeeping is corrupted - the same memory sector was released twice or ownership was mishandled.

Solutions

  1. Audit code that obtains SectorAlignedMemory from FASTER APIs and ensure Dispose/Return is called exactly once per acquisition.
  2. Do not share pool-allocated buffers across threads or keep copies of the struct after disposing it.
  3. If the buffer was returned by a library API (e.g. log scan), let the library manage its lifetime - do not dispose or return it yourself.
  4. If it occurs inside the library with single-threaded, correct usage, report a bug with the call stack.

Example fix

// before
var buf = allocator.GetBuffer();
buf.Return();
// ... later in cleanup
buf.Return(); // double free
// after
var buf = allocator.GetBuffer();
buf.Return();
buf.ReturnPool = false; // or drop the reference after first Return()
Defensive patterns

Strategy: try-catch

Validate before calling

// Track buffer ownership explicitly; return each buffer exactly once
private readonly HashSet<SectorAlignedMemory> returned = new();
void SafeReturn(SectorAlignedMemory buf)
{
    if (!returned.Add(buf)) throw new InvalidOperationException("Buffer already returned");
    buf.Return();
}

Type guard

bool CanReturn(SectorAlignedMemory buf) => !buf.Free; // Free == already returned to the pool

Try / catch

try
{
    buf.Return();
}
catch (FasterException ex) when (ex.Message.Contains("already-free block"))
{
    logger.LogError(ex, "Double-free of sector-aligned buffer detected; ownership bug in caller");
}

Prevention

When it happens

Trigger: Internal double-dispose of sector-aligned buffers: calling Dispose/Return twice on the same buffer object, sharing a buffer between threads without synchronization, or a library bug where the same SectorAlignedMemory is returned to the pool by two code paths.

Common situations: User code reusing or disposing buffers obtained from FasterLog/FASTER allocators after ownership transferred back to the library; wrapping pool buffers in another disposable that is disposed twice; use-after-free patterns where a stale buffer reference is returned.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15). Data as JSON: /api/errors/5e57c096a50e8e45. Report an issue: GitHub.

Appendix: source

Thrown at cs/src/core/Utilities/BufferPool.cs:79

        private int level;
        internal int Level => this.level
#if CHECK_FREE
            & ~kFreeBitMask
#endif
            ;

        internal SectorAlignedBufferPool pool;

#if CHECK_FREE
        internal bool Free
        {
            get => (level & kFreeBitMask) != 0;
            set 
            {
                if (value)
                {
                    if (Free)
                        throw new FasterException("Attempting to return an already-free block");
                    this.level |= kFreeBitMask;
                }
                else
                {
                    if (!Free)
                        throw new FasterException("Attempting to allocate an already-allocated block");
                    this.level &= ~kFreeBitMask;
                }
            }
        }
#endif // CHECK_FREE

        /// <summary>
        /// Default constructor
        /// </summary>
        public SectorAlignedMemory(int level = default)
        {
            this.level = level;

View on GitHub (pinned to 321d872eab)