Skip to content

Commit

Permalink
include: ssp: fortify <sys/socket.h>
Browse files Browse the repository at this point in the history
The entire recv*() implementation set is ripe for opportunities to
validate, so do what we can with what we have.

Reviewed by:	markj
Sponsored by:	Klara, Inc.
Sponsored by:	Stormshield
Differential Revision:	https://reviews.freebsd.org/D45686
  • Loading branch information
kevans91 committed Jul 13, 2024
1 parent 2aba0ee commit 1f155d4
Show file tree
Hide file tree
Showing 20 changed files with 2,641 additions and 7 deletions.
4 changes: 2 additions & 2 deletions include/ssp/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
INCS= poll.h random.h ssp.h stdio.h stdlib.h string.h strings.h uio.h unistd.h
INCS+= wchar.h
INCS= poll.h random.h socket.h ssp.h stdio.h stdlib.h string.h strings.h
INCS+= uio.h unistd.h wchar.h
INCSDIR= ${INCLUDEDIR}/ssp

.include <bsd.prog.mk>
119 changes: 119 additions & 0 deletions include/ssp/socket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024, Klara, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SSP_SOCKET_H_
#define _SSP_SOCKET_H_

#include <ssp/ssp.h>

#if __SSP_FORTIFY_LEVEL > 0

#include <sys/_null.h>

__BEGIN_DECLS

__ssp_inline void
__ssp_check_msghdr(struct msghdr *hdr)
{
if (__ssp_bos(hdr->msg_name) < hdr->msg_namelen)
__chk_fail();

__ssp_check_iovec(hdr->msg_iov, hdr->msg_iovlen);

if (__ssp_bos(hdr->msg_control) < hdr->msg_controllen)
__chk_fail();
}

__ssp_redirect_raw_impl(int, getpeername, getpeername,
(int fdes, struct sockaddr *__restrict name, socklen_t *__restrict namelen))
{
size_t namesz = __ssp_bos(name);

if (namesz != (size_t)-1 && namesz < *namelen)
__chk_fail();

return (__ssp_real(getpeername)(fdes, name, namelen));
}

__ssp_redirect_raw_impl(int, getsockname, getsockname,
(int fdes, struct sockaddr *__restrict name,
socklen_t *__restrict namelen))
{
size_t namesz = __ssp_bos(name);

if (namesz != (size_t)-1 && namesz < *namelen)
__chk_fail();

return (__ssp_real(getsockname)(fdes, name, namelen));
}

__ssp_redirect(ssize_t, recv, (int __sock, void *__buf, size_t __len,
int __flags), (__sock, __buf, __len, __flags));

__ssp_redirect_raw_impl(ssize_t, recvfrom, recvfrom,
(int s, void *buf, size_t len, int flags,
struct sockaddr *__restrict from,
socklen_t *__restrict fromlen))
{
if (__ssp_bos(buf) < len)
__chk_fail();
if (from != NULL && __ssp_bos(from) < *fromlen)
__chk_fail();

return (__ssp_real(recvfrom)(s, buf, len, flags, from, fromlen));
}

__ssp_redirect_raw_impl(ssize_t, recvmsg, recvmsg,
(int s, struct msghdr *hdr, int flags))
{
__ssp_check_msghdr(hdr);
return (__ssp_real(recvmsg)(s, hdr, flags));
}

#if __BSD_VISIBLE
struct timespec;

__ssp_redirect_raw_impl(ssize_t, recvmmsg, recvmmsg,
(int s, struct mmsghdr *__restrict hdrvec, size_t vlen, int flags,
const struct timespec *__restrict timeout))
{
const size_t vecsz = __ssp_bos(hdrvec);

if (vecsz != (size_t)-1 && vecsz / sizeof(*hdrvec) < vlen)
__chk_fail();

for (size_t i = 0; i < vlen; i++) {
__ssp_check_msghdr(&hdrvec[i].msg_hdr);
}

return (__ssp_real(recvmmsg)(s, hdrvec, vlen, flags, timeout));
}
#endif

__END_DECLS

#endif /* __SSP_FORTIFY_LEVEL > 0 */
#endif /* _SSP_SOCKET_H_ */
3 changes: 2 additions & 1 deletion lib/libc/sys/recv.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@

#include <sys/types.h>
#include <sys/socket.h>
#include <ssp/ssp.h>
#include "libc_private.h"

#include <stddef.h>

