0

I have some error handling code that uses ptrace detection like How to detect if the current process is being run by GDB? Valgrind does not use ptrace, so debuggerIsAttached() returns false even if Valgrind is running with --vgdb=full.

valgrind.h has RUNNING_ON_VALGRIND but I need to additionally figure out if vgdb is present and it is safe to raise a SIGTRAP. How do I do this?

Valgrind seems to redirect /proc/self/cmdline, so I cannot just search for the command option.


Here is an example:

  • When run as gdb --args ./checkit stop, it stops in debug_break()
  • When run as valgrind --vgdb=full --vgdb-error=0 ./checkit stop and attached with gdb -ex 'target remote | /usr/lib/valgrind/../../bin/vgdb' ./checkit it prints "My name is checkit" demonstrating that the Valgrind command line options are not present and exits.
#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>

bool is_ptrace_debugger_present() {
   char buf[4096];

   const int statusFd = open("/proc/self/status", O_RDONLY);
   if (statusFd == -1) {
      return false;
   }

   const ssize_t numRead = read(statusFd, buf, sizeof(buf) - 1);
   close(statusFd);
   if (numRead <= 0) {
      return false;
   }

   buf[numRead] = '\0';
   char tracerPidString[] = "TracerPid:";
   char const* tracerPidPtr = strstr(buf, tracerPidString);
   if (!tracerPidPtr) {
      return false; // Can't tell
   }

   for (const char *characterPtr = tracerPidPtr + sizeof(tracerPidString) - 1; characterPtr <= buf + numRead;
            ++characterPtr) {
      if (isspace(*characterPtr)) {
         continue;
      }
      return isdigit(*characterPtr) != 0 && *characterPtr != '0';
   }

   return false;
}


bool is_vgdb_debugger_present() {
   char buff[4096];

   const int statusFd = open("/proc/self/cmdline", O_RDONLY);
   if (statusFd == -1) {
      return false;
   }

   const ssize_t numRead = read(statusFd, buff, sizeof(buff) - 1);
   close(statusFd);
   if (numRead <= 0) {
      return false;
   }

   buff[numRead] = '\0';
   char* progpath = strdup(buff);
   char* progbase = basename(progpath);
   fprintf(stdout, "My name is %s\n", progbase);
   if (strcmp("valgrind", progbase) != 0) {
      free(progpath);
      return false;
   }
   free(progpath);

   ssize_t idx=0;
   while (idx < numRead) {
      if (strcmp("--vgdb=yes", buff+idx) == 0 ||
               strcmp("--vgdb=full", buff+idx) == 0) {
         return true;
      } else {
         idx += strlen(buff+idx+1);
      }
   }

   return false;
}


bool is_debugger_present() {
   if (is_ptrace_debugger_present()) return true;
   if (is_vgdb_debugger_present()) return true;
   return false;
}

bool debug_break() {
   if (!is_debugger_present()) return false;
   __asm__("int $0x3");
   return true;
}


int main(int argc, char* argv[]) {
   if (argc >= 2) {
      debug_break();
   }
   return EXIT_SUCCESS;
}
gibbss
  • 2,013
  • 1
  • 15
  • 22
  • If you present a [mre] with your claim, it'll be easer for everyone to help. – Ted Lyngmo Oct 29 '21 at 19:47
  • Currently, I do not see an easy way to detect if vgdb is active from the program itself. Note that scanning command line args is fragile: the default value for --vgdb is yes. Valgrind options can also be changed via the env variable VALGRIND_OPTS or via ~/.valgrindrc or ./.valgrindrc. Likely the best/easiest would be to have a new client request and/or monitor command to get the value of command line options. There is already today a monitor command that allows to change command line options dynamically. – phd Oct 30 '21 at 12:26
  • Waiting for the more robust solution of having a client request to get the value of a valgrind parameter, you could launch from your program something like: system ("vgdb -l | grep "); (with self pid being the pid of the calling process) and check the return status. Again that will only work with default setup of e.g. --vgdb-prefix. – phd Oct 30 '21 at 12:30
  • After some testing it seems just detecting the vgdb gdbserver is not enough. You can detect the server using the quick and dirty approach of searching for the vgdb process pipe to the current pid. However, if you raise a SIGTRAP without a client attached, the process still dies with an unhandled signal rather than stopping. – gibbss Nov 02 '21 at 18:22

0 Answers0