Skip to content

Commit

Permalink
[BZ #4181]
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Ulrich Drepper committed Mar 15, 2007
1 parent 02c9069 commit 6cb988f
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 114 deletions.
16 changes: 16 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion inet/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
36 changes: 18 additions & 18 deletions inet/inet6_opt.c
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -102,28 +102,26 @@ 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)
{
if (extbuf != NULL)
add_padding (extbuf, offset, npad);
/* 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. */
if (extbuf != NULL)
{
/* Now prepare the option itself. */
struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);

opt->ip6o_type = type;
opt->ip6o_len = len;

*databufp = opt + 1;
}
else
offset += npad;

return offset + sizeof (struct ip6_opt) + len;
}
Expand All @@ -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;
}
Expand Down
207 changes: 207 additions & 0 deletions inet/test-inet6_opt.c
Original file line number Diff line number Diff line change
@@ -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;
}
42 changes: 35 additions & 7 deletions sysdeps/unix/sysv/linux/check_pf.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down
Loading

0 comments on commit 6cb988f

Please sign in to comment.