ssize_t
recv(int s, void *buf, size_t len, int flags)
__ssp_real(recv)(int s, void *buf, size_t len, int flags)
{
/*
* POSIX says recv() shall be a cancellation point, so call the
Expand Down
3 changes: 2 additions & 1 deletion lib/libc/sys/recvfrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <ssp/ssp.h>
#include "libc_private.h"

__weak_reference(__sys_recvfrom, __recvfrom);

#pragma weak recvfrom
ssize_t
recvfrom(int s, void *buf, size_t len, int flags,
__ssp_real(recvfrom)(int s, void *buf, size_t len, int flags,
struct sockaddr * __restrict from, socklen_t * __restrict fromlen)
{
return (INTERPOS_SYS(recvfrom, s, buf, len, flags, from, fromlen));
Expand Down
3 changes: 2 additions & 1 deletion lib/libc/sys/recvmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <ssp/ssp.h>
#include "libc_private.h"

__weak_reference(__sys_recvmsg, __recvmsg);

#pragma weak recvmsg
ssize_t
recvmsg(int s, struct msghdr *msg, int flags)
__ssp_real(recvmsg)(int s, struct msghdr *msg, int flags)
{
return (INTERPOS_SYS(recvmsg, s, msg, flags));
}
1 change: 1 addition & 0 deletions lib/libc/tests/secure/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/}
# sys/ headers
FORTIFY_TCATS+= random
FORTIFY_TCATS+= select
FORTIFY_TCATS+= socket
FORTIFY_TCATS+= uio

# non-sys/ headers
Expand Down
45 changes: 45 additions & 0 deletions lib/libc/tests/secure/fortify_poll_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <sys/random.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/wait.h>
Expand Down Expand Up @@ -66,6 +67,50 @@ new_symlink(size_t __len)
return (linkname);
}

/*
* For our purposes, first descriptor will be the reader; we'll send both
* raw data and a control message over it so that the result can be used for
* any of our recv*() tests.
*/
static void __unused
new_socket(int sock[2])
{
unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
static char sockbuf[256];
ssize_t rv;
size_t total = 0;
struct msghdr hdr = { 0 };
struct cmsghdr *cmsg;
int error, fd;

error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
ATF_REQUIRE(error == 0);

while (total != sizeof(sockbuf)) {
rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);

ATF_REQUIRE_MSG(rv > 0,
"expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
"%zd exceeds total %zu", rv, sizeof(sockbuf));
total += rv;
}

hdr.msg_control = ctrl;
hdr.msg_controllen = sizeof(ctrl);

cmsg = CMSG_FIRSTHDR(&hdr);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
fd = STDIN_FILENO;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));

error = sendmsg(sock[1], &hdr, 0);
ATF_REQUIRE(error != -1);
}

/*
* Constructs a tmpfile that we can use for testing read(2) and friends.
*/
Expand Down
45 changes: 45 additions & 0 deletions lib/libc/tests/secure/fortify_random_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <sys/random.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/wait.h>
Expand Down Expand Up @@ -66,6 +67,50 @@ new_symlink(size_t __len)
return (linkname);
}

/*
* For our purposes, first descriptor will be the reader; we'll send both
* raw data and a control message over it so that the result can be used for
* any of our recv*() tests.
*/
static void __unused
new_socket(int sock[2])
{
unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
static char sockbuf[256];
ssize_t rv;
size_t total = 0;
struct msghdr hdr = { 0 };
struct cmsghdr *cmsg;
int error, fd;

error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
ATF_REQUIRE(error == 0);

while (total != sizeof(sockbuf)) {
rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);

ATF_REQUIRE_MSG(rv > 0,
"expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
"%zd exceeds total %zu", rv, sizeof(sockbuf));
total += rv;
}

hdr.msg_control = ctrl;
hdr.msg_controllen = sizeof(ctrl);

cmsg = CMSG_FIRSTHDR(&hdr);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
fd = STDIN_FILENO;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));

error = sendmsg(sock[1], &hdr, 0);
ATF_REQUIRE(error != -1);
}

/*
* Constructs a tmpfile that we can use for testing read(2) and friends.
*/
Expand Down
45 changes: 45 additions & 0 deletions lib/libc/tests/secure/fortify_select_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <sys/random.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/wait.h>
Expand Down Expand Up @@ -66,6 +67,50 @@ new_symlink(size_t __len)
return (linkname);
}

/*
* For our purposes, first descriptor will be the reader; we'll send both
* raw data and a control message over it so that the result can be used for
* any of our recv*() tests.
*/
static void __unused
new_socket(int sock[2])
{
unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
static char sockbuf[256];
ssize_t rv;
size_t total = 0;
struct msghdr hdr = { 0 };
struct cmsghdr *cmsg;
int error, fd;

error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
ATF_REQUIRE(error == 0);

while (total != sizeof(sockbuf)) {
rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);

ATF_REQUIRE_MSG(rv > 0,
"expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
"%zd exceeds total %zu", rv, sizeof(sockbuf));
total += rv;
}

hdr.msg_control = ctrl;
hdr.msg_controllen = sizeof(ctrl);

cmsg = CMSG_FIRSTHDR(&hdr);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
fd = STDIN_FILENO;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));

error = sendmsg(sock[1], &hdr, 0);
ATF_REQUIRE(error != -1);
}

/*
* Constructs a tmpfile that we can use for testing read(2) and friends.
*/
Expand Down
Loading

0 comments on commit 1f155d4

Please sign in to comment.