Audit Perl's 5.24.1 Security Changes on Linux
You will finish with a small, repeatable audit for the two security changes documented for Perl 5.24.1: PerlIO debugging no longer obeys PERLIO_DEBUG until debugging is explicitly enabled, and core tools and many modules stop treating the current directory as an optional-module search path. You will also check the interpreter actually installed on this host.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell and the perl command. The examples are read-only apart from deliberately supplying -I. to one short-lived process. Do not run these checks from a directory containing untrusted Perl files if you are testing a real application.
Checkpoint
This guide covers the changes between Perl 5.24.0 and 5.24.1. It does not upgrade Perl, edit system files, or promise that a later Perl release has identical internals.
1. Identify the documentation and interpreter
The perl5241delta page is release documentation, not normally an executable command. Confirm the installed packages and interpreter before interpreting any result:
$ dpkg-query -W -f='${Package} ${Version}\n' perl-doc perl-base
perl-doc 5.38.2-3.2ubuntu0.6
perl-base 5.38.2-3.2ubuntu0.6
$ perl -V:version -V:archname
version='5.38.2';
archname='x86_64-linux-gnu-thread-multi';
Your package revision and architecture may differ. If perl-doc is absent, the interpreter can still be checked, but the local perl5241delta manpage is not installed by that package.
The installed interpreter here is newer than 5.24.1. Treat its output as evidence about this host, not as a reconstruction of every historical implementation detail.
2. Check whether the current directory is in @INC
Perl stores module search paths in @INC. A literal . means the process may search its current working directory. That becomes dangerous when a privileged or otherwise trusted process runs in a directory another user can write, because an optional module name can resolve to an attacker-controlled file.
Inspect the normal search path without loading a module:
$ perl -e 'print join("\n", @INC), "\n"'
/etc/perl
/usr/local/lib/x86_64-linux-gnu/perl/5.38.2
/usr/local/share/perl/5.38.2
/usr/lib/x86_64-linux-gnu/perl5/5.38
/usr/share/perl5
/usr/lib/x86_64-linux-gnu/perl-base
/usr/lib/x86_64-linux-gnu/perl/5.38
/usr/share/perl/5.38
/usr/local/lib/site_perl
On this host there is no .. Do not assume that result for an older installation or for a program that adds paths itself. A compact pass or fail check is:
$ perl -e 'print grep { $_ eq "." } @INC ? "dot-in-\@INC\n" : "no-dot-in-\@INC\n"'
no-dot-in-@INC
The -I option intentionally adds a path. Use it only when you have a controlled directory and understand why the program needs it:
$ perl -I. -e 'print grep { $_ eq "." } @INC ? "dot-in-\@INC\n" : "no-dot-in-\@INC\n"'
dot-in-@INC
This does not change the host or any persistent configuration. It changes one process, which exits immediately.
3. Remove a trailing dot from your own script
Perl 5.24.1 changed many core tools and modules so they remove a final . before looking for optional modules. The release note calls out Storable and says that base was an exception in that release because its optional-module handling still had unresolved difficulties.
If your application deliberately adds the current directory, make the security decision visible near the start of the script instead of relying on a global assumption:
#!/usr/bin/perl
BEGIN { pop @INC if $INC[-1] eq '.' }
use strict;
That exact pattern removes the dot only when it is the last entry. It does not remove another path that happens to be a directory containing the script. Keep the dot only when it is genuinely required, and prefer an explicit, owned library directory when you control the deployment.
For an optional module lookup, localise the search path so the rest of the process is unaffected:
my $can_foo = eval {
local @INC = @INC;
pop @INC if $INC[-1] eq '.';
require Foo;
};
The variable name is only an example. Replace Foo with a module you actually expect, and check $@ or the result in real error handling. Do not hide a failed required dependency behind an empty eval.
Checkpoint
If a test fails only after removing ., find the undeclared local dependency and install or package it in a trusted library location. Restoring an unsafe search path is a compatibility shortcut, not a fix.
4. Understand the PerlIO debugging boundary
Before 5.24.1, PerlIO debugging could write to the file named by PERLIO_DEBUG before Perl had fully processed command-line switches. That could create or overwrite a path even when a taint-mode switch was intended to protect the process. Perl 5.24.1 requires the -Di debugging switch before producing PerlIO debugging output.
Use standard error as the destination while testing, so the example cannot overwrite a chosen file:
$ PERLIO_DEBUG=/dev/stderr perl -e 'print "normal output\n"'
normal output
With a debugging build, adding -Di enables PerlIO diagnostics and sends them to standard error by default:
$ PERLIO_DEBUG=/dev/stderr perl -Di -e 'print "debugged output\n"'
PerlIO diagnostics appear here, followed by the program output
Exact diagnostic lines vary, and a non-debugging build may reject -D entirely. On this host, Perl 5.38.2 reports that it must be recompiled with -DDEBUGGING to use the switch, then still prints the program's ordinary output. That is a build characteristic, not evidence that the 5.24.1 rule is absent.
Do not set PERLIO_DEBUG to a production log path while investigating. The variable is useful for controlled diagnostics, but debug output can be noisy and may disclose paths or I/O details. If you need a file, choose a new, access-controlled path and check its ownership before removing it.
5. Check the other 5.24.1 compatibility change
The release also reverted a Perl 5.24.0 hashbang change. Perl had redirected some interpreter paths containing perl followed by 6, which broke legitimate paths such as /opt/perl64/bin/perl. Perl 5.24.1 removed that redirection. This is normally something you confirm during an application upgrade, not something to toggle at runtime.
For a script migration, inspect the first line and run the intended interpreter explicitly:
$ head -n 1 ./your-script.pl
#!/usr/bin/perl
$ /usr/bin/perl ./your-script.pl
program-specific output
Replace the path and output with values from your application. Do not execute an untrusted script merely to test its hashbang. If the script is yours, compare its interpreter path with command -v perl and perl -V:version. The revert fixes a compatibility bug; it does not make an arbitrary interpreter path trusted.
Done means
- You recorded the installed
perlandperl-docversions. - You checked
@INCand know whether the current directory can supply modules. - Your own optional-module lookups remove a trailing
.or use an explicitly trusted library path. - You tested
PERLIO_DEBUGwith/dev/stderr, without risking a useful file. - You know whether the installed interpreter is a debugging build before relying on
-Di. - You reviewed application hashbangs and did not change services, packages or persistent configuration.