Skip to content

Implement the clone3 syscall#1639

Open
retrocpugeek wants to merge 1 commit into
qilingframework:devfrom
retrocpugeek:fix/clone3-syscall
Open

Implement the clone3 syscall#1639
retrocpugeek wants to merge 1 commit into
qilingframework:devfrom
retrocpugeek:fix/clone3-syscall

Conversation

@retrocpugeek

Copy link
Copy Markdown

Summary

Implements the clone3 syscall, which currently has no handler in Qiling — it only appears in the number→name maps. Recent glibc issues clone3 (not the legacy clone) from pthread_create, so thread creation fails on any guest built against a modern glibc. And because Qiling doesn't return -ENOSYS for unimplemented syscalls, glibc's built-in clone3clone fallback never fires.

Closes #1638.

What it does

Adds ql_syscall_clone3, which unpacks struct clone_args and delegates to the existing ql_syscall_clone. Non-obvious translations:

  • child_stack = stack + stack_sizeclone3 passes the stack base plus a separate size; legacy clone wants the highest stack address.
  • exit_signal is its own struct clone_args field; legacy clone packs it into the low CSIGNAL byte of flags.
  • x8664 pre-swap — ql_syscall_clone swaps newtls<->child_tidptr to undo x8664's raw-syscall register order; since clone3 hands over already-logical args, we pre-swap on x8664 so it cancels out.

The legacy clone path is untouched.

Testing

  • New self-contained regression test test_elf_multithread.ELFTest.test_clone3_translates_to_clone drives ql_syscall_clone3 directly and asserts the translated arguments for both the generic path and the x8664 swap. Runs on stock unicorn — no clone3 binary required.
  • Full test_elf_multithread.py suite passes on stock unicorn 2.1.3 (24 tests, 1 pre-existing skip).
  • Verified end-to-end separately with a real MIPS64 BE glibc pthread binary: threads are created and joined correctly with this handler in place.

Modern glibc (recent toolchains) issues clone3 from pthread_create rather
than clone, so thread creation fails on any guest built against it: Qiling
does not return -ENOSYS for unimplemented syscalls (it logs a warning and
leaves the return register untouched), so glibc's clone3->clone fallback
never fires.

Add ql_syscall_clone3, which unpacks struct clone_args and delegates to the
existing clone() handler. Translations: child_stack = stack + stack_size
(clone3 passes the stack base plus a size; legacy clone wants the highest
address), exit_signal folded into the flags' CSIGNAL byte, and an x8664
pre-swap that cancels ql_syscall_clone's arch-specific newtls<->child_tidptr
swap.

Add a self-contained regression test (test_clone3_translates_to_clone) that
drives ql_syscall_clone3 directly and asserts the translation for both the
generic path and the x8664 swap. Runs on stock unicorn; no clone3 binary
needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant