1

I'd like to dump the [vvar] segment from a linux userspace program (which may be running on x86, arm, or mips...). My problem is that only the first page(s) of the vvar segment are mapped, and I get a sigbus when I attempt to access the unpopulated pages.

I had two workarounds, neither of which worked. The first was to install a sigbus handler, but that doesn't work, simply because I cannot mmap over the memory that is assigned to [vvar]. Thus, when the sig-handler returns, the page will still not be mapped, and when it tries to rerun the same instruction, it will recursively generate another sigbus.

My second attempt was to try to determine which pages are mapped, and to only dump the populated pages. But unfortunately, the /proc/self/pagemap does not seem to work for the [vvar] pages, so I'm not sure how to tell if they're populated or not.

Is there a way to determine which vvar pages are mapped, or to recover from a sigbus when reading the vvar page?

The following is a simplified version of my code so far:

 // read segment info from /proc/self/maps:
 get_local_segment_info("[vvar]", &e); 
 // returns e.start_addr=0x7fffb4b6e000, e.size=16384
 ASSERT(e.start_addr != NULL);
#if 1
 gzwrite(gz_fd, e.start_addr, e.size);
 // this fails with a sigbus
#else
 self_pagemap_fd = open("/proc/self/pagemap",O_RDONLY);
 ASSERT(self_pagemap_fd >= 0);
 off64_t off = lseek64(self_pagemap_fd , 
 (((uintptr_t)e.start_addr) / page_size) * sizeof(uint64_t),
 SEEK_SET);
 ASSERTf(off >= 0, "ERROR: could not lseek64 (%s)",strerror(errno));
 size_t nread = read( self_pagemap_fd , 
 (uint8_t *)pagemaps,
 sizeof(uint64_t) * num_pages);
 ASSERT(nread > 0);
 printf("pagemap[0]=0x%" PRIx64 "\n", pagemaps[0]); 
 // prints pagemap[0]=0x0
 ...
 // presumably, I'd walk through the pages here, and only copy the populated ones, but
 // pagemaps contains all zeros, so I can't tell which pages are populated
#endif
asked Jan 30, 2024 at 15:28

1 Answer 1

1

I managed to solve this, and posting here in case anyone else runs into this issue.

This is possible by creating a SIGBUS handler that returns to a different spot. This can be done through siglongjmp/sigsetjmp. Basically it looks something like:

bool is_addr_mapped(const void *addr)
{
 if(sigsetjmp(g_sigjmp_mark,1) == 0) {
 g_do_siglongjmp = 1;
 volatile char c = *(const char *)addr;
 c=c; // avoid unused variable warning
 g_do_siglongjmp = 0;
 return TRUE;
 } else {
 g_do_siglongjmp = 0;
 return FALSE;
 }
}

And then, in the signal handler

static void
sig_handler(int signo, siginfo_t *si, void *uc)
{
 if (signo == SIGBUS && g_do_siglongjmp)
 {
 siglongjmp(g_ecd_gzwrite_siginfo.sigjmp_mark, 1);
 }
 ...
}

From there, you can simply loop through the pages, and copy the ones that are mapped, and skip those that are not.

answered Feb 6, 2024 at 13:01
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.