artin's Creations

where the magic happens

When HHVM doesn't work quite right

July 20th, 2015

Despite generally resisting the hype train, in the past year I've jumped onto two relatively new Facebook projects. ReactJS and HHVM. I'm a huge proponent of both and enjoy working with them immensely. Except when I don't.

Around 3 months ago I chose HHVM for a new project and was initially very excited by the static analysis tools, built in profiling and great performance. Despite many great examples of HHVM deployments, shortly after deploying to production we started hitting OOMs pretty frequently:

HHVM Sizes (day)
OOM killing our HHVM server

With some heap profiling I first located a leak in mongofill-hhvm, and removed our one instance of create_function (known to leak memory) but still experienced poor reliability post-fix. At this point, we jumped onto the nightly builds as some leaks were fixed. At this point, we started hitting segfaults and our HHVM binary occasionally refusing to accept fastcgi requests.

diffing heap dumps from jemalloc:

-------------------- /tmp/heaps/.9371.29.u29.heap => /tmp/heaps/.9371.30.u30.heap --------------------
Total2639139 B
 4740389 179.6
179.6%  4740389 179.6bson_realloc
       0   0.0
179.6%  2639139 100.0HPHP::AsyncFuncImpl::ThreadFunc
       0   0.0
179.6% -3317645 -125.7HPHP::ElemSlow
       0   0.0
179.6%  1422744  53.9HPHP::ExecutionContext::executeFunctions
       0   0.0
179.6%  -678506 -25.7HPHP::ExecutionContext::invokeFunc
-------------------- /tmp/heaps/.9371.30.u30.heap => /tmp/heaps/.9371.31.u31.heap --------------------
Total4598623 B
 4598623 100.0
100.0%  4598623 100.0bson_realloc
       0   0.0
100.0%  4598623 100.0HPHP::AsyncFuncImpl::ThreadFunc
       0   0.0
100.0%  3317645  72.1HPHP::ElemSlow
       0   0.0
100.0%  7916269 172.1HPHP::ExecutionContext::executeFunctions
       0   0.0
100.0%  7916269 172.1HPHP::ExecutionContext::invokeFunc
-------------------- /tmp/heaps/.9371.31.u31.heap => /tmp/heaps/.9371.32.u32.heap --------------------
Total5829623 B
 3317645  56.9
%  56.9%  3317645  56.9HPHP::MemoryManager::newSlab
 2511977  43.1
100.0%  2511977  43.1bson_realloc
       0   0.0
100.0%  5829623 100.0HPHP::AsyncFuncImpl::ThreadFunc
       0   0.0
100.0%  2511977  43.1HPHP::ExecutionContext::executeFunctions
       0   0.0
100.0%  2511977  43.1HPHP::ExecutionContext::invokeFunc
-------------------- /tmp/heaps/.9371.32.u32.heap => /tmp/heaps/.9371.33.u33.heap --------------------
Total: -947455 B
-250.2% -250.2%  2370189 -250.2bson_realloc
-0.0% -250.2%  -947455 100.0HPHP::AsyncFuncImpl::ThreadFunc
-0.0% -250.2% -3317645 350.2HPHP::ElemSlow
-0.0% -250.2%  -947455 100.0HPHP::ExecutionContext::executeFunctions
-0.0% -250.2%  -947455 100.0HPHP::ExecutionContext::invokeFunc

Tired of plugging the leaks (pun intended!) I decided to configure our box to be more tolerant of such issues and that's what I wanted to share today.

First, I disabled HHVM via systemd and substituted in supervisord. Next, I made it run 5 instances concurrently on separate ports:

command=hhvm --config /etc/hhvm/php.ini --config /etc/hhvm/server.ini --user www-data --mode server  -vPidFile=/var/run/hhvm/pid_9010 -p 9010 -d hhvm.admin_server.port=9011

This gave us:

# supervisorctl status
hhvm_9010                        RUNNING    pid 14800uptime 9:53:37
hhvm_9020                        RUNNING    pid 14784
uptime 9:53:38
hhvm_9030                        RUNNING    pid 14780
uptime 9:53:38
hhvm_9040                        RUNNING    pid 17395
uptime 0:26:00
hhvm_9050                        RUNNING    pid 14783
uptime 9:53:38

Coupled with some nginx load balancing, that gave us an instant noticeable boost in stability.

upstream hhvm 

