views:

77

answers:

2

Okay, I've written my first functional PHP extension. It worked but it was a proof-of-concept only. Now I'm writing another one which actually does what the boss wants.

What I'd like to know, from all you PHP-heads out there, is whether this code makes sense. Have I got a good grasp of things like emalloc and the like, or is there stuff there that's going to turn around later and try to bite my hand off?

Below is the code for one of the functions. It returns a base64 of a string that has also been Blowfish encrypted. When the function is called, it is supplied with two strings, the text to encrypt and encode, and the key for the encryption phase. It's not using PHP's own base64 functions because, at this point, I don't know how to link to them. And it's not using PHP's own mcrypt functions for the same reason. Instead, it links in the SSLeay BF_ecb_encrypt functions.

PHP_FUNCTION(Blowfish_Base64_encode)
{
    char *psData = NULL;
    char *psKey = NULL;
    int argc = ZEND_NUM_ARGS();
    int psData_len;
    int psKey_len;

    char *Buffer = NULL;
    char *pBuffer = NULL;

    char *Encoded = NULL;

    BF_KEY Context;

    int i = 0;

    unsigned char Block[ 8 ];
    unsigned char * pBlock = Block;

    char *plaintext;
    int plaintext_len;
    int cipher_len = 0;

    if (zend_parse_parameters(argc TSRMLS_CC, "ss", &psData, &psData_len, &psKey, &psKey_len) == FAILURE) 
        return;

    Buffer = (char *) emalloc( psData_len * 2 );
    pBuffer = Buffer;

    Encoded = (char *) emalloc( psData_len * 4 );

    BF_set_key( &Context, psKey_len, psKey );

    plaintext = psData;
    plaintext_len = psData_len;

    for (;;)
    {
        if (plaintext_len--)
        {
            Block[ i++ ] = *plaintext++;
            if (i == 8 )
            {
                BF_ecb_encrypt( Block, pBuffer, &Context, BF_ENCRYPT );
                pBuffer += 8;
                cipher_len += 8;
                memset( Block, 0, 8 );
                i = 0;
            }
        } else {
            BF_ecb_encrypt( Block, pBuffer, &Context, BF_ENCRYPT );
            cipher_len += 8;
            break;
        }
    }
    b64_encode( Encoded, Buffer, cipher_len );
    RETURN_STRINGL( Encoded, strlen( Encoded ), 0 );
}

You'll notice that I have two emalloc calls, for Encoded and for Buffer. Only Encoded is passed back to the caller, so I'm concerned that Buffer won't be freed. Is that the case? Should I use malloc/free for Buffer?

If there are any other glaring errors, I'd really appreciate knowing.

+2  A: 

emalloc() allocates memory per request, and it's free()'d automatically when the runtime ends.

You should, however, compile PHP with

--enable-debug --enable-maintainer-zts

It will tell you if anything goes wrong (it can detect memory leaks if you've used the e*() functions and report_memleaks is set in your php.ini).

And yes, you should efree() Buffer.

Flavius
+1  A: 

You'll notice that I have two emalloc calls, for Encoded and for Buffer. Only Encoded is passed back to the caller, so I'm concerned that Buffer won't be freed. Is that the case? Should I use malloc/free for Buffer?

Yes, you should free it with efree before returning.

Although PHP has safety net and memory allocated with emalloc will be freed at the end of the request, it's still a bug to leak memory and, depending you will warned if running a debug build with report_memleaks = On.

Artefacto