php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #27458 unserilizing doubles might crash
Submitted: 2004-03-02 09:19 UTC Modified: 2004-03-23 18:19 UTC
From: hoesh at dorsum dot hu Assigned:
Status: No Feedback Package: Reproducible crash
PHP Version: Irrelevant OS: WIN
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: hoesh at dorsum dot hu
New email:
PHP Version: OS:

 

 [2004-03-02 09:19 UTC] hoesh at dorsum dot hu
Description:
------------
When a double value stored within the session file, the web server might crash. It was a mystical problem for us. To solve this issue, there need some workaround at the VAR_UNSERIALIZER.RE source code:

"d:" (iv | nv | nvexp) ";"	{
	*p = YYCURSOR;
	INIT_PZVAL(*rval);
	ZVAL_DOUBLE(*rval, atof(start + 2));
	return 1;
}

The problem is the difference in the implementation of the function 'atof'. While general C implementations looks ahead until a non digital character found, microsoft C tries to determine the string's length with a call to 'strlen'. This can couse the crash some times, when the accessible memory doesn't contain '\0'. The report about the memory dump is following in the 'Reproducible code' section. For a quick sight I placed the various implementations of the 'atof' function within the 'Expected result' block.

As we figured out, this issue comes up only when unserializing sessions, not zval represented strings, as those zvals are closed with '\0'.


Reproduce code:
---------------
Here is the report from Microsoft. They thought somthings wrong with PHP... :)

Call stack:
----------------
0c19e228 019240e2 1b0419d8 43e72508 03019af0 msvcrt!atof+0x25 (CONV: cdecl)
[atof.c @ 57]
WARNING: Stack unwind information not available. Following frames may be
wrong.
0c19e2d4 03019b88 1b097000 0c19e3b4 43e72508 php4ts+0x940e2
03019c88 00000000 00010002 00000000 0005006d 0x3019b88

00 0c19e1f8 780167f9 msvcrt!strlen(void)+0x20 [intel\strlen.asm @ 78]
01 0c19e228 019240e2 msvcrt!atof(char * nptr = 0x1b0419d8
"1077611306196.2860107421875;a:5:{s:5:"tabid";s:5:"tab_5";s:3:"ct0";s:13:"1077612213758";s:3:"ct1";a:1:{s:13:"403b0afeae402";s:13:"1077612210164";}}s:9:"post_vars";a:0:...


Expected result:
----------------
-- MICROSOFT C IMPLEMENTATION --

double __cdecl atof(
        REG1 const char *nptr
        )
{

#ifdef _MT
        struct _flt fltstruct;      /* temporary structure */
#endif  /* _MT */

        /* scan past leading space/tab characters */

        while ( isspace((int)(unsigned char)*nptr) )
                nptr++;

        /* let _fltin routine do the rest of the work */

#ifdef _MT
        return( *(double *)&(_fltin2( &fltstruct, nptr, strlen(nptr), 0, 0 )->
        dval) );
#else  /* _MT */
        return( *(double *)&(_fltin( nptr, strlen(nptr), 0, 0 )->dval) );
#endif  /* _MT */
}

-- GENERAL C IMPLEMENTATION (may differ at some point) --

double
atof(p)
register char *p;
{
	register c;
	double fl, flexp, exp5;
	double big = 72057594037927936.;  /*2^56*/
	double ldexp();
	int nd;
	register eexp, exp, neg, negexp, bexp;

	neg = 1;
	while((c = *p++) == ' ')
		;
	if (c == '-')
		neg = -1;
	else if (c=='+')
		;
	else
		--p;

	exp = 0;
	fl = 0;
	nd = 0;
	while ((c = *p++), isdigit(c)) {
		if (fl<big)
			fl = 10*fl + (c-'0');
		else
			exp++;
		nd++;
	}

	if (c == '.') {
		while ((c = *p++), isdigit(c)) {
			if (fl<big) {
				fl = 10*fl + (c-'0');
				exp--;
			}
		nd++;
		}
	}

	negexp = 1;
	eexp = 0;
	if ((c == 'E') || (c == 'e')) {
		if ((c= *p++) == '+')
			;
		else if (c=='-')
			negexp = -1;
		else
			--p;

		while ((c = *p++), isdigit(c)) {
			eexp = 10*eexp+(c-'0');
		}
		if (negexp<0)
			eexp = -eexp;
		exp = exp + eexp;
	}

	negexp = 1;
	if (exp<0) {
		negexp = -1;
		exp = -exp;
	}


	if((nd+exp*negexp) < -LOGHUGE){
		fl = 0;
		exp = 0;
	}
	flexp = 1;
	exp5 = 5;
	bexp = exp;
	for (;;) {
		if (exp&01)
			flexp *= exp5;
		exp >>= 1;
		if (exp==0)
			break;
		exp5 *= exp5;
	}
	if (negexp<0)
		fl /= flexp;
	else
		fl *= flexp;
	fl = ldexp(fl, negexp*bexp);
	if (neg<0)
		fl = -fl;
	return(fl);
}



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2004-03-02 09:30 UTC] derick@php.net
Where did you get that code from?
 [2004-03-02 11:30 UTC] hoesh at dorsum dot hu
Hi Derick!

Which one? Microsoft ships the sources of the RTL with MSVC distribution. The generic C implementation was taken from http://minnie.tuhs.org/UnixTree/V7/usr/src/libc/gen/atof.c.html, found by google with "atof.c" keyword. The var_unserializer.re is located at ext/standard, but I'm sure you know it. The report was created by a M$ engineer, from a user dump. M$ was about to find the reason why IIS stops without _any_ notice, warning or alert.

Under high load, the occourance of this problem increases exponentially. I can't figure out, why.

An easy solution to solve this problem should be to put one '\0' at the end of the loaded stream, instead of protecting each double's unserialization, and all floats should go well under all Wind[+]ws.

Right now I'm about to store strings in the session instead of doubles, and cast them runtime to double values.
 [2004-03-16 11:46 UTC] iliaa@php.net
Not enough information was provided for us to be able
to handle this bug. Please re-read the instructions at
http://bugs.php.net/how-to-report.php

If you can provide more information, feel free to add it
to this bug and change the status back to "Open".

Thank you for your interest in PHP.


Please provide a short PHP script that can be used to 
replicate the problem. 
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 04:01:38 2024 UTC