With my fast approaching Europe trip, I didn't want to be tethered to my terminal and wanted it to detect failure automatically. nginx does provide health_check in its commercial builds, but at this time I'm not too inclined to pay $1500/server/year for the pleasure. Thus, I needed to ping these with minimal effort. I decided to go directly to the fcgi process rather than use nginx to avoid needing to work around the load balancing, and remove the web server as a potential false positive.

I considered learning the fcgi spec but decided to cheat instead. tcpdump to the rescue!

tcpdump of HHVM requests
A tcpdump of HHVM requests

We only really care about the 32 bytes of data used in the first request (indented for emphasis), which cleaned up a little look just like this:

0101 685c 0008 0000 0001 0000 0000 0000 0104 685c 0000 0000 0105 685c 0000 0000

Then to nc, the swiss army knife of networking

testing our payload
Sweet! That was easy!

Next, all that was needed was to iterate the HHVM instances and restart any duds.


readonly PAYLOAD='0101 685c 0008 0000 0001 0000 0000 0000 0104 685c 0000 0000 0105 685c 0000 0000'
readonly HOST=

supervisorctl status 
awk '{print $1 " " $4 " " $6}' tr -',' |

MATCHES=$(echo $PAYLOAD tr -' ' xxd --nc -w 3 $HOST $PORT grep 'X-Powered-By: HHVM' wc -l)
        if [ 
$MATCHES -ne 1 ]
-"$NAME is not functioning as expected, restarting..."
supervisorctl restart $NAME

Since implementation, we've had no observed instability and can sleep a little easier. Next, we will likely implement ngx.location.capture_multi on our read requests so that one HHVM node can fail without any end-user impact.

I'm incredibly grateful for the work the HHVM team has put in, and fully intend to stick with it despite the bumps. Hats off to all those involved and the incredibly active community!

Reverse Engineering Shopify Private APIs

March 1st, 2013

Note: The full source is available on GitHub

Recently when working to migrate an e-commerce website to the aspiring Shopify Cloud Platform, generating coupon codes through the API was dismissed as being a obvious, simple, and apparently one of the most requested features back in March, 2011.

Unfortunately, I was wrong. So I contacted their support team to see what's up!

Shopify Support Snapshot
Thanks for the help, Brian. /s

Being unaccustomed to "no", and particularly impatient I decided to develop my own solution utilizing the very same API's Shopify created for themselves in their admin panel.

So it all began when I decided to poke at how they're being loaded into Shopify's back-end

GET /admin/discounts.json?limit=50&order=id+DESC&direction=next HTTP/1.1

"discounts": [{
}, ... ]

Wait, so, that looks pretty friendly right? They've already done the work, so why can't I use it? ... I can! So, here's how.

First, let's take at the full HTTP request. (Snipped to the interesting parts)

 GET /admin/discounts.json?limit=50&order=id+DESC&direction=next HTTP/1.1
 X-CSRF-Token: +QjKt70XBMis/iZXz8VsvbfHkOcH+h45N38os4O1lJo=
 X-Requested-With: XMLHttpRequest
 X-Shopify-Api-Features: pagination-headers
 Cookie: _secure_session_id=150d716ebc55cf62xxx; storefront_digest=056eb6c39dd92c5171360c97d0xxxx;

Nothing particularly special, there's a token we need to watch out for and, of course, our session cookies. So first thing's first, let's tackle the login form. I've trimmed this down to the bare necessities for your viewing pleasure

<form accept-charset="UTF-8" action="/admin/auth/login" method="post">
input name="utf8" type="hidden" value="&#x2713;" />
input name="authenticity_token" type="hidden" value="+QjKt70XBMis/iZXz8VsvbfHkOcH+h45N38os4O1lJo=" />
input type="hidden" name="redirect" value="" id="redirect" />
input type="email" name="login" size="30" id="login-input" class="email" />
input type="password" name="password" size="16" id="password" />

div id="open-id" style="display:none">
div class="ppb clearfix">
label id="open_id" for="openid-input" class="open-id">OpenID</label>  
input type="text" name="openid_url" value="" class="url" id="openid-input" />

Spoiler: Looks like Shopify are at least playing with OpenID integration

In the interest of minimizing maintainance let's parse all of those <input>'s dynamically. I chose to use regex over a DOM parser because it seemed more appropriate in such a hacky project, and will save us having to worry about broken markup. We'll do this in two parts, first we'll grab the login form, and then the key/value pairs embedded within it.

