PURPOSE:
I am theorising as to how one would go about creating a pointer scanner in a Linux environment.DISCLAIMER:
My findings have been tested on Debian Bookworm (current stable) and a Gentoo system with a custom kernel. No differences have been observed.PROBLEM:
Without attaching debuggers to a target process, I want to be able to identify the VMA of the stack of each thread/child task. This should be achievable using the proc pseudo-filesystem
DISCUSSION:
Prior to Linux 4.5, /proc/[parent_tid]/maps
would label the parent task's stack region with [STACK]
in the pathname field, and label each child task's stack region with [STACK:child_tid]
.
Following Linux 4.5, only the parent task's stack region maintains its [STACK]
label. Child task stack regions now have no label. In the commit message for this change (See link 1), Johannes Weiner states that child task stack VMAs can still be viewed by observing their process maps via /proc/[parent_tid]/task/[child_tid]/maps
.
This has proven ineffective to me. The memory region maps are identical across parent and child tasks. /proc/[parent_tid]/maps
≡ /proc/[parent_tid]/task/[child_tid*]/maps
. This ultimately means that the [STACK]
label is attached to the same region.
/proc/[tid]/stat
can be used to find the VMA of the bottom of the stack for a given task. It is the 28th value (see man 5 proc). Once again, /proc/[parent_tid]/stat
≡ /proc/[parent_tid]/task/[child_tid*]/stat
. Clearly the child task does not share the start of the stack with every other task in the process.
clone(2)
, the system call used to create new child tasks, takes a stack pointer as a parameter. The most obvious way to acquire memory for a stack is via an anonymous mmap(2)
. Anonymous memory maps do not have a label in /proc/[tid]/maps
. From observing a few single threaded and multi-threaded processes, there is a direct correlation between the number of anonymous memory mappings and the threads of a program. While such mappings are used for more than thread stacks, I very much expect thread stacks to be allocated this way. Each memory mapping gets its own entry in /proc/[tid]/maps
. Surely there is a way to determine which of these act as child task stacks?
What am I getting wrong here?
RELATED LINKS:
- Commit for removing the
[STACK:tid]
label from/proc/[tid]/maps
:
https://lists.ubuntu.com/archives/kernel-team/2016-March/074681.html