Skip to content

Commit 5c5a0c1

Browse files
rurbantonycoz
authored andcommitted
Storable: improve recursion depth check
Store each recursive SV and only ++depth when recursing into this same SV, not nested into 2 levels, for performance reasons. (We'd really need to store a hash, but this would be slow.) So it only protects from the simpliest stack exhaustion attacks, others still will segfault. Decrease the max depth numbers: 2000 for RV and AV, 1000 for HV, experimentally tested for normal stack sizes. At least it doesnt falsely error with an arrayref nested into another big array, as with the CPAN write_metadata_cache. Check now also refs recursion into each other. Closes #257. (cherry picked from commit cd2b11a) Conflicts: dist/Storable/Storable.pm pod/perlcdelta.pod
1 parent cb3aaa8 commit 5c5a0c1

File tree

2 files changed

+60
-19
lines changed

2 files changed

+60
-19
lines changed

dist/Storable/Storable.xs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -362,16 +362,20 @@ typedef struct stcxt {
362362
SV *(**retrieve_vtbl)(pTHX_ struct stcxt *, const char *); /* retrieve dispatch table */
363363
SV *prev; /* contexts chained backwards in real recursion */
364364
SV *my_sv; /* the blessed scalar who's SvPVX() I am */
365+
SV *recur_sv; /* check only one recursive SV */
365366
int in_retrieve_overloaded; /* performance hack for retrieving overloaded objects */
366367
int flags; /* controls whether to bless or tie objects */
367-
U16 av_depth; /* avoid stack overflows RT #97526 */
368-
U16 hv_depth; /* avoid stack overflows RT #97526 */
368+
U16 recur_depth; /* avoid stack overflows RT #97526 */
369369
} stcxt_t;
370370

371371
/* Note: We dont count nested scalars. This will have to count all refs
372372
without any recursion detection. */
373373
/* JSON::XS has 512 */
374-
#define MAX_DEPTH 3000
374+
#if PTRSIZE == 8
375+
# define MAX_DEPTH 2000
376+
#else
377+
# define MAX_DEPTH 1200
378+
#endif
375379
#define MAX_DEPTH_ERROR "Max. recursion depth with nested structures exceeded"
376380

377381
static int storable_free(pTHX_ SV *sv, MAGIC* mg);
@@ -1430,8 +1434,8 @@ static void reset_context(stcxt_t *cxt)
14301434
{
14311435
cxt->entry = 0;
14321436
cxt->s_dirty = 0;
1433-
cxt->av_depth = 0;
1434-
cxt->hv_depth = 0;
1437+
cxt->recur_sv = NULL;
1438+
cxt->recur_depth = 0;
14351439
cxt->optype &= ~(ST_STORE|ST_RETRIEVE); /* Leave ST_CLONE alone */
14361440
}
14371441

@@ -2123,6 +2127,7 @@ static int known_class(pTHX_
21232127
*/
21242128
static int store_ref(pTHX_ stcxt_t *cxt, SV *sv)
21252129
{
2130+
int retval;
21262131
int is_weak = 0;
21272132
TRACEME(("store_ref (0x%" UVxf ")", PTR2UV(sv)));
21282133

@@ -2148,10 +2153,21 @@ static int store_ref(pTHX_ stcxt_t *cxt, SV *sv)
21482153
} else
21492154
PUTMARK(is_weak ? SX_WEAKREF : SX_REF);
21502155

2151-
/*if (cxt->entry && ++cxt->ref_cnt > MAX_REF_CNT) {
2152-
CROAK(("Max. recursion depth with nested refs exceeded"));
2153-
}*/
2154-
return store(aTHX_ cxt, sv);
2156+
TRACEME(("recur_depth %u, recur_sv (0x%" UVxf ")", cxt->recur_depth,
2157+
PTR2UV(cxt->recur_sv)));
2158+
if (cxt->entry && cxt->recur_sv == sv) {
2159+
if (++cxt->recur_depth > MAX_DEPTH) {
2160+
CROAK((MAX_DEPTH_ERROR));
2161+
}
2162+
}
2163+
cxt->recur_sv = sv;
2164+
2165+
retval = store(aTHX_ cxt, sv);
2166+
if (cxt->entry && cxt->recur_sv == sv && cxt->recur_depth > 0) {
2167+
TRACEME(("recur_depth --%u", cxt->recur_depth));
2168+
--cxt->recur_depth;
2169+
}
2170+
return retval;
21552171
}
21562172

21572173
/*
@@ -2424,9 +2440,14 @@ static int store_array(pTHX_ stcxt_t *cxt, AV *av)
24242440
TRACEME(("size = %d", (int)l));
24252441
}
24262442

2427-
if (cxt->entry && ++cxt->av_depth > MAX_DEPTH) {
2428-
CROAK((MAX_DEPTH_ERROR));
2443+
TRACEME(("recur_depth %u, recur_sv (0x%" UVxf ")", cxt->recur_depth,
2444+
PTR2UV(cxt->recur_sv)));
2445+
if (cxt->entry && cxt->recur_sv == (SV*)av) {
2446+
if (++cxt->recur_depth > MAX_DEPTH) {
2447+
CROAK((MAX_DEPTH_ERROR));
2448+
}
24292449
}
2450+
cxt->recur_sv = (SV*)av;
24302451

24312452
/*
24322453
* Now store each item recursively.
@@ -2457,7 +2478,10 @@ static int store_array(pTHX_ stcxt_t *cxt, AV *av)
24572478
return ret;
24582479
}
24592480

2460-
if (cxt->entry) --cxt->av_depth;
2481+
if (cxt->entry && cxt->recur_sv == (SV*)av && cxt->recur_depth > 0) {
2482+
TRACEME(("recur_depth --%u", cxt->recur_depth));
2483+
--cxt->recur_depth;
2484+
}
24612485
TRACEME(("ok (array)"));
24622486

24632487
return 0;
@@ -2570,9 +2594,14 @@ static int store_hash(pTHX_ stcxt_t *cxt, HV *hv)
25702594
TRACEME(("size = %d, used = %d", (int)l, (int)HvUSEDKEYS(hv)));
25712595
}
25722596

2573-
if (cxt->entry && ++cxt->av_depth > MAX_DEPTH) {
2574-
CROAK((MAX_DEPTH_ERROR));
2597+
TRACEME(("recur_depth %u, recur_sv (0x%" UVxf ")", cxt->recur_depth,
2598+
PTR2UV(cxt->recur_sv)));
2599+
if (cxt->entry && cxt->recur_sv == (SV*)hv) {
2600+
if (++cxt->recur_depth > (MAX_DEPTH >> 1)) {
2601+
CROAK((MAX_DEPTH_ERROR));
2602+
}
25752603
}
2604+
cxt->recur_sv = (SV*)hv;
25762605

25772606
/*
25782607
* Save possible iteration state via each() on that table.
@@ -2852,7 +2881,10 @@ static int store_hash(pTHX_ stcxt_t *cxt, HV *hv)
28522881
TRACEME(("ok (hash 0x%" UVxf ")", PTR2UV(hv)));
28532882

28542883
out:
2855-
if (cxt->entry) --cxt->hv_depth;
2884+
if (cxt->entry && cxt->recur_sv == (SV*)hv && cxt->recur_depth > 0) {
2885+
TRACEME(("recur_depth --%u", cxt->recur_depth));
2886+
--cxt->recur_depth;
2887+
}
28562888
HvRITER_set(hv, riter); /* Restore hash iterator state */
28572889
HvEITER_set(hv, eiter);
28582890

@@ -2971,9 +3003,15 @@ static int store_lhash(pTHX_ stcxt_t *cxt, HV *hv, unsigned char hash_flags)
29713003
}
29723004
TRACEME(("size = %" UVuf ", used = %" UVuf, len, (UV)HvUSEDKEYS(hv)));
29733005

