2007-03-15  Jakub Jelinek  <jakub@redhat.com>
	[BZ #4181]
	* inet/inet6_opt.c (add_padding): Only insert padding if npad > 0.
	(inet6_opt_append): Don't check extlen is big enough if extbuf
	is NULL.
	(inet6_opt_finish): Likewise.
	* inet/Makefile (tests): Add test-inet6_opt.
	* inet/test-inet6_opt.c: New test.

	* sysdeps/unix/sysv/linux/ifaddrs.c (__netlink_request): Never
	reallocate the buffer, instead fail for MSG_TRUNC or for EBUSY
	NLMSG_ERR.  Instead use a page sized buffer.
	* sysdeps/unix/sysv/linux/check_pf.c (make_request): Use page sized
	buffer.
This commit is contained in:
Ulrich Drepper 2007-03-15 20:05:19 +00:00
parent 02c906999c
commit 6cb988fa7b
6 changed files with 295 additions and 116 deletions

View File

@ -1,3 +1,19 @@
2007-03-15 Jakub Jelinek <jakub@redhat.com>
[BZ #4181]
* inet/inet6_opt.c (add_padding): Only insert padding if npad > 0.
(inet6_opt_append): Don't check extlen is big enough if extbuf
is NULL.
(inet6_opt_finish): Likewise.
* inet/Makefile (tests): Add test-inet6_opt.
* inet/test-inet6_opt.c: New test.
* sysdeps/unix/sysv/linux/ifaddrs.c (__netlink_request): Never
reallocate the buffer, instead fail for MSG_TRUNC or for EBUSY
NLMSG_ERR. Instead use a page sized buffer.
* sysdeps/unix/sysv/linux/check_pf.c (make_request): Use page sized
buffer.
2007-03-14 Richard Henderson <rth@redhat.com>
* sysdeps/alpha/fpu/s_llround.c: New file.

View File

@ -52,7 +52,7 @@ routines := htonl htons \
aux := check_pf ifreq
tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \
tst-gethnm test-ifaddrs bug-if1
tst-gethnm test-ifaddrs bug-if1 test-inet6_opt
include ../Rules

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2006 Free Software Foundation, Inc.
/* Copyright (C) 2006, 2007 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
@ -51,7 +51,7 @@ add_padding (uint8_t *extbuf, int offset, int npad)
{
if (npad == 1)
extbuf[offset] = IP6OPT_PAD1;
else
else if (npad > 0)
{
struct ip6_opt *pad_opt = (struct ip6_opt *) (extbuf + offset);
@ -102,21 +102,17 @@ inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
int data_offset = offset + sizeof (struct ip6_opt);
int npad = (align - data_offset % align) & (align - 1);
/* Now we can check whether the buffer is large enough. */
if (data_offset + npad + len > extlen)
return -1;
if (npad != 0)
{
if (extbuf != NULL)
add_padding (extbuf, offset, npad);
offset += npad;
}
/* Now prepare the option itself. */
if (extbuf != NULL)
{
/* Now we can check whether the buffer is large enough. */
if (data_offset + npad + len > extlen)
return -1;
add_padding (extbuf, offset, npad);
offset += npad;
/* Now prepare the option itself. */
struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
opt->ip6o_type = type;
@ -124,6 +120,8 @@ inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
*databufp = opt + 1;
}
else
offset += npad;
return offset + sizeof (struct ip6_opt) + len;
}
@ -145,12 +143,14 @@ inet6_opt_finish (void *extbuf, socklen_t extlen, int offset)
/* Required padding at the end. */
int npad = (8 - (offset & 7)) & 7;
/* Make sure the buffer is large enough. */
if (offset + npad > extlen)
return -1;
if (extbuf != NULL)
add_padding (extbuf, offset, npad);
{
/* Make sure the buffer is large enough. */
if (offset + npad > extlen)
return -1;
add_padding (extbuf, offset, npad);
}
return offset + npad;
}

207
inet/test-inet6_opt.c Normal file
View File