private function getFields($data false) {
$data $data ?: $this->initGetData($this->store);

        if (
$this->inputs $this->getInputs($matches[1]);
is_array($this->inputs) ? $this->inputs false;

Then the fields

private function getInputs($form$inputs = []) {
    if (!(
$els preg_match_all('/(<input[^>]+>)/is'$form$matches)))
    for (
$i 0$i $els$i++) {
$el preg_replace('/\s{2,}/'' '$matches[1][$i]);
        if (
$inputs[$name[1]] = $value[1];

Once we have the data necessary, posting it to Shopify is a piece of cake. Cake's good, right?

public function login() {
$fields $this->inputs ?: $this->getFields();

$fields['login']  = $this->username;
$fields['password'] = $this->password;

$url $this->store self::_LOGIN_URL;
$this->ch curl_init($url);
CURLOPT_POST       => true,
CURLOPT_POSTFIELDS => http_build_query($fields),
CURLOPT_HTTPHEADER => ['Shopify-Auth-Mechanisms:password']
$data curl_exec($this->ch);
$http_code curl_getinfo($this->chCURLINFO_HTTP_CODE);
$http_code == 200 && $this->setToken($data);

The astute among you may notice the setToken call at the end. We'll get to this shortly. Also, setOptions is a function I crafted to keep the code clean, it will take care of setting the cookie jar and user-agent upon each request. Yes --Cake, and cookies.

private function setOpts($extra = []) {    
$default = [
$options $default array_filter($extra, function($v) {
        return !

So, back to it.. Now we're logged in - that's great! Let's see if we can request from the discounts.json file we saw used earlier.

= [
'limit' => 50
'order' => 'id+DESC'
'direction' => 'next'
$url $this->store $function urldecode(http_build_query($parameters));
$ch curl_init($url);    

$response curl_exec($ch);
$data json_decode($response);


stdClass Object
applies_once] => 
applies_to_id] => 
code] => wyrrw4
[ends_at] => 
id] => 14256508
[minimum_order_amount] => 0.00
[starts_at] => 2013-03-01T00:00:00-08:00
[status] => enabled
[usage_limit] => 1
[value] => 5.0
[discount_type] => percentage
[applies_to_resource] => 
times_used] => 0


Awesome! It worked. POSTing turns out to be a little trickier, but let's get to it..

A Cross-site Request Forgery (CSRF) token is used for all POST requests internally as shown below

X-CSRF-Token: +QjKt70XBMis/iZXz8VsvbfHkOcH+h45N38os4O1lJo=

A little poking around reveals this token in the document body

<meta content="+QjKt70XBMis/iZXz8VsvbfHkOcH+h45N38os4O1lJo=" name="csrf-token" />            

Once again we'll resort to regex. By co-incidence, or not, the arrangement of these parameters has switched once before, so that's worth keeping an eye out for!

if (preg_match('/<meta content="(.*)" name="csrf-token" \/>/i'$data$token)) {
$this->_token $token[1];

As noted after the login call we grab the token to avoid excess HTTP requests. Now let's wrap it into something usable

public function doRequest($method$function$parameters) {
$this->ch curl_init();        
$url = (!filter_var($functionFILTER_VALIDATE_URL) ? $this->store '') . $function;
    switch (
$method) {
CURLOPT_POSTFIELDS => json_encode($parameters),
CURLOPT_URL => $url,
'X-Shopify-Api-Features: pagination-headers',
'X-CSRF-Token: ' $this->_token,
'X-Requested-With: XMLHttpRequest',
'Content-Type: application/json',
'Accept: application/json'

CURLOPT_URL => $url . (count($parameters) ? '?' urldecode(http_build_query($parameters)) : '')

$response curl_exec($this->ch);
$data json_decode($response);
is_object($data) ? $data $response;

Sure enough, that worked too! So what other cool stuff can we do?

Spoiler: I like graphs.

You may have noticed the flashy new dashboard in Shopify 2. Fortunately, with little effort, we can access this data too!

There's a couple of things we need to take note of here, the callback (this is JSONP, we'll get to that in a moment), and the token. The token is used as authentication, and set inline in the document body.


As soon as I saw this it was pretty obvious it was a base64 string and an md5 hash, whilst this probably isn't terribly useful for us it's nice to know! Let's decode it. (Note: I snipped these to keep this store private)


So the base64 is an array containing the store ID and a timestamp. Perhaps the hash is used for performance metrics, or more likely a checksum of the array to avoid people grabbing analytics of other stores. Doesn't matter much to us, as we aren't trying to do anything malicious here.

Due to the same origin policy XHR requests to external locations (scheme, hostname and ports must be consistent). The exceptions being JSONP, and CORS. CORS is considered a better solution however in this instance Shopify is using JSONP, that's what the callback parameter is for. We'll need to strip out that callback when we parse the response.

To do so, I've defined the callback as a static fake_function and strip it out with regular string functions:

if ($reportCenter) {
    if (
strpos($response'fake_function') !== FALSE) {
$response substr($responsestrpos($response'{'));
$response substr($response0, -2);

This allows us to access the report center data such as

stdClass Object
start_date] => 2013-02-22
[end_date] => 2013-03-01
[search_terms] => Array
0] => stdClass Object
terms] =>
[count] => 1
[percentage] => 100


top_referrals] => Array
0] => stdClass Object
referrer] =>
[count] => 530
[percentage] => 56.025369978858


Remember hackers, the full code & demo is available to fork:


Hacking games to make them better

February 5th, 2011

The duplication of items has been a huge issue in a variety of games, ruining the economy in both official and in particular, private servers. A select few have figured out solutions to this, however the majority of which are offline solutions usually within stored procedures which hasn't proven too reliable since the database is only amended every 5 minutes by default in Knight Online. As such, we needed to figure out a live way to do it.

Fortunately, to communicate between the various server files a block of shared memory (or Memory Mapped File) is used. This means tapping into the data isn't all that difficult. Reversing the structure is much trickier. As such, I'll provide a near-complete structure below which will serve as a good base for this article.

#define MAX_USER_ID_SIZE        20
#define MAX_ACC_ID_SIZE         38
#define MAX_USER                1500

#define SLOT_MAX                14
#define HAVE_MAX                28      
#define ITEMCOUNT_MAX           9999
#define WAREHOUSE_MAX           196     

int     nNum;   
short   sDuration;      
short   sCount;         
__int64 nSerialNum;     
char cPadding[8];

int     nNum;
short   sDuration;
short   sCount;    
__int64 nSerialNum;     

struct _USER_DATA 
char    m_id[MAX_USER_ID_SIZE+1];                       
char    m_Accountid[MAX_ACC_ID_SIZE+1];         
DWORD   m_bZone;        

float   m_curx
float   m_cury;         
float   m_curz;                                                 

BYTE    m_bNation;
BYTE    m_bRace;
short   m_sClass;
BYTE    m_bHairColor;
BYTE    m_bRank;
BYTE    m_bTitle;
BYTE    m_bLevel;
int     m_iExp;
int     m_iLoyalty;
BYTE    m_bFace;
BYTE    m_bCity;
short   m_bKnights;
short   m_sClan;
short   m_sUnknown;
BYTE    m_bFame;

byte m_unknown2;
byte m_Hits;
byte m_Mana;
byte m_SP;
byte m_STR;
byte m_HP;
byte m_DEX;
byte m_INT;
byte m_MP;
byte m_Authority;
byte m_Points;
byte m_unknown3;

DWORD m_Gold;
signed short m_Bind;
int m_Bank;

char Garbage[39]; // skill/stat stuff etc
//_ITEM_DATA m_sItemArray[HAVE_MAX+SLOT_MAX]; // 42*8 bytes
INN_ITEM_DATA m_sWarehouseArray[WAREHOUSE_MAX]; //196*8 bytes

BYTE    m_bLogout;
BYTE    m_bWarehouse;
DWORD   m_dwTime;       

We will cast the block of memory to that structure so we don't have to loop, and apply lots of mathematical offsets, it generally makes the code cleaner to work with, and easier to maintain.

Some internal declarations may look like this:


typedef std::vector<_USER_DATA*> UserDataArray;
extern UserDataArray currentUsers;
HANDLE m_hUsersMutex;

void myPopulateFunction()


So first of all, let's connect to the file and build up our local array. (You'll probably want to place this in your main, or equivalent.)
Note: I'm not going to incorporate lots of error handling etc in this guide, you will need to do that yourself.


m_hMMFile == NULL)

m_lpMMFile = (char *)MapViewOfFile (m_hMMFileFILE_MAP_WRITE000);

if (!


Okay, so this will open up the shared memory files that Knight Online uses to access the user data from all the various applications (aujard and ebenezer for example) and map it so we can access its data from our application. Next up, loading all of the users from the memory block and casting it to a nice struct which was defined above!

void myPopulateFunction
() {
WaitForSingleObjectm_hUsersMutexINFINITE );

// This is where we'll do our stuff.


That's a rough outline for our function, we use a mutex to prevent any of our other functions accessing the array while it's being written to which could cause memory exceptions and such. So let's load our users now.

void myPopulateFunction
() {
WaitForSingleObjectm_hUsersMutexINFINITE );


    for (
int i=0MAX_USERi++) {
pUser = (_USER_DATA*)(m_lpMMFile+(i*8000));


Because the struct above isn't entirely complete, when looping each user we can't use sizeof(_USER_DATA) as we ideally should - as such it's hard coded as 8000 bytes, this may need maintaining in future versions. This will build an array of pointers to each user - so all data is always accurate without you needing to re-populate. This has its merits and cons, so in some situations we need to create a static array too which we'll do next in the dupescanner example. So next, let's just take a look at iterating through the array so we can actually use it!

::vector<_USER_DATA*>::iterator iter;
for (
iter currentUsers.begin(); iter != currentUsers.end(); iter++) {
// our magic

Really simple stuff there, you can then access the properties of each user directly. Beware of the null structs (as again, these are simply pointers to a block of memory) so do a check for something like (*iter)->m_id having a positive length.

An example of applying the above code could be to find a pointer to a struct from a given username, how about this:

getStructByUser(const char *user) {
WaitForSingleObjectm_hUsersMutexINFINITE );

std::vector<_USER_DATA*>::iterator iter;
    for (
iter currentUsers.begin(); iter != currentUsers.end(); iter++) {
        if (!
_strnicmp(user, (*iter)->m_idMAX_USER_ID_SIZE)
            return *


There are so many more potential applications to this code - for example a decent speed hacking detector, zone scanning for if users are stuck in the war zone, or bugs with invading and whatever, and of course the biggie, dupe scanning. Let's create a quick draft on how you may achieve something like this! You'll probably want to create it in a separate thread, so add something like this to our definitions

struct threadStruct

And finally, the code!

unsigned int __stdcall DupeScanThread
(LPVOID lp) { 
threadStructpStruct = (threadStruct*)lp;

UserDataArraypUsers = (UserDataArray*)pStruct->curUsers;
UserDataArray pTemp;

map<__int64_USER_DATA*> userMap;

WaitForSingleObject(pStruct->hMutexINFINITE );
pTemp.assign(pUsers->begin(), pUsers->end());

    for (
iter pTemp.begin(); iter != pTemp.end(); iter++) {
_USER_DATApUser = *iter;
        if (!
strlen(pUser->m_id)) continue;

        for (
int i=0i<HAVE_MAX+SLOT_MAX;i++) {
            if (
pUser->m_sItemArray[i].nSerialNum && pUser->m_sItemArray[i].nNum 0) { 
                if (!
userMap.insert(make_pair(pUser->m_sItemArray[i].nSerialNum, *pUser)).second) {
_USER_DATAotherDuper userMap.find(pUser->m_sItemArray[i].nSerialNum)->second;
// Do some magic

You may notice that I only ran it once in the above example, you may want to add an extra parameter for bEnabled, then add a while (bEnabled) { sleep(sensible integer); pTemp.clear(), re fill, the interation, etc } to run it constantly! I haven't gone into the full depths of utilising the mapped file to send packets (for disconnecting and such) in this guide it's focussed KNIGHT_DB only, but I may cover that at some point in the future.

To initialise the thread, it'll be something like this:

threadStruct params
params.hMutex = &m_hUsersMutex;
params.curUsers = &currentUsers;
HANDLE hThread = (HANDLE)_beginthreadex(NULL0DupeScanThread, &params0NULL);

Hope you have as much fun with this as I did!

Native Executable Patching

February 5th, 2011

I was continuously asked how to apply security patches which were being released, instead of including instructions in each patch that I released I decided to put together a quick general guide. While this guide primarily focuses on Knight Online private server files, it serves as a good example and all terminology and methods will apply.

To begin, we'll start with the screen you'll be prompted with just after you open ebenezer in OllyDbg. I'm using Ollydbg because it's easier, don't use a hex editor it's just silly. Olly will translate opcodes to hex for me and will make far less mistakes! Also note, I'm using the classic version (v1.10 final) - the v2 alpha is becoming increasingly popular and sports some exciting new features and some amazing improvements. So be sure to check it out! The images now reflect OllyDBG 2.01

OllyDbg OEP Picture (Original Entry Point)

On the right, you have the first screen you'll be confronted with after loading your executable. Briefly, starting from the top left you have your 'CPU', this is the main window you'll be using which consists of a disassembly of the image from memory at the specified location, this is what translates your 'hex' into assembly and visa versa. To the right hand side you have your registers, you may think of these like your typical variables but remember they're pointers, either way these represent the values your assembly code is using.

Just like in most native languages you need to de-reference a pointer before you use the value, this is why you may see "DWORD PTR DS:[pointer]", which means there's 4 bytes at this location that we want, and we want an unsigned value of it. For all you fellow readers from a linux background, a DWORD is typedef'd in the winapi as an unsigned integer.

On the bottom left we have the dump, this is usually where I'd follow the values of registers but it's multi-purpose and is also very handy for viewing a region of memory, think of this as your traditional hex editor. Finally, to the right of the dump is the stack, I wont fully explain it here since it's not necessary for this patch, generally you can use it for following parameters passed to a function and it's tempory storage. Every thread has its own stack. Now, moving onto the actual patch. Let's navigate to the area we want to patch, on the far left of the CPU you have the address you're currently viewing. The currently executing line of code is at eip (Instruction pointer, a register).

We want to goto 0x00498B59 (The 0x represents hex, like &H in basic). To do this we press CTRL+G or can navigate by the menu, a window like this will appear:

OllyDbg Goto Picture

Press enter (or "Follow Expression"), and it'll take us to the code we want to modify. As you can see, it's exactly how osmanx said it would be. Now lets make the appropriate adjustments. You can see here that it's comparing the 8bit BL register to 0x11 (Rememer everything is in hex). If it's equal, it goes to the same location as if it's 0x07, This looks like it was meant to be expanded at some point. They're both going to the same location regardless, so we want to change 0x11 to goto our codecave.

To do this, double click the line we want to modify which is 0x00498B5C and modify it to the code displayed. "JE 00499218"

OllyDbg Assemble Picture

Press "Assemble", if all goes well and you typed it correctly it'll modify the hex appropriately for you.

OllyDbg Assembled Picture

Now, tap enter to follow that jump to our code cave, or use CTRL+G -> "00499218" if you want to do it manually.

This will take you to our code cave which is just a block of INT3's, this instruction is used to trigger a breakpoint but is also often padded between functions. It's an ideal location for us to make our patch since it isn't executed!

Code Cave

Assemble, just like you did before our block of code.

Assembled instructions

Next, right click in the main CPU window, navigate to "copy to executable" and select "copy all modifications" as shown in the following image.

OllyDbg Save To Disk Picture
OllyDbg Copy To Executable Picture

Then it'll copy the memory image to a new window and allow you to save it to disk with the modifications, you can do that once again by following the navigation to "Save File" as shown to the right

Save file

OllyDBG 2.0 will give an additional confirmation menu

OllyDBG 2.0 confirmation

Then a little window will appear asking you where to save it. I'd suggest you make a backup EVERY time, incase you make a mistake or the patch is incorrect (though i'm sure it isn't!).

Save as executable

That concludes this article. I hope I didn't miss anything important out. If you have any questions feel free to e-mail me I feel as I fear I may have rushed towards the end but I will amend it appropriately if anyone points out a mistake or particularly vague area.

PHP 5.3.3 x86 Vulnerability

January 13th, 2011

Recently a new vulnerability has been exposed (and patched) which targets specific platforms as a result of certain assumptions made within the zend core (specifically zend_strtod.c) when handling floating-point arithmetic.

The issue was only being manifested by gcc builds with -O2 so a recompile could fix it, reportedly -O0 fixes it but I would recommend using -mfpmath=sse which will favour a newer instruction set rather than the older deprecated x87 math instructions. A diff of the new PHP revision revealed the actual commited patch was an additional keyword in a declaration:

                double aadj


                volatile double aadj

The volatile keyword instructs the compiler not to perform optimisations

Fortunately this is an x64 server and doesn't adopt the x87 fpu but it did affect my laptop. I applied a quick software patch which was something like this (Note: both must be placed at the top of your index, or executed file):

if (strpos(implode($_REQUEST), '2.2250738585072011') die();


($_REQUEST, function (&$x$v$k) {
                    if (
strpos($v.$k'2.2250738585072011')) unset($x); 

The first example will stop script execution if the dangerous value is detected, the second will unset the affecting variable - with improper error handling in some situations this could cause errors, though.

Fortunately however, as previously stated a patch has been commited so if possible upgrade to PHP 5.3.5 or 5.2.17

To read more, see the Official PHP Bug Report