Go 1.19.4 repairs filesystem names and HTTP/2 cache accounting
December's security fixes reject problematic Windows filesystem names and bound an HTTP/2 header cache by estimated bytes. Both corrections clarify what their original boundaries failed to contain.
A directory prefix does not make every name beneath it an ordinary file. On Windows, a reserved device name can identify a device even when a caller believes it is opening a path under a chosen root. Go 1.19.4 repairs that mismatch in os.DirFS and http.Dir, together with a separate HTTP/2 memory-accounting defect.
The release history dates the update to 6 December 2022. Its security announcement describes two corrections and announces Go 1.18.9 alongside it. The useful connection is that both fixes concern what a boundary actually counts or admits.
I value Go's small filesystem interfaces because they make consumers easier to understand. That simplicity depends on naming rules remaining explicit. A slash-separated name accepted by a portable interface still has to become an appropriate path on the operating system that opens it.
A rooted filesystem could open a device
The announcement identifies CVE-2022-41720 in os and net/http. It gives a concrete example where opening COM1 through a DirFS rooted at C:/tmp opens the device. The apparent location beneath a directory does not remove the reserved meaning of the final name.
The same advisory describes an additional Windows case involving a DirFS rooted at the current drive's root. A crafted path could escape that drive and access another path on the system. These are distinct examples of why directory-root handling must account for platform path grammar.
Both interfaces provide read-only access according to the announcement. That qualification limits the claim. The issue is unintended access through the supported operations, rather than a general facility to write arbitrary files. Its consequence still depends on which names a caller allows an external source to request.
An application serving files may think in terms of a tree containing ordinary directory entries. The operating system can assign special meaning to a supplied component. Correcting the conversion between those models is part of implementing the interface, not something that can be established by string concatenation alone.
The conversion rejects platform-specific syntax
The released DirFS implementation separates validation and joining. It rejects an empty root, validates the portable filesystem path, and converts that name using an internal safe-path helper before adding the root. The checks occur before Open or Stat uses the resulting operating-system path.
The Windows conversion helper checks individual components for reserved device names. It also rejects NUL bytes, backslashes and colons in the incoming filesystem name. Those characters can introduce meanings outside the portable slash-separated naming rules the interface expects.
Reserved names with extensions require particular care. The implementation comments explain that Windows versions differ in whether some such names remain reserved. The helper consults the platform's full-path behavior for that case. A policy that only compares an entire component against a short list can miss the role of an extension.
My preference is for this logic to live in the conversion layer. The consumer of an fs.FS should not have to reconstruct the target platform's reserved-name behavior before every read. The implementation can enforce the representation boundary where the portable name becomes a platform path.
An empty root now reports an error
The announcement also documents a behavior change for DirFS with an empty root. Previously that state acted like a slash root, so a relative-looking requested name could select a path from that root. The corrected implementation rejects the empty-root case.
That is useful for configuration handling. Missing configuration should not silently select a broader filesystem location than the application intended. Returning an error makes the missing root observable at the operation that needs it, instead of allowing a plausible-looking path to conceal the configuration defect.
This correction does not turn DirFS into a complete isolation mechanism. The released documentation explicitly explains that symbolic links can point outside the chosen prefix. It also notes that a relative root is affected by later changes to the working directory. Those documented properties remain relevant when a directory contains content controlled by another party.
The distinction is practical. A safer name conversion and a general guarantee of filesystem confinement are different requirements. The release addresses the former defects while retaining an interface whose documented behavior includes following ordinary filesystem links.
An entry cap did not bound cache bytes
The second correction, CVE-2022-41717, affects HTTP/2 servers. Each connection contains a cache of canonical HTTP header names supplied by the client. The announcement says the old entry-count limit could still allow approximately 64 MiB of allocation per open connection when keys were very large.
The released HTTP/2 implementation instead accounts for estimated bytes. For a new header it includes a fixed map-overhead allowance and twice the key length, accounting for the stored key and canonical value. It caches the new entry only when the cumulative estimate fits the configured bound.
The bound in this implementation is 2,048 estimated bytes for those cached keys. This is an accounting policy for the cache, not an assertion that an entire HTTP/2 connection consumes only that amount of memory. Other connection state and request processing remain separate resources.
The function still returns the canonical header value when it decides against caching it. That distinction preserves the operation's behavior while limiting retained optimization state. A cache can decline an insertion without rejecting the request merely because it has stopped retaining new keys.
I regard this as a useful example of selecting the unit that matches the resource. Counting entries works as a memory proxy only when entry size is reasonably constrained. Here the client influenced key length, so the cache needed accounting that included length. The patch changes retention policy at the point where a header would otherwise become persistent connection state.