Skip to content

Instantly share code, notes, and snippets.

@isislovecruft
Created July 9, 2015 01:56
Show Gist options
  • Save isislovecruft/836111acdccfbfb4a284 to your computer and use it in GitHub Desktop.
Save isislovecruft/836111acdccfbfb4a284 to your computer and use it in GitHub Desktop.
Implement bzero(3) in ctypes for PyStringObject/PyBytesObject.
#!/usr/bin/env python2.7
def PyStringObject_ctypes_bzero(string):
"""Use ctypes to manipulate the internal value of a PyStringObject (or
PyBytesObject, for Python>=2.7.8), setting all the bytes to ``"\x00"``
(``"\0"`` in C), similarly to the libc bzero(3) function.
.. note: This function avoids copying the PyStringObject, as CFFI might
do, since any changes to one of Python's "immutable" strings would
result in a new PyStringObject/PyBytesObject with a different
address/id().
To get the offset in memory of the PyStringObject's sval, we must subtract
the size (in bytes) for the following internal Python objects.
/* Py_ssize_t is a signed integral type such that sizeof(Py_ssize_t) ==
* sizeof(size_t). C99 doesn't define such a thing directly (size_t is an
* unsigned integral type). See PEP 353 for details.
*/
#ifdef HAVE_SSIZE_T
typedef ssize_t Py_ssize_t;
#elif SIZEOF_VOID_P == SIZEOF_SIZE_T
typedef Py_intptr_t Py_ssize_t;
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
#define PyObject_HEAD \
_PyObject_HEAD_EXTRA \
Py_ssize_t ob_refcnt; \
struct _typeobject *ob_type;
#define PyObject_VAR_HEAD \
PyObject_HEAD \
Py_ssize_t ob_size; /* Number of items in variable part */
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate; /* Removed in Python 2.7.x */
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
*/
} PyStringObject;
:param str string: All bytes within the **string** will be zeroed-out,
that is, they will be set to ``"\x00"``.
"""
import ctypes
sizeof_voidp = ctypes.sizeof(ctypes.c_voidp)
sizeof_int = ctypes.sizeof(ctypes.c_int)
sizeof_long = ctypes.sizeof(ctypes.c_long)
# Py_ssize_t (for ob_refcount) can be defined as having the size of
# ssize_t (if our compiler and variant of C9* have ssize_t), otherwise
# it has the size of "an integer large enough to store a pointer",
# a.k.a. C99 intptr_t, but only if the sizeof(intptr_t) is equal to
# the sizeof(void*). Since we can't check the size of intptr_t from
# ctypes, we'll just fallback to sizeof(void*) and hope for the best.
# What could possibly go wrong?
sizeof_Py_ssize_t = ctypes.sizeof(
ctypes.c_ssize_t if hasattr(ctypes, 'c_ssize_t') else ctypes.c_voidp)
# PyStringObjects don't appear to have PyObject_HEAD_EXTRA allocated…
offset = (sizeof_Py_ssize_t + # Py_ssize_t ob_refcnt;
sizeof_voidp + # struct _typeobject *ob_type;
sizeof_Py_ssize_t + # Py_ssize_t ob_size;
sizeof_long + # long ob_shash;
sizeof_int) # int ob_sstate;
for i in range(len(string)):
ctypes.c_char.from_address(id(key) + offset + i) = "\x00"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment