Skip to content

Commit 919cd58

Browse files
committed
fix(pydriver): prepend subprocess warmup to user code
After snapshot restore, the first subprocess.run with capture_output crashes on an unresolvable CoW page fault. Prepending two no-op subprocess.run(['true']) calls in the same PyRun_String resolves the needed CoW pages before the piped subprocess runs. Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent 7e54f3d commit 919cd58

1 file changed

Lines changed: 15 additions & 15 deletions

File tree

examples/python-agent-driver/hl_pydriver.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ static int run_code_with_exceptions(const char *code)
199199
return 1;
200200
}
201201

202+
static const char warmup_prefix[] =
203+
"import subprocess as __sp\n"
204+
"__sp.run(['true'])\n"
205+
"__sp.run(['true'])\n"
206+
"del __sp\n";
207+
202208
static void py_run_user_code(const uint8_t *fc, size_t fc_len)
203209
{
204210
if (g_py_fsbase)
@@ -209,20 +215,22 @@ static void py_run_user_code(const uint8_t *fc, size_t fc_len)
209215
if (!code)
210216
return;
211217

212-
char stack_buf[4096];
218+
size_t prefix_len = sizeof(warmup_prefix) - 1;
219+
size_t total = prefix_len + code_len;
220+
char stack_buf[8192];
213221
char *buf;
214-
if (code_len < sizeof(stack_buf)) {
215-
memcpy(stack_buf, code, code_len);
216-
stack_buf[code_len] = '\0';
222+
if (total < sizeof(stack_buf)) {
217223
buf = stack_buf;
218224
} else {
219-
buf = malloc(code_len + 1);
225+
buf = malloc(total + 1);
220226
if (!buf)
221227
return;
222-
memcpy(buf, code, code_len);
223-
buf[code_len] = '\0';
224228
}
225229

230+
memcpy(buf, warmup_prefix, prefix_len);
231+
memcpy(buf + prefix_len, code, code_len);
232+
buf[total] = '\0';
233+
226234
int exit_code = run_code_with_exceptions(buf);
227235

228236
if (buf != stack_buf)
@@ -344,14 +352,6 @@ int main(int argc, char **argv, char **envp)
344352
fflush(stdout);
345353
fflush(stderr);
346354

347-
/* Hand-rolled exit_group via inline syscall: skips glibc's
348-
* exit() atexit chain AND any TLS state glibc's syscall()
349-
* wrapper might touch (seen the latter corrupt Python's TLS
350-
* between first-call halt and second-call re-entry in
351-
* testing). The kernel's exit_group handler on
352-
* Unikraft-Hyperlight halts the VM cleanly — same end effect
353-
* as a normal return, just without the destructive cleanup.
354-
*/
355355
register long rax __asm__("rax") = 231; /* SYS_exit_group */
356356
register long rdi __asm__("rdi") = 0;
357357
__asm__ volatile("syscall" : : "r"(rax), "r"(rdi)

0 commit comments

Comments
 (0)