Skip to content

Commit a1884aa

Browse files
WIP handle abnormal process initialization
1 parent 0565fde commit a1884aa

File tree

2 files changed

+133
-114
lines changed

2 files changed

+133
-114
lines changed

src/demon/linux/demon_core_linux.c

Lines changed: 116 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,25 +1438,10 @@ dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity
14381438
}
14391439

14401440
internal void
1441-
dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
1441+
dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_subprocesses, pid_t pid)
14421442
{
14431443
Temp scratch = scratch_begin(&arena, 1);
14441444

1445-
//
1446-
// move pid to the active list
1447-
//
1448-
B32 debug_subprocesses = 0;
1449-
for EachNode(n, DMN_LNX_ProcessLaunch, dmn_lnx_state->pending_creation.first)
1450-
{
1451-
if(n->pid == pid)
1452-
{
1453-
dmn_lnx_process_launch_list_remove(&dmn_lnx_state->pending_creation, n);
1454-
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, n);
1455-
debug_subprocesses = n->debug_subprocesses;
1456-
break;
1457-
}
1458-
}
1459-
14601445
ELF_Hdr64 exe_ehdr = dmn_lnx_ehdr_from_pid(pid);
14611446
int memory_fd = open((char*)str8f(scratch.arena, "/proc/%d/mem", pid).str, O_RDWR);
14621447
DMN_LNX_ProcessAuxv auxv = dmn_lnx_auxv_from_pid(pid, exe_ehdr.e_ident[ELF_Identifier_Class]);
@@ -1559,7 +1544,6 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
15591544
process->xcr0 = xcr0;
15601545
process->xsave_size = Max(xsave_size, sizeof(X64_XSave));
15611546
process->xsave_layout = xsave_layout;
1562-
process->thread_count = 1;
15631547
MemoryCopyTyped(&process->probe_vaddrs[0], &probe_vaddrs[0], DMN_LNX_ProbeType_Count);
15641548
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->pid_ht, pid, process);
15651549
// push create process event
@@ -2091,95 +2075,89 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
20912075
{
20922076
Temp scratch = scratch_begin(0, 0);
20932077

2094-
pid_t pid = 0;
2095-
2096-
//- rjf: unpack command line
2097-
int argc = (int)(params->cmd_line.node_count);
2098-
char **argv = push_array(scratch.arena, char *, argc+1);
2078+
// setup target command line
2079+
U64 argc = params->cmd_line.node_count + 1;
2080+
char **argv = push_array(scratch.arena, char *, argc);
20992081
{
21002082
U64 idx = 0;
21012083
for(String8Node *n = params->cmd_line.first; n != 0; n = n->next, idx += 1)
21022084
{
21032085
argv[idx] = (char *)push_str8_copy(scratch.arena, n->string).str;
21042086
}
21052087
}
2106-
2107-
//- rjf: unpack path
2108-
char *path = (char *)push_str8_copy(scratch.arena, params->path).str;
2109-
2110-
//- rjf: unpack environment
2111-
char **env = push_array(scratch.arena, char *, os_lnx_state.default_env_count + params->env.node_count + 1);
2088+
2089+
// setup target environment
2090+
U64 envc = os_lnx_state.default_env_count + params->env.node_count + 1;
2091+
char **envp = push_array(scratch.arena, char *, envc);
21122092
{
2113-
MemoryCopyTyped(env, os_lnx_state.default_env, os_lnx_state.default_env_count);
2093+
// copy default environment
2094+
MemoryCopyTyped(envp, os_lnx_state.default_env, os_lnx_state.default_env_count);
21142095

2096+
// copy user environment
21152097
U64 idx = os_lnx_state.default_env_count;
21162098
for(String8Node *n = params->env.first; n != 0; n = n->next, idx += 1)
21172099
{
2118-
env[idx] = (char *)push_str8_copy(scratch.arena, n->string).str;
2100+
envp[idx] = (char *)push_str8_copy(scratch.arena, n->string).str;
21192101
}
21202102
}
21212103

2122-
//- rjf: fork
2123-
pid = fork();
2104+
// create zero-terminated work directory path
2105+
char *work_dir_path = (char *)push_str8_copy(scratch.arena, params->path).str;
2106+
2107+
// fork process
2108+
pid_t pid = fork();
21242109

21252110
// child process
21262111
if(pid == 0)
21272112
{
2128-
// set current working directory to tracee
2129-
if(OS_LNX_RETRY_ON_EINTR(chdir(path)) < 0) { goto child_exit; }
2113+
// change work directory to tracee
2114+
if(OS_LNX_RETRY_ON_EINTR(chdir(work_dir_path)) < 0) { goto child_exit; }
21302115

2131-
// notify parent that we are going to execve
2132-
OS_LNX_RETRY_ON_EINTR(raise(SIGSTOP));
2116+
// @first_sigstop notify parent that we are going to execve
2117+
if(OS_LNX_RETRY_ON_EINTR(raise(SIGSTOP)) < 0) { goto child_exit; }
21332118

2134-
// replace process with target
2135-
OS_LNX_RETRY_ON_EINTR(execve(argv[0], argv, env));
2119+
// replace process with target program
2120+
if(OS_LNX_RETRY_ON_EINTR(execve(argv[0], argv, envp)) < 0) { goto child_exit; }
21362121

2137-
// execve failed -- exit
21382122
child_exit:;
21392123
abort();
21402124
}
21412125
// parent process
21422126
else if(pid > 0)
21432127
{
2144-
B32 kill_child_process = 1;
2128+
B32 failed_to_seize = 1;
21452129

21462130
// try to seize child process
2147-
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SEIZE, pid, 0, 0) < 0)) { Assert(0 && "failed to seize"); goto parent_exit; }
2131+
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SEIZE, pid, 0, 0) < 0)) { goto parent_exit; }
21482132

