Skip to content

Commit

Permalink
fddev: add debugging wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
mmcgee-jump committed Aug 21, 2023
1 parent 17d82b4 commit ae899b8
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
37 changes: 37 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "fddev",
"type": "cppdbg",
"request": "launch",
"cwd": "${workspaceFolder}/build/native/gcc/bin",
"program": "${workspaceFolder}/build/native/gcc/bin/fddev",
"args": ["--no-sandbox"],
"miDebuggerPath": "${workspaceFolder}/build/native/gcc/bin/fddbg",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
},
{
"description": "Don't detach on fork",
"text": "-gdb-set detach-on-fork off",
"ignoreFailures": false
},
{
"description": "Stay parent after fork",
"text": "-gdb-set follow-fork-mode parent",
"ignoreFailures": false
}
]
}
]
}
3 changes: 3 additions & 0 deletions src/app/fddbg/Local.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ifdef FD_HAS_HOSTED
$(call make-bin,fddbg,main,fd_util)
endif
148 changes: 148 additions & 0 deletions src/app/fddbg/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* This is a pretty strange wrapper...
We want to support "F5" debugging from, eg, VS code. To do that, VS
code has an agent on our development box running as the user. It is
not root and has no capabilities. That agent forks a child process
that then exec's "gdb", which then loads and runs our program.
We need to run our program as root sometimes. You might imagine a few
ways to do that,
(a) Run the agent as root? Not supported by VS code. It's probably
feasible to make changes to the agent code itself to get this
working but it seems difficult.
(b) Run gdb as root? The agent won't launch it as root, but we
could for example setuid the gdb binary itself. This doesn't
work quite right because it prevents VS code from communicating
with GDB, eg, to pause execution. See this bug
https://github.com/microsoft/vscode-cpptools/issues/4243
(c) Run the program as root, eg, using setuid? This isn't really
possible. A non-root GDB will not attach to a root program for
security reasons.
So we can't really get the program running as root in any scenario.
What else can we do? Just run it with all capabilities... that's what
this program does.
The sequence here is that, VS code sets this binary as it's "gdb"
path, then, when launched this program,
(1) Fork off a sudo process which `setcaps` the current binary to
have all capabilities.
(2) The parent waits for the sudo process, then reruns itself
again to have all capabilities
(3) Now running with capabilities, we do a `CAP_AMBIENT_RAISE` so
that capabilities survive a `/bin/gdb` invocation which would
normally strip them for security reasons.
(4) Now we finally exec `/bin/gdb` with the arguments provided by
the IDE. */
#define _GNU_SOURCE

#include "../../util/fd_util.h"

#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/xattr.h>
#include <sys/wait.h>
#include <linux/limits.h>
#include <linux/capability.h>

static int
has_all_capabilities( void ) {
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];

capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = 0;
FD_TEST( syscall( SYS_capget, &capheader, &capdata ) >= 0 );
return
capdata[0].permitted == 0xFFFFFFFF &&
( capdata[1].permitted & 0x000001FF ) == 0x000001FF;
}

static void
raise_all_capabilities( void ) {
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];

capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = 0;
FD_TEST( syscall( SYS_capget, &capheader, &capdata ) >= 0 );

capdata[0].effective = 0xFFFFFFFF;
capdata[0].inheritable = 0xFFFFFFFF;
capdata[1].effective = 0xFFFFFFFF;
capdata[1].inheritable = 0xFFFFFFFF;
FD_TEST( syscall(SYS_capset, &capheader, &capdata) >= 0 );

for ( int cap = 0; cap <= CAP_LAST_CAP; cap++ )
FD_TEST( !prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) );
}

static void
self_exe( char * path ) {
long count = readlink( "/proc/self/exe", path, PATH_MAX );
FD_TEST( count >= 0 && count < PATH_MAX );
path[ count ] = '\0';
}

int
main( int argc,
char ** argv ) {
fd_boot( &argc, &argv );

if( FD_UNLIKELY( argc > 1 && !strcmp( argv[1], "--setcap" ) ) ) {
struct vfs_cap_data {
__le32 magic_etc;
struct {
__le32 permitted;
__le32 inheritable;
} data[2];
} cap_data;

cap_data.magic_etc = VFS_CAP_REVISION_2;
cap_data.data[0].permitted = 0xFFFFFFFF;
cap_data.data[0].inheritable = 0xFFFFFFFF;
cap_data.data[1].permitted = 0xFFFFFFFF;
cap_data.data[1].inheritable = 0xFFFFFFFF;

char self_path[ PATH_MAX ];
self_exe( self_path );
FD_TEST( !setxattr( self_path, "security.capability", &cap_data, sizeof(cap_data), 0) );
} else {
if( FD_UNLIKELY( !has_all_capabilities() ) ) {
if ( FD_UNLIKELY( argc > 1 && !strcmp( argv[1], "--withcap" ) ) ) FD_LOG_ERR(( "missing capabilities" ));

char self_path[ PATH_MAX ];
self_exe( self_path );

pid_t child = fork();
FD_TEST( child >= 0 );
if( child == 0 ) execv( "/bin/sudo", (char *[]){ "sudo", self_path, "--setcap", NULL } );
else {
int wstatus;
FD_TEST( waitpid( child, &wstatus, 0 ) >= 0 );
FD_TEST( WIFEXITED( wstatus ) && !WEXITSTATUS( wstatus ) );
char self_path[ PATH_MAX ];
self_exe( self_path );
FD_TEST( execv( self_path, (char *[]){ "fddbg", "--withcap", NULL } ) >= 0 );
}
} else {
raise_all_capabilities();

char * args[ 32 ];
int start = argc > 1 && !strcmp( argv[1], "--withcap" ) ? 2 : 1;
args[0] = "/bin/gdb";
for( int i=start; i<argc; i++ ) args[i-start+1] = argv[i];
args[ argc-start+1 ] = NULL;

FD_TEST( execv( "/bin/gdb", args ) >= 0 );
}
}
}

0 comments on commit ae899b8

Please sign in to comment.