Re: [PATCH] libsemanage: treat module data as bytes in the Python bindings

James Carter <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <CAP+JOzR9S4Gpn-Q1Q3KVDT5o9qPMv1ayjSgBLBJYW2bQUL3b0Q@mail.gmail.com>
On Thu, Aug 13, 2026 at 2:41 PM Stephen Smalley
<[email protected]> wrote:
>
> 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]>

Acked-by: James Carter <[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.