NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/38992: kernel requires incorrect alignment for cmsghdr cmsg_len field
>Number: 38992
>Category: kern
>Synopsis: kernel requires incorrect alignment for cmsghdr cmsg_len field
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Fri Jun 20 00:30:00 +0000 2008
>Originator: Taylor R Campbell <campbell%mumble.net@localhost>
>Release: NetBSD 4.0_STABLE
>Organization:
>Environment:
System: NetBSD slate.localdomain 4.0_STABLE NetBSD 4.0_STABLE (GENERIC) #0: Sat
Jun 7 06:55:02 UTC 2008
riastradh%slate.localdomain@localhost:/home/riastradh/netbsd/4/obj/sys/arch/macppc/compile/GENERIC
macppc
Architecture: powerpc
Machine: macppc
>Description:
Note: The machine I am using to submit this PR runs NetBSD
4.0_STABLE, but the problem persists in -current as of a couple
days ago.
sendmsg(2) yields EINVAL for correctly aligned message header
lengths, and proceeds happpily with incorrectly aligned ones.
In a msghdr, msg_controllen should be set to the actual size of
the control data buffer computed with CMSG_SPACE, and in a
cmsghdr within the control data buffer, cmsg_len should be set
to the number of bytes actually used by that header's control
datum, including the header, computed with CMSG_LEN.
sendmsg(2) yields EINVAL if msg_controllen and cmesg_len
disagree, however, which is often the case -- specifically, on
architectures whose alignment exceeds the width of a file
descriptor, so that CMSG_SPACE (sizeof (int)) differs from
CMSG_LEN (sizeof (int)).
>How-To-Repeat:
Construct a msghdr with ancillary data for sending a file
descriptor to another process with sendmsg(2) as follows:
struct msghdr msg;
union {
struct cmsghdr align_me_please;
char data [CMSG_SPACE (sizeof (int))];
} control_buffer;
struct cmsghdr *cmsg_ptr;
char one = 1;
struct iovec iov;
iov.iov_base = &one;
iov.iov_len = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (void *) &control_buffer;
msg.msg_controllen = sizeof (control_buffer);
cmsg_ptr = CMSG_FIRSTHDR (&msg);
cmsg_ptr->cmsg_len = CMSG_LEN (sizeof (int));
cmsg_ptr->cmsg_level = SOL_SOCKET;
cmsg_ptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA (cmsg_ptr)) = fd;
Then pass the msghdr to sendmsg(2). On platforms whose
alignment is wider than the size of an int (e.g., PowerPC,
SPARC, &c.), sendmsg(2) will fail with EINVAL. Different
choices of CMSG_SPACE and CMSG_LEN in different places will
cause different problems.
(The above fragment blithely uses CMSG_SPACE, which expands to
a non-constant expression, to calculate the size of a stack-
allocated array. Perhaps it should use dynamic allocation
instead, but I wrote the above to reflect directly the idiom
that is described in Stevens' book and that is presently
supported and documented in OpenBSD and elsewhere.)
For a complete program that exhibits the problem, fetch
<http://mumble.net/~campbell/tmp/passfd.c>
<http://mumble.net/~campbell/tmp/passfd-main.c>
and compile with
gcc -g -Wall -c passfd-main.c -o passfd-main.o
gcc -DMSGHDR_CMSG_SPACE -DCMSGHDR_CMSG_LEN \
-DCMSG_BUFFER_STATIC_UNION -g -Wall \
-c passfd.c -o passfd.o
gcc passfd-main.o passfd.o -o passfd-test
Finally, run `passfd-test' with an argument, any random message
to send between processes. sendmsg(2) will fail with EINVAL
(which is returned from `send_fd'). Currently, I believe there
are no definitions of MSGHDR_MSG_*, CMSGHDR_CMSG_*, or
CMSG_BUFFER_* for passfd.c that will make the program run
correctly everywhere; run `gcc -c passfd.c -o passfd.o' to see
what options one has, or read the source. Although for me
-DMSGHDR_CMSG_SPACE -DCMSGHDR_CMSG_SPACE -DCMSG_BUFFER_MALLOC
will cause no output to be written to stderr -- the goal --, I
believe that the kernel is examining a bogus, random file
descriptor in the process of transmitting the message, which
could be a security vulnerability; using ALLOCA, STATIC_UNION,
or STATIC_ARRAY instead of MALLOC causes sendmsg(2) to yield
EBADF for me. I believe the correct options should be
-DMSGHDR_CMSG_SPACE -DCMSGHDR_MSG_LEN -DCMSG_BUFFER_MALLOC
but on my PowerPC, these cause sendmsg(2) to yield EINVAL.
>Fix:
Apply the following patch to src/sys/kern/uipc_usrreq.c
(OpenBSD checks for two possible values of control->m_len,
either cm->cmsg_len or CMSG_ALIGN (cm_cmsg_len); FreeBSD does
this more lenient test):
--- uipc_usrreq.c.~1.115.~ 2008-06-15 01:53:13.000000000 +0000
+++ uipc_usrreq.c 2008-06-19 23:13:49.000000000 +0000
@@ -1291,7 +1291,7 @@
/* Sanity check the control message header. */
if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
- cm->cmsg_len != control->m_len)
+ cm->cmsg_len > control->m_len)
return (EINVAL);
/*
Home |
Main Index |
Thread Index |
Old Index