[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.