Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when NULL pointers are returned from "foreign functions" using ctypes in python?

Tags:

python

c

ctypes

When wrapping C functions with ctypes what happens when a method returns a NULL pointer to a struct mapped to a python class derived from ctypes.Structure? Also what happens when valid pointers passed to a method are set to NULL values? The following test will help us find out:

//python_ctypes_test.c
#include <stdlib.h>

struct test_struct {
    int a;
    int b;
    int c;
    int d;
} typedef test_struct;

test_struct* get_test_struct(const int valid) {
    test_struct* newStruct = 0;
    if (valid > 0) {
        newStruct = malloc(sizeof *newStruct);
        newStruct->a = 1;
        newStruct->b = 2;
        newStruct->c = 3;
        newStruct->d = 4;
    }
    return newStruct;
}

void get_two_floats(const int valid, float* float1, float* float2) {
    if (valid > 0) {
        *float1 = 1.1f;
        *float2 = 2.2f;
    } else {
        float1 = 0;
        float2 = 0;
    }
}

#python_ctypes_test.py
import ctypes
import ctypes.util

dll = ctypes.CDLL('libpython_ctypes_test.so')

class _test_struct(ctypes.Structure):
    _fields_ = [("a", ctypes.c_int),
                ("b", ctypes.c_int),
                ("c", ctypes.c_int),
                ("d", ctypes.c_int)]

    def wrap(self, something):
        self.a,self.b,self.c,self.d = something

    def unwrap(self):
        part1 = self.a,self.b
        part2 = self.c,self.d
        return part1, part2

dll.get_test_struct.restype = ctypes.POINTER(_test_struct)
dll.get_test_struct.argtypes = [ctypes.c_int]
def py_get_test_struct(valid):
    return dll.get_test_struct(valid).contents.unwrap()

def py_get_test_struct_safe(valid):
    testStructPtr = dll.get_test_struct(valid)
    if testStructPtr: 
        return testStructPtr.contents.unwrap()
    else:
        return None

dll.get_two_floats.restype = None
dll.get_two_floats.argtypes = [ctypes.c_int, 
        ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float)]
def py_get_two_floats(valid):
    float1_value = ctypes.c_float(0)
    float1 = ctypes.pointer(float1_value)
    float2_value = ctypes.c_float(0)
    float2 = ctypes.pointer(float2_value)

    dll.get_two_floats(valid, float1, float2)

    return float1_value.value, float2_value.value

print py_get_two_floats(True)
print py_get_two_floats(False)
print py_get_test_struct_safe(True)
print py_get_test_struct_safe(False)
print py_get_test_struct(True)
print py_get_test_struct(False)
like image 238
trukvl Avatar asked Dec 05 '25 09:12

trukvl


1 Answers

As it turns out, the developer need not be concerned about the address of a pointer, that has been passed to a function, changing.

print py_get_two_floats(True)
  (1.100000023841858, 2.200000047683716)

print py_get_two_floats(False)
  (0.0, 0.0)

In the second case the values remain unchanged from their initialized state. This is generally true. Had float1_value and float2_value been initialized to 10 and 20. Those values would have been remained unchanged as well.

As @eryksun points out:

float1 and float2 are local variables that contain the address of the two floats. You can set the value to 0, or any other address, but that's just changing a local variable. That's not the same as dereferencing the pointer (e.g. *float1) to access the floating point value at the address.

In the case of return values things are handled differently. The object returned evaluates to true (non-null) or false (null).

print py_get_test_struct_safe(True)
  ((1, 2), (3, 4))

print py_get_test_struct_safe(False)
  None

Attempting to access a NULL pointer results in a ValueError exception.

print py_get_test_struct(True)
  ((1, 2), (3, 4))

print py_get_test_struct(False)
Traceback (most recent call last):
  File "python_ctypes.py", line 52, in <module>
    print py_get_test_struct(False)
  File "python_ctypes.py", line 24, in py_get_test_struct
    return dll.get_test_struct(valid).contents.unwrap()
ValueError: NULL pointer access
like image 125
trukvl Avatar answered Dec 07 '25 23:12

trukvl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!