C Calling custom jail_set function from Python's ctype works, FreeBSD's jail_set doesn't

I've written the following files:
jailtest/jailtest.h
C:
#include <sys/uio.h>

/*
 * Public API of the jailtest library.
 */

typedef struct jail {
        char *name;
        int jid;
} jail;
typedef struct iovec iovec;

int jail_set(iovec*, int niov, int flags);
jailtest/jailtest_priv.h
C:
/* empty */
jailtest/main.c
C:
#include "jailtest.h"
#include "jailtest_priv.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int jail_set(struct iovec *iov, int niov, int flags)
{
        printf("flags: %d\n", flags);
        for (int i=0; i<niov; i++) {
                if (i%2 == 0) {
                        printf("%s=", (char *)iov[I].iov_base);
                } else if (i%2 == 1) {
                        if (iov[I].iov_base != NULL) {
                                printf("'%s'\n", (char *)iov[I].iov_base);
                        } else {
                                printf("NULL\n");
                        }
                }
        }
        return 23;
}

prog/main.c
C:
#include <jailtest.h>
#include <stdio.h>

int main()
{
        jail* jails = listjails();
        printf("name: %s\njid: %d\n", jails[0].name, jails[0].jid);
        iovec iovecs[] = {
                { .iov_base = "name",      .iov_len = 5 },
                { .iov_base = "emptyjail", .iov_len = 10},
                { .iov_base = "persist",   .iov_len = 8},
                { .iov_base = NULL,        .iov_len = 0},
                { .iov_base = "vnet",      .iov_len = 5},
                { .iov_base = "",          .iov_len = 1},
                { .iov_base = "errormsg",  .iov_len = 8},
                { .iov_base = "",          .iov_len = 1},
        };
        int ret = jail_set(iovecs, 8, 0);
        printf("jail_set returned %d\n", ret);
        return 0;
}

These are built and run with the following commands:
sh:
# cc  -Wall -Wextra -Werror -Wno-unused-result -fPIC -c -o jailtest/main.o jailtest/main.c
# cc  -Wall -Wextra -Werror -Wno-unused-result -fPIC -shared -o jailtest/libjailtest.so jailtest/main.o 
# cc -Ijailtest -Wall -Wextra -Werror -Wno-unused-result -fPIC -c -o prog/main.o prog/main.c
# cc -Ijailtest -Wall -Wextra -Werror -Wno-unused-result -fPIC -o prog/prog prog/main.o -Ljailtest -ljailtest
# LD_LIBRARY_PATH=jailtest prog/prog
name='emptyjail'
persist=NULL
vnet=''
errormsg=''
jail_set returned 23

Then there is the respective Python code
jailtest/jailtest.py
Code:
from ctypes import (
        CDLL,
        Structure,
        c_char_p,
        c_int,
        c_void_p,
        c_size_t,
        POINTER as Pointer,
        create_string_buffer,
        cast,
)

lib = CDLL('./libjailtest.so')
#lib = CDLL('libjail.so')

class Jail(Structure):
        _fields_ = [
                ("name", c_char_p),
                ("jid", c_int),
        ]

class iovec(Structure):
        _fields_ = [
                ('iov_base', c_void_p),
                ('iov_len', c_size_t),
        ]

class Iovecs:
        def __init__(self, values={}):
                self._values = values
        def iovs(self):
                v = self._values
                n = len(v)
                s = n * 2
                iovec_array = (iovec * s)()
                i = 0
                for key,value in v.items():
                        iovec_array[I].iov_base = cast(create_string_buffer(key),  c_void_p)
                        iovec_array[I].iov_len = len(key)
                        i += 1
                        iovec_array[I].iov_base = cast(create_string_buffer(value),  c_void_p)
                        iovec_array[I].iov_len = len(value)
                        i += 1
                return iovec_array
        def niovs(self):
                return len(self._values)*2
        def add(self, name, value):
                self._values[name] = value

jail_set = lib.jail_set
jail_set.argtypes = [Pointer(iovec), c_int, c_int]
jail_set.restype = c_int

iv = Iovecs({
        b'name': b'emptyjail',
        b'persist': b'\0',
        b'vnet': b'',
        b'errormsg': b'',
})
ret = jail_set(iv.iovs(), iv.niovs(), 1)
print('jail_set: %d'%ret)

Using the custom libjailtest.so the code works:
sh:
# python3.11 jailtest.py
flags: 1
name='emptyjail'
persist=''
vnet=''
errormsg=''
jail_set: 23

But apparently FreeBSD's jail_set cannot be called like this:
sh:
# python3.11 jailtest.py
jail_set: -1

What's the issue here? How do I need to correctly call jail_set?

Greetings
Fips
 
Back
Top