diff options
author | Nicholas Clark <nick@ccl4.org> | 2012-06-27 23:34:04 +0200 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2016-05-10 11:02:24 +0100 |
commit | a52f2cced5b51a96e90a2604111245e6096dae5b (patch) | |
tree | a29566e3d00a9492a0f67d38029b53353033d3c6 /pp_ctl.c | |
parent | 614273add497cd4fbed447fdad84ef323b226b18 (diff) | |
download | perl-a52f2cced5b51a96e90a2604111245e6096dae5b.tar.gz |
Validate the 'require Bare::Word' pathname.
At runtime in require, validate the generated filename after translation
of '::' to '/' (and possible conversion from VMS to Unix format) to keep
the code simpler. Reject empty module names, module names starting with
'/' or '.' (ie absolute paths, hidden files, and '..'), and module names
containing NUL bytes or '/.' (ie hidden files and '..').
Add a test for Perl_load_module(), and check that it now rejects module
names which fall foul of the above rules.
Most of these can't trigger for a sinple bareword require since the
illegal module name will already have been rejected during parsing. However,
the Perl_load_module() fakes up a rquire optree including a bareword
OP_CONST, which *isn't* restricted by the lexer.
Note that this doesn't apply to non-bareword pathnames: these are both
unaffected:
require "/foo/bar.pm";
$x = "/foo/bar.pm"; require $x;
[ This is cherry-picked from a branch Nicholas wrote 4 years ago, but
which was never merged. I've kept the body of the diff the same, modulo
rebasing, but re-worded the commit title and message.
Only one test was changed: the final one in load-module.t, since a
\0 in a pathname is now trapped earlier and gives a "can't locate" error
instead. For the same reason, it also required the addition of
"no warnings 'syscalls';".
- DAPM ]
Diffstat (limited to 'pp_ctl.c')
-rw-r--r-- | pp_ctl.c | 40 |
1 files changed, 40 insertions, 0 deletions
@@ -3727,6 +3727,46 @@ S_require_file(pTHX_ SV *const sv) DIE(aTHX_ "Attempt to reload %s aborted.\n" "Compilation failed in require", unixname); } + + if (PL_op->op_flags & OPf_KIDS) { + SVOP * const kid = (SVOP*)cUNOP->op_first; + + if (kid->op_type == OP_CONST && (kid->op_private & OPpCONST_BARE)) { + /* require foo (or use foo) with a bareword. + Perl_load_module fakes up the identical optree, but its + arguments aren't restricted by the parser to real barewords. + */ + const STRLEN package_len = len - 3; + const char slashdot[2] = {'/', '.'}; +#ifdef DOSISH + const char backslashdot[2] = {'\\', '.'}; +#endif + + /* Disallow *purported* barewords that map to absolute + filenames, filenames relative to the current or parent + directory, or (*nix) hidden filenames. Also sanity check + that the generated filename ends .pm */ + if (!path_searchable || len < 3 || name[0] == '.' + || !memEQ(name + package_len, ".pm", 3)) + DIE(aTHX_ "Bareword in require maps to disallowed filename \"%"SVf"\"", sv); + if (memchr(name, 0, package_len)) { + /* diag_listed_as: Bareword in require contains "%s" */ + DIE(aTHX_ "Bareword in require contains \"\\0\""); + } + if (ninstr(name, name + package_len, slashdot, + slashdot + sizeof(slashdot))) { + /* diag_listed_as: Bareword in require contains "%s" */ + DIE(aTHX_ "Bareword in require contains \"/.\""); + } +#ifdef DOSISH + if (ninstr(name, name + package_len, backslashdot, + backslashdot + sizeof(backslashdot))) { + /* diag_listed_as: Bareword in require contains "%s" */ + DIE(aTHX_ "Bareword in require contains \"\\.\""); + } +#endif + } + } } PERL_DTRACE_PROBE_FILE_LOADING(unixname); |