@ -0,0 +1,207 @@
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OPT_X 42
#define OPT_Y 43
#define OPT_Z 44
static void *
encode_inet6_opt (socklen_t *elp)
{
void *eb = NULL;
socklen_t el;
int cl;
void *db;
int offset;
uint8_t val1;
uint16_t val2;
uint32_t val4;
uint64_t val8;
*elp = 0;
#define CHECK() \
if (cl == -1) \
{ \
printf ("cl == -1 on line %d\n", __LINE__); \
free (eb); \
return NULL; \
}
/* Estimate the length */
cl = inet6_opt_init (NULL, 0);
CHECK ();
cl = inet6_opt_append (NULL, 0, cl, OPT_X, 12, 8, NULL);
CHECK ();
cl = inet6_opt_append (NULL, 0, cl, OPT_Y, 7, 4, NULL);
CHECK ();
cl = inet6_opt_append (NULL, 0, cl, OPT_Z, 7, 1, NULL);
CHECK ();
cl = inet6_opt_finish (NULL, 0, cl);
CHECK ();
el = cl;
eb = malloc (el + 8);
if (eb == NULL)
{
puts ("malloc failed");
return NULL;
}
/* Canary. */
memcpy (eb + el, "deadbeef", 8);
cl = inet6_opt_init (eb, el);
CHECK ();
cl = inet6_opt_append (eb, el, cl, OPT_X, 12, 8, &db);
CHECK ();
val4 = 0x12345678;
offset = inet6_opt_set_val (db, 0, &val4, sizeof (val4));
val8 = 0x0102030405060708LL;
inet6_opt_set_val (db, offset, &val8, sizeof (val8));
cl = inet6_opt_append (eb, el, cl, OPT_Y, 7, 4, &db);
CHECK ();
val1 = 0x01;
offset = inet6_opt_set_val (db, 0, &val1, sizeof (val1));
val2 = 0x1331;
offset = inet6_opt_set_val (db, offset, &val2, sizeof (val2));
val4 = 0x01020304;
inet6_opt_set_val (db, offset, &val4, sizeof (val4));
cl = inet6_opt_append (eb, el, cl, OPT_Z, 7, 1, &db);
CHECK ();
inet6_opt_set_val (db, 0, (void *) "abcdefg", 7);
cl = inet6_opt_finish (eb, el, cl);
CHECK ();
if (memcmp (eb + el, "deadbeef", 8) != 0)
{
puts ("Canary corrupted");
free (eb);
return NULL;
}
*elp = el;
return eb;
}
int
decode_inet6_opt (void *eb, socklen_t el)
{
int ret = 0;
int seq = 0;
int cl = 0;
int offset;
uint8_t type;
socklen_t len;
uint8_t val1;
uint16_t val2;
uint32_t val4;
uint64_t val8;
void *db;
char buf[8];
while ((cl = inet6_opt_next (eb, el, cl, &type, &len, &db)) != -1)
switch (type)
{
case OPT_X:
if (seq++ != 0)
{
puts ("OPT_X is not first");
ret = 1;
}
if (len != 12)
{
printf ("OPT_X's length %d != 12\n", len);
ret = 1;
}
offset = inet6_opt_get_val (db, 0, &val4, sizeof (val4));
if (val4 != 0x12345678)
{
printf ("OPT_X's val4 %x != 0x12345678\n", val4);
ret = 1;
}
offset = inet6_opt_get_val (db, offset, &val8, sizeof (val8));
if (offset != len || val8 != 0x0102030405060708LL)
{
printf ("OPT_X's val8 %llx != 0x0102030405060708\n",
(long long) val8);
ret = 1;
}
break;
case OPT_Y:
if (seq++ != 1)
{
puts ("OPT_Y is not second");
ret = 1;
}
if (len != 7)
{
printf ("OPT_Y's length %d != 7\n", len);
ret = 1;
}
offset = inet6_opt_get_val (db, 0, &val1, sizeof (val1));
if (val1 != 0x01)
{
printf ("OPT_Y's val1 %x != 0x01\n", val1);
ret = 1;
}
offset = inet6_opt_get_val (db, offset, &val2, sizeof (val2));
if (val2 != 0x1331)
{
printf ("OPT_Y's val2 %x != 0x1331\n", val2);
ret = 1;
}
offset = inet6_opt_get_val (db, offset, &val4, sizeof (val4));
if (offset != len || val4 != 0x01020304)
{
printf ("OPT_Y's val4 %x != 0x01020304\n", val4);
ret = 1;
}
break;
case OPT_Z:
if (seq++ != 2)
{
puts ("OPT_Z is not third");
ret = 1;
}
if (len != 7)
{
printf ("OPT_Z's length %d != 7\n", len);
ret = 1;
}
offset = inet6_opt_get_val (db, 0, buf, 7);
if (offset != len || memcmp (buf, "abcdefg", 7) != 0)
{
buf[7] = '\0';
printf ("OPT_Z's buf \"%s\" != \"abcdefg\"\n", buf);
ret = 1;
}
break;
default:
printf ("Unknown option %d\n", type);
ret = 1;
break;
}
if (seq != 3)
{
puts ("Didn't see all of OPT_X, OPT_Y and OPT_Z");
ret = 1;
}
return ret;
}
int
main (void)
{
void *eb;
socklen_t el;
eb = encode_inet6_opt (&el);
if (eb == NULL)
return 1;
if (decode_inet6_opt (eb, el))
return 1;
return 0;
}

View File

