LCOV - code coverage report
Current view: top level - app/fddbg - main.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 29 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 3 0.0 %

          Line data    Source code
       1             : /* This is a pretty strange wrapper...
       2             : 
       3             :    We want to support "F5" debugging from, eg, VS code. To do that, VS
       4             :    code has an agent on our development box running as the user. It is
       5             :    not root and has no capabilities. That agent forks a child process
       6             :    that then exec's "gdb", which then loads and runs our program.
       7             : 
       8             :    We need to run our program as root sometimes. You might imagine a few
       9             :    ways to do that,
      10             : 
      11             :     (a) Run the agent as root? Not supported by VS code. It's probably
      12             :         feasible to make changes to the agent code itself to get this
      13             :         working but it seems difficult.
      14             :     (b) Run gdb as root? The agent won't launch it as root, but we
      15             :         could for example setuid the gdb binary itself. This doesn't
      16             :         work quite right because it prevents VS code from communicating
      17             :         with GDB, eg, to pause execution. See this bug
      18             :         https://github.com/microsoft/vscode-cpptools/issues/4243
      19             :     (c) Run the program as root, eg, using setuid? This isn't really
      20             :         possible. A non-root GDB will not attach to a root program for
      21             :         security reasons.
      22             : 
      23             :    So we can't really get the program running as root in any scenario.
      24             :    What else can we do? Just run it with all capabilities... that's what
      25             :    this program does.
      26             : 
      27             :    The sequence here is that, VS code sets this binary as it's "gdb"
      28             :    path, then, when launched this program,
      29             : 
      30             :     (1) Fork off a sudo process which `setcaps` the current binary to
      31             :         have all capabilities.
      32             :     (2) The parent waits for the sudo process, then reruns itself
      33             :         again to have all capabilities
      34             :     (3) Now running with capabilities, we do a `CAP_AMBIENT_RAISE` so
      35             :         that capabilities survive a `/bin/gdb` invocation which would
      36             :         normally strip them for security reasons.
      37             :     (4) Now we finally exec `/bin/gdb` with the arguments provided by
      38             :         the IDE. */
      39             : #define _GNU_SOURCE
      40             : 
      41             : #include "../../util/fd_util.h"
      42             : 
      43             : #include <stdlib.h>
      44             : #include <unistd.h>
      45             : #include <stdlib.h>
      46             : #include <string.h>
      47             : #include <stdio.h>
      48             : #include <sys/types.h>
      49             : #include <sys/prctl.h>
      50             : #include <sys/syscall.h>
      51             : #include <sys/xattr.h>
      52             : #include <sys/wait.h>
      53             : #include <linux/limits.h>
      54             : #include <linux/capability.h>
      55             : 
      56             : static int
      57           0 : has_all_capabilities( void ) {
      58           0 :   struct __user_cap_header_struct capheader;
      59           0 :   struct __user_cap_data_struct capdata[2];
      60             : 
      61           0 :   capheader.version = _LINUX_CAPABILITY_VERSION_3;
      62           0 :   capheader.pid = 0;
      63           0 :   FD_TEST( syscall( SYS_capget, &capheader, &capdata ) >= 0 );
      64           0 :   return
      65           0 :     capdata[0].permitted == 0xFFFFFFFF &&
      66           0 :     ( capdata[1].permitted & 0x000001FF ) == 0x000001FF;
      67           0 : }
      68             : 
      69             : static void
      70           0 : raise_all_capabilities( void ) {
      71           0 :   struct __user_cap_header_struct capheader;
      72           0 :   struct __user_cap_data_struct capdata[2];
      73             : 
      74           0 :   capheader.version = _LINUX_CAPABILITY_VERSION_3;
      75           0 :   capheader.pid = 0;
      76           0 :   FD_TEST( syscall( SYS_capget, &capheader, &capdata ) >= 0 );
      77             : 
      78           0 :   capdata[0].effective = 0xFFFFFFFF;
      79           0 :   capdata[0].inheritable = 0xFFFFFFFF;
      80           0 :   capdata[1].effective = 0xFFFFFFFF;
      81           0 :   capdata[1].inheritable = 0xFFFFFFFF;
      82           0 :   FD_TEST( syscall(SYS_capset, &capheader, &capdata) >= 0 );
      83             : 
      84           0 :   for ( int cap = 0; cap <= CAP_LAST_CAP; cap++ )
      85           0 :     FD_TEST( !prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) );
      86           0 : }
      87             : 
      88             : static void
      89           0 : self_exe( char * path ) {
      90           0 :   long count = readlink( "/proc/self/exe", path, PATH_MAX );
      91           0 :   FD_TEST( count >= 0 && count < PATH_MAX );
      92           0 :   path[ count ] = '\0';
      93           0 : }
      94             : 
      95             : int
      96             : main( int argc,
      97             :       char ** argv ) {
      98             :   fd_boot( &argc, &argv );
      99             : 
     100             :   if( FD_UNLIKELY( argc > 1 && !strcmp( argv[1], "--setcap" ) ) ) {
     101             :     struct vfs_cap_data {
     102             :         __le32 magic_etc;
     103             :         struct {
     104             :             __le32 permitted;
     105             :             __le32 inheritable;
     106             :         } data[2];
     107             :     } cap_data;
     108             : 
     109             :     cap_data.magic_etc = VFS_CAP_REVISION_2;
     110             :     cap_data.data[0].permitted = 0xFFFFFFFF;
     111             :     cap_data.data[0].inheritable = 0xFFFFFFFF;
     112             :     cap_data.data[1].permitted = 0xFFFFFFFF;
     113             :     cap_data.data[1].inheritable = 0xFFFFFFFF;
     114             : 
     115             :     char self_path[ PATH_MAX ];
     116             :     self_exe( self_path );
     117             :     FD_TEST( !setxattr( self_path, "security.capability", &cap_data, sizeof(cap_data), 0) );
     118             :   } else {
     119             :     if( FD_UNLIKELY( !has_all_capabilities() ) ) {
     120             :       if ( FD_UNLIKELY( argc > 1 && !strcmp( argv[1], "--withcap" ) ) ) FD_LOG_ERR(( "missing capabilities" ));
     121             : 
     122             :       char self_path[ PATH_MAX ];
     123             :       self_exe( self_path );
     124             : 
     125             :       pid_t child = fork();
     126             :       FD_TEST( child >= 0 );
     127             :       if( child == 0 ) execv( "/bin/sudo", (char *[]){ "sudo", self_path, "--setcap", NULL } );
     128             :       else {
     129             :         int wstatus;
     130             :         FD_TEST( waitpid( child, &wstatus, 0 ) >= 0 );
     131             :         FD_TEST( WIFEXITED( wstatus ) && !WEXITSTATUS( wstatus ) );
     132             :         char self_path[ PATH_MAX ];
     133             :         self_exe( self_path );
     134             :         FD_TEST( execv( self_path, (char *[]){ "fddbg", "--withcap", NULL } ) >= 0 );
     135             :       }
     136             :     } else {
     137             :       raise_all_capabilities();
     138             : 
     139             :       char * args[ 32 ];
     140             :       int start = argc > 1 && !strcmp( argv[1], "--withcap" ) ? 2 : 1;
     141             :       args[0] = "/bin/gdb";
     142             :       for( int i=start; i<argc; i++ ) args[i-start+1] = argv[i];
     143             :       args[ argc-start+1 ] = NULL;
     144             : 
     145             :       FD_TEST( execv( "/bin/gdb", args ) >= 0 );
     146             :     }
     147             :   }
     148             : }

Generated by: LCOV version 1.14