2974-
if (cxt->entry && ++cxt->hv_depth > MAX_DEPTH) {
2975-
CROAK((MAX_DEPTH_ERROR));
3006+
TRACEME(("recur_depth %u, recur_sv (0x%" UVxf ")", cxt->recur_depth,
3007+
PTR2UV(cxt->recur_sv)));
3008+
if (cxt->entry && cxt->recur_sv == (SV*)hv) {
3009+
if (++cxt->recur_depth > (MAX_DEPTH >> 1)) {
3010+
CROAK((MAX_DEPTH_ERROR));
3011+
}
29763012
}
3013+
cxt->recur_sv = (SV*)hv;
3014+
29773015
array = HvARRAY(hv);
29783016
for (i = 0; i <= (Size_t)HvMAX(hv); i++) {
29793017
HE* entry = array[i];
@@ -2985,7 +3023,10 @@ static int store_lhash(pTHX_ stcxt_t *cxt, HV *hv, unsigned char hash_flags)
29853023
return ret;
29863024
}
29873025
}
2988-
if (cxt->entry) --cxt->hv_depth;
3026+
if (cxt->entry && cxt->recur_sv == (SV*)hv && cxt->recur_depth > 0) {
3027+
TRACEME(("recur_depth --%u", cxt->recur_depth));
3028+
--cxt->recur_depth;
3029+
}
29893030
assert(ix == len);
29903031
return ret;
29913032
}
@@ -5078,7 +5119,6 @@ static SV *retrieve_tied_array(pTHX_ stcxt_t *cxt, const char *cname)
50785119
return (SV *) 0; /* Failed */
50795120

50805121
sv_upgrade(tv, SVt_PVAV);
5081-
AvREAL_off((AV *)tv);
50825122
sv_magic(tv, sv, 'P', (char *)NULL, 0);
50835123
SvREFCNT_dec(sv); /* Undo refcnt inc from sv_magic() */
50845124

dist/Storable/__Storable__.pm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Copyright (c) 1995-2001, Raphael Manfredi
33
# Copyright (c) 2002-2014 by the Perl 5 Porters
44
# Copyright (c) 2015-2016 cPanel Inc
5+
# Copyright (c) 2017 Reini Urban
56
#
67
# You may redistribute only under the same terms as Perl 5, as specified
78
# in the README file that comes with the distribution.

0 commit comments

Comments
 (0)