The iconv overflow occurs while announcing a character set
CVE-2024-2961 affects conversion to ISO-2022-CN-EXT. The fix and its guard-page regression test explain why state-transition output needs its own capacity check.
A character converter may need to write bytes that do not represent a character. Stateful encodings also emit control sequences to describe how subsequent bytes should be interpreted. In glibc's ISO-2022-CN-EXT converter, two such paths failed to check their output space. The 17 April advisory for CVE-2024-2961 identifies an output-buffer overflow during conversion to that encoding. The small size of the missing check makes the regression test especially useful for understanding what went wrong.
C was my first language, and its explicit memory model remains part of how I reason about interfaces. This bug is a reminder that a correct buffer length at a call boundary does not establish that every internal write respects it. The implementation must preserve that obligation through each state transition, including transitions that produce bookkeeping bytes before character data.
The conversion direction determines the affected operation
The advisory covers GNU C Library versions 2.39 and older. It names conversion to ISO-2022-CN-EXT, rather than conversion from it, and credits Charles Fol with reporting the problem. It describes fixed-value writes beyond the output boundary and lists fixes on multiple release branches. Those details matter more for exposure analysis than the presence of the general iconv interface in an application.
The converter at the fixing commit has separate loops for each direction. Its output path tracks both the currently selected character set and which sets it has announced. Before emitting character bytes, it may have to announce a set and issue a shift sequence. An output-space argument therefore has to cover more than the bytes carrying the character itself.
This distinction explains why a generic statement that an application processes Chinese text is too broad. A relevant execution path must select this destination converter. The application's configuration may determine that selection, or it may expose a choice to another caller. Understanding exposure requires following that choice to the converter actually used. The advisory does not justify treating every text-processing operation as affected.
It also explains why input size alone is insufficient to reason about output size. The converter's current state changes which control bytes it needs next. Two calls processing similar character data may begin with different announcement state. The capacity obligation applies at the point of each output operation, regardless of how plausible the original allocation estimate looked.
The missing check precedes four writes
The fixing patch adds a four-byte capacity check to the SS2 and SS3 designation branches. Each branch writes an escape byte, a dollar sign and two designation bytes. The existing SO designation branch already has the corresponding check. When insufficient space remains, the new branches set the full-output result and stop before writing the designation.
The important ordering is visible in the patch. Checking after the designation would discover exhaustion after memory had already been modified. Checking only before the final character bytes would also be too late, because those bytes are not the first output of the transition. The check must protect the control sequence where the sequence is emitted.
There are several separate capacity decisions in the surrounding function. After a designation, the converter may need a shift sequence. After that, it needs room for the character bytes. Reading the function as a single conceptual operation can hide these intermediate writes. Reading it as a sequence of writes makes the missing obligations more apparent.
The advisory explains that the overflow bytes have constrained values. That limits the write, but it does not establish harmlessness. The consequence depends on what object occupies the adjacent memory and how the application later uses it. The public advisory names application crashes and overwriting a neighboring variable as possible outcomes. A claim about arbitrary application code execution would require further evidence about that application's memory layout and behavior.
I prefer this level of description because it separates the library defect from a particular exploitation claim. The defect is precise enough to justify a library correction. An application-specific consequence needs its own account of how the corrupted state affects execution. Combining the two into a single broad assertion makes an incident harder to assess.
The regression test places the buffer against inaccessible memory
The patch adds tst-iconv-iso-2022-cn-ext.c. It allocates two memory pages and marks the second page inaccessible with PROT_NONE. The permitted output region ends exactly where that protected page begins. A write beyond the advertised output capacity therefore reaches memory the process cannot access.
This arrangement tests the physical boundary independently of the converter's reported result. A function could report that output space was exhausted after already writing too much. Looking only at the return value would miss that failure. The protected page makes the unintended write observable even if the function subsequently tries to return the right error.
The test varies the amount of available space by moving the output pointer backward from the page boundary. It exercises the SO, SS2 and SS3 cases with selected input strings. For each capacity in the tested interval, it expects conversion to fail with E2BIG. The existing SO path supplies coverage for the related branch as well as the two branches changed by the patch.
The state reset between iterations is another useful detail. The test calls iconv with null arguments to restore the initial state before the next capacity case. Without that step, a previous iteration could change the announcement state used by the following iteration. Then the loop would vary both buffer capacity and converter state, making a failure less directly attributable to the intended boundary condition.
Its SO case varies capacity from zero through eight bytes. The SS2 and SS3 cases each vary it from zero through thirteen. Those ranges deliberately include capacities that permit some output without permitting the complete conversion. They help distinguish an immediate full-buffer rejection from exhaustion after the converter has already made progress.
The resulting test is narrower than a broad collection of successful conversions. It asks whether specific state changes remain inside a precisely placed output region. That is an appropriate question for this defect. Correct conversion with abundant space does not exercise the condition introduced by an almost-full destination buffer.
The patch preserves the caller's recovery contract
Full output is an expected condition for a converter that accepts a caller-provided buffer. The library needs to stop with a result the caller can handle. Internally, the fix uses __GCONV_FULL_OUTPUT, while the regression test checks the public iconv failure and E2BIG. The distinction reflects the separation between the conversion module and the API observed by its caller.
For an application, the practical consequence is that buffer growth and retry logic depend on the converter stopping safely. Adding more initial capacity might make a particular input stop reproducing the bug, but it would not repair the missing library check. A capacity estimate belongs to allocation policy. The converter still has to enforce the exact capacity it receives.
This is a useful way to assess proposed workarounds. A larger buffer changes the probability of reaching a boundary for a particular workload. A corrected converter changes what happens when that boundary is reached. The latter restores the API property callers need for bounded processing. Treating those measures as equivalent would leave the underlying memory obligation unresolved.
Package provenance matters when applying the fix
The advisory lists a main development fix and backports for older glibc branches. Its reference to the development branch does not mean operators must wait for a future release or replace their distribution's entire toolchain. The relevant evidence is whether the installed vendor package contains the correction. An upstream version number alone cannot establish the backport status.
For maintainers of a program using this conversion, a useful investigation records the selected destination and the library package supplying it. That connects an application execution path to the vendor's fix information. It also keeps the claim appropriately narrow. Discovering that a program links glibc does not establish that it invokes the affected converter, while discovering a reachable affected conversion gives a concrete reason to prioritize the package update.
The regression test captures the property that the corrected package must preserve. Every tested designation path must stop at the caller's output boundary, including when there is space for only part of the control sequence. That property belongs to each state-transition write, independently of the amount of character data still waiting to be converted.