[PATCH] libsemanage: treat module data as bytes in the Python bindings
Stephen Smalley <[email protected]>
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <[email protected]> |
semanage_module_install() / semanage_module_install_info() take a (char *data, size_t data_len) pair, and semanage_module_extract() returns (void **mapped_data, size_t *data_len). In both directions the payload is an opaque module blob (.pp / bzip2 / CIL / HLL), not a NUL-terminated C string. On Python 3 the current SWIG bindings get this wrong on both sides: - install()/install_info(): the SWIG default for a bare "char *" only accepts str objects and UTF-8-encodes them, so a .pp blob cannot be passed at all, and even for CIL text the separately supplied data_len is wrong for any non-ASCII content since Python len(str) counts code points, not UTF-8 bytes. - extract(): %cstring_output_allocate_size turns the mapped buffer into a str via UTF-8 decoding, which for a binary module either raises UnicodeDecodeError or (with surrogateescape) yields a str that cannot be fed straight back into install(). Add a (char *module_data, size_t data_len) input typemap that accepts any object exposing the buffer protocol (bytes, bytearray, mmap, ...) as a single argument and derives the length from it, and apply it to the (char *data, size_t data_len) pair of semanage_module_install_info() as well. Replace the extract() output typemap with an explicit multi-argument typemap that returns a bytes object built from the mapped buffer and then unmaps it, so extract() -> install() round-trips. Drop the now-unused <cstring.i> include. The Python signatures become semanage_module_install(sh, data, name, ext_lang) semanage_module_install_info(sh, modinfo, data) There are no in-tree Python callers. Fixes: https://github.com/SELinuxProject/selinux/issues/502 Signed-off-by: Stephen Smalley <[email protected]> --- libsemanage/src/semanageswig_python.i | 43 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/libsemanage/src/semanageswig_python.i b/libsemanage/src/semanageswig_python.i index 0ca09763..1729e965 100644 --- a/libsemanage/src/semanageswig_python.i +++ b/libsemanage/src/semanageswig_python.i @@ -102,9 +102,46 @@ %apply int *OUTPUT { unsigned int * }; %apply int *OUTPUT { uint16_t * }; -%include <cstring.i> -/* This is needed to properly mmap binary data in SWIG */ -%cstring_output_allocate_size(void **mapped_data, size_t *data_len, munmap(*$1, *$2)); +/* + * semanage_module_extract(): the (void **mapped_data, size_t *data_len) pair + * is an opaque module blob (.pp / bzip2 / CIL / HLL). Return it to Python + * as a bytes object; %cstring_output_allocate_size would UTF-8-decode it + * into a str, which corrupts binary modules. + */ +%typemap(in, numinputs=0) (void **mapped_data, size_t *data_len) + (void *temp_data = NULL, size_t temp_len = 0) { + $1 = &temp_data; + $2 = &temp_len; +} +%typemap(argout) (void **mapped_data, size_t *data_len) { + if (*$1) { + $result = SWIG_AppendOutput( + $result, + PyBytes_FromStringAndSize((const char *)*$1, *$2)); + munmap(*$1, *$2); + } else { + $result = SWIG_AppendOutput($result, SWIG_Py_Void()); + } +} + +/* + * semanage_module_install() and semanage_module_install_info() take a + * (char *data, size_t data_len) pair that is an opaque module blob + * (.pp / bzip2 / CIL / HLL), not a NUL-terminated C string. Accept any + * Python object exposing the buffer protocol (bytes, bytearray, mmap, ...) + * as a single argument and derive the length from it. + */ +%typemap(in) (char *module_data, size_t data_len) (Py_buffer view) { + view.obj = NULL; + if (PyObject_GetBuffer($input, &view, PyBUF_SIMPLE) < 0) + SWIG_fail; + $1 = (char *)view.buf; + $2 = (size_t)view.len; +} +%typemap(freearg) (char *module_data, size_t data_len) { + PyBuffer_Release(&view$argnum); +} +%apply (char *module_data, size_t data_len) { (char *data, size_t data_len) }; %typemap(in, numinputs=0) char **(char *temp=NULL) { $1 = &temp; -- 2.55.0