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 indebug_break()
- When run as
valgrind --vgdb=full --vgdb-error=0 ./checkit stop
and attached withgdb -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;
}