classification
Title: fcntl.ioctl on openbsd
Type: Stage:
Components: Extension Modules Versions:
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: loewis, skin_pup, tim.peters
Priority: low Keywords:

Created on 2002-02-23 05:34 by skin_pup, last changed 2002-02-25 17:36 by loewis. This issue is now closed.

Messages (7)
msg9360 - (view) Author: Jeremy Rossi (skin_pup) Date: 2002-02-23 05:34
From the OpenBSD man page -------

#include <sys/ioctl.h>
int
ioctl(int d, unsigned long request, ...);
--

On OpenBSD ioctl takes an unsigned long for the action
to be preformed on d. The function fcntl_ioctl() in
Modules/fcntlmodule.c will only accept an int for the
second argument without rasing an error. On OpenBSD
(maybe free/net also I have not checked) an unsigned
long should be the largest allowed.

From Modules/fcntlmodule.c -------
PyArg_ParseTuple(args, "iis#:ioctl", &fd, &code, &str,
&len))
--
msg9361 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2002-02-23 23:05
Logged In: YES 
user_id=21627

Can you give a practical example of an fcntl operation where
this is a problem? For all practical purposes, a byte would
be sufficient.

Also, in POSIX, the argument to fcntl is of type int, see

http://www.opengroup.org/onlinepubs/007904975/functions/fcntl.html

So I can't see the bug.
msg9362 - (view) Author: Jeremy Rossi (skin_pup) Date: 2002-02-24 03:15
Logged In: YES 
user_id=323435

From the current man pages of OpenBSD and FreeBSD. It stats
that the second argument of ioctl is an unsigned int.  

http://www.openbsd.org/cgi-bin/man.cgi?query=ioctl
http://www.freebsd.org/cgi-bin/man.cgi?query=ioctl

Pythons fcntl.ioctl() does not allow the second argumnet to
be anything other then a C int, this does not allow required
operations to be preformed with ioctl on the two BSD systems.  

For a practical example.  On the openbsd system the /dev/pf
is the direct inteface to the firewall, the only things I am
able to preform on this file in python are to turn the
firewall on and off.  This is allowed because the ioctl
un_signed ints (536888321 in base 10) that prefrom this
action happen to be small enough to fit in to an int.  While
the ioctl unsigned int (3229893651 in base 10) for reporting
the status of connections is larger then a C int and python
raises an exception before calling the system ioctl call.  

The following is the code in question.

import fcntl
import struct
import os
fd = os.open("/dev/pf",os.O_RDWR)
null = '\0'*(struct.calcsize("LLLLIIII"))
x = 3229893651 
null = fcntl.ioctl(fd,x,null)
print struct.unpack("LLLLIIII",null)

---output---
$ sudo python ./py-pfctl.py
Traceback (most recent call last):
  File "./py-pfctl.py", line 8, in ?
    null = fcntl.ioctl(fd,x,null)
OverflowError: long int too large to convert to int



msg9363 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2002-02-24 14:58
Logged In: YES 
user_id=21627

This won't be easy to change: If we declare the type of
ioctl to be unsigned, then we break systems where it is
signed (as it should be).

As a work-around, try using 0xC0844413 (i.e. the hexadecimal
version) as the value for the ioctl. Python will understand
this as a negative value, but your system will likely still
understand it as the right ioctl command.
msg9364 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2002-02-24 21:36
Logged In: YES 
user_id=31435

Gotta love it <wink>.

I don't believe ioctl is a POSIX Classic function.  There's 
a good discussion of why the POSIX Realtime Extensions 
added a workalike posix_devctl() instead, in

http://www.usenix.org/publications/login/standards/22.posix.
html

Martin, the URL you gave is actually for fcntl, not ioctl.  
You can s/fcntl/ioctl/ in your URL to get the Single UNIX 
Specification's ioctl page, though, which also says "int".  
I agree OpenBSD is out of line with best current practice 
because of that.

It appears that Jeremy must be using Python 2.2, or running 
on a 64-bit machine, since his line

x = 3229893651

raises OverflowError on 32-bit boxes before the 2.2 release.

As Martin suggests, using a hex literal instead has always 
been the intended way to deal with cases "like this".  The 
situation will get a lot worse if OpenBSD is ported to a 64-
bit box with sizeof(long)==8, and some yahoo actually 
defines a ioctl arg that requires more than 32 bits.

Before then, I suggest we leave this alone (by the time it 
may matter for real, OpenBSD should be feeling a lot more 
pressure to conform to the larger open standards).
msg9365 - (view) Author: Jeremy Rossi (skin_pup) Date: 2002-02-24 22:23
Logged In: YES 
user_id=323435

Thank you Martin the 0xC0844413 does indeed work for me, 
but I am working on writing a thin wrapper that will accept 
un_signed long ints for ioctl.  (Never done C before, but I 
guess this is as good as any to learn)

But to looking forward I have done some checking and it 
seams to me that all the *BSD's including BSDi use unsigned 
longs for ioctl.  I was not able to find documentation for  
darwin on the web, bit I think it is safe to assume that it 
also takes a unsigned long for ioctl.  NetBSD also have 
been ported to 64bit systems.

NetBSD:
http://www.tac.eu.org/cgi-bin/man-cgi?ioctl++NetBSD-current

-- BEGIN cut and paste from a BSDi systems.
$ uname -a
BSD/OS xxxx.xxxxxx.com 2.1 BSDI BSD/OS 2.1 Kernel #2: Mon 
Jan 27 16:12:45 MST 1997     
web@xxxx.xxxxxx.com:/usr/src/sys/compile/USR  i386


$ man ioctl | head
IOCTL(2)                    BSD Programmer's 
Manual                   IOCTL(2)

NAME
     ioctl - control device

SYNOPSIS
     #include <sys/ioctl.h>

     int
     ioctl(int d, unsigned long request, char *argp);
--END
msg9366 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2002-02-25 17:36
Logged In: YES 
user_id=21627

On your system, 'unsigned long' is the same type as
'unsigned int'. Furthermore, passing a negative number -n is
the same as passing 2**32-n, atleast on a 32-bit
architecture. So even if your driver processes unsigned
ints, you will be able to pass the correct value using the
corresponding negative number.

Furthermore, if you use the hex notation, it will work even
on 64-bit ports unmodified: unsigned long will be 64 bit,
but so will be the Python int type, and the hex literal then
indicates a positive integer, which is well in range.

As for ioctl not being Posix: It actually is, in the latest
revision of Posix (IEEE Std 1003.1-2001), which is identical
to Single Unix (see the top of the ioctl page).

In any case, I think this is not worth fixing, as I cannot
see a problem arising from it that cannot be easily
worked-around, hence closing the report.
History
Date User Action Args
2002-02-23 05:34:25skin_pupcreate