November 29, 2023·6 min read·engineeringperlsecurityregexwindows

Perl 5.38.2 fixes regex compilation and Windows shell lookup

Perl's November maintenance release repairs a one-byte regex buffer overflow and a Windows shell search problem. The fixes protect different boundaries, so their exposure conditions need separate explanations.

A regular expression can cross a memory boundary before matching any text. A shell invocation can cross a privilege boundary before interpreting its command. Perl 5.38.2 fixes one problem at each of those stages. Understanding the inputs that select those stages is more useful than treating the release as an undifferentiated security update.

The Perl release history dates 5.38.2 to November 29. Its release notes describe CVE-2023-47038 in regex compilation and CVE-2023-47039 in Windows binary lookup. The same notes explain that they compare against 5.38.0 and deliberately skip 5.38.1, which was a broken release available only briefly.

I still prefer Perl for one-off text transformations, so the regex compiler is a particularly relevant part of this release. The convenience of accepting a pattern should not obscure that the interpreter parses and transforms that pattern before it searches an input string. Here the memory defect sits in that preparatory work.

The regex problem concerns property-name parsing

The release notes describe CVE-2023-47038 as a write past a buffer end through an illegal user-defined Unicode property. A crafted regex compiled by Perl 5.30.0 through 5.38.0 can cause an attacker-controlled one-byte overflow in a heap buffer. Nathan Mills reported the vulnerability directly to the Perl security team.

That description identifies the pattern as an input of interest. A service that accepts caller-supplied regex text asks the interpreter to compile data chosen by that caller. It does not need to reach a successful match before the compiler can encounter malformed property syntax. Conversely, the advisory does not say that ordinary subject text alone turns every fixed application pattern into an exploit.

The one-byte size describes the overwrite, not a guarantee that the effect is harmless. The notes identify attacker control over the byte and the heap allocation involved. They do not establish a universally reliable code-execution result. Keeping the account at that level avoids replacing a concrete memory-safety report with either an unsupported exploit claim or an unsupported dismissal.

The implementation gives more detail about the parser state that needed repair. Comparing the 5.38.0 regex compiler with the 5.38.2 compiler shows a correction to how the parser restarts after stripping an initial utf8:: package prefix. The code handles that prefix specially because it can identify an official Unicode property or a user-defined one.

Removing a prefix changes the restart position

In the older code, stripping the prefix advances the lookup-name pointer. A later strict reparse resets the source index to zero along with the output index. Those two actions no longer describe the same logical starting point after the prefix has been removed from the lookup view.

The corrected code records a separate restart index named i_zero. When it strips utf8::, it sets that restart position past the prefix. A strict reparse then resets the source index to i_zero while resetting the output index to zero. The input position and output position can differ because they now refer to different views of the name.

The underscore handling changes with the same distinction. Its test for the beginning of the relevant name compares the source index with i_zero, instead of assuming that the relevant beginning is always absolute offset zero. This keeps the boundary check aligned with the parser's adjusted starting point.

This source comparison explains the shape of the correction without supplying an exploit pattern. The important implementation fact is that normalization changed the beginning of the logical input. Restarting a later pass needed to preserve that changed beginning. Resetting every index to zero was not a valid universal initialization step.

The release notes identify the vulnerability, while the tagged source identifies the corresponding index handling. Neither source claims that disabling embedded regex code is the remedy. The reported problem concerns property parsing during compilation, so an unrelated option for evaluation behavior would not establish that this compiler defect had been repaired.

CVE-2023-47039 has a different prerequisite. The release notes say Windows Perl searched for cmd.exe in a way that could select a file in the current working directory before reaching the intended system shell. An attacker with limited privileges could place an executable under that name in a weakly protected location.

The advisory gives C:\ProgramData as an example of such a location. If an administrator then invokes the affected Perl-using executable from that location, Perl can run the attacker's binary with the administrator's authority. The privilege difference between the person controlling the directory and the person launching the process is central to that account.

This is not a claim that Perl scripts become malicious merely because their working directory changes. The problem is executable resolution for a shell that the interpreter intends to invoke. A trusted caller can ask for an ordinary operation while the search rule selects an untrusted program to implement it.

The report came through Intel's Product Security Incident Response Team, following a report by GitHub user ycdxsb. That route and the separate identifier help keep it distinct from the regex issue. Windows shell search and Unicode-property compilation share a release vehicle, but they have different inputs and affected execution paths.

The fixed default names the system shell directly

The Windows implementation at the release tag makes the default-shell correction concrete. Its get_shell function obtains the Windows system directory with GetSystemDirectoryA, checks the resulting length, and appends cmd.exe. The shell vector then contains the resulting path and its command options.

Choosing a path derived from the system directory removes the need to discover the default shell by its bare filename in an ambient search. The implementation also handles failure to obtain the directory and rejects a path that would exceed its buffer. Both checks belong to constructing the selected program's pathname.

An explicit PERL5SHELL setting remains a separate branch. The runtime documentation describes that variable as the Windows override for the shell Perl uses internally for operations such as backticks or system. The fixed default does not mean that an application can ignore an override it deliberately supplies.

That distinction prevents an overly broad reading of the patch. The default-shell code now selects the system location. Explicitly configuring another shell still asks Perl to use that configuration. A full account needs both the safer default and the continuing override, since the source implements both.

I regard this as a useful correction to the relationship between caller intent and executable identity. Asking for command processing should not accidentally delegate that request to a same-named file in a directory controlled by someone else. The path construction in the patch makes the default selection explicit.

Release identity is part of the update

The notes' warning about 5.38.1 matters when choosing an artifact. They identify 5.38.2 as the release whose documented comparison includes these security changes and exclude the broken intermediate release. Reading only a sequential version number would miss that explicit instruction about how the maintenance history should be interpreted.

The fixes also illustrate why a single exposure question is insufficient. For the regex issue, the relevant input is a crafted compiled pattern reaching the property parser. For the Windows issue, the relevant environment includes shell invocation, executable search, and a working directory an attacker can influence. One deployment can satisfy one set of conditions without satisfying the other. A review of allowed regex syntax therefore answers a different question from a review of Windows launch directories. Neither review substitutes for installing the corrected interpreter where its affected behavior is available.

Application validation after either operation cannot repair the interpreter behavior that has already occurred. Inspecting a match result follows compilation. Inspecting command output follows executable selection. The maintenance update changes the earlier implementation stages where these defects arise.

For callers retaining Perl's existing programming model, 5.38.2 supplies those interpreter-level corrections. The regex parser preserves the appropriate restart position after removing a property prefix, and the Windows default-shell path comes from the system directory. Those are the concrete changes underlying the two security entries.