https://bugs.kde.org/show_bug.cgi?id=457619

            Bug ID: 457619
           Summary: Instructions are not consistently executed after
                    returning from a SIGSEGV signal handler
           Product: valgrind
           Version: 3.18.1
          Platform: Ubuntu Packages
                OS: Linux
            Status: REPORTED
          Severity: crash
          Priority: NOR
         Component: memcheck
          Assignee: jsew...@acm.org
          Reporter: tanning-skull...@icloud.com
  Target Milestone: ---

Created attachment 151170
  --> https://bugs.kde.org/attachment.cgi?id=151170&action=edit
Minimal reproducible example

SUMMARY
Valgrind's memcheck does not properly and consistently execute instructions
after returning from a SIGSEGV signal handler


STEPS TO REPRODUCE
1. Compile the following minimal reproducible example with gcc (Code is also
attached): e.g. "gcc -g test.c -o test"

====================
#include <cstdlib>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

__attribute__((naked)) void eval(uint8_t *mem) {
  // First argument is in rdi
  asm(".intel_syntax noprefix");
  asm("mov rax,0");
  asm("mov QWORD PTR [rdi+rax*1],0x0");
  asm("mov rax,0"); // <-- remove this and it works
  asm("mov QWORD PTR [rdi+rax*1],0x0");
  asm("ret");
  asm(".att_syntax prefix");
}

size_t getOSMemoryPageSize() noexcept { return (size_t)sysconf(_SC_PAGE_SIZE);
}

uint8_t *memory = nullptr;
void allocMemory() { memory = (uint8_t *)mmap(nullptr, getOSMemoryPageSize(),
PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); }
void unprotectMemory() { mprotect(memory, getOSMemoryPageSize(), PROT_READ |
PROT_WRITE); }

void signalHandler(int32_t const signalId, siginfo_t *const si, void *const
ptr) {
  long const offsetInMemory = (uint8_t *)si->si_addr - memory;

  char msg[1000];
  int len = sprintf(msg, "==> offset in memory: %lu\n", offsetInMemory);
  write(2, msg, (size_t)len);

  if (offsetInMemory < 0 || offsetInMemory >= getOSMemoryPageSize()) {
_exit(1); }

  // mprotect is signal safe on GNU libc
  unprotectMemory();
}

int main(void) {
  // Allocate protected (non-readable, non-writable) memory
  allocMemory();

  // Set up a signal handler for SIGSEGV
  struct sigaction sa = {};
  sa.sa_sigaction = signalHandler;
  sa.sa_flags = SA_SIGINFO | SA_NODEFER;
  sigfillset(&sa.sa_mask);
  sigaction(SIGSEGV, &sa, nullptr);

  // Execute an asm function that touches the protected memory, which triggers
the signal handler where the protection of the memory is subsequently removed
(it is set as readable and writable). After the signal handler returns, the
failed instruction is retried and can now successfully execute the memory
writes
  eval(memory);
}
====================

2. Execute normally ("./test") and then with memcheck ("valgrind
--tool=memcheck ./test")

OBSERVED RESULT
Program prints the following when executed normally:
"==> offset in memory: 0"

Program prints the following when executed with memcheck:
"==> offset in memory: 0
==> offset in memory: 67334144"

EXPECTED RESULT
Program prints the following both when executed normally and when executed with
memcheck
"==> offset in memory: 0"

SOFTWARE/OS VERSIONS
gcc-10 (Ubuntu 10.3.0-15ubuntu1) 10.3.0
Ubuntu 22.04 LTS
valgrind-3.18.1
uname -r: 5.15.0-1015-aws

ADDITIONAL INFORMATION
When the program is executed under vgdb, it can be seen that the rax register
has a wrong value after the signal handler returns (namely 67334144, see
outputs). Additionally, using the gdb command "i r rax", it can be observed
that the second "mov rax,0" instruction does not have any effect on the value
stored in the rax register.

The following assembly sequence works interestingly:
mov rax,0
mov QWORD PTR [rdi+rax*1],0x0
mov QWORD PTR [rdi+rax*1],0x0
ret

And this also works:
mov QWORD PTR [rdi],0x0
mov rax,0
mov QWORD PTR [rdi+rax*1],0x0
ret

NOTE: memcheck produces a warning about an "Invalid write of size 8". This is
due to the memory that is accessed being deliberately marked with PROT_NONE
(neither readable, writable nor executable). This protection is only removed in
the signal handler.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to