标题: randomize_va_space对SystemTap/cmdline_str()的影响
这类问题都是上下文强相关的,先说一下关键环境信息,CentOS 7.x。
$ uname -r 3.10.0-1160.11.1.el7.x86_64
$ stap -V Systemtap translator/driver (version 4.0/0.176, rpm 4.0-13.el7)
当时用如下命令监控对sshd_config的读写操作:
stap -e 'probe kernel.function("vfs_read"),kernel.function("vfs_write") {filename=fullpath_struct_file(task_current(),$file);if(filename == @1) {printf("[%u][%s][%s]\n",pid(),cmdline_str(),probefunc())}}' /etc/ssh/sshd_config
然后在另一个bash中手工执行:
grep PermitRootLogin /etc/ssh/sshd_config
cmdline_str()可以取回命令行。然后将stap丢那里保持执行,意外发现有个进程读 取sshd_config,但cmdline_str()取不到命令行。
[16097][grep --color=auto PermitRootLogin /etc/ssh/sshd_config][vfs_read] [17108][ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""][vfs_read]
16097是手工执行的grep,17108是另一个进程。"ps auwx | grep 17108"发现该进程 已经退出了。
stap -v filemonitor_1.stp /etc/ssh/sshd_config
grep PermitRootLogin /etc/ssh/sshd_config
$ uname -r
3.10.0-1160.11.1.el7.x86_64
返回指定pid的ppid,没有容错
function ppid_ex:long ( pid:long ) { task = pid2task( pid ) ppid = task_pid( task_parent( task ) ) return ppid }
probe kernel.function("vfs_read"), kernel.function("vfs_write") { filename = fullpath_struct_file( task_current(), $file ); if ( filename == @1 ) { p = pid() p1 = ppid() p2 = ppid_ex( p1 ) p3 = ppid_ex( p2 ) p4 = ppid_ex( p3 ) printf ( "[%u][%s][%u][%s][%u][%s][%u][%s][%u][%s][%s][%s]\n", p, pid2execname( p ), p1, pid2execname( p1 ), p2, pid2execname( p2 ), p3, pid2execname( p3 ), p4, pid2execname( p4 ), probefunc(), cmdline_str() ) } }
后来用filemonitor_1.stp发现有这么一种进程树关系:
systemd(1) bash(18341) // 执行完会退出 some.sh(18354) // 执行完会退出 some.sh(18420) // 执行完会退出 grep(18430) // 执行完会退出
有个定时任务在执行some.sh,最终有条grep命令访问sshd_config。
SystemTap/cmdline_str()取不到前述grep(18430)的命令行。grep(16097)是在bash 中手工执行的,不存在该问题。
排查过程比较偶然,直接说结论,最后发现是randomize_va_space的锅。
cat /proc/sys/kernel/randomize_va_space sysctl -n kernel.randomize_va_space
此时为0,cmdline_str()受影响,取不到前述grep(18430)的命令行。
echo 2 > /proc/sys/kernel/randomize_va_space sysctl -w kernel.randomize_va_space=2
此时为2,cmdline_str()始终正常,可以取到前述grep(18430)的命令行。
如今一般环境中randomize_va_space缺省为2,但我这个环境中因故该值为0,意外发 现这个坑。难道ASLR会影响对task->mm->arg_start、task->mm->arg_end的访问?不 予深究。
开头就强调过,这类问题都是上下文强相关的。若没有碰上这种问题,无所谓,若碰 上了,不妨检查一下randomize_va_space。
顺便说一下为什么SystemTap没有提供API获取指定进程的cmdline。
参看
/usr/share/systemtap/tapset/linux/context.stp
function cmdline_args:string(n:long, m:long, delim:string) function cmdline_arg:string(n:long) function cmdline_str:string()
/usr/share/systemtap/tapset/linux/task.stp
function pid2execname:string (pid:long) function task_execname:string (task:long) kernel_string(@task(task)->comm)
虽然可以指定task,但并未切换task,task->mm->arg_start、task->mm->arg_end这 两个指针无法用user_string()访问用户态内存。
pid2execname()从task->comm取字符串,这个变量在内核态,无需切换task。下面是 在crash中查看task->comm:
crash> struct task_struct.comm -xo struct task_struct { [0x678] char comm[16]; }
如果欺骗ps,只改task->mm->arg_start处的argv[0]不行,还得改task->comm。
扯远了,就这样吧。