From nobody Wed Jun 11 23:16:46 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4bHhPV4LrKz5ydVk; Wed, 11 Jun 2025 23:16:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4bHhPV2DPsz3L4g; Wed, 11 Jun 2025 23:16:46 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1749683806; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=RGzqYrm+z+gXk5eJAoelOaInM9FNxFRqYtTpt242eF4=; b=UK43taRZJUO9w3kLEpok3Bf341txKAnV34DBWeb5ZhxnZ8s1cMAvEdutgNicKqubPhCis+ raa9419inF3kVUrhZYcnyaPdAJfcF5sB6HpnLGz503EIzJGmrFNx7rfCt6VRIiwmK0h7lV QlEQDjdUwlqua4/8hNUaDuCuuFUPy4giPA6BHhYFSn8N/qp9Hv50pyvPfdQolx5uUwoRp/ +j6zJFdelOWctTHhBklaZVJlsN8Yy9H9TYwLnnJ1SpUWgPSSXbX7fiJFqDqTEv8xzwO7Nv 0k0XZCmqZymBs9lPA/UnQst87sdIBKTMh+uJkNnNaLlLDLgtH6oD9ye5zGIdXA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1749683806; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=RGzqYrm+z+gXk5eJAoelOaInM9FNxFRqYtTpt242eF4=; b=ME+8QsaymyEP9mQ2W8biEU+KRuEueURq5Kn0d27OziOqmDtN7uhIIcYauSmKQ3c+HpmNmS dtyQuwiccNIMdwiusP0qRvCbRfAJmg7/GFtQie11lzA9I5lMIKzFRsgyynZ2b7dD+HN7jQ /+HJaCIkdDjW/6HloHmTbIw1a5+sW/eyFZUqjqjXwR9DOv4VbaTUZtaJtww9wJ7hC0SSYg 3xF6xyuP4tiGbmtqf5PcHO5DgIebHDUDaPuxqpPrk2LrNErQ8DlZ7Mrfg45HLmFOOrn24x xzm6keBMEZZVNssdevoxS8aZQGkC90kf0f1QulXBTToZIxZxCoczh4dyo54PDw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1749683806; a=rsa-sha256; cv=none; b=cMiLYAQjCZBzS98O4L1IPJu9ixRdeJF4GWmKLL/KpmiPwnJKcDfX3jEwdgv4QAYl7AvOHh 98gWwrPL3iXfAeNCG7q/V/66Amh9uFDTAcE+gudRLnoA0DSZmbJrp5FgFh2xdpJX3nL3hO uSBEJuB/Bvh2eJbUs55we8bJcveSXP9yH0YqqS7jOFLF33qXF0cleq+tSNHQ1arXwpVBmq xN/Z/wMfO2At5lULmOqwR2p7b3L+hjIY2CW7p+96vW+2s+wxpme9gkBkn9NSWa7r0wFcOb 1GuiSSv0Cik3KusVHnpPk+QSXxNLHfH6buwPjMPpZbM0XXZAwIj3XqkxFwho4Q== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4bHhPV1n7Kzn6q; Wed, 11 Jun 2025 23:16:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 55BNGkOn046389; Wed, 11 Jun 2025 23:16:46 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 55BNGk9H046386; Wed, 11 Jun 2025 23:16:46 GMT (envelope-from git) Date: Wed, 11 Jun 2025 23:16:46 GMT Message-Id: <202506112316.55BNGk9H046386@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Warner Losh Subject: git: b0334450aa52 - main - include: ssp: fortify List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: imp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: b0334450aa527ccbac7a3c37d1ba75ef96c7be91 Auto-Submitted: auto-generated The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=b0334450aa527ccbac7a3c37d1ba75ef96c7be91 commit b0334450aa527ccbac7a3c37d1ba75ef96c7be91 Author: Ricardo Branco AuthorDate: 2025-05-18 17:55:23 +0000 Commit: Warner Losh CommitDate: 2025-06-11 23:16:22 +0000 include: ssp: fortify sig2str(3) Reviewed by: imp, kib, des, jilles Pull Request: https://github.com/freebsd/freebsd-src/pull/1696 Closes: https://github.com/freebsd/freebsd-src/pull/1696 --- include/signal.h | 4 + include/ssp/Makefile | 2 +- include/ssp/signal.h | 50 ++++ lib/libc/gen/sig2str.c | 3 +- lib/libc/tests/secure/Makefile | 1 + lib/libc/tests/secure/fortify_signal_test.c | 316 +++++++++++++++++++++++ lib/libc/tests/secure/generate-fortify-tests.lua | 12 + 7 files changed, 386 insertions(+), 2 deletions(-) diff --git a/include/signal.h b/include/signal.h index 22fefb63568f..9247aff56879 100644 --- a/include/signal.h +++ b/include/signal.h @@ -72,6 +72,10 @@ typedef __pthread_t pthread_t; #endif #endif /* __POSIX_VISIBLE || __XSI_VISIBLE */ +#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0 +#include +#endif + __BEGIN_DECLS int raise(int); diff --git a/include/ssp/Makefile b/include/ssp/Makefile index ff622aeecfe9..92e24dd4750a 100644 --- a/include/ssp/Makefile +++ b/include/ssp/Makefile @@ -1,4 +1,4 @@ -INCS= poll.h random.h socket.h ssp.h stdio.h stdlib.h string.h strings.h +INCS= poll.h random.h socket.h ssp.h signal.h stdio.h stdlib.h string.h strings.h INCS+= uio.h unistd.h wchar.h INCSDIR= ${INCLUDEDIR}/ssp diff --git a/include/ssp/signal.h b/include/ssp/signal.h new file mode 100644 index 000000000000..9f1a926b9db6 --- /dev/null +++ b/include/ssp/signal.h @@ -0,0 +1,50 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025, Ricardo Branco + * + * 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_SIGNAL_H_ +#define _SSP_SIGNAL_H_ + +#include + +#if __SSP_FORTIFY_LEVEL > 0 + +#include + +__BEGIN_DECLS + +__ssp_redirect_raw_impl(int, sig2str, sig2str, + (int signum, char *__restrict str)) +{ + if (__ssp_bos(str) < SIG2STR_MAX) + __chk_fail(); + + return (__ssp_real(sig2str)(signum, str)); +} + +__END_DECLS + +#endif /* __SSP_FORTIFY_LEVEL > 0 */ +#endif /* _SSP_SIGNAL_H_ */ diff --git a/lib/libc/gen/sig2str.c b/lib/libc/gen/sig2str.c index 9d0ede0f67fa..869a09ab9db4 100644 --- a/lib/libc/gen/sig2str.c +++ b/lib/libc/gen/sig2str.c @@ -35,13 +35,14 @@ #include #include #include +#include #include "un-namespace.h" static const char rtmin_str[] = "RTMIN"; static const char rtmax_str[] = "RTMAX"; int -sig2str(int signum, char *str) +__ssp_real(sig2str)(int signum, char *str) { if (signum <= 0 || signum > SIGRTMAX) return (-1); diff --git a/lib/libc/tests/secure/Makefile b/lib/libc/tests/secure/Makefile index beaa01457cfe..515f8f53a43e 100644 --- a/lib/libc/tests/secure/Makefile +++ b/lib/libc/tests/secure/Makefile @@ -10,6 +10,7 @@ FORTIFY_TCATS+= uio # non-sys/ headers FORTIFY_TCATS+= poll +FORTIFY_TCATS+= signal FORTIFY_TCATS+= stdlib FORTIFY_TCATS+= stdio FORTIFY_TCATS+= string diff --git a/lib/libc/tests/secure/fortify_signal_test.c b/lib/libc/tests/secure/fortify_signal_test.c new file mode 100644 index 000000000000..03cfb9a9a13a --- /dev/null +++ b/lib/libc/tests/secure/fortify_signal_test.c @@ -0,0 +1,316 @@ +/* @generated by `generate-fortify-tests.lua "signal"` */ + +#define _FORTIFY_SOURCE 2 +#define TMPFILE_SIZE (1024 * 32) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static FILE * __unused +new_fp(size_t __len) +{ + static char fpbuf[LINE_MAX]; + FILE *fp; + + ATF_REQUIRE(__len <= sizeof(fpbuf)); + + memset(fpbuf, 'A', sizeof(fpbuf) - 1); + fpbuf[sizeof(fpbuf) - 1] = '\0'; + + fp = fmemopen(fpbuf, sizeof(fpbuf), "rb"); + ATF_REQUIRE(fp != NULL); + + return (fp); +} + +/* + * Create a new symlink to use for readlink(2) style tests, we'll just use a + * random target name to have something interesting to look at. + */ +static const char * __unused +new_symlink(size_t __len) +{ + static const char linkname[] = "link"; + char target[MAXNAMLEN]; + int error; + + ATF_REQUIRE(__len <= sizeof(target)); + + arc4random_buf(target, sizeof(target)); + + error = unlink(linkname); + ATF_REQUIRE(error == 0 || errno == ENOENT); + + error = symlink(target, linkname); + ATF_REQUIRE(error == 0); + + 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. + */ +static int __unused +new_tmpfile(void) +{ + char buf[1024]; + ssize_t rv; + size_t written; + int fd; + + fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644); + ATF_REQUIRE(fd >= 0); + + written = 0; + while (written < TMPFILE_SIZE) { + rv = write(fd, buf, sizeof(buf)); + ATF_REQUIRE(rv > 0); + + written += rv; + } + + ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET)); + return (fd); +} + +static void +disable_coredumps(void) +{ + struct rlimit rl = { 0 }; + + if (setrlimit(RLIMIT_CORE, &rl) == -1) + _exit(EX_OSERR); +} + +/* + * Replaces stdin with a file that we can actually read from, for tests where + * we want a FILE * or fd that we can get data from. + */ +static void __unused +replace_stdin(void) +{ + int fd; + + fd = new_tmpfile(); + + (void)dup2(fd, STDIN_FILENO); + if (fd != STDIN_FILENO) + close(fd); +} + +ATF_TC(sig2str_before_end); +ATF_TC_HEAD(sig2str_before_end, tc) +{ +} +ATF_TC_BODY(sig2str_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[SIG2STR_MAX + 1]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = SIG2STR_MAX + 1; + const size_t __idx __unused = __len - 1; + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_end); +ATF_TC_HEAD(sig2str_end, tc) +{ +} +ATF_TC_BODY(sig2str_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[SIG2STR_MAX]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = SIG2STR_MAX; + const size_t __idx __unused = __len - 1; + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_heap_before_end); +ATF_TC_HEAD(sig2str_heap_before_end, tc) +{ +} +ATF_TC_BODY(sig2str_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX + 1); + const size_t __len = SIG2STR_MAX + 1; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_heap_end); +ATF_TC_HEAD(sig2str_heap_end, tc) +{ +} +ATF_TC_BODY(sig2str_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX); + const size_t __len = SIG2STR_MAX; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_heap_after_end); +ATF_TC_HEAD(sig2str_heap_after_end, tc) +{ +} +ATF_TC_BODY(sig2str_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX - 1); + const size_t __len = SIG2STR_MAX - 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + sig2str(1, __stack.__buf); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, sig2str_before_end); + ATF_TP_ADD_TC(tp, sig2str_end); + ATF_TP_ADD_TC(tp, sig2str_heap_before_end); + ATF_TP_ADD_TC(tp, sig2str_heap_end); + ATF_TP_ADD_TC(tp, sig2str_heap_after_end); + return (atf_no_error()); +} diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua index 6c2a80b20609..c9cd9353a869 100755 --- a/lib/libc/tests/secure/generate-fortify-tests.lua +++ b/lib/libc/tests/secure/generate-fortify-tests.lua @@ -478,6 +478,18 @@ local all_tests = { init = poll_init, }, }, + signal = { + -- + { + func = "sig2str", + bufsize = "SIG2STR_MAX", + arguments = { + "1", + "__buf", + }, + exclude = excludes_stack_overflow, + }, + }, stdio = { -- {