1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) PLUMgrid, Inc. 3*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 4*387f9dfdSAndroid Build Coastguard Worker 5*387f9dfdSAndroid Build Coastguard Workerimport bcc 6*387f9dfdSAndroid Build Coastguard Workerimport ctypes 7*387f9dfdSAndroid Build Coastguard Workerimport errno 8*387f9dfdSAndroid Build Coastguard Workerimport os 9*387f9dfdSAndroid Build Coastguard Workerimport subprocess 10*387f9dfdSAndroid Build Coastguard Workerimport shutil 11*387f9dfdSAndroid Build Coastguard Workerimport time 12*387f9dfdSAndroid Build Coastguard Workerimport unittest 13*387f9dfdSAndroid Build Coastguard Worker 14*387f9dfdSAndroid Build Coastguard Workerclass TestUprobes(unittest.TestCase): 15*387f9dfdSAndroid Build Coastguard Worker def test_simple_library(self): 16*387f9dfdSAndroid Build Coastguard Worker text = b""" 17*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 18*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(stats, u64, 1); 19*387f9dfdSAndroid Build Coastguard Workerstatic void incr(int idx) { 20*387f9dfdSAndroid Build Coastguard Worker u64 *ptr = stats.lookup(&idx); 21*387f9dfdSAndroid Build Coastguard Worker if (ptr) 22*387f9dfdSAndroid Build Coastguard Worker ++(*ptr); 23*387f9dfdSAndroid Build Coastguard Worker} 24*387f9dfdSAndroid Build Coastguard Workerint count(struct pt_regs *ctx) { 25*387f9dfdSAndroid Build Coastguard Worker bpf_trace_printk("count() uprobe fired"); 26*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 27*387f9dfdSAndroid Build Coastguard Worker if (pid == PID) 28*387f9dfdSAndroid Build Coastguard Worker incr(0); 29*387f9dfdSAndroid Build Coastguard Worker return 0; 30*387f9dfdSAndroid Build Coastguard Worker}""" 31*387f9dfdSAndroid Build Coastguard Worker test_pid = os.getpid() 32*387f9dfdSAndroid Build Coastguard Worker text = text.replace(b"PID", b"%d" % test_pid) 33*387f9dfdSAndroid Build Coastguard Worker b = bcc.BPF(text=text) 34*387f9dfdSAndroid Build Coastguard Worker b.attach_uprobe(name=b"c", sym=b"malloc_stats", fn_name=b"count", pid=test_pid) 35*387f9dfdSAndroid Build Coastguard Worker b.attach_uretprobe(name=b"c", sym=b"malloc_stats", fn_name=b"count", pid=test_pid) 36*387f9dfdSAndroid Build Coastguard Worker libc = ctypes.CDLL("libc.so.6") 37*387f9dfdSAndroid Build Coastguard Worker libc.malloc_stats.restype = None 38*387f9dfdSAndroid Build Coastguard Worker libc.malloc_stats.argtypes = [] 39*387f9dfdSAndroid Build Coastguard Worker libc.malloc_stats() 40*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(b[b"stats"][ctypes.c_int(0)].value, 2) 41*387f9dfdSAndroid Build Coastguard Worker b.detach_uretprobe(name=b"c", sym=b"malloc_stats", pid=test_pid) 42*387f9dfdSAndroid Build Coastguard Worker b.detach_uprobe(name=b"c", sym=b"malloc_stats", pid=test_pid) 43*387f9dfdSAndroid Build Coastguard Worker 44*387f9dfdSAndroid Build Coastguard Worker def test_simple_binary(self): 45*387f9dfdSAndroid Build Coastguard Worker text = b""" 46*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 47*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(stats, u64, 1); 48*387f9dfdSAndroid Build Coastguard Workerstatic void incr(int idx) { 49*387f9dfdSAndroid Build Coastguard Worker u64 *ptr = stats.lookup(&idx); 50*387f9dfdSAndroid Build Coastguard Worker if (ptr) 51*387f9dfdSAndroid Build Coastguard Worker ++(*ptr); 52*387f9dfdSAndroid Build Coastguard Worker} 53*387f9dfdSAndroid Build Coastguard Workerint count(struct pt_regs *ctx) { 54*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 55*387f9dfdSAndroid Build Coastguard Worker incr(0); 56*387f9dfdSAndroid Build Coastguard Worker return 0; 57*387f9dfdSAndroid Build Coastguard Worker}""" 58*387f9dfdSAndroid Build Coastguard Worker b = bcc.BPF(text=text) 59*387f9dfdSAndroid Build Coastguard Worker pythonpath = b"/usr/bin/python3" 60*387f9dfdSAndroid Build Coastguard Worker symname = b"_start" 61*387f9dfdSAndroid Build Coastguard Worker b.attach_uprobe(name=pythonpath, sym=symname, fn_name=b"count") 62*387f9dfdSAndroid Build Coastguard Worker b.attach_uretprobe(name=pythonpath, sym=symname, fn_name=b"count") 63*387f9dfdSAndroid Build Coastguard Worker with os.popen(pythonpath.decode() + " -V") as f: 64*387f9dfdSAndroid Build Coastguard Worker pass 65*387f9dfdSAndroid Build Coastguard Worker self.assertGreater(b[b"stats"][ctypes.c_int(0)].value, 0) 66*387f9dfdSAndroid Build Coastguard Worker b.detach_uretprobe(name=pythonpath, sym=symname) 67*387f9dfdSAndroid Build Coastguard Worker b.detach_uprobe(name=pythonpath, sym=symname) 68*387f9dfdSAndroid Build Coastguard Worker 69*387f9dfdSAndroid Build Coastguard Worker def test_mount_namespace(self): 70*387f9dfdSAndroid Build Coastguard Worker text = b""" 71*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 72*387f9dfdSAndroid Build Coastguard WorkerBPF_TABLE("array", int, u64, stats, 1); 73*387f9dfdSAndroid Build Coastguard Workerstatic void incr(int idx) { 74*387f9dfdSAndroid Build Coastguard Worker u64 *ptr = stats.lookup(&idx); 75*387f9dfdSAndroid Build Coastguard Worker if (ptr) 76*387f9dfdSAndroid Build Coastguard Worker ++(*ptr); 77*387f9dfdSAndroid Build Coastguard Worker} 78*387f9dfdSAndroid Build Coastguard Workerint count(struct pt_regs *ctx) { 79*387f9dfdSAndroid Build Coastguard Worker bpf_trace_printk("count() uprobe fired"); 80*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 81*387f9dfdSAndroid Build Coastguard Worker if (pid == PID) 82*387f9dfdSAndroid Build Coastguard Worker incr(0); 83*387f9dfdSAndroid Build Coastguard Worker return 0; 84*387f9dfdSAndroid Build Coastguard Worker}""" 85*387f9dfdSAndroid Build Coastguard Worker # Need to import libc from ctypes to access unshare(2) 86*387f9dfdSAndroid Build Coastguard Worker libc = ctypes.CDLL("libc.so.6", use_errno=True) 87*387f9dfdSAndroid Build Coastguard Worker 88*387f9dfdSAndroid Build Coastguard Worker # Need to find path to libz.so.1 89*387f9dfdSAndroid Build Coastguard Worker libz_path = None 90*387f9dfdSAndroid Build Coastguard Worker p = subprocess.Popen(["ldconfig", "-p"], stdout=subprocess.PIPE) 91*387f9dfdSAndroid Build Coastguard Worker for l in p.stdout: 92*387f9dfdSAndroid Build Coastguard Worker n = l.split() 93*387f9dfdSAndroid Build Coastguard Worker if n[0] == b"libz.so.1": 94*387f9dfdSAndroid Build Coastguard Worker # if libz was already found, override only if new lib is more 95*387f9dfdSAndroid Build Coastguard Worker # specific (e.g. libc6,x86-64 vs libc6) 96*387f9dfdSAndroid Build Coastguard Worker if not libz_path or len(n[1].split(b",")) > 1: 97*387f9dfdSAndroid Build Coastguard Worker libz_path = n[-1] 98*387f9dfdSAndroid Build Coastguard Worker p.wait() 99*387f9dfdSAndroid Build Coastguard Worker p.stdout.close() 100*387f9dfdSAndroid Build Coastguard Worker p = None 101*387f9dfdSAndroid Build Coastguard Worker 102*387f9dfdSAndroid Build Coastguard Worker self.assertIsNotNone(libz_path) 103*387f9dfdSAndroid Build Coastguard Worker 104*387f9dfdSAndroid Build Coastguard Worker # fork a child that we'll place in a separate mount namespace 105*387f9dfdSAndroid Build Coastguard Worker child_pid = os.fork() 106*387f9dfdSAndroid Build Coastguard Worker if child_pid == 0: 107*387f9dfdSAndroid Build Coastguard Worker # Unshare CLONE_NEWNS 108*387f9dfdSAndroid Build Coastguard Worker if libc.unshare(0x00020000) == -1: 109*387f9dfdSAndroid Build Coastguard Worker e = ctypes.get_errno() 110*387f9dfdSAndroid Build Coastguard Worker raise OSError(e, errno.errorcode[e]) 111*387f9dfdSAndroid Build Coastguard Worker 112*387f9dfdSAndroid Build Coastguard Worker # Remount root MS_REC|MS_PRIVATE 113*387f9dfdSAndroid Build Coastguard Worker if libc.mount(None, b"/", None, (1<<14)|(1<<18) , None) == -1: 114*387f9dfdSAndroid Build Coastguard Worker e = ctypes.get_errno() 115*387f9dfdSAndroid Build Coastguard Worker raise OSError(e, errno.errorcode[e]) 116*387f9dfdSAndroid Build Coastguard Worker 117*387f9dfdSAndroid Build Coastguard Worker if libc.mount(b"tmpfs", b"/tmp", b"tmpfs", 0, None) == -1: 118*387f9dfdSAndroid Build Coastguard Worker e = ctypes.get_errno() 119*387f9dfdSAndroid Build Coastguard Worker raise OSError(e, errno.errorcode[e]) 120*387f9dfdSAndroid Build Coastguard Worker 121*387f9dfdSAndroid Build Coastguard Worker shutil.copy(libz_path, b"/tmp") 122*387f9dfdSAndroid Build Coastguard Worker 123*387f9dfdSAndroid Build Coastguard Worker libz = ctypes.CDLL("/tmp/libz.so.1") 124*387f9dfdSAndroid Build Coastguard Worker time.sleep(3) 125*387f9dfdSAndroid Build Coastguard Worker libz.zlibVersion() 126*387f9dfdSAndroid Build Coastguard Worker time.sleep(5) 127*387f9dfdSAndroid Build Coastguard Worker os._exit(0) 128*387f9dfdSAndroid Build Coastguard Worker 129*387f9dfdSAndroid Build Coastguard Worker libname = b"/tmp/libz.so.1" 130*387f9dfdSAndroid Build Coastguard Worker symname = b"zlibVersion" 131*387f9dfdSAndroid Build Coastguard Worker text = text.replace(b"PID", b"%d" % child_pid) 132*387f9dfdSAndroid Build Coastguard Worker b = bcc.BPF(text=text) 133*387f9dfdSAndroid Build Coastguard Worker b.attach_uprobe(name=libname, sym=symname, fn_name=b"count", pid=child_pid) 134*387f9dfdSAndroid Build Coastguard Worker b.attach_uretprobe(name=libname, sym=symname, fn_name=b"count", pid=child_pid) 135*387f9dfdSAndroid Build Coastguard Worker time.sleep(5) 136*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(b[b"stats"][ctypes.c_int(0)].value, 2) 137*387f9dfdSAndroid Build Coastguard Worker b.detach_uretprobe(name=libname, sym=symname, pid=child_pid) 138*387f9dfdSAndroid Build Coastguard Worker b.detach_uprobe(name=libname, sym=symname, pid=child_pid) 139*387f9dfdSAndroid Build Coastguard Worker os.wait() 140*387f9dfdSAndroid Build Coastguard Worker 141*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__": 142*387f9dfdSAndroid Build Coastguard Worker unittest.main() 143