From patchwork Wed Jul 20 09:02:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arjun Shankar X-Patchwork-Id: 56188 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 772C33858C55 for ; Wed, 20 Jul 2022 09:03:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 772C33858C55 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1658307802; bh=wOm/drq9Q9rWArd1kAHva4o2wFXw2RF1ER7Jc/ofpxk=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=tHI418hGIYiVZci8SwtyHDihYypCNNUXE7LcpT2WxhIfV2zdSOF1utSTN81vp4yDD iBPxrz825cgoKCn4j1PSh4OiJP8xP78TJBYegBrlFBca12OIfLbN+FSiEhHjqBt/Jw LU/3cvRXlveeedDDaKsGmE9GuKFkbGmqWfXcx/iU= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 787823858C2C for ; Wed, 20 Jul 2022 09:02:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 787823858C2C Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-259-9BOX3AiKMROsmZAgqTSygg-1; Wed, 20 Jul 2022 05:02:55 -0400 X-MC-Unique: 9BOX3AiKMROsmZAgqTSygg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8058229ABA0F; Wed, 20 Jul 2022 09:02:55 +0000 (UTC) Received: from x1carbon.redhat.com (unknown [10.40.194.237]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 83C0C2166B26; Wed, 20 Jul 2022 09:02:54 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH v2] socket: Check lengths before advancing pointer in CMSG_NXTHDR Date: Wed, 20 Jul 2022 11:02:41 +0200 Message-Id: <20220720090241.3235289-1-arjun@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, URIBL_BLACK autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Arjun Shankar via Libc-alpha From: Arjun Shankar Reply-To: Arjun Shankar Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" The inline and library functions that the CMSG_NXTHDR macro may expand to increment the pointer to the header before checking the stride of the increment against available space. Since C only allows incrementing pointers to one past the end of an array, the increment must be done after a length check. This commit fixes that and includes a regression test for CMSG_FIRSTHDR and CMSG_NXTHDR. The Linux, Hurd, and generic headers are all changed. Tested on Linux on armv7hl, i686, x86_64, aarch64, ppc64le, and s390x. [BZ #28846] --- v1: https://sourceware.org/pipermail/libc-alpha/2022-May/138816.html Changes in v2: * Addressed review comments from Siddhesh: - Mentioned bug # in the commit message. - It wasn't clear in v1 that __cmsg may safely be assumed to lie between __mhdr->msg_control and the end of the buffer. Added a comment explaining why. - Stopped using len in *any* arithmetic in ways that it could lead to an overflow/wrap. - Checked a sum for wrap before casting it to unsigned for use in an unsigned comparison. - Added a new test with len == SIZE_MAX so that we test the overflow/wrap case. bits/socket.h | 37 +++++++++-- socket/Makefile | 1 + socket/tst-cmsghdr-skeleton.c | 92 +++++++++++++++++++++++++++ socket/tst-cmsghdr.c | 56 ++++++++++++++++ sysdeps/mach/hurd/bits/socket.h | 37 +++++++++-- sysdeps/unix/sysv/linux/bits/socket.h | 37 +++++++++-- sysdeps/unix/sysv/linux/cmsg_nxthdr.c | 33 +++++++--- 7 files changed, 264 insertions(+), 29 deletions(-) create mode 100644 socket/tst-cmsghdr-skeleton.c create mode 100644 socket/tst-cmsghdr.c diff --git a/bits/socket.h b/bits/socket.h index 2b99dea33b..69cd1de888 100644 --- a/bits/socket.h +++ b/bits/socket.h @@ -245,6 +245,12 @@ struct cmsghdr + CMSG_ALIGN (sizeof (struct cmsghdr))) #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +/* Given a length, return the additional padding necessary such that + len + CMSG_PADDING(len) == CMSG_ALIGN (len). */ +#define CMSG_PADDING(len) ((sizeof (size_t) \ + - ((len) & (sizeof (size_t) - 1))) \ + & (sizeof (size_t) - 1)) + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg) __THROW; #ifdef __USE_EXTERN_INLINES @@ -254,18 +260,35 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, _EXTERN_INLINE struct cmsghdr * __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) { + /* We may safely assume that __cmsg lies between __mhdr->msg_control and + __mhdr->msg_controllen because the user is required to obtain the first + cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs + via CMSG_NXTHDR, setting lengths along the way. However, we don't yet + trust the value of __cmsg->cmsg_len and therefore do not use it in any + pointer arithmetic until we check its value. */ + + unsigned char * msg_control_ptr = (unsigned char *) __mhdr->msg_control; + unsigned char * cmsg_ptr = (unsigned char *) __cmsg; + + /* The current header is malformed, too small to be a full header. */ if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) - /* The kernel header does this so there may be a reason. */ return (struct cmsghdr *) 0; + /* There isn't enough space between __cmsg and the end of the buffer to + hold the current cmsg *and* the next one. */ + if (((size_t) + (msg_control_ptr + __mhdr->msg_controllen - cmsg_ptr) + < sizeof (struct cmsghdr) + CMSG_PADDING (__cmsg->cmsg_len)) + || ((size_t) + (msg_control_ptr + __mhdr->msg_controllen - cmsg_ptr + - sizeof (struct cmsghdr) - CMSG_PADDING (__cmsg->cmsg_len)) + < __cmsg->cmsg_len)) + + return (struct cmsghdr *) 0; + + /* Now, we trust cmsg_len and can use it to find the next header. */ __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)); - if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control - + __mhdr->msg_controllen) - || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) - > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) - /* No more entries. */ - return (struct cmsghdr *) 0; return __cmsg; } #endif /* Use `extern inline'. */ diff --git a/socket/Makefile b/socket/Makefile index 156eec6c85..2bde78387f 100644 --- a/socket/Makefile +++ b/socket/Makefile @@ -34,6 +34,7 @@ routines := accept bind connect getpeername getsockname getsockopt \ tests := \ tst-accept4 \ tst-sockopt \ + tst-cmsghdr \ # tests tests-internal := \ diff --git a/socket/tst-cmsghdr-skeleton.c b/socket/tst-cmsghdr-skeleton.c new file mode 100644 index 0000000000..4c6898569b --- /dev/null +++ b/socket/tst-cmsghdr-skeleton.c @@ -0,0 +1,92 @@ +/* Test ancillary data header creation. + Copyright (C) 2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* We use the preprocessor to generate the function/macro tests instead of + using indirection because having all the macro expansions alongside + each other lets the compiler warn us about suspicious pointer + arithmetic across subsequent CMSG_{FIRST,NXT}HDR expansions. */ + +#include + +#define RUN_TEST_CONCAT(suffix) run_test_##suffix +#define RUN_TEST_FUNCNAME(suffix) RUN_TEST_CONCAT (suffix) + +static void +RUN_TEST_FUNCNAME (CMSG_NXTHDR_IMPL) (void) +{ + struct msghdr m = {0}; + struct cmsghdr *cmsg; + char cmsgbuf[3 * CMSG_SPACE (sizeof (PAYLOAD))] = {0}; + + m.msg_control = cmsgbuf; + m.msg_controllen = sizeof (cmsgbuf); + + /* First header should point to the start of the buffer. */ + cmsg = CMSG_FIRSTHDR (&m); + TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); + + /* If the first header length consumes the entire buffer, there is no + space remaining for additional headers. */ + cmsg->cmsg_len = sizeof (cmsgbuf); + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg == NULL); + + /* The first header length is so big, using it would cause an overflow. */ + cmsg = CMSG_FIRSTHDR (&m); + TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); + cmsg->cmsg_len = SIZE_MAX; + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg == NULL); + + /* The first header leaves just enough space to hold another header. */ + cmsg = CMSG_FIRSTHDR (&m); + TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); + cmsg->cmsg_len = sizeof (cmsgbuf) - sizeof (struct cmsghdr); + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg != NULL); + + /* The first header leaves space but not enough for another header. */ + cmsg = CMSG_FIRSTHDR (&m); + TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); + cmsg->cmsg_len ++; + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg == NULL); + + /* The second header leaves just enough space to hold another header. */ + cmsg = CMSG_FIRSTHDR (&m); + TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); + cmsg->cmsg_len = CMSG_LEN (sizeof (PAYLOAD)); + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg != NULL); + cmsg->cmsg_len = sizeof (cmsgbuf) + - CMSG_SPACE (sizeof (PAYLOAD)) /* First header. */ + - sizeof (struct cmsghdr); + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg != NULL); + + /* The second header leaves space but not enough for another header. */ + cmsg = CMSG_FIRSTHDR (&m); + TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg != NULL); + cmsg->cmsg_len ++; + cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); + TEST_VERIFY_EXIT (cmsg == NULL); + + return; +} diff --git a/socket/tst-cmsghdr.c b/socket/tst-cmsghdr.c new file mode 100644 index 0000000000..68c96d3c9d --- /dev/null +++ b/socket/tst-cmsghdr.c @@ -0,0 +1,56 @@ +/* Test ancillary data header creation. + Copyright (C) 2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +#define PAYLOAD "Hello, World!" + +/* CMSG_NXTHDR is a macro that calls an inline function defined in + bits/socket.h. In case the function cannot be inlined, libc.so carries + a copy. Both versions need to be tested. */ + +#define CMSG_NXTHDR_IMPL CMSG_NXTHDR +#include "tst-cmsghdr-skeleton.c" +#undef CMSG_NXTHDR_IMPL + +static struct cmsghdr * (* cmsg_nxthdr) (struct msghdr *, struct cmsghdr *); + +#define CMSG_NXTHDR_IMPL cmsg_nxthdr +#include "tst-cmsghdr-skeleton.c" +#undef CMSG_NXTHDR_IMPL + +static int +do_test (void) +{ + static void *handle; + + run_test_CMSG_NXTHDR (); + + handle = xdlopen (LIBC_SO, RTLD_LAZY); + cmsg_nxthdr = (struct cmsghdr * (*) (struct msghdr *, struct cmsghdr *)) + xdlsym (handle, "__cmsg_nxthdr"); + + run_test_cmsg_nxthdr (); + + return 0; +} + +#include diff --git a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h index 5b35ea81ec..0e9a3d6b0e 100644 --- a/sysdeps/mach/hurd/bits/socket.h +++ b/sysdeps/mach/hurd/bits/socket.h @@ -249,6 +249,12 @@ struct cmsghdr + CMSG_ALIGN (sizeof (struct cmsghdr))) #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +/* Given a length, return the additional padding necessary such that + len + CMSG_PADDING(len) == CMSG_ALIGN (len). */ +#define CMSG_PADDING(len) ((sizeof (size_t) \ + - ((len) & (sizeof (size_t) - 1))) \ + & (sizeof (size_t) - 1)) + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg) __THROW; #ifdef __USE_EXTERN_INLINES @@ -258,18 +264,35 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, _EXTERN_INLINE struct cmsghdr * __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) { + /* We may safely assume that __cmsg lies between __mhdr->msg_control and + __mhdr->msg_controllen because the user is required to obtain the first + cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs + via CMSG_NXTHDR, setting lengths along the way. However, we don't yet + trust the value of __cmsg->cmsg_len and therefore do not use it in any + pointer arithmetic until we check its value. */ + + unsigned char * msg_control_ptr = (unsigned char *) __mhdr->msg_control; + unsigned char * cmsg_ptr = (unsigned char *) __cmsg; + + /* The current header is malformed, too small to be a full header. */ if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) - /* The kernel header does this so there may be a reason. */ return (struct cmsghdr *) 0; + /* There isn't enough space between __cmsg and the end of the buffer to + hold the current cmsg *and* the next one. */ + if (((size_t) + (msg_control_ptr + __mhdr->msg_controllen - cmsg_ptr) + < sizeof (struct cmsghdr) + CMSG_PADDING (__cmsg->cmsg_len)) + || ((size_t) + (msg_control_ptr + __mhdr->msg_controllen - cmsg_ptr + - sizeof (struct cmsghdr) - CMSG_PADDING (__cmsg->cmsg_len)) + < __cmsg->cmsg_len)) + + return (struct cmsghdr *) 0; + + /* Now, we trust cmsg_len and can use it to find the next header. */ __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)); - if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control - + __mhdr->msg_controllen) - || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) - > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) - /* No more entries. */ - return (struct cmsghdr *) 0; return __cmsg; } #endif /* Use `extern inline'. */ diff --git a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h index 4f1f810ea1..f056d8c708 100644 --- a/sysdeps/unix/sysv/linux/bits/socket.h +++ b/sysdeps/unix/sysv/linux/bits/socket.h @@ -307,6 +307,12 @@ struct cmsghdr + CMSG_ALIGN (sizeof (struct cmsghdr))) #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +/* Given a length, return the additional padding necessary such that + len + CMSG_PADDING(len) == CMSG_ALIGN (len). */ +#define CMSG_PADDING(len) ((sizeof (size_t) \ + - ((len) & (sizeof (size_t) - 1))) \ + & (sizeof (size_t) - 1)) + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg) __THROW; #ifdef __USE_EXTERN_INLINES @@ -316,18 +322,35 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, _EXTERN_INLINE struct cmsghdr * __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) { + /* We may safely assume that __cmsg lies between __mhdr->msg_control and + __mhdr->msg_controllen because the user is required to obtain the first + cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs + via CMSG_NXTHDR, setting lengths along the way. However, we don't yet + trust the value of __cmsg->cmsg_len and therefore do not use it in any + pointer arithmetic until we check its value. */ + + unsigned char * msg_control_ptr = (unsigned char *) __mhdr->msg_control; + unsigned char * cmsg_ptr = (unsigned char *) __cmsg; + + /* The current header is malformed, too small to be a full header. */ if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) - /* The kernel header does this so there may be a reason. */ return (struct cmsghdr *) 0; + /* There isn't enough space between __cmsg and the end of the buffer to + hold the current cmsg *and* the next one. */ + if (((size_t) + (msg_control_ptr + __mhdr->msg_controllen - cmsg_ptr) + < sizeof (struct cmsghdr) + CMSG_PADDING (__cmsg->cmsg_len)) + || ((size_t) + (msg_control_ptr + __mhdr->msg_controllen - cmsg_ptr + - sizeof (struct cmsghdr) - CMSG_PADDING (__cmsg->cmsg_len)) + < __cmsg->cmsg_len)) + + return (struct cmsghdr *) 0; + + /* Now, we trust cmsg_len and can use it to find the next header. */ __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)); - if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control - + __mhdr->msg_controllen) - || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) - > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) - /* No more entries. */ - return (struct cmsghdr *) 0; return __cmsg; } #endif /* Use `extern inline'. */ diff --git a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c index 15b7a3a925..70afbe36f4 100644 --- a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c +++ b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c @@ -23,18 +23,35 @@ struct cmsghdr * __cmsg_nxthdr (struct msghdr *mhdr, struct cmsghdr *cmsg) { + /* We may safely assume that cmsg lies between mhdr->msg_control and + mhdr->msg_controllen because the user is required to obtain the first + cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs + via CMSG_NXTHDR, setting lengths along the way. However, we don't yet + trust the value of cmsg->cmsg_len and therefore do not use it in any + pointer arithmetic until we check its value. */ + + unsigned char * msg_control_ptr = (unsigned char *) mhdr->msg_control; + unsigned char * cmsg_ptr = (unsigned char *) cmsg; + + /* The current header is malformed, too small to be a full header. */ if ((size_t) cmsg->cmsg_len < sizeof (struct cmsghdr)) - /* The kernel header does this so there may be a reason. */ - return NULL; + return (struct cmsghdr *) 0; + + /* There isn't enough space between cmsg and the end of the buffer to + hold the current cmsg *and* the next one. */ + if (((size_t) + (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr) + < sizeof (struct cmsghdr) + CMSG_PADDING (cmsg->cmsg_len)) + || ((size_t) + (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr + - sizeof (struct cmsghdr) - CMSG_PADDING (cmsg->cmsg_len)) + < cmsg->cmsg_len)) + + return (struct cmsghdr *) 0; + /* Now, we trust cmsg_len and can use it to find the next header. */ cmsg = (struct cmsghdr *) ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len)); - if ((unsigned char *) (cmsg + 1) > ((unsigned char *) mhdr->msg_control - + mhdr->msg_controllen) - || ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len) - > ((unsigned char *) mhdr->msg_control + mhdr->msg_controllen))) - /* No more entries. */ - return NULL; return cmsg; } libc_hidden_def (__cmsg_nxthdr)