/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2026 The FreeBSD Foundation */ #include #include #include #include #include #include #include #include #define FMT_ERR(s) s ": %s", strerror(errno) ATF_TC(mmap_offset_overflow); ATF_TC_HEAD(mmap_offset_overflow, tc) { atf_tc_set_md_var(tc, "descr", "mmap offset overflow test"); atf_tc_set_md_var(tc, "require.kmods", "snd_dummy"); } ATF_TC_BODY(mmap_offset_overflow, tc) { uint8_t *buf; off_t off; size_t len; int fd; fd = open("/dev/dsp.dummy", O_RDWR); ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open")); /* off + len will overflow and wrap back to 0. */ off = 0xfffffffffffff000; len = 0x1000; buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off); ATF_REQUIRE_MSG(buf == MAP_FAILED, FMT_ERR("mmap")); munmap(buf, len); close(fd); } /* * Verify that a MAP_SHARED mapping of a DSP device's software buffer remains * valid after the file descriptor is closed. */ ATF_TC(mmap_buffer_lifetime); ATF_TC_HEAD(mmap_buffer_lifetime, tc) { atf_tc_set_md_var(tc, "descr", "mmap data survives close()"); atf_tc_set_md_var(tc, "require.kmods", "snd_dummy"); } ATF_TC_BODY(mmap_buffer_lifetime, tc) { audio_buf_info abi; uint8_t *buf; size_t len; int fd, arg; fd = open("/dev/dsp.dummy", O_RDWR); ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open")); arg = (2 << 16) | 14; /* 2*16KB */ ATF_REQUIRE_MSG(ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) == 0, FMT_ERR("SNDCTL_DSP_SETFRAGMENT")); ATF_REQUIRE_MSG(ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi) == 0, FMT_ERR("SNDCTL_DSP_GETOSPACE")); len = abi.bytes; ATF_REQUIRE_MSG(len >= PAGE_SIZE, "buffer too small: %zu", len); buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ATF_REQUIRE_MSG(buf != MAP_FAILED, FMT_ERR("mmap")); for (size_t i = 0; i < len; i++) { ATF_REQUIRE_MSG(buf[i] == 0, "mmap data corrupted at offset %zu: want 0 got 0x%02x", i, buf[i]); } memset(buf, 0xa5, len); for (size_t i = 0; i < len; i++) { ATF_REQUIRE_MSG(buf[i] == 0xa5, "mmap data corrupted at offset %zu: want 0xa5 got 0x%02x", i, buf[i]); } ATF_REQUIRE(close(fd) == 0); /* Closing the device causes the buffer to be reset. */ for (size_t i = 0; i < len; i++) { ATF_REQUIRE_MSG(buf[i] == 0 || buf[i] == 0xa5, "mmap data corrupted at offset %zu: got 0x%02x", i, buf[i]); } memset(buf, 0xa5, len); ATF_REQUIRE(munmap(buf, len) == 0); } ATF_TC(mmap_reject_prot_exec); ATF_TC_HEAD(mmap_reject_prot_exec, tc) { atf_tc_set_md_var(tc, "descr", "mmap PROT_EXEC rejection test"); atf_tc_set_md_var(tc, "require.kmods", "snd_dummy"); } ATF_TC_BODY(mmap_reject_prot_exec, tc) { uint8_t *buf; size_t len; int fd; fd = open("/dev/dsp.dummy", O_RDWR); ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open")); len = 8; buf = mmap(NULL, len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); ATF_REQUIRE_MSG(buf == MAP_FAILED, FMT_ERR("mmap")); munmap(buf, len); close(fd); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, mmap_offset_overflow); ATF_TP_ADD_TC(tp, mmap_buffer_lifetime); ATF_TP_ADD_TC(tp, mmap_reject_prot_exec); return (atf_no_error()); }