# DF-3103 — embedded-ICB read uses unvalidated l_ea/l_ad: wild OOB heap read

## What

Two cooperating defects:

1. `udf_vget()` (sys/vfs/udf/udf_vfsops.c:527) computes the file-entry copy
   size as `size = UDF_FENTRY_SIZE + fe->l_ea + fe->l_ad` in **wrapping
   32-bit int arithmetic from fully attacker-controlled disk fields**, then
   `kmalloc(size)` + `bcopy`.  Crafted `l_ea`/`l_ad` wrap the sum to a small
   value (e.g. `176 + 0x1000 + 0xFFFFF150 = 0x100000200` → int `0x200`) so
   the `M_UDFFENTRY` allocation is only 512 bytes while the on-disk fields
   stay huge in the copied structure.
2. The embedded-ICB path of `udf_readatoffset()`
   (sys/vfs/udf/udf_vnops.c:1037-1039) then does
   `*data = &fentry->data[l_ea]; *size = l_ad;` with **no bounds check and no
   offset handling**.  With the wrapped fields: `*data` = 4272 bytes into a
   512-byte allocation, `*size` = `(int)0xFFFFF150 = -5936`, which
   `udf_read()`'s `uiomove(data, size, uio)` (:403) receives as
   `(size_t)-5936` → it happily moves `uio_resid` bytes of **kernel heap
   from far past the allocation** into the user's buffer.

Also demonstrated with `l_ea = 0x40000000` (file `EMB2`): `*data` lands ~1 GB
past the object; `uiomove()`'s fault handling turns the unmapped wild read
into `EFAULT` — proving the kernel dereferences the wild pointer.

Related pass-1 finding: DF-0880 covers the *bcopy source over-read* at the
same `udf_vget` site; this finding is the distinct **vnops sink**: the
integer-wrap → undersized allocation → `&fentry->data[l_ea]` + negative
`*size` → sign-extended `uiomove` chain in `udf_readatoffset()`/`udf_read()`.

## Reproduce

    ./run.sh     # mounts the crafted image (root), then as uid 1001:
                 #  - 3x read(EMB, 96)   -> 96 OOB heap bytes each
                 #  - read(EMB, 1024/4096)
                 #  - read(EMB2, 96)     -> EFAULT (wild pointer +1GB)

## Expected (stock kernel)

    read() returned 96 bytes from embedded file (requested 96)
    DF3103_LEAK_CONFIRMED: 96 bytes copied from kernel heap at
      fentry->data[0x1000] (~3.7KB past a 512-byte M_UDFFENTRY allocation)
    ...
    read(96) failed: Bad address        <- EMB2 (l_ea=0x40000000)

Patched kernel (fix.diff: vget validation + embedded-path offset handling):
`open /mnt/EMB: Invalid argument` — the malformed file entry is rejected
before any allocation or read.

## Fix validation

See DF-3102/fix_validate.sh + df31xx_combined_fix.diff (shared driver for
DF-3102/3103/3104); fix_run.log excerpt in this directory.
