Message ID | af04d8c92a793a8f02ed3fa2039247e6687a429a.1642148513.git.fweimer@redhat.com |
---|---|
State | New |
Headers | show |
Series | CVE-2022-23218, CVE-2022-23219: sunrpc buffer overflows | expand |
On 14/01/2022 13:53, Florian Weimer via Libc-alpha wrote: > --- > include/sys/un.h | 12 +++++++ > socket/Makefile | 6 +++- > socket/sockaddr_un_set.c | 41 ++++++++++++++++++++++++ > socket/tst-sockaddr_un_set.c | 62 ++++++++++++++++++++++++++++++++++++ > 4 files changed, 120 insertions(+), 1 deletion(-) > create mode 100644 socket/sockaddr_un_set.c > create mode 100644 socket/tst-sockaddr_un_set.c LGTM. Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org> > > diff --git a/include/sys/un.h b/include/sys/un.h > index bdbee99980..152afd9fc7 100644 > --- a/include/sys/un.h > +++ b/include/sys/un.h > @@ -1 +1,13 @@ > #include <socket/sys/un.h> > + > +#ifndef _ISOMAC > + > +/* Set ADDR->sun_family to AF_UNIX and ADDR->sun_path to PATHNAME. > + Return 0 on success or -1 on failure (due to overlong PATHNAME). > + The caller should always use sizeof (struct sockaddr_un) as the > + socket address length, disregaring the length of PATHNAME. > + Only concrete (non-abstract) pathnames are supported. */ > +int __sockaddr_un_set (struct sockaddr_un *addr, const char *pathname) > + attribute_hidden; > + > +#endif /* _ISOMAC */ > diff --git a/socket/Makefile b/socket/Makefile > index 39333e10ca..156eec6c85 100644 > --- a/socket/Makefile > +++ b/socket/Makefile > @@ -29,13 +29,17 @@ headers := sys/socket.h sys/un.h bits/sockaddr.h bits/socket.h \ > routines := accept bind connect getpeername getsockname getsockopt \ > listen recv recvfrom recvmsg send sendmsg sendto \ > setsockopt shutdown socket socketpair isfdtype opensock \ > - sockatmark accept4 recvmmsg sendmmsg > + sockatmark accept4 recvmmsg sendmmsg sockaddr_un_set > > tests := \ > tst-accept4 \ > tst-sockopt \ > # tests > > +tests-internal := \ > + tst-sockaddr_un_set \ > + # tests-internal > + > tests-time64 := \ > tst-sockopt-time64 \ > # tests > diff --git a/socket/sockaddr_un_set.c b/socket/sockaddr_un_set.c > new file mode 100644 > index 0000000000..0bd40dc34e > --- /dev/null > +++ b/socket/sockaddr_un_set.c > @@ -0,0 +1,41 @@ > +/* Set the sun_path member of struct sockaddr_un. > + 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 > + <https://www.gnu.org/licenses/>. */ > + > +#include <errno.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <sys/un.h> > + > +int > +__sockaddr_un_set (struct sockaddr_un *addr, const char *pathname) > +{ > + size_t name_length = strlen (pathname); > + > + /* The kernel supports names of exactly sizeof (addr->sun_path) > + bytes, without a null terminator, but userspace does not; see the > + SUN_LEN macro. */ > + if (name_length >= sizeof (addr->sun_path)) > + { > + __set_errno (EINVAL); /* Error code used by the kernel. */ > + return -1; > + } > + > + addr->sun_family = AF_UNIX; > + memcpy (addr->sun_path, pathname, name_length + 1); > + return 0; > +} > diff --git a/socket/tst-sockaddr_un_set.c b/socket/tst-sockaddr_un_set.c > new file mode 100644 > index 0000000000..29c2a81afd > --- /dev/null > +++ b/socket/tst-sockaddr_un_set.c > @@ -0,0 +1,62 @@ > +/* Test the __sockaddr_un_set function. > + 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 > + <https://www.gnu.org/licenses/>. */ > + > +/* Re-compile the function because the version in libc is not > + exported. */ > +#include "sockaddr_un_set.c" > + > +#include <support/check.h> > + > +static int > +do_test (void) > +{ > + struct sockaddr_un sun; > + > + memset (&sun, 0xcc, sizeof (sun)); > + __sockaddr_un_set (&sun, ""); > + TEST_COMPARE (sun.sun_family, AF_UNIX); > + TEST_COMPARE (__sockaddr_un_set (&sun, ""), 0); > + > + memset (&sun, 0xcc, sizeof (sun)); > + TEST_COMPARE (__sockaddr_un_set (&sun, "/example"), 0); > + TEST_COMPARE_STRING (sun.sun_path, "/example"); > + > + { > + char pathname[108]; /* Length of sun_path (ABI constant). */ > + memset (pathname, 'x', sizeof (pathname)); > + pathname[sizeof (pathname) - 1] = '\0'; > + memset (&sun, 0xcc, sizeof (sun)); > + TEST_COMPARE (__sockaddr_un_set (&sun, pathname), 0); > + TEST_COMPARE (sun.sun_family, AF_UNIX); > + TEST_COMPARE_STRING (sun.sun_path, pathname); > + } > + > + { > + char pathname[109]; > + memset (pathname, 'x', sizeof (pathname)); > + pathname[sizeof (pathname) - 1] = '\0'; > + memset (&sun, 0xcc, sizeof (sun)); > + errno = 0; > + TEST_COMPARE (__sockaddr_un_set (&sun, pathname), -1); > + TEST_COMPARE (errno, EINVAL); > + } > + > + return 0; > +} > + > +#include <support/test-driver.c>
diff --git a/include/sys/un.h b/include/sys/un.h index bdbee99980..152afd9fc7 100644 --- a/include/sys/un.h +++ b/include/sys/un.h @@ -1 +1,13 @@ #include <socket/sys/un.h> + +#ifndef _ISOMAC + +/* Set ADDR->sun_family to AF_UNIX and ADDR->sun_path to PATHNAME. + Return 0 on success or -1 on failure (due to overlong PATHNAME). + The caller should always use sizeof (struct sockaddr_un) as the + socket address length, disregaring the length of PATHNAME. + Only concrete (non-abstract) pathnames are supported. */ +int __sockaddr_un_set (struct sockaddr_un *addr, const char *pathname) + attribute_hidden; + +#endif /* _ISOMAC */ diff --git a/socket/Makefile b/socket/Makefile index 39333e10ca..156eec6c85 100644 --- a/socket/Makefile +++ b/socket/Makefile @@ -29,13 +29,17 @@ headers := sys/socket.h sys/un.h bits/sockaddr.h bits/socket.h \ routines := accept bind connect getpeername getsockname getsockopt \ listen recv recvfrom recvmsg send sendmsg sendto \ setsockopt shutdown socket socketpair isfdtype opensock \ - sockatmark accept4 recvmmsg sendmmsg + sockatmark accept4 recvmmsg sendmmsg sockaddr_un_set tests := \ tst-accept4 \ tst-sockopt \ # tests +tests-internal := \ + tst-sockaddr_un_set \ + # tests-internal + tests-time64 := \ tst-sockopt-time64 \ # tests diff --git a/socket/sockaddr_un_set.c b/socket/sockaddr_un_set.c new file mode 100644 index 0000000000..0bd40dc34e --- /dev/null +++ b/socket/sockaddr_un_set.c @@ -0,0 +1,41 @@ +/* Set the sun_path member of struct sockaddr_un. + 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 + <https://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> + +int +__sockaddr_un_set (struct sockaddr_un *addr, const char *pathname) +{ + size_t name_length = strlen (pathname); + + /* The kernel supports names of exactly sizeof (addr->sun_path) + bytes, without a null terminator, but userspace does not; see the + SUN_LEN macro. */ + if (name_length >= sizeof (addr->sun_path)) + { + __set_errno (EINVAL); /* Error code used by the kernel. */ + return -1; + } + + addr->sun_family = AF_UNIX; + memcpy (addr->sun_path, pathname, name_length + 1); + return 0; +} diff --git a/socket/tst-sockaddr_un_set.c b/socket/tst-sockaddr_un_set.c new file mode 100644 index 0000000000..29c2a81afd --- /dev/null +++ b/socket/tst-sockaddr_un_set.c @@ -0,0 +1,62 @@ +/* Test the __sockaddr_un_set function. + 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 + <https://www.gnu.org/licenses/>. */ + +/* Re-compile the function because the version in libc is not + exported. */ +#include "sockaddr_un_set.c" + +#include <support/check.h> + +static int +do_test (void) +{ + struct sockaddr_un sun; + + memset (&sun, 0xcc, sizeof (sun)); + __sockaddr_un_set (&sun, ""); + TEST_COMPARE (sun.sun_family, AF_UNIX); + TEST_COMPARE (__sockaddr_un_set (&sun, ""), 0); + + memset (&sun, 0xcc, sizeof (sun)); + TEST_COMPARE (__sockaddr_un_set (&sun, "/example"), 0); + TEST_COMPARE_STRING (sun.sun_path, "/example"); + + { + char pathname[108]; /* Length of sun_path (ABI constant). */ + memset (pathname, 'x', sizeof (pathname)); + pathname[sizeof (pathname) - 1] = '\0'; + memset (&sun, 0xcc, sizeof (sun)); + TEST_COMPARE (__sockaddr_un_set (&sun, pathname), 0); + TEST_COMPARE (sun.sun_family, AF_UNIX); + TEST_COMPARE_STRING (sun.sun_path, pathname); + } + + { + char pathname[109]; + memset (pathname, 'x', sizeof (pathname)); + pathname[sizeof (pathname) - 1] = '\0'; + memset (&sun, 0xcc, sizeof (sun)); + errno = 0; + TEST_COMPARE (__sockaddr_un_set (&sun, pathname), -1); + TEST_COMPARE (errno, EINVAL); + } + + return 0; +} + +#include <support/test-driver.c>