2149-
if(dmn_lnx_state->tracer_tid == 0)
2150-
{
2151-
dmn_lnx_state->tracer_tid = gettid();
2152-
}
2153-
else
2154-
{
2155-
AssertAlways(dmn_lnx_state->tracer_tid == gettid());
2156-
}
2133+
// ensure tracer ops are issued on thread that sizes processes
2134+
if(dmn_lnx_state->tracer_tid == 0) { dmn_lnx_state->tracer_tid = gettid(); }
2135+
AssertAlways(dmn_lnx_state->tracer_tid == gettid());
21572136

2158-
// alloc pid node
2159-
DMN_LNX_ProcessLaunch *pending_stopsig = dmn_lnx_state->free_pids.first;
2160-
if(pending_stopsig) { dmn_lnx_process_launch_list_remove(&dmn_lnx_state->free_pids, pending_stopsig); }
2161-
else { pending_stopsig = push_array(dmn_lnx_state->arena, DMN_LNX_ProcessLaunch, 1); }
2137+
// alloc pending process node
2138+
DMN_LNX_ProcessLaunch *pending_proc = dmn_lnx_state->free_pids.first;
2139+
if(pending_proc) { dmn_lnx_process_launch_list_remove(&dmn_lnx_state->free_pids, pending_proc); }
2140+
else { pending_proc = push_array(dmn_lnx_state->arena, DMN_LNX_ProcessLaunch, 1); }
21622141

2163-
// add to list
2164-
pending_stopsig->debug_subprocesses = params->debug_subprocesses;
2165-
pending_stopsig->pid = pid;
2166-
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->pending_stopsig, pending_stopsig);
2142+
// add pending proc node to the list
2143+
*pending_proc = (DMN_LNX_ProcessLaunch){ params->debug_subprocesses, pid, DMN_LNX_ProcessLaunchState_SigStop };
2144+
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->pending_procs, pending_proc);
21672145

2168-
// tracee was successfully sizeed
2169-
kill_child_process = 0;
2146+
// tracee was successfully seized
2147+
failed_to_seize = 0;
21702148

21712149
parent_exit:;
2172-
if(kill_child_process)
2150+
if(failed_to_seize)
21732151
{
21742152
Assert(0 && "failed to ptrace child process");
2175-
if(OS_LNX_RETRY_ON_EINTR(kill(SIGKILL, pid)) < 0) { Assert(0 && "failed to kill child process"); }
2153+
OS_LNX_RETRY_ON_EINTR(kill(SIGKILL, pid));
2154+
OS_LNX_RETRY_ON_EINTR(waitpid(pid, 0, WNOHANG));
21762155
pid = 0;
21772156
}
21782157
}
21792158

21802159
scratch_end(scratch);
2181-
Assert(pid < max_U32);
2182-
return (U32)pid;
2160+
return pid;
21832161
}
21842162

21852163
internal B32
@@ -2394,64 +2372,97 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
23942372

