From: Sean Christopherson <seanjc@google.com>
To: Ackerley Tng <ackerleytng@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
Christian Borntraeger <borntraeger@linux.ibm.com>,
Janosch Frank <frankja@linux.ibm.com>,
Claudio Imbrenda <imbrenda@linux.ibm.com>,
kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
David Hildenbrand <david@redhat.com>,
Fuad Tabba <tabba@google.com>
Subject: Re: [PATCH 6/6] KVM: selftests: Verify that faulting in private guest_memfd memory fails
Date: Tue, 30 Sep 2025 07:58:15 -0700 [thread overview]
Message-ID: <aNvwB2fr2p45hhC0@google.com> (raw)
In-Reply-To: <diqza52c1im6.fsf@google.com>
On Tue, Sep 30, 2025, Ackerley Tng wrote:
> Sean Christopherson <seanjc@google.com> writes:
> > How's this look?
> >
> > static void test_fault_sigbus(int fd, size_t accessible_size, size_t mmap_size)
> > {
> > struct sigaction sa_old, sa_new = {
> > .sa_handler = fault_sigbus_handler,
> > };
> > const uint8_t val = 0xaa;
> > uint8_t *mem;
> > size_t i;
> >
> > mem = kvm_mmap(mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
> >
> > sigaction(SIGBUS, &sa_new, &sa_old);
> > if (sigsetjmp(jmpbuf, 1) == 0) {
> > memset(mem, val, mmap_size);
> > TEST_FAIL("memset() should have triggered SIGBUS");
> > }
> > if (sigsetjmp(jmpbuf, 1) == 0) {
> > (void)READ_ONCE(mem[accessible_size]);
> > TEST_FAIL("load at first unaccessible byte should have triggered SIGBUS");
> > }
> > sigaction(SIGBUS, &sa_old, NULL);
> >
> > for (i = 0; i < accessible_size; i++)
> > TEST_ASSERT_EQ(READ_ONCE(mem[i]), val);
> >
> > kvm_munmap(mem, mmap_size);
> > }
> >
> > static void test_fault_overflow(int fd, size_t total_size)
> > {
> > test_fault_sigbus(fd, total_size, total_size * 4);
> > }
> >
>
> Is it intentional that the same SIGBUS on offset mem + total_size is
> triggered twice? The memset would have worked fine until offset mem +
> total_size, which is the same SIGBUS case as mem[accessible_size]. Or
> was it meant to test that both read and write trigger SIGBUS?
The latter (test both read and write). I plan on adding this in a separate
commit, i.e. it should be obvious in the actual patches.
> > static void test_fault_private(int fd, size_t total_size)
> > {
> > test_fault_sigbus(fd, 0, total_size);
> > }
> >
>
> I would prefer more unrolling to avoid mental hoops within test code,
> perhaps like (not compile tested):
>
> static void assert_host_fault_sigbus(uint8_t *mem)
> {
> struct sigaction sa_old, sa_new = {
> .sa_handler = fault_sigbus_handler,
> };
>
> sigaction(SIGBUS, &sa_new, &sa_old);
> if (sigsetjmp(jmpbuf, 1) == 0) {
> (void)READ_ONCE(*mem);
> TEST_FAIL("Reading %p should have triggered SIGBUS", mem);
> }
> sigaction(SIGBUS, &sa_old, NULL);
> }
>
> static void test_fault_overflow(int fd, size_t total_size)
> {
> uint8_t *mem = kvm_mmap(total_size * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
> int i;
>
> for (i = 0; i < total_size; i++)
> TEST_ASSERT_EQ(READ_ONCE(mem[i]), val);
>
> assert_host_fault_sigbus(mem + total_size);
>
> kvm_munmap(mem, mmap_size);
> }
>
> static void test_fault_private(int fd, size_t total_size)
> {
> uint8_t *mem = kvm_mmap(total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
> int i;
>
> assert_host_fault_sigbus(mem);
>
> kvm_munmap(mem, mmap_size);
> }
Why? That loses coverage for read to private memory getting SIBUGS. I genuinely
don't understand the desire to copy+paste uninteresting code.
> assert_host_fault_sigbus() can then be flexibly reused for conversion
assert_host_fault_sigbus() is a misleading name in the sense that it suggests
that the _only_ thing the helper does is assert that a SIGBUS occurred. It's
not at all obvious that there's a write to "mem" in there.
> tests (coming up) at various offsets from the mmap()-ed addresses.
>
> At some point, sigaction, sigsetjmp, etc could perhaps even be further
> wrapped. For testing memory_failure() for guest_memfd we will want to
> check for SIGBUS on memory failure injection instead of on host fault.
>
> Would be nice if it looked like this (maybe not in this patch series):
>
> + TEST_ASSERT_WILL_SIGBUS(READ_ONCE(mem[i]))
> + TEST_ASSERT_WILL_SIGBUS(WRITE_ONCE(mem[i]))
> + TEST_ASSERT_WILL_SIGBUS(madvise(MADV_HWPOISON))
Ooh, me likey. Definitely can do it now. Using a macro means we can print out
the actual action that didn't generate a SIGUBS, e.g. hacking the test to read
byte 0 generates:
'(void)READ_ONCE(mem[0])' should have triggered SIGBUS
Hmm, how about TEST_EXPECT_SIGBUS? TEST_ASSERT_xxx() typically asserts on a
value, i.e. on the result of a previous action. And s/WILL/EXPECT to make it
clear that the action is expected to SIGBUS _now_.
And if we use a descriptive global variable, we can extract the macro to e.g.
test_util.h or kvm_util.h (not sure we want to do that right away; probably best
left to the future).
static sigjmp_buf expect_sigbus_jmpbuf;
void fault_sigbus_handler(int signum)
{
siglongjmp(expect_sigbus_jmpbuf, 1);
}
#define TEST_EXPECT_SIGBUS(action) \
do { \
struct sigaction sa_old, sa_new = { \
.sa_handler = fault_sigbus_handler, \
}; \
\
sigaction(SIGBUS, &sa_new, &sa_old); \
if (sigsetjmp(expect_sigbus_jmpbuf, 1) == 0) { \
action; \
TEST_FAIL("'%s' should have triggered SIGBUS", #action); \
} \
sigaction(SIGBUS, &sa_old, NULL); \
} while (0)
static void test_fault_sigbus(int fd, size_t accessible_size, size_t map_size)
{
const char val = 0xaa;
char *mem;
size_t i;
mem = kvm_mmap(map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
TEST_EXPECT_SIGBUS(memset(mem, val, map_size));
TEST_EXPECT_SIGBUS((void)READ_ONCE(mem[accessible_size]));
for (i = 0; i < accessible_size; i++)
TEST_ASSERT_EQ(READ_ONCE(mem[i]), val);
kvm_munmap(mem, map_size);
}
> >> If split up as described above, this could be
> >>
> >> if (flags & GUEST_MEMFD_FLAG_MMAP &&
> >> flags & GUEST_MEMFD_FLAG_DEFAULT_SHARED) {
> >> gmem_test(mmap_supported_fault_supported, vm, flags);
> >> gmem_test(fault_overflow, vm, flags);
> >> } else if (flags & GUEST_MEMFD_FLAG_MMAP) {
> >> gmem_test(mmap_supported_fault_sigbus, vm, flags);
> >
> > I find these unintuitive, e.g. is this one "mmap() supported, test fault sigbus",
> > or is it "mmap(), test supported fault sigbus". I also don't like that some of
> > the test names describe the _result_ (SIBGUS), where as others describe _what_
> > is being tested.
> >
>
> I think of the result (SIGBUS) as part of what's being tested. So
> test_supported_fault_sigbus() is testing that mmap is supported, and
> faulting will result in a SIGBUS.
For an utility helper, e.g. test_fault_sigbus(), or test_write_sigbus(), that's
a-ok. But it doesn't work for the top-level test functions because trying to
follow that pattern effectively prevents bundling multiple individual testcases,
e.g. test_fallocate() becomes what? And test_invalid_punch_hole_einval() is
quite obnoxious.
> > In general, I don't like test names that describe the result, because IMO what
> > is being tested is far more interesting. E.g. from a test coverage persective,
> > I don't care if attempting to fault in (CoCO) private memory gets SIGBUS versus
> > SIGSEGV, but I most definitely care that we have test coverage for the "what".
> >
>
> The SIGBUS is part of the contract with userspace and that's also part
> of what's being tested IMO.
I don't disagree, but IMO bleeding those details into the top-level functions
isn't necessary. Random developer that comes along isn't going to care whether
KVM is supposed to SIGBUS or SIGSEGV unless there is a failure. And as above,
doing so either singles out sigbus or necessitates truly funky names.
next prev parent reply other threads:[~2025-09-30 14:58 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-26 16:31 [PATCH 0/6] KVM: Avoid a lurking guest_memfd ABI mess Sean Christopherson
2025-09-26 16:31 ` [PATCH 1/6] KVM: guest_memfd: Add DEFAULT_SHARED flag, reject user page faults if not set Sean Christopherson
2025-09-29 8:38 ` David Hildenbrand
2025-09-29 8:57 ` Fuad Tabba
2025-09-29 9:01 ` David Hildenbrand
2025-09-29 9:04 ` Fuad Tabba
2025-09-29 9:43 ` Ackerley Tng
2025-09-29 10:15 ` Patrick Roy
2025-09-29 10:22 ` David Hildenbrand
2025-09-29 10:51 ` Ackerley Tng
2025-09-29 16:55 ` Sean Christopherson
2025-09-30 0:15 ` Sean Christopherson
2025-09-30 8:36 ` Ackerley Tng
2025-10-01 14:22 ` Vishal Annapurve
2025-10-01 16:15 ` Sean Christopherson
2025-10-01 16:31 ` Vishal Annapurve
2025-10-01 17:16 ` Sean Christopherson
2025-10-01 22:13 ` Vishal Annapurve
2025-10-02 0:04 ` Sean Christopherson
2025-10-02 15:41 ` Vishal Annapurve
2025-10-03 0:12 ` Sean Christopherson
2025-10-03 4:10 ` Vishal Annapurve
2025-10-03 16:13 ` Sean Christopherson
2025-10-03 20:30 ` Vishal Annapurve
2025-09-29 16:54 ` Sean Christopherson
2025-09-26 16:31 ` [PATCH 2/6] KVM: selftests: Stash the host page size in a global in the guest_memfd test Sean Christopherson
2025-09-29 9:12 ` Fuad Tabba
2025-09-29 9:17 ` David Hildenbrand
2025-09-29 10:56 ` Ackerley Tng
2025-09-29 16:58 ` Sean Christopherson
2025-09-30 6:52 ` Ackerley Tng
2025-09-26 16:31 ` [PATCH 3/6] KVM: selftests: Create a new guest_memfd for each testcase Sean Christopherson
2025-09-29 9:18 ` David Hildenbrand
2025-09-29 9:24 ` Fuad Tabba
2025-09-29 11:02 ` Ackerley Tng
2025-09-26 16:31 ` [PATCH 4/6] KVM: selftests: Add test coverage for guest_memfd without GUEST_MEMFD_FLAG_MMAP Sean Christopherson
2025-09-29 9:21 ` David Hildenbrand
2025-09-29 9:24 ` Fuad Tabba
2025-09-26 16:31 ` [PATCH 5/6] KVM: selftests: Add wrappers for mmap() and munmap() to assert success Sean Christopherson
2025-09-29 9:24 ` Fuad Tabba
2025-09-29 9:28 ` David Hildenbrand
2025-09-29 11:08 ` Ackerley Tng
2025-09-29 17:32 ` Sean Christopherson
2025-09-30 7:09 ` Ackerley Tng
2025-09-30 14:24 ` Sean Christopherson
2025-10-01 10:18 ` Ackerley Tng
2025-09-26 16:31 ` [PATCH 6/6] KVM: selftests: Verify that faulting in private guest_memfd memory fails Sean Christopherson
2025-09-29 9:24 ` Fuad Tabba
2025-09-29 9:28 ` David Hildenbrand
2025-09-29 14:38 ` Ackerley Tng
2025-09-29 18:10 ` Sean Christopherson
2025-09-29 18:35 ` Sean Christopherson
2025-09-30 7:53 ` Ackerley Tng
2025-09-30 14:58 ` Sean Christopherson [this message]
2025-10-01 10:26 ` Ackerley Tng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aNvwB2fr2p45hhC0@google.com \
--to=seanjc@google.com \
--cc=ackerleytng@google.com \
--cc=borntraeger@linux.ibm.com \
--cc=david@redhat.com \
--cc=frankja@linux.ibm.com \
--cc=imbrenda@linux.ibm.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pbonzini@redhat.com \
--cc=tabba@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.