Straightening out ioctl() size confusion
[Posted September 9, 2003 by corbet]
The
ioctl() system call includes a general "command" argument
which specifies which operation the calling program wishes to perform. The
Linux kernel has long had a mechanism for defining these command arguments,
with the goal of keeping them all unique. If no two drivers implement the
same command codes, there is no danger of strange things happen if the
wrong code is passed to the wrong driver. A world where "rewind the tape"
for one driver never translates to "initiate self destruct" for another is a
safer place to be for all of us.
The Linux kernel takes things a little further by encoding some useful
information in the command codes. Along with driver-specific "magic" and
command numbers, the ioctl() command code includes the direction
of data movement (if any) between kernel and user space and the size of the
data to be moved. The kernel itself does not do anything with those
values, but their presence does enable a driver to perform some checks.
If, for example, the size of a structure used as an ioctl()
argument changes, the driver can use the size field in the command code to
determine whether the application is using the older version or not. Some
kernel code actually does check the sizes to be sure that things match up.
The command codes are created using some macros in
<asm/ioctl.h>. A driver defining codes would use one
of these macros:
_IOR(type, number, size)
_IOW(type, number, size)
_IORW(type, number, size)
The macro used specifies whether the ioctl() operation reads or
writes kernel-space data (or both); type is the driver's "magic"
code, and number is the command-specific code. The confusion
comes in with the argument called size; it is supposed to be the
type of the data to be passed between kernel and user space. So, for
example, the "get tape position" code is defined as:
#define MTIOCPOS _IOR('m', 3, struct mtpos)
The problem is that a number of hackers saw the size argument and
assumed that they were expected to pass the size of the expected data
transfer. The result was a number of definitions like:
#define CIOC_KERNEL_VERSION _IOWR('c', 10, sizeof (int))
As a result, the actual size value, as encoded within the command, was the
size of the size value, or, on most architectures, four bytes. Since most
code never looks at that size value, things worked, but the values defined
were not as intended. Another problem that occasionally came up was that
some code used very large size values, overflowing the space allotted in
the command word, thus corrupting the rest of the command code. Once
again, things worked, but not quite in the way people expected.
One of the themes of 2.6 development has been the addition of type checking
anywhere that the compiler can be coerced into doing it. So the obvious
thing to do was to add checking to the generation of command codes; Arnd Bergmann
submitted a patch which does exactly that.
It adds a bit of preprocessor magic in the form of this macro:
#define _IOC_TYPECHECK(t) \
((sizeof(t) == sizeof(t[1]) && \
sizeof(t) < (1 << _IOC_SIZEBITS)) ? \
sizeof(t) : __invalid_size_argument_for_IOC)
The first test ensures that an actual type (as opposed to a simple size)
has been passed in; the second makes sure it is not too large.
All that remains is the inconvenient fact that the old, erroneous codes
have found their way into a number of application programs. Changing those
codes would break those applications, and that's something the kernel hackers
try never to do. So, for these cases, a new set of macros (with names like
_IOW_BAD() has been introduced, and the erroneous uses have been
moved over to the new macros. The command codes remain unchanged, but the
mistake is noted so that it is not replicated when somebody copies the code
in question.
(
Log in to post comments)