23952373
// wait for signals from the running threads
23962374
{
2397-
U64 stopped_threads = 0;
2398-
B32 is_halt_done = 0;
2399-
do
2375+
B32 is_halt_done = 0;
2376+
for(U64 stopped_threads = 0;;)
24002377
{
2401-
// do not wait if there are no processes
2402-
if(dmn_lnx_state->pending_stopsig.count == 0 && dmn_lnx_state->pending_creation.count == 0 && dmn_lnx_state->active_process_count == 0)
2378+
// do not wait if there are no pending or active processes
2379+
if(dmn_lnx_state->pending_procs.count == 0 && dmn_lnx_state->active_process_count == 0)
24032380
{
24042381
dmn_lnx_handle_not_attached(arena, &events);
24052382
break;
24062383
}
24072384

2408-
mutex_drop(dmn_lnx_state->halter_mutex);
2409-
24102385
// wait for a signal
24112386
int status = 0;
24122387
pid_t wait_id = 0;
2413-
for(;;)
24142388
{
2415-
wait_id = OS_LNX_RETRY_ON_EINTR(waitpid(-1, &status, __WALL|__WNOTHREAD));
2416-
if(wait_id == -1) { InvalidPath; } // TODO: graceful exit
2417-
break;
2389+
mutex_drop(dmn_lnx_state->halter_mutex);
2390+
for(;;)
2391+
{
2392+
wait_id = OS_LNX_RETRY_ON_EINTR(waitpid(-1, &status, __WALL|__WNOTHREAD));
2393+
if(wait_id == -1) { InvalidPath; } // TODO: graceful exit
2394+
break;
2395+
}
2396+
mutex_take(dmn_lnx_state->halter_mutex);
24182397
}
24192398

2420-
mutex_take(dmn_lnx_state->halter_mutex);
2421-
24222399
// unpack status
24232400
int wifexited = WIFEXITED(status);
24242401
int wifsignaled = WIFSIGNALED(status);
24252402
int wifstopped = WIFSTOPPED(status);
24262403
int wstopsig = WSTOPSIG(status);
24272404
int event_code = (status >> 16);
24282405

2429-
// intercept our SIGSTOP and signal tracee to continue to execve
2430-
if(wstopsig == SIGSTOP)
2406+
// intercept signals meant for the process launch sequence
24312407
{
2432-
B32 is_pending_launch = 0;
2433-
for EachNode(n, DMN_LNX_ProcessLaunch, dmn_lnx_state->pending_stopsig.first)
2408+
// pid -> pending process launch
2409+
DMN_LNX_ProcessLaunch *pending_proc = 0;
2410+
for EachNode(n, DMN_LNX_ProcessLaunch, dmn_lnx_state->pending_procs.first) { if(n->pid == wait_id) { pending_proc = n; break; } }
2411+
2412+
if(pending_proc)
24342413
{
2435-
if(n->pid == wait_id)
2414+
// process unexpectedly exited
2415+
if(wifexited) { pending_proc->state = DMN_LNX_ProcessLaunchState_Exit; }
2416+
2417+
if(pending_proc->state == DMN_LNX_ProcessLaunchState_Exec && wstopsig == SIGTRAP && event_code == PTRACE_EVENT_EXEC)
24362418
{
2437-
// move node to the pending creation list
2438-
dmn_lnx_process_launch_list_remove(&dmn_lnx_state->pending_stopsig, n);
2439-
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->pending_creation, n);
2419+
// move pending process node to the free list
2420+
dmn_lnx_process_launch_list_remove(&dmn_lnx_state->pending_procs, pending_proc);
2421+
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, pending_proc);
24402422

2441-
// set trace options
2442-
// TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFROK | PTRACE_O_TRACEVFORKDONE
2443-
void *trace_options = (void *)(PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE);
2444-
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SETOPTIONS, wait_id, 0, trace_options)) < 0) { Assert(0 && "failed to set options"); InvalidPath; }
2423+
// push create process events
2424+
dmn_lnx_handle_create_process(arena, &events, pending_proc->debug_subprocesses, wait_id);
24452425

2446-
// signal process to continue to execve
2447-
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_CONT, wait_id, 0, 0)) < 0) { InvalidPath; }
2426+
// override event code so the main code path does not create another process
2427+
event_code = PTRACE_EVENT_STOP;
2428+
}
2429+
else
2430+
{
2431+
B32 shutdown_process = 1;
24482432

2449-
is_pending_launch = 1;
2433+
if(pending_proc->state == DMN_LNX_ProcessLaunchState_SigStop && wstopsig == SIGSTOP) // @first_sigstop
2434+
{
2435+
// set trace options and singal to proceed to the execve
2436+
//
2437+
// TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFROK | PTRACE_O_TRACEVFORKDONE
2438+
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SETOPTIONS, wait_id, 0, (void *)(PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE))) >= 0)
2439+
{
2440+
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_CONT, wait_id, 0, 0)) >= 0)
2441+
{
2442+
// update pending process state
2443+
pending_proc->state = DMN_LNX_ProcessLaunchState_Exec;
2444+
shutdown_process = 0;
2445+
}
2446+
}
2447+
}
2448+
else if(pending_proc->state == DMN_LNX_ProcessLaunchState_Exit)
2449+
{
2450+
// move pending process node to the free list
2451+
dmn_lnx_process_launch_list_remove(&dmn_lnx_state->pending_procs, pending_proc);
2452+
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, pending_proc);
2453+
shutdown_process = 0;
2454+
}
24502455

