GDBserver x86_64 issue and a possible solution
Problem description
When You try to attach the gdbserver to a running process as a root from a shell the server quits with a message: "stack corruption detected Aborted"
Steps to reproduce
- Connect to a x86_64 emulator by 'adb shell';
- become root;
- Try to attach to any running process with by PID (for example 5432): /data/data/com.termux/files/usr/bin/gdbserver --attach :23945 5432 or try to start any process to debug /data/data/com.termux/files/usr/bin/gdbserver :23945 /data/data/com.termux/files/usr/bin/less
- The server fails to start with the message: stack corruption detected Aborted
Expected behavior
The server attaches to the process with the given PID: Attached; pid = 7734 Listening on port 23945
or (for the case when the process is started by gdbserver) Process /data/data/com.termux/files/usr/bin/less created; pid = 7674 Listening on port 23945
Additional information
gdbserver during startup calls function void linux_ptrace_test_ret_to_nx(void) (from nat/linux-ptrace.c): ... elf_gregset_t regs; ... if (ptrace (PTRACE_GETREGS, child, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) ®s) < 0) ...
And elf_gregset_t is a typedef of an array of 23 elements and the kernel returns 27 elements. There are also additional usages of this typedef in GDB server.
The issue is because of NDK's sysroot/usr/include/sys/procfs.h. Just to compare. The NDK r21d version of sys/procfs.h ... #include <sys/ucontext.h> __BEGIN_DECLS typedef unsigned long elf_greg_t; typedef elf_greg_t elf_gregset_t[NGREG]; ...
GNU version bits/procfs.h (from my current Ubuntu installation) ... /* And the whole bunch of them. We could have used `struct user_regs_struct' directly in the typedef, but tradition says that the register set is an array, which does have some peculiar semantics, so leave it that way. */ #define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof (elf_greg_t)) typedef elf_greg_t elf_gregset_t[ELF_NGREG]; ...
One way to fix it is to patch the NDK's sysroot/usr/include/sys/procfs.h before the build ... typedef unsigned long elf_greg_t; #if defined(__x86_64__) #define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t)) typedef elf_greg_t elf_gregset_t[ELF_NGREG]; #else typedef elf_greg_t elf_gregset_t[NGREG]; #endif ...
One way to fix it is to patch the NDK's sysroot/usr/include/sys/procfs.h before the build
Note that if Bionic libc uses elf_greg_t somewhere internally, changing its size in header files may cause further issues.
Headers, especially type definitions, must correspond to ones used by libc internally.
may cause further issues
Yes I understand it. That's why it is "a possible solution". Any additional solutions are welcome. Usually there is a self-test suit to test a GNU package. Other way is to dig deep inside and to check the flow. This is GNU package to work with GNU kernel. And kernel returns from arch/x86/kernel/ptrace.c
case PTRACE_GETREGS:
return copy_regset_to_user (
child, task_user_regset_view (get_current ()),
REGSET_GENERAL, 0, sizeof(struct user_regs_struct), datap);
and sizeof(struct user_regs_struct) comes from arch/x86/include/asm/user_64.h
struct user_regs_struct
{
unsigned long r15;
unsigned long r14;
unsigned long r13;
unsigned long r12;
unsigned long bp;
unsigned long bx;
unsigned long r11;
unsigned long r10;
unsigned long r9;
unsigned long r8;
unsigned long ax;
unsigned long cx;
unsigned long dx;
unsigned long si;
unsigned long di;
unsigned long orig_ax;
unsigned long ip;
unsigned long cs;
unsigned long flags;
unsigned long sp;
unsigned long ss;
unsigned long fs_base;
unsigned long gs_base;
unsigned long ds;
unsigned long es;
unsigned long fs;
unsigned long gs;
};
And as addition: now the gdbserver for x86_64 does not work at all. And nobody can test it on Android x86_64. And if we have a working version we can see feedback from community.
Well I've reported a bug to NDK team. So we can check https://android-review.googlesource.com/c/platform/bionic/+/1419433
The changes have been merged to fix the issue:
--- a/libc/include/sys/procfs.h
+++ b/libc/include/sys/procfs.h
@@ -26,16 +26,24 @@
* SUCH DAMAGE.
*/
-#ifndef _SYS_PROCFS_H_
-#define _SYS_PROCFS_H_
+#pragma once
#include <sys/cdefs.h>
+#include <sys/ptrace.h>
#include <sys/ucontext.h>
__BEGIN_DECLS
+#if defined(__arm__)
+#define ELF_NGREG (sizeof(struct user_regs) / sizeof(elf_greg_t))
+#elif defined(__aarch64__)
+#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t))
+#else
+#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t))
+#endif
+
typedef unsigned long elf_greg_t;
-typedef elf_greg_t elf_gregset_t[NGREG];
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
typedef fpregset_t elf_fpregset_t;
@@ -58,5 +66,3 @@
#define ELF_PRARGSZ 80
__END_DECLS
-
-#endif
The fix will be in NDK r22.
And we can apply the solution now to solve the startup issue for x86_64.
This issue/PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Ref https://github.com/android/ndk/issues/1347
Closing. We already are using NDK r26b.
The aforementioned patch has been applied in upstream: https://android.googlesource.com/toolchain/prebuilts/ndk/r26/+/refs/heads/main/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/sys/procfs.h