mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
socket: Check lengths before advancing pointer in CMSG_NXTHDR
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] Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
This commit is contained in:
@ -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,38 @@ 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;
|
||||
|
||||
size_t __size_needed = sizeof (struct cmsghdr)
|
||||
+ __CMSG_PADDING (__cmsg->cmsg_len);
|
||||
|
||||
/* 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)
|
||||
< __size_needed)
|
||||
|| ((size_t)
|
||||
(__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr
|
||||
- __size_needed)
|
||||
< __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'. */
|
||||
|
Reference in New Issue
Block a user