2451-
break;
2456+
// exit on abnormal process init
2457+
if(shutdown_process)
2458+
{
2459+
OS_LNX_RETRY_ON_EINTR(kill(wait_id, SIGKILL));
2460+
pending_proc->state = DMN_LNX_ProcessLaunchState_Exit;
2461+
}
2462+
2463+
continue;
24522464
}
24532465
}
2454-
if(is_pending_launch) { continue; }
24552466
}
24562467

24572468
if(wifstopped || wifsignaled || wifexited)
@@ -2561,26 +2572,18 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
25612572
{
25622573
DMN_LNX_Entity *thread = dmn_lnx_thread_from_pid(wait_id);
25632574
DMN_LNX_Entity *process = thread->parent;
2564-
2565-
if(process == dmn_lnx_nil_entity)
2575+
if(process->debug_subprocesses)
25662576
{
2567-
dmn_lnx_handle_create_process(arena, &events, wait_id);
2577+
dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0);
2578+
dmn_lnx_handle_create_process(arena, &events, 1, wait_id);
25682579
}
25692580
else
25702581
{
2571-
if(process->debug_subprocesses)
2582+
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_DETACH, wait_id, 0, 0)) >= 0)
25722583
{
25732584
dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0);
2574-
dmn_lnx_handle_create_process(arena, &events, wait_id);
2575-
}
2576-
else
2577-
{
2578-
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_DETACH, wait_id, 0, 0)) >= 0)
2579-
{
2580-
dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0);
2581-
}
2582-
else { Assert(0 && "failed to detach"); }
25832585
}
2586+
else { Assert(0 && "failed to detach"); }
25842587
}
25852588
}break;
25862589
case PTRACE_EVENT_VFORK_DONE:
@@ -2630,7 +2633,13 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
26302633
}
26312634
}
26322635
else { Assert(0 && "unexpected stop code"); }
2633-
} while(stopped_threads < running_threads.count);
2636+
2637+
// do not wait if all threads are stopped and there are no launching processes
2638+
if(stopped_threads >= running_threads.count && dmn_lnx_state->pending_procs.count == 0)
2639+
{
2640+
break;
2641+
}
2642+
}
26342643

26352644
// finalize halter state
26362645
if(is_halt_done)

src/demon/linux/demon_core_linux.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,22 @@ struct DMN_LNX_EntityList
248248
////////////////////////////////
249249
//~ rjf: Main State Bundle
250250

251+
typedef U32 DMN_LNX_ProcessLaunchState;
252+
enum
253+
{
254+
DMN_LNX_ProcessLaunchState_Null,
255+
DMN_LNX_ProcessLaunchState_SigStop,
256+
DMN_LNX_ProcessLaunchState_Exec,
257+
DMN_LNX_ProcessLaunchState_Exit,
258+
};
259+
251260
typedef struct DMN_LNX_ProcessLaunch DMN_LNX_ProcessLaunch;
252261
struct DMN_LNX_ProcessLaunch
253262
{
254-
B32 debug_subprocesses;
255-
pid_t pid;
263+
B32 debug_subprocesses;
264+
pid_t pid;
265+
DMN_LNX_ProcessLaunchState state;
266+
256267
DMN_LNX_ProcessLaunch *next;
257268
DMN_LNX_ProcessLaunch *prev;
258269
};
@@ -286,14 +297,13 @@ struct DMN_LNX_State
286297
DMN_LNX_Entity *entities_base;
287298
DMN_LNX_Entity *free_entity;
288299
U64 entities_count;
289-
HashTable *tid_ht; // thread id -> thread entity
290-
HashTable *pid_ht; // process id -> process entity
300+
301+
HashTable *tid_ht; // thread id -> thread entity
302+
HashTable *pid_ht; // process id -> process entity
291303

292304
// pid tracking
293305
U64 active_process_count;
294-
DMN_LNX_ProcessLaunchList pending_stopsig;
295-
DMN_LNX_ProcessLaunchList pending_creation;
296-
DMN_LNX_ProcessLaunchList active_pids;
306+
DMN_LNX_ProcessLaunchList pending_procs;
297307
DMN_LNX_ProcessLaunchList free_pids;
298308

299309
// halter

0 commit comments

Comments
 (0)