[Bug 297325] geli: BIO_DELETE passed through unmapped when authentication is enabled

From: <bugzilla-noreply_at_freebsd.org>
Date: Fri, 07 Aug 2026 04:30:49 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=297325

            Bug ID: 297325
           Summary: geli: BIO_DELETE passed through unmapped when
                    authentication is enabled
           Product: Base System
           Version: 15.1-RELEASE
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: dr.johannes.bruegmann@gmail.com

Created attachment 273518
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=273518&action=edit
dmesg output, disk / provider characteristics

I am a FreeBSD user, not a kernel developer. The observations are from my
machine and accurate; the code analysis was done with AI assistance and I
cannot
verify it myself. I am not able to run follow-up experiments beyond simple
commands.

## Summary

With `geli init -a hmac/sha256`, HMAC sectors are interleaved with data
sectors,
so logical and physical offsets differ (geli(8): only 89% of the provider is
usable at 4k/HMAC-SHA256). `g_eli_start()` translates offsets for reads and
writes, but passes `BIO_DELETE` through unchanged. TRIM therefore hits
unrelated
physical regions and destroys live data.

## Observed

FreeBSD 15.1-RELEASE-p2 amd64, root pool on geli `-a hmac/sha256 -s 4096`,
`zpool create -o ashift=12 -o autotrim=on`, WD_BLACK SN8100 8TB.

After ~84 power-on hours (install, one `pkg upgrade` of 47 base packages, a few
`bectl destroy`):

```
scan: scrub repaired 4.72M in 00:00:56 with 6157 errors
  nda0p1.eli  ONLINE  0 0 1.81K
errors: Permanent errors ... (6157 files, incl. /usr/src, package caches,
        /usr/lib/libprivateclang.so.19, /boot/kernel/if_ice.ko)
GEOM_ELI: nda0p1.eli: Failed to authenticate 4096 bytes at offset 137443987456
```

SMART clean: 0 media errors, empty error log, 0% used.

Most affected files were written once at install time and never touched again —
consistent with "their blocks were trimmed away", not with faulty writes.
Damage
grew sharply after operations that free many blocks at once.

## Analysis

`sys/geom/eli/g_eli_integrity.c` (lines 342, 398, 476) translates for
read/write:

```c
cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) *
    sc->sc_bytes_per_sector;
```

`sys/geom/eli/g_eli.c`, `g_eli_start()` (~line 477) does not:

```c
case BIO_DELETE:
        if (!(sc->sc_flags & G_ELI_FLAG_NODELETE))
                break;              /* pass through */
...
case BIO_DELETE:
        cbp->bio_to = cp->provider;
        g_io_request(cbp, cp);      /* clone keeps logical offset/length */
```

The deviation grows linearly with the offset (~11%), so a TRIM for logical
137 GB lands >15 GB away — reliably on unrelated live data.

## Reproduce (expected)

1. `geli init -a hmac/sha256 -s 4096 <prov>` (no `-T`)
2. `zpool create -o ashift=12 -o autotrim=on tank <prov>.eli`
3. Write several GB, delete a large part so ZFS issues TRIM
4. `zpool scrub tank`

## Workaround

`geli init -T` (or `geli configure -T`) and `zpool set autotrim=off`.

## Suggested fix

Translate offset and length for `BIO_DELETE` when `G_ELI_FLAG_AUTH` is set, or
reject `BIO_DELETE` with `EOPNOTSUPP` in that case. At minimum geli(8) should
warn that `-a` must not be combined with TRIM pass-through.


## Side note

On an authenticated provider, never-written sectors cannot be read at all (no
valid HMAC). `zpool create` on a freshly initialised provider therefore fails
with "one or more devices is currently unavailable" until the provider has been
written once in full. A note in geli(8) would help.

-- 
You are receiving this mail because:
You are the assignee for the bug.