1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
|
/*
Fakeroot Next Generation - run command with fake root privileges
This program is copyrighted. Copyright information is available at the
AUTHORS file at the root of the source tree for the fakeroot-ng project
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include "syscalls.h"
#include "arch/platform.h"
#include "process.h"
#include "chroot.h"
// XXX
// Not implemented functions:
// acct
bool sys_getuid( int sc_num, pid_t pid, pid_state *state )
{
switch( state->state ) {
default:
case pid_state::NONE:
state->state=pid_state::RETURN;
break;
case pid_state::RETURN:
ptlib_set_retval( pid, 0 );
state->state=pid_state::NONE;
break;
}
return true;
}
bool sys_fork( int sc_num, pid_t pid, pid_state *state )
{
if( state->state==pid_state::NONE ) {
state->state=pid_state::RETURN;
state->context_state[0]=0;
} else if( state->state==pid_state::RETURN ) {
state->state=pid_state::NONE;
}
return true;
}
bool sys_vfork( int sc_num, pid_t pid, pid_state *state )
{
if( state->state==pid_state::NONE ) {
state->state=pid_state::RETURN;
state->context_state[0]=NEW_PROCESS_SAME_VM;
} else if( state->state==pid_state::RETURN ) {
state->state=pid_state::NONE;
}
return true;
}
bool sys_clone( int sc_num, pid_t pid, pid_state *state )
{
if( state->state==pid_state::NONE ) {
state->state=pid_state::RETURN;
// Need to mark context_state[0] based on the type of new process being created
state->context_state[0]=0;
int_ptr flags=ptlib_get_argument( pid, 1 );
if( (flags&(CLONE_PARENT|CLONE_THREAD))!=0 )
state->context_state[0]|=NEW_PROCESS_SAME_PARENT;
if( (flags&CLONE_FS)!=0 )
state->context_state[0]|=NEW_PROCESS_SAME_ROOT;
if( (flags&CLONE_FILES)!=0 )
state->context_state[0]|=NEW_PROCESS_SAME_FD;
if( (flags&CLONE_VM)!=0 )
state->context_state[0]|=NEW_PROCESS_SAME_VM;
} else if( state->state==pid_state::RETURN ) {
state->state=pid_state::NONE;
}
return true;
}
// Function interface is different - returns an extra bool to signify whether to send a trap after the call
// context_state[0] is state machine:
// 0 - just returned from execve
// 1 - got a SIGTRAP after execve
// if context_state[1] is not 0, force error on syscall
bool sys_execve( int sc_num, pid_t pid, pid_state *state, bool &trap_after_call )
{
trap_after_call=false;
if( state->state==pid_state::NONE ) {
state->context_state[1]=0; // Don't force error by default
if( log_level>0 ) {
char cmd[PATH_MAX];
ptlib_get_string( pid, (void *)ptlib_get_argument( pid, 1 ), cmd, sizeof(cmd) );
dlog("execve: "PID_F" calling execve for executing %s\n", pid, cmd );
dlog(NULL);
}
if( chroot_is_chrooted( state ) ) {
if( !chroot_translate_param( pid, state, 1, true, true ) ) {
// We had an error translating the file name - pass the error on
state->context_state[1]=errno;
ptlib_set_syscall( pid, PREF_NOP );
// REDIRECT2 is set anyways
}
}
// On some platforms "execve" returns, when successful, with SYS_restart_syscall or some such thing
state->state=pid_state::REDIRECT2;
state->context_state[0]=0;
} else if( state->state==pid_state::REDIRECT2 ) {
if( state->context_state[0]==0 ) {
// Execve returned
state->state=pid_state::NONE;
if( ptlib_success( pid, sc_num ) && state->context_state[1]==0 ) {
dlog("execve: "PID_F" successfully execed a new command\n", pid );
// All memory allocations performed before the exec are now null and void
state->memory=NULL;
state->shared_memory=NULL;
state->shared_mem_local=shared_mem();
#if PTLIB_TRAP_AFTER_EXEC
// The platform sends a SIGTRAP to the process after a successful execve, which results in us thinking it was
// a syscall. We need to absorb it
state->state=pid_state::REDIRECT2;
state->context_state[0]=1;
if( state->trace_mode==TRACE_SYSCALL ) {
// We are not in the "NONE" state, but the syscall is over. Tell parent to trap
trap_after_call=true;
}
#endif
} else if( state->context_state[1]!=0 ) {
dlog("execve: "PID_F" chroot translation forced error on us: %s\n", pid, strerror(state->context_state[1]) );
ptlib_set_error( pid, state->orig_sc, state->context_state[1] );
} else {
dlog("execve: "PID_F" failed with error %s\n", pid, strerror(ptlib_get_error(pid, sc_num)) );
}
} else {
state->state=pid_state::NONE;
dlog("execve: "PID_F" absorbed dummy SIGTRAP after successful execve\n", pid );
// If the trace mode is not SYSCALL, the post handling will not generate a TRACE. If PTLIB_TRAP_AFTER_EXEC is set,
// a trace is required, however, even if not in TRACE_SYSCALL
trap_after_call=true;
}
}
return true;
}
bool sys_sigreturn( int sc_num, pid_t pid, pid_state *state )
{
// This is not a function call. In particular, this "not function call" may wreak haevoc in our state keeping, and
// thus the special handling
if( state->state==pid_state::NONE ) {
// Upon syscall exit, at least on Linux, the syscall is "-1"
state->state=pid_state::REDIRECT2;
} else if( state->state==pid_state::REDIRECT2 ) {
state->state=pid_state::NONE;
}
return true;
}
bool sys_setsid( int sc_num, pid_t pid, pid_state *state )
{
// We do not do any actual manipulation on the syscall. We just keep track over the process' session ID
if( state->state==pid_state::NONE ) {
state->state=pid_state::RETURN;
} else if( state->state==pid_state::RETURN ) {
state->state=pid_state::NONE;
if( ptlib_success( pid, sc_num ) ) {
state->session_id=pid;
}
}
return true;
}
// This call needs to be emulated under one of two conditions:
// 1. Platform does not support "wait" by parent on a debugged child (PTLIB_PARENT_CAN_WAIT=0)
// 2. The parent is a debugger (we are emulating the entire ptrace interface)
//
// Of course, with PTRACE_TRACEME, it is possible that the process not have a debugee when it
// starts the wait, but does have one by the time wait should return. We therefor emulate the
// entire system call, always :-(
static bool real_wait4( int sc_num, pid_t pid, pid_state *state, pid_t param1, int *param2, int param3, void *param4 )
{
if( state->state==pid_state::NONE ) {
state->context_state[0]=param1; // pid
state->context_state[1]=(int_ptr)param2; // status
state->context_state[2]=param3; // options
state->context_state[3]=(int_ptr)param4; // rusage
dlog("wait4: %d num debugees: %d num children: %d, queue %s\n", pid, state->num_debugees, state->num_children,
state->waiting_signals.empty()?"is empty":"has signals" );
// Test whether the (emulated) call should fail
// XXX This is nowhere near the exhustive tests we need to do. We only aim to emulate strace and ourselves at this point in time
if( state->num_children!=0 || state->num_debugees!=0 || !state->waiting_signals.empty() ) {
// Only wait if there was no error
state->state=pid_state::WAITING;
} else {
// Set an ECHILD return code
state->state=pid_state::REDIRECT2;
ptlib_set_syscall( pid, PREF_NOP ); // NOP call
state->context_state[0]=-ECHILD;
}
} else if( state->state==pid_state::REDIRECT2 ) {
// We may get here under two conditions.
// Either the wait was performed by us and a NOP was carried out, in which case the syscall is going to be PREF_NOP
// and context_state[0] contains the desired return code (negative for error)
// Or
// A function substancially similar to wait was carried out, in which case context_state[0] contains a backup of the original
// content of the fourth parameter register, which may have not been used by the original syscall if it was not wait4
if( sc_num==PREF_NOP ) {
// Performed NOP - set return codes
if( ((long)state->context_state[0])>=0 )
ptlib_set_retval( pid, state->context_state[0] );
else
ptlib_set_error( pid, state->orig_sc, -state->context_state[0] );
ptlib_set_syscall( pid, state->orig_sc );
} else {
// If an actual wait syscall was carried out, we may need to restore the original content of argument 4
ptlib_set_argument( pid, 4, state->context_state[0] );
}
ptlib_set_syscall( pid, state->orig_sc );
state->state=pid_state::NONE;
}
if( state->state==pid_state::WAITING ) {
if( !state->waiting_signals.empty() ) {
// Let's see what was asked for
pid_t wait_pid=(pid_t)state->context_state[0];
std::list<pid_state::wait_state>::iterator child=state->waiting_signals.begin();
if( wait_pid<-1 ) {
// We are looking for process with session id= -pid
while( child!=state->waiting_signals.end() && state[child->pid()].session_id!=-wait_pid )
++child;
} else if( wait_pid==-1 ) {
// Wait for anything. Just leave child as it is
} else if( wait_pid==0 ) {
// Wait for session_id==parent's
while( child!=state->waiting_signals.end() && state[child->pid()].session_id!=state->session_id )
++child;
} else {
// Wait for exact match
while( child!=state->waiting_signals.end() && child->pid()!=wait_pid )
++child;
}
if( child!=state->waiting_signals.end() ) {
// We have what to report - allow the syscall to return
// Fill in the rusage
if( ((void *)state->context_state[3])!=NULL )
ptlib_set_mem( pid, &child->usage(), (void *)state->context_state[3], sizeof(child->usage()) );
// Is this a report about a terminated program?
if( !child->debugonly() )
{
// If the parent never carried out the actual "wait", the child will become a zombie
// We turn the syscall into a waitpid with the child's pid explicitly given
#ifdef SYS_wait4
ptlib_set_syscall( pid, SYS_wait4 );
#else
ptlib_set_syscall( pid, SYS_waitpid );
#endif
state->saved_state[0]=(void *)ptlib_get_argument( pid, 4 ); // Save the fourth argument
ptlib_set_argument( pid, 1, child->pid() );
ptlib_set_argument( pid, 2, state->context_state[1] );
ptlib_set_argument( pid, 3, state->context_state[2] );
ptlib_set_argument( pid, 4, state->context_state[3] );
} else {
// We need to explicitly set all the arguments
if( ((void *)state->context_state[1])!=NULL )
ptlib_set_mem( pid, &child->status(), (void *)state->context_state[1], sizeof(child->status()) );
ptlib_set_syscall( pid, PREF_NOP );
state->context_state[0]=child->pid();
}
state->waiting_signals.erase( child );
state->state=pid_state::REDIRECT2;
} else {
dlog("wait4: "PID_F" hanged in wait for %d\n", pid, wait_pid );
}
}
if( state->state==pid_state::WAITING && (state->context_state[2]&WNOHANG)!=0 ) {
// Client asked never to hang
state->state=pid_state::REDIRECT2;
ptlib_set_syscall( pid, PREF_NOP );
state->context_state[0]=0;
}
}
return state->state!=pid_state::WAITING;
}
bool sys_wait4( int sc_num, pid_t pid, pid_state *state )
{
if( state->state==pid_state::NONE ) {
pid_t param1=(pid_t)ptlib_get_argument(pid, 1); // pid
int *param2=(int *)ptlib_get_argument(pid, 2); // status
int param3=ptlib_get_argument(pid, 3); // options
void *param4=(void *)ptlib_get_argument(pid, 4); // rusage
return real_wait4( sc_num, pid, state, param1, param2, param3, param4 );
} else {
return real_wait4( sc_num, pid, state, 0, NULL, 0, NULL );
}
}
// We just set the variables and let wait4 handle our case
bool sys_waitpid( int sc_num, pid_t pid, pid_state *state )
{
if( state->state==pid_state::NONE ) {
pid_t param1=ptlib_get_argument(pid, 1); // pid
int *param2=(int *)ptlib_get_argument(pid, 2); // status
int param3=ptlib_get_argument(pid, 3); // options
return real_wait4( sc_num, pid, state, param1, param2, param3, NULL );
} else {
return real_wait4( sc_num, pid, state, 0, NULL, 0, NULL );
}
}
// We want to prevent the process from killing us
bool sys_kill( int sc_num, pid_t pid, pid_state *state )
{
if( state->state==pid_state::NONE ) {
state->state=pid_state::RETURN;
if( ((pid_t)ptlib_get_argument( pid, 1 ))==getpid() ) {
// Process tried to send us a signal. Can't allow that
state->state=pid_state::REDIRECT2;
ptlib_set_syscall( pid, PREF_NOP);
}
} else if( state->state==pid_state::RETURN ) {
state->state=pid_state::NONE;
} else if( state->state==pid_state::REDIRECT2 ) {
state->state=pid_state::NONE;
ptlib_set_error( pid, state->orig_sc, EPERM );
}
return true;
}
|