lericson/pylibmc — Issue #63: pylibmc 'gets' double free crash
Author: cosminl
Created: 2011-11-01 11:42:36+00:00
State: open

I'm getting a crash on a fresh Debian 6.0.2 machine with the following installed:

* libc 2.11.2-10
* python 2.6.6-3+squeeze6
* libmemcached-1.0.2 (installed from source)
* pylibmc-1.2.2 (installed from source)

The test code (memcached running on port 15000 on loopback):

```python
import pylibmc
import logging as log
from binascii import a2b_hex

log.basicConfig(level=log.DEBUG)

mc = pylibmc.Client(['127.0.0.1:15000'],
binary=True,
behaviors= {'tcp_nodelay':True, 'ketama':True, 'cas':True, 'no_block':True, "failure_limit": 5, "_retry_timeout": 30})

hash_text="07d482b6266767bbcd3a1b6359d785a53961a3a5d875883e530b56842e498ac4"
hash_=a2b_hex(hash_text)
expire_time=3600
max_codes=3

def inc():
try:
if mc.add(hash_, 1, time=expire_time) == True:
log.debug('first time for {0}'.format(hash_text))
return 1

# increment the count
while True:
num, cas = mc.gets(hash_)
if mc.cas(hash_, num + 1, cas, time=expire_time):
return num + 1
except pylibmc.Error, e:
log.exception(e)
return -1

inc()
mc.disconnect_all()
```

root@debian:~# python test.py
DEBUG:root:first code for 07d482b6266767bbcd3a1b6359d785a53961a3a5d875883e530b56842e498ac4
root@debian:~# python test.py
*** glibc detected *** python: double free or corruption (top): 0x08bbc190 ***

Since I don't have much experience with libmemcached and/or pylibmc, I can't say exactly what's the right behaviour, but it looks like the code below (PylibMC_Client_gets function) does a double free of the res0 variable:

```C
[...]
if (rc == MEMCACHED_SUCCESS && res != NULL) {
ret = Py_BuildValue("(NL)",
_PylibMC_parse_memcached_result(res),
memcached_result_cas(res));

/* we have to fetch the last result from the mget cursor */
res = memcached_fetch_result(self->mc, res0, &rc);
if (res || rc != MEMCACHED_END) {
memcached_quit(self->mc);
Py_DECREF(ret);
ret = NULL;
PyErr_SetString(PyExc_RuntimeError, "fetch not done");
}
```

the `memcached_fetch_result` call here seems to do some resource release on `res0`;

```C
[...]
/* deallocate any indirect buffers, even though the struct itself
is on our stack */
if (res0 != NULL) {
memcached_result_free(res0);
}
```

...and the `if` branch here double free's it.