--- JSON-XS/XS.xs 2010/01/19 01:07:27 1.105 +++ JSON-XS/XS.xs 2010/08/17 23:27:36 1.108 @@ -44,6 +44,8 @@ #define SHORT_STRING_LEN 16384 // special-case strings of up to this size +#define DECODE_WANTS_OCTETS(json) ((json)->flags & F_UTF8) + #define SB do { #define SE } while (0) @@ -194,11 +196,17 @@ // scan a group of digits, and a trailing exponent static void -json_atof_scan1 (const char *s, NV *accum, int *expo, int postdp) +json_atof_scan1 (const char *s, NV *accum, int *expo, int postdp, int maxdepth) { UV uaccum = 0; int eaccum = 0; + // if we recurse too deep, skip all remaining digits + // to avoid a stack overflow attack + if (expect_false (--maxdepth <= 0)) + while (((U8)*s - '0') < 10) + ++s; + for (;;) { U8 dig = (U8)*s - '0'; @@ -208,7 +216,7 @@ if (dig == (U8)((U8)'.' - (U8)'0')) { ++s; - json_atof_scan1 (s, accum, expo, 1); + json_atof_scan1 (s, accum, expo, 1, maxdepth); } else if ((dig | ' ') == 'e' - '0') { @@ -244,7 +252,7 @@ if (uaccum >= (UV_MAX - 9) / 10) { if (postdp) *expo -= eaccum; - json_atof_scan1 (s, accum, expo, postdp); + json_atof_scan1 (s, accum, expo, postdp, maxdepth); if (postdp) *expo += eaccum; break; @@ -272,7 +280,8 @@ neg = 1; } - json_atof_scan1 (s, &accum, &expo, 0); + // a recursion depth of ten gives us >>500 bits + json_atof_scan1 (s, &accum, &expo, 0, 10); return neg ? -accum : accum; } @@ -1396,6 +1405,7 @@ ENTER; SAVETMPS; PUSHMARK (SP); XPUSHs (HeVAL (he)); + sv_2mortal (sv); PUTBACK; count = call_sv (HeVAL (cb), G_ARRAY); SPAGAIN; @@ -1406,6 +1416,7 @@ return sv; } + SvREFCNT_inc (sv); FREETMPS; LEAVE; } } @@ -1541,7 +1552,7 @@ (unsigned long)SvCUR (string), (unsigned long)json->max_size); } - if (json->flags & F_UTF8) + if (DECODE_WANTS_OCTETS (json)) sv_utf8_downgrade (string, 0); else sv_utf8_upgrade (string); @@ -1922,24 +1933,36 @@ if (!self->incr_text) self->incr_text = newSVpvn ("", 0); + /* if utf8-ness doesn't match the decoder, need to upgrade/downgrade */ + if (!DECODE_WANTS_OCTETS (self) == !SvUTF8 (self->incr_text)) + if (DECODE_WANTS_OCTETS (self)) + { + if (self->incr_pos) + self->incr_pos = utf8_length ((U8 *)SvPVX (self->incr_text), + (U8 *)SvPVX (self->incr_text) + self->incr_pos); + + sv_utf8_downgrade (self->incr_text, 0); + } + else + { + sv_utf8_upgrade (self->incr_text); + + if (self->incr_pos) + self->incr_pos = utf8_hop ((U8 *)SvPVX (self->incr_text), self->incr_pos) + - (U8 *)SvPVX (self->incr_text); + } + // append data, if any if (jsonstr) { - if (SvUTF8 (jsonstr)) - { - if (!SvUTF8 (self->incr_text)) - { - /* utf-8-ness differs, need to upgrade */ - sv_utf8_upgrade (self->incr_text); - - if (self->incr_pos) - self->incr_pos = utf8_hop ((U8 *)SvPVX (self->incr_text), self->incr_pos) - - (U8 *)SvPVX (self->incr_text); - } - } - else if (SvUTF8 (self->incr_text)) - sv_utf8_upgrade (jsonstr); + /* make sure both strings have same encoding */ + if (SvUTF8 (jsonstr) != SvUTF8 (self->incr_text)) + if (SvUTF8 (jsonstr)) + sv_utf8_downgrade (jsonstr, 0); + else + sv_utf8_upgrade (jsonstr); + /* and then just blindly append */ { STRLEN len; const char *str = SvPV (jsonstr, len);