Home / Alt manpages / perl5223delta(1)

  • perl5223delta(1)
  • User command
  • linux

Harden an Old Perl Application After the 5.22.3 Security Changes

You will finish with a small audit for the two security changes described by perl5223delta: PerlIO tracing now needs -Di, and core code no longer treats the current directory as an optional module source. You will also have a safe way to check an interpreter before touching a service. Allow about twenty minutes for one application and its test command.

The local reference is perl5223delta(1) from the perl-doc package, version 5.38.2-3.2ubuntu0.6. The man page documents the changes between Perl 5.22.2 and 5.22.3; it is not a general guide to every later Perl release. The checks below use the installed perl, which is version 5.38.2, so treat its output as a check of this host rather than proof of behaviour on an old 5.22 binary.

1. Record the interpreter and the application entry point

Start with read-only commands. Use the same interpreter that launches the application; a shell path and a service path can select different Perl installations.

$ command -v perl
/usr/bin/perl
$ perl -V:version -V:patchlevel -V:subversion
version='5.38.2';
patchlevel='38';
subversion='2';

Now identify the script or service wrapper you actually intend to test. Replace the placeholder with a real path, but do not run it yet:

$ APP='/path/to/application.pl'
$ test -r "$APP" && printf 'readable: %s\n' "$APP"
readable: /path/to/application.pl

Checkpoint: if command -v perl or the path is not what the deployment uses, stop here. Fix the inventory first. Do not edit a system service merely to make this check pass.

2. Check whether the current directory is in @INC

Perl searches the directories in @INC when loading modules. The 5.22.3 change removed the default current-directory entry when core code and tools look for optional modules, reducing the risk of loading attacker-controlled code from a writable directory such as /tmp. Your own program may still add a directory explicitly, and PERL5LIB can alter the search path.

$ perl -e 'print join("\n", @INC), "\n"'
/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

Exact directories vary by installation. What matters is whether the final entry is the single-character directory .. Check it without relying on the displayed list:

$ perl -e 'print defined $INC[-1] && $INC[-1] eq "." ? "dot is present\n" : "dot is absent\n"'
dot is absent

Checkpoint: repeat the check with the environment used by the application. A user or service definition that sets PERL5LIB can introduce a risky writable path even when the interpreter's default list is clean:

$ PERL5LIB=. perl -e 'print join("\n", @INC), "\n"'
.
/usr/share/perl5
... system paths follow ...

Do not copy the literal ellipsis into a script. It marks output that depends on the machine.

3. Remove a trailing dot in legacy code

If an older program deliberately keeps the current directory at the end of @INC, make the removal explicit near the start of the program. This is an ordinary code change, so test it in a checkout first. The condition only removes the final entry when it is exactly .; it does not disturb named application directories.

BEGIN {
    pop @INC if @INC && $INC[-1] eq '.';
}
use strict;
use warnings;

For an optional module load, localise the list so the rest of the process keeps its normal search path:

my $can_load = eval {
    local @INC = @INC;
    pop @INC if @INC && $INC[-1] eq '.';
    require Optional::Module;
    1;
};
printf "optional module: %s\n", $can_load ? 'available' : 'absent';

Do not replace a known application library directory with . just to imitate an old deployment. Use an absolute, owned path and review its permissions instead.

4. Treat PerlIO tracing as an explicit diagnostic

The 5.22.3 change prevents PERLIO_DEBUG from silently selecting a trace file before Perl has parsed its switches. Tracing is now requested with -Di. With a debugging build, the trace goes to standard error by default; setting PERLIO_DEBUG redirects it to the named file. A normal distribution build may report that it was not compiled with debugging support.

$ perl -Di -e 'print qq(PerlIO debug test\n)' 2>&1 | head
Recompile perl with -DDEBUGGING to use -D switch (did you mean -d ?)
PerlIO debug test

That output is expected from the installed non-debugging Perl. It is not a reason to rebuild production Perl. If you are diagnosing a separate test interpreter, confirm its build first:

$ perl -V:ccflags
ccflags='...'

Never point PERLIO_DEBUG at a shared or sensitive file. Tracing can disclose implementation details and appends to the selected path on builds that support it. Use a private temporary file with restrictive permissions, and remove it after the diagnostic. This is the only step here that writes diagnostic output.

5. Run the application's own smoke test

After reviewing module paths, run the application's harmless validation command, not a guessed invocation. For a script with a documented check mode it might look like this:

$ perl "$APP" --check-config
configuration OK

The option above is a placeholder for an option your application really documents. Do not pass it to an application that does not support it. If the application is a service, test the command in staging first; restarting a production service is outside this guide and may interrupt users.

If the check fails with a missing optional module, inspect @INC, PERL5LIB, the working directory and the application's explicit use lib statements. Do not solve the failure by adding the current directory to a privileged service's module path.

Done means

  • You recorded the exact Perl binary and version used by the application.
  • You checked the effective @INC and PERL5LIB environment.
  • No writable current-directory entry is used unintentionally for optional modules.
  • Any PerlIO trace was requested explicitly, kept private and removed after use.
  • The application's real smoke test passes without a service restart or policy change.