@ -1,5 +1,5 @@
/* Determine protocol families for which interfaces exist. Linux version.
Copyright (C) 2003, 2006 Free Software Foundation, Inc.
Copyright (C) 2003, 2006, 2007 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
@ -71,17 +71,38 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
memset (&nladdr, '\0', sizeof (nladdr));
nladdr.nl_family = AF_NETLINK;
#ifdef PAGE_SIZE
/* Help the compiler optimize out the malloc call if PAGE_SIZE
is constant and smaller or equal to PTHREAD_STACK_MIN/4. */
const size_t buf_size = PAGE_SIZE;
#else
const size_t buf_size = __getpagesize ();
#endif
bool use_malloc = false;
char *buf;
if (__libc_use_alloca (buf_size))
buf = alloca (buf_size);
else
{
buf = malloc (buf_size);
if (buf != NULL)
use_malloc = true;
else
goto out_fail;
}
struct iovec iov = { buf, buf_size };
if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
(struct sockaddr *) &nladdr,
sizeof (nladdr))) < 0)
return -1;
goto out_fail;
*seen_ipv4 = false;
*seen_ipv6 = false;
bool done = false;
char buf[4096];
struct iovec iov = { buf, sizeof (buf) };
struct in6ailist
{
struct in6addrinfo info;
@ -101,10 +122,10 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0));
if (read_len < 0)
return -1;
goto out_fail;
if (msg.msg_flags & MSG_TRUNC)
return -1;
goto out_fail;
struct nlmsghdr *nlmh;
for (nlmh = (struct nlmsghdr *) buf;
@ -186,7 +207,7 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
{
*in6ai = malloc (in6ailistlen * sizeof (**in6ai));
if (*in6ai == NULL)
return -1;
goto out_fail;
*in6ailen = in6ailistlen;
@ -198,6 +219,13 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
while (in6ailist != NULL);
}
if (use_malloc)
free (buf);
return 0;
out_fail:
if (use_malloc)
free (buf);
return 0;
}

View File

@ -122,37 +122,36 @@ int
__netlink_request (struct netlink_handle *h, int type)
{
struct netlink_res *nlm_next;
struct netlink_res **new_nlm_list;
static volatile size_t buf_size = 4096;
char *buf;
struct sockaddr_nl nladdr;
struct nlmsghdr *nlmh;
ssize_t read_len;
bool done = false;
#ifdef PAGE_SIZE
/* Help the compiler optimize out the malloc call if PAGE_SIZE
is constant and smaller or equal to PTHREAD_STACK_MIN/4. */
const size_t buf_size = PAGE_SIZE;
#else
const size_t buf_size = __getpagesize ();
#endif
bool use_malloc = false;
char *buf;
if (__netlink_sendreq (h, type) < 0)
return -1;
size_t this_buf_size = buf_size;
size_t orig_this_buf_size = this_buf_size;
if (__libc_use_alloca (this_buf_size))
buf = alloca (this_buf_size);
if (__libc_use_alloca (buf_size))
buf = alloca (buf_size);
else
{
buf = malloc (this_buf_size);
buf = malloc (buf_size);
if (buf != NULL)
use_malloc = true;
else
goto out_fail;
}
struct iovec iov = { buf, this_buf_size };
struct iovec iov = { buf, buf_size };
if (h->nlm_list != NULL)
new_nlm_list = &h->end_ptr->next;
else
new_nlm_list = &h->nlm_list;
if (__netlink_sendreq (h, type) < 0)
goto out_fail;
while (! done)
{
@ -172,48 +171,7 @@ __netlink_request (struct netlink_handle *h, int type)
continue;
if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0))
{
if (this_buf_size >= SIZE_MAX / 2)
goto out_fail;
nlm_next = *new_nlm_list;
while (nlm_next != NULL)
{
struct netlink_res *tmpptr;
tmpptr = nlm_next->next;
free (nlm_next);
nlm_next = tmpptr;
}
*new_nlm_list = NULL;
if (__libc_use_alloca (2 * this_buf_size))
buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size);
else
{
this_buf_size *= 2;
char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size);
if (new_buf == NULL)
goto out_fail;
buf = new_buf;
use_malloc = true;
}
buf_size = this_buf_size;
iov.iov_base = buf;
iov.iov_len = this_buf_size;
/* Increase sequence number, so that we can distinguish
between old and new request messages. */
h->seq++;
if (__netlink_sendreq (h, type) < 0)
goto out_fail;
continue;
}
goto out_fail;
size_t count = 0;
size_t remaining_len = read_len;
@ -237,36 +195,6 @@ __netlink_request (struct netlink_handle *h, int type)
struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh);
if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
errno = EIO;
else if (nlerr->error == -EBUSY
&& orig_this_buf_size != this_buf_size)
{
/* If EBUSY and MSG_TRUNC was seen, try again with a new
netlink socket. */
struct netlink_handle hold = *h;
if (__netlink_open (h) < 0)
{
*h = hold;
goto out_fail;
}
__netlink_close (&hold);
orig_this_buf_size = this_buf_size;
nlm_next = *new_nlm_list;
while (nlm_next != NULL)
{
struct netlink_res *tmpptr;
tmpptr = nlm_next->next;
free (nlm_next);
nlm_next = tmpptr;
}
*new_nlm_list = NULL;
count = 0;
h->seq++;
if (__netlink_sendreq (h, type) < 0)
goto out_fail;
break;
}
else
errno = -nlerr->error;
goto out_fail;