Skip to content

NVIDIA driver 2.3.7

Choose a tag to compare

@nullmoth nullmoth released this 18 Oct 22:02
· 14 commits to main since this release

NVIDIA driver 2.3.7

A security release. Everything 2.3.6 does, plus the fixes below. If you are on 2.3.6 this is a
straight upgrade: run install.sh and reboot once.

Security

This release closes a batch of defects found by auditing the driver against the bug classes Apple and
NVIDIA have published fixes for in their own graphics stacks. Several were reachable by any program on
the machine, and one was reachable from a web page.

  • A video with a bad conformance window could write outside its buffer. The window is unbounded in
    the bitstream, and a window wider than the picture made the visible width negative — a value then used
    as a copy length. Both the H.264 and HEVC decoders now refuse a window that does not fit the picture.
    These decoders run in every program that plays video, so the stream can arrive from a web page, a mail
    attachment, or a preview.
  • The framebuffer published kernel addresses to any program. Every trace line it wrote went into a
    registry property that needs no privilege to read, and the lines carried kernel code addresses, heap
    addresses and GPU physical addresses — the pieces an attacker needs before anything else. The trace is
    now off unless the nvfbtrace boot argument asks for it, and no log line prints an address.
  • GPU firmware moved to a directory only the installer can write. It was read from a shared folder
    any user can write, with no check of what was in it. It now lives in /Library/NullMoth/nvfw, owned by
    root with group and other write cleared, is checked against the digests the package shipped, and is
    bounded in size. The installer refuses a firmware directory it did not create.
  • A client's teardown could run against its own in-flight call, freeing the list of memory ranges
    while that list was being used to build a mapping. Each client now holds a lock across its calls and
    its teardown, the teardown flag is set atomically, and the ranges are released when the client is
    freed rather than when it closes.
  • A card mapping had to land inside one of this card's apertures, so a caller cannot hand over an
    arbitrary physical address and have it mapped.
  • Texture and buffer regions are bounded by subtraction, not addition. x + width wrapped in 32 bits,
    so a region starting near the top of the address space passed the bounds test and reached the GPU copy
    as a negative offset; on the read side that returned memory from outside the texture. A region whose
    64-bit fields do not fit the copy path is now refused outright instead of being narrowed into a
    different region that looks valid.
  • Shader code is bounded before the compiler reads it, and a module the reader rejects is no longer
    compiled anyway. Shader libraries are program-supplied data and can come from a game's content, a mod
    or a download.
  • The kernel mapping table is serialised. Two mappings could take the same slot, and a release could
    clear an entry while a lookup was reading it.
  • The installer checks and installs a private copy of the package. It verified the package and then copied
    from the same folder minutes later, so anything that could write the extracted folder could swap a kernel
    extension in between (PR #266, thanks Aniruddh Sahay). The accelerator extension was still read from the
    original folder after that change; it now comes from the checked copy too. The package's own links are now
    relative, so none of them points outside it.
  • A user-space door into the display engine is closed. Two driver calls let a root process open a display
    session whose embedded pointers the display engine trusts as kernel addresses. Nothing the driver ships uses
    them, so they now return "unsupported" (PR #267, thanks Aniruddh Sahay).
  • Smaller bounds, each reachable from an ordinary program: the plane index passed to a system lookup, the
    firmware read length, the offset an argument encoder turns into a GPU address, a sysmem mapping length
    that wrapped, and an address resolution that trusted identifiers from the caller instead of the
    kernel's own record.

Fixes

  • macOS 26 icons and Liquid Glass draw again. App icons were missing pieces — the App Store without its A,
    Calendar without its header, Photos without its petals, Safari without its needle, System Settings without its
    gears — and System Settings showed no icons at all. The system's icon and glass shaders call a linked visible
    function behind a function constant. The driver decided whether such a function had to wait for its pipeline by
    reading the shader compiler's error text, and when 2.3.6's compiler stopped refusing these outright its wording
    changed, nothing matched, and the shader was dropped. The driver now asks the compiler for the function's
    references instead. Checked against the same icons rendered on Apple silicon: 45 to 71 differing pixels of
    65,536 on five of six, where 2.3.6 differed by 4,562 to 18,192. If your icons still look wrong after updating,
    restart once more so the system redraws them.
  • Window dragging is smoother. A command buffer committed while the driver was waiting on an earlier one only
    reached the GPU after that wait, so the compositor and the GPU took turns (PR #289, thanks watchthelight: 35 to
    63 frames a second dragging a window on a 165 Hz panel).
  • The installer kept no copy of the GPU firmware when upgrading, because the firmware had moved and
    the backup only looked in the new location. Both ways back — the uninstaller and the automatic fallback
    that runs when a new driver cannot start — are gated on that copy, so on an upgrade they would have put
    the previous driver back with no firmware at all, leaving a Mac with no display and no way out. The
    backup now falls back to the old location.
  • The classic H.264 decoder is built from source like the other one. The two shipped libraries differ
    only in the untiling layout the GPU writes; the classic one used to be produced by rewriting one
    instruction in the finished library at a fixed offset, which meant a source fix could never reach it.

For people building on this

An external video encoder can now register beside ours instead of replacing it. Set NVMTL_NVENCVT in
/Library/GPUBundles/nvmtl/nvrm610.conf to 1, or to a list of program names, and the driver loads
libnvencvt.dylib from its own bundle directory. It is off by default, never loaded into the window
server, and refused unless both the library and the directory holding it are owned by root with group
and other write cleared. VideoToolbox picks between the encoders by rating, so both can be installed and
compared.

Donations

This work is free. Donations help keep it going: https://nullmothsystems.com/donate