Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

64-bit argument for fcntl.ioctl()

Tags:

python

ioctl

In my Python (2.7.3) code, I'm trying to use an ioctl call, accepting a long int (64 bit) as an argument. I'm on a 64-bit system, so a 64-bit int is the same size as a pointer.

My problem is that Python doesn't seem to accept a 64-bit int as the argument for a fcntl.ioctl() call. It happily accepts a 32-bit int or a 64-bit pointer - but what I need is to pass a 64-bit int.

Here's my ioctl handler:

static long trivial_driver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    long err = 0;

    switch (cmd)
    {
        case 1234:
            printk("=== (%u) Driver got arg %lx; arg<<32 is %lx\n", cmd, arg, arg<<32);
            break;
        case 5678:
            printk("=== (%u) Driver got arg %lx\n", cmd, arg);
            break;
        default:
            printk("=== OH NOES!!! %u %lu\n", cmd, arg);
            err = -EINVAL;
    }

    return err;
}

In existing C code, I use the call like this:

static int trivial_ioctl_test(){
    int ret;
    int fd = open(DEV_NAME, O_RDWR);

    unsigned long arg = 0xffff;

    ret = ioctl(fd, 1234, arg); // ===(1234) Driver got arg ffff; arg<<32 is ffff00000000
    arg = arg<<32;
    ret = ioctl(fd, 5678, arg); // === (5678) Driver got arg ffff00000000
    close(fd);

}

In python, I open the device file, and then I get the following results:

>>> from fcntl import ioctl
>>> import os
>>> fd = os.open (DEV_NAME, os.O_RDWR, 0666)
>>> ioctl(fd, 1234, 0xffff)
0
>>> arg = 0xffff<<32
>>> # Kernel log: === (1234) Driver got arg ffff; arg<<32 is ffff00000000
>>> # This demonstrates that ioctl() happily accepts a 32-bit int as an argument.
>>> import struct
>>> ioctl(fd, 5678, struct.pack("L",arg))
'\x00\x00\x00\x00\xff\xff\x00\x00'
>>> # Kernel log: === (5678) Driver got arg 7fff9eb1fcb0
>>> # This demonstrates that ioctl() happily accepts a 64-bit pointer as an argument.
>>> ioctl(fd, 5678, arg)

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ioctl(fd, 5678, arg)
OverflowError: signed integer is greater than maximum
>>> # Kernel log: (no change - OverflowError is within python)
>>> # Oh no! Can't pass a 64-bit int!
>>> 

Is there any way Python can pass my 64-bit argument to ioctl()?

like image 609
Ziv Avatar asked Jun 20 '13 09:06

Ziv


1 Answers

Whether or not this is possible using Python's fcntl.ioctl() will be system-dependent. Tracing through the source code, the error message is coming from the following test on line 658 of getargs.c...

else if (ival > INT_MAX) {
    PyErr_SetString(PyExc_OverflowError,
    "signed integer is greater than maximum");
    RETURN_ERR_OCCURRED;
}

...and on my system, /usr/include/limits.h tells me...

#  define INT_MAX   2147483647

...which is (presumably) (2 ** ((sizeof(int) * 8) - 1)) - 1.

So, unless you're working on a system where sizeof(int) is at least 8, you'll have to call the underlying C function directly using the ctypes module, but it's platform-specific.

Assuming Linux, something like this ought to work...

from ctypes import *

libc = CDLL('libc.so.6')

fd = os.open (DEV_NAME, os.O_RDWR, 0666)
value = c_uint64(0xffff<<32)
libc.ioctl(fd, 5678, value)
like image 157
Aya Avatar answered Sep 22 '22 13:09

Aya