Skip to content

Commit 41188aa

Browse files
committed
[perl #117265] correctly handle overloaded strings
1 parent 788436d commit 41188aa

File tree

10 files changed

+53
-48
lines changed

10 files changed

+53
-48
lines changed

doio.c

+18-12
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw,
206206
*--tend = '\0';
207207

208208
if (num_svs) {
209+
const char *p;
210+
STRLEN nlen = 0;
209211
/* New style explicit name, type is just mode and layer info */
210212
#ifdef USE_STDIO
211213
if (SvROK(*svp) && !strchr(oname,'&')) {
@@ -216,11 +218,13 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw,
216218
goto say_false;
217219
}
218220
#endif /* USE_STDIO */
219-
if (!IS_SAFE_PATHNAME(*svp, "open"))
221+
p = (SvOK(*svp) || SvGMAGICAL(*svp)) ? SvPV(*svp, nlen) : NULL;
222+
223+
if (p && !IS_SAFE_PATHNAME(p, nlen, "open"))
220224
goto say_false;
221225

222-
name = (SvOK(*svp) || SvGMAGICAL(*svp)) ?
223-
savesvpv (*svp) : savepvs ("");
226+
name = p ? savepvn(p, nlen) : savepvs("");
227+
224228
SAVEFREEPV(name);
225229
}
226230
else {
@@ -1661,9 +1665,9 @@ Perl_apply(pTHX_ I32 type, SV **mark, SV **sp)
16611665
}
16621666
}
16631667
else {
1664-
const char *name = SvPV_nomg_const_nolen(*mark);
1668+
const char *name = SvPV_nomg_const(*mark, len);
16651669
APPLY_TAINT_PROPER();
1666-
if (!IS_SAFE_PATHNAME(*mark, "chmod") ||
1670+
if (!IS_SAFE_PATHNAME(name, len, "chmod") ||
16671671
PerlLIO_chmod(name, val)) {
16681672
tot--;
16691673
}
@@ -1697,9 +1701,9 @@ Perl_apply(pTHX_ I32 type, SV **mark, SV **sp)
16971701
}
16981702
}
16991703
else {
1700-
const char *name = SvPV_nomg_const_nolen(*mark);
1704+
const char *name = SvPV_nomg_const(*mark, len);
17011705
APPLY_TAINT_PROPER();
1702-
if (!IS_SAFE_PATHNAME(*mark, "chown") ||
1706+
if (!IS_SAFE_PATHNAME(name, len, "chown") ||
17031707
PerlLIO_chown(name, val, val2)) {
17041708
tot--;
17051709
}
@@ -1800,9 +1804,9 @@ nothing in the core.
18001804
APPLY_TAINT_PROPER();
18011805
tot = sp - mark;
18021806
while (++mark <= sp) {
1803-
s = SvPV_nolen_const(*mark);
1807+
s = SvPV_const(*mark, len);
18041808
APPLY_TAINT_PROPER();
1805-
if (!IS_SAFE_PATHNAME(*mark, "unlink")) {
1809+
if (!IS_SAFE_PATHNAME(s, len, "unlink")) {
18061810
tot--;
18071811
}
18081812
else if (PerlProc_geteuid() || PL_unsafe) {
@@ -1881,9 +1885,9 @@ nothing in the core.
18811885
}
18821886
}
18831887
else {
1884-
const char * const name = SvPV_nomg_const_nolen(*mark);
1888+
const char * const name = SvPV_nomg_const(*mark, len);
18851889
APPLY_TAINT_PROPER();
1886-
if (!IS_SAFE_PATHNAME(*mark, "utime")) {
1890+
if (!IS_SAFE_PATHNAME(name, len, "utime")) {
18871891
tot--;
18881892
}
18891893
else
@@ -2376,10 +2380,12 @@ Perl_start_glob (pTHX_ SV *tmpglob, IO *io)
23762380
dVAR;
23772381
SV * const tmpcmd = newSV(0);
23782382
PerlIO *fp;
2383+
STRLEN len;
2384+
const char *s = SvPV(tmpglob, len);
23792385

23802386
PERL_ARGS_ASSERT_START_GLOB;
23812387

2382-
if (!IS_SAFE_SYSCALL(tmpglob, "pattern", "glob"))
2388+
if (!IS_SAFE_SYSCALL(s, len, "pattern", "glob"))
23832389
return NULL;
23842390

23852391
ENTER;

embed.fnc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1598,7 +1598,7 @@ Ap |I32 |whichsig_sv |NN SV* sigsv
15981598
Ap |I32 |whichsig_pv |NN const char* sig
15991599
Ap |I32 |whichsig_pvn |NN const char* sig|STRLEN len
16001600
: used to check for NULs in pathnames and other names
1601-
AiR |bool |is_safe_syscall|NN SV *pv|NN const char *what|NN const char *op_name
1601+
AiR |bool |is_safe_syscall|NN const char *pv|STRLEN len|NN const char *what|NN const char *op_name
16021602
: Used in pp_ctl.c
16031603
p |void |write_to_stderr|NN SV* msv
16041604
: Used in op.c

embed.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@
231231
#define instr Perl_instr
232232
#define is_ascii_string Perl_is_ascii_string
233233
#define is_lvalue_sub() Perl_is_lvalue_sub(aTHX)
234-
#define is_safe_syscall(a,b,c) S_is_safe_syscall(aTHX_ a,b,c)
234+
#define is_safe_syscall(a,b,c,d) S_is_safe_syscall(aTHX_ a,b,c,d)
235235
#define is_uni_alnum(a) Perl_is_uni_alnum(aTHX_ a)
236236
#define is_uni_alnum_lc(a) Perl_is_uni_alnum_lc(aTHX_ a)
237237
#define is_uni_alnumc(a) Perl_is_uni_alnumc(aTHX_ a)

ext/File-Glob/Glob.xs

+6-5
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ csh_glob(pTHX_ AV *entries, SV *patsv)
136136
else pat = SvPV_nomg(patsv,len), is_utf8 = !!SvUTF8(patsv);
137137
patend = pat + len;
138138

139+
assert(SvTYPE(entries) != SVt_PVAV);
140+
sv_upgrade((SV *)entries, SVt_PVAV);
141+
142+
if (!IS_SAFE_SYSCALL(pat, len, "pattern", "glob"))
143+
return FALSE;
144+
139145
/* extract patterns */
140146
s = pat-1;
141147
while (++s < patend) {
@@ -225,11 +231,6 @@ csh_glob(pTHX_ AV *entries, SV *patsv)
225231
}
226232
end_of_parsing:
227233

228-
assert(SvTYPE(entries) != SVt_PVAV);
229-
sv_upgrade((SV *)entries, SVt_PVAV);
230-
if (!IS_SAFE_SYSCALL(patsv, "pattern", "glob"))
231-
return FALSE;
232-
233234
if (patav) {
234235
I32 items = AvFILLp(patav) + 1;
235236
SV **svp = AvARRAY(patav);

inline.h

+5-6
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ S_isALNUM_lazy(pTHX_ const char* p)
288288
/* ------------------------------- perl.h ----------------------------- */
289289

290290
/*
291-
=for apidoc AiR|bool|is_safe_syscall|SV *pv|const char *what|const char *op_name
291+
=for apidoc AiR|bool|is_safe_syscall|const char *pv|STRLEN len|const char *what|const char *op_name
292292
293293
Test that the given C<pv> doesn't contain any internal NUL characters.
294294
If it does, set C<errno> to ENOENT, optionally warn, and return FALSE.
@@ -301,21 +301,20 @@ Used by the IS_SAFE_SYSCALL() macro.
301301
*/
302302

303303
PERL_STATIC_INLINE bool
304-
S_is_safe_syscall(pTHX_ SV *pv, const char *what, const char *op_name) {
304+
S_is_safe_syscall(pTHX_ const char *pv, STRLEN len, const char *what, const char *op_name) {
305305
/* While the Windows CE API provides only UCS-16 (or UTF-16) APIs
306306
* perl itself uses xce*() functions which accept 8-bit strings.
307307
*/
308308

309309
PERL_ARGS_ASSERT_IS_SAFE_SYSCALL;
310310

311-
if (SvPOK(pv) && SvCUR(pv) >= 1) {
312-
char *p = SvPVX(pv);
311+
if (pv && len > 1) {
313312
char *null_at;
314-
if (UNLIKELY((null_at = (char *)memchr(p, 0, SvCUR(pv)-1)) != NULL)) {
313+
if (UNLIKELY((null_at = (char *)memchr(pv, 0, len-1)) != NULL)) {
315314
SETERRNO(ENOENT, LIB_INVARG);
316315
Perl_ck_warner(aTHX_ packWARN(WARN_SYSCALLS),
317316
"Invalid \\0 character in %s for %s: %s\\0%s",
318-
what, op_name, p, null_at+1);
317+
what, op_name, pv, null_at+1);
319318
return FALSE;
320319
}
321320
}

perl.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -5692,9 +5692,9 @@ extern void moncontrol(int);
56925692

56935693
/* check embedded \0 characters in pathnames passed to syscalls,
56945694
but allow one ending \0 */
5695-
#define IS_SAFE_SYSCALL(pv, what, op_name) (S_is_safe_syscall(aTHX_ (pv), (what), (op_name)))
5695+
#define IS_SAFE_SYSCALL(p, len, what, op_name) (S_is_safe_syscall(aTHX_ (p), (len), (what), (op_name)))
56965696

5697-
#define IS_SAFE_PATHNAME(pv, op_name) IS_SAFE_SYSCALL((pv), "pathname", (op_name))
5697+
#define IS_SAFE_PATHNAME(p, len, op_name) IS_SAFE_SYSCALL((p), (len), "pathname", (op_name))
56985698

56995699
#if defined(OEMVS)
57005700
#define NO_ENV_ARRAY_IN_MAIN

perlio.c

+12-8
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,9 @@ PerlIO_openn(pTHX_ const char *layers, const char *mode, int fd,
312312
if (*args == &PL_sv_undef)
313313
return PerlIO_tmpfile();
314314
else {
315-
const char *name = SvPV_nolen_const(*args);
316-
if (!IS_SAFE_PATHNAME(*args, "open"))
315+
STRLEN len;
316+
const char *name = SvPV_nolen_const(*args, len);
317+
if (!IS_SAFE_PATHNAME(name, len, "open"))
317318
return NULL;
318319

319320
if (*mode == IoTYPE_NUMERIC) {
@@ -2725,8 +2726,9 @@ PerlIOUnix_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers,
27252726
#endif
27262727
}
27272728
if (imode != -1) {
2728-
const char *path = SvPV_nolen_const(*args);
2729-
if (!IS_SAFE_PATHNAME(*args, "open"))
2729+
STRLEN len;
2730+
const char *path = SvPV_const(*args, len);
2731+
if (!IS_SAFE_PATHNAME(path, len, "open"))
27302732
return NULL;
27312733
fd = PerlLIO_open3(path, imode, perm);
27322734
}
@@ -3039,10 +3041,11 @@ PerlIOStdio_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers,
30393041
{
30403042
char tmode[8];
30413043
if (PerlIOValid(f)) {
3042-
const char * const path = SvPV_nolen_const(*args);
3044+
STRLEN len;
3045+
const char * const path = SvPV_const(*args, len);
30433046
PerlIOStdio * const s = PerlIOSelf(f, PerlIOStdio);
30443047
FILE *stdio;
3045-
if (!IS_SAFE_PATHNAME(*args, "open"))
3048+
if (!IS_SAFE_PATHNAME(path, len, "open"))
30463049
return NULL;
30473050
PerlIOUnix_refcnt_dec(fileno(s->stdio));
30483051
stdio = PerlSIO_freopen(path, (mode = PerlIOStdio_mode(mode, tmode)),
@@ -3055,8 +3058,9 @@ PerlIOStdio_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers,
30553058
}
30563059
else {
30573060
if (narg > 0) {
3058-
const char * const path = SvPV_nolen_const(*args);
3059-
if (!IS_SAFE_PATHNAME(*args, "open"))
3061+
STRLEN len;
3062+
const char * const path = SvPV_const(*args, len);
3063+
if (!IS_SAFE_PATHNAME(path, len, "open"))
30603064
return NULL;
30613065
if (*mode == IoTYPE_NUMERIC) {
30623066
mode++;

pp_ctl.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -3597,7 +3597,8 @@ STATIC PerlIO *
35973597
S_check_type_and_open(pTHX_ SV *name)
35983598
{
35993599
Stat_t st;
3600-
const char *p = SvPV_nolen_const(name);
3600+
STRLEN len;
3601+
const char *p = SvPV_const(name, len);
36013602
int st_rc;
36023603

36033604
PERL_ARGS_ASSERT_CHECK_TYPE_AND_OPEN;
@@ -3608,7 +3609,7 @@ S_check_type_and_open(pTHX_ SV *name)
36083609
* rather than for the .pm file.
36093610
* This check prevents a \0 in @INC causing problems.
36103611
*/
3611-
if (!IS_SAFE_PATHNAME(name, "require"))
3612+
if (!IS_SAFE_PATHNAME(p, len, "require"))
36123613
return NULL;
36133614

36143615
st_rc = PerlLIO_stat(p, &st);
@@ -3637,7 +3638,7 @@ S_doopen_pm(pTHX_ SV *name)
36373638
* warning referring to the .pmc which the user probably doesn't
36383639
* know or care about
36393640
*/
3640-
if (!IS_SAFE_PATHNAME(name, "require"))
3641+
if (!IS_SAFE_PATHNAME(p, namelen, "require"))
36413642
return NULL;
36423643

36433644
if (namelen > 3 && memEQs(p + namelen - 3, 3, ".pm")) {
@@ -3772,7 +3773,7 @@ PP(pp_require)
37723773
name = SvPV_const(sv, len);
37733774
if (!(name && len > 0 && *name))
37743775
DIE(aTHX_ "Null filename used");
3775-
if (!IS_SAFE_PATHNAME(sv, "require")) {
3776+
if (!IS_SAFE_PATHNAME(name, len, "require")) {
37763777
DIE(aTHX_ "Can't locate %s: %s",
37773778
pv_escape(newSVpvs_flags("",SVs_TEMP),SvPVX(sv),SvCUR(sv),
37783779
SvCUR(sv)*2,NULL, SvUTF8(sv)?PERL_PV_ESCAPE_UNI:0),

proto.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -1761,11 +1761,11 @@ PERL_CALLCONV bool Perl_is_ascii_string(const U8 *s, STRLEN len)
17611761
PERL_CALLCONV I32 Perl_is_lvalue_sub(pTHX)
17621762
__attribute__warn_unused_result__;
17631763

1764-
PERL_STATIC_INLINE bool S_is_safe_syscall(pTHX_ SV *pv, const char *what, const char *op_name)
1764+
PERL_STATIC_INLINE bool S_is_safe_syscall(pTHX_ const char *pv, STRLEN len, const char *what, const char *op_name)
17651765
__attribute__warn_unused_result__
17661766
__attribute__nonnull__(pTHX_1)
1767-
__attribute__nonnull__(pTHX_2)
1768-
__attribute__nonnull__(pTHX_3);
1767+
__attribute__nonnull__(pTHX_3)
1768+
__attribute__nonnull__(pTHX_4);
17691769
#define PERL_ARGS_ASSERT_IS_SAFE_SYSCALL \
17701770
assert(pv); assert(what); assert(op_name)
17711771

t/io/open.t

-6
Original file line numberDiff line numberDiff line change
@@ -419,21 +419,17 @@ pass("no crash when open autovivifies glob in freed package");
419419
like($WARN, qr/^Invalid \\0 character in pathname for chmod: $temp_match\\0\.invalid/,
420420
"also on chmod"); $WARN = '';
421421

422-
$TODO = "broken for overloading";
423422
is(chmod(0444, $fno), 0, "chmod fails with \\0 in name (overload)");
424423
like($WARN, qr/^Invalid \\0 character in pathname for chmod: $temp_match\\0\.overload/,
425424
"also on chmod"); $WARN = '';
426-
undef $TODO;
427425

428426
is (glob($fn), undef, "glob fails with \\0 in name");
429427
like($WARN, qr/^Invalid \\0 character in pattern for glob: $temp_match\\0\.invalid/,
430428
"also on glob"); $WARN = '';
431429

432-
$TODO = "broken for overloading";
433430
is (glob($fno), undef, "glob fails with \\0 in name (overload)");
434431
like($WARN, qr/^Invalid \\0 character in pattern for glob: $temp_match\\0\.overload/,
435432
"also on glob"); $WARN = '';
436-
undef $TODO;
437433

438434
{
439435
no warnings 'syscalls';
@@ -465,12 +461,10 @@ pass("no crash when open autovivifies glob in freed package");
465461
like($WARN, qr/^Invalid \\0 character in pathname for unlink: $temp_match\\0\.invalid/,
466462
"also on unlink"); $WARN = '';
467463

468-
$TODO = "broken for overloading";
469464
is (unlink($fno), 0, "unlink fails with \\0 in name (overload)");
470465
like($WARN, qr/^Invalid \\0 character in pathname for unlink: $temp_match\\0\.overload/,
471466
"also on unlink"); $WARN = '';
472467

473-
local $TODO = "this is broken for overloading";
474468
ok(-f $temp, "nothing removed the temp file");
475469
is((stat $temp)[2], $final_mode, "nothing changed its mode");
476470
is((stat $temp)[9], $final_mtime, "nothing changes its mtime");

0 commit comments

Comments
 (0)