brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · c397336 Raw
386 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <test_progs.h>4 5#ifdef __x86_64__6 7#include <unistd.h>8#include <asm/ptrace.h>9#include <linux/compiler.h>10#include <linux/stringify.h>11#include <sys/wait.h>12#include <sys/syscall.h>13#include <sys/prctl.h>14#include <asm/prctl.h>15#include "uprobe_syscall.skel.h"16#include "uprobe_syscall_executed.skel.h"17 18__naked unsigned long uretprobe_regs_trigger(void)19{20	asm volatile (21		"movq $0xdeadbeef, %rax\n"22		"ret\n"23	);24}25 26__naked void uretprobe_regs(struct pt_regs *before, struct pt_regs *after)27{28	asm volatile (29		"movq %r15,   0(%rdi)\n"30		"movq %r14,   8(%rdi)\n"31		"movq %r13,  16(%rdi)\n"32		"movq %r12,  24(%rdi)\n"33		"movq %rbp,  32(%rdi)\n"34		"movq %rbx,  40(%rdi)\n"35		"movq %r11,  48(%rdi)\n"36		"movq %r10,  56(%rdi)\n"37		"movq  %r9,  64(%rdi)\n"38		"movq  %r8,  72(%rdi)\n"39		"movq %rax,  80(%rdi)\n"40		"movq %rcx,  88(%rdi)\n"41		"movq %rdx,  96(%rdi)\n"42		"movq %rsi, 104(%rdi)\n"43		"movq %rdi, 112(%rdi)\n"44		"movq   $0, 120(%rdi)\n" /* orig_rax */45		"movq   $0, 128(%rdi)\n" /* rip      */46		"movq   $0, 136(%rdi)\n" /* cs       */47		"pushf\n"48		"pop %rax\n"49		"movq %rax, 144(%rdi)\n" /* eflags   */50		"movq %rsp, 152(%rdi)\n" /* rsp      */51		"movq   $0, 160(%rdi)\n" /* ss       */52 53		/* save 2nd argument */54		"pushq %rsi\n"55		"call uretprobe_regs_trigger\n"56 57		/* save  return value and load 2nd argument pointer to rax */58		"pushq %rax\n"59		"movq 8(%rsp), %rax\n"60 61		"movq %r15,   0(%rax)\n"62		"movq %r14,   8(%rax)\n"63		"movq %r13,  16(%rax)\n"64		"movq %r12,  24(%rax)\n"65		"movq %rbp,  32(%rax)\n"66		"movq %rbx,  40(%rax)\n"67		"movq %r11,  48(%rax)\n"68		"movq %r10,  56(%rax)\n"69		"movq  %r9,  64(%rax)\n"70		"movq  %r8,  72(%rax)\n"71		"movq %rcx,  88(%rax)\n"72		"movq %rdx,  96(%rax)\n"73		"movq %rsi, 104(%rax)\n"74		"movq %rdi, 112(%rax)\n"75		"movq   $0, 120(%rax)\n" /* orig_rax */76		"movq   $0, 128(%rax)\n" /* rip      */77		"movq   $0, 136(%rax)\n" /* cs       */78 79		/* restore return value and 2nd argument */80		"pop %rax\n"81		"pop %rsi\n"82 83		"movq %rax,  80(%rsi)\n"84 85		"pushf\n"86		"pop %rax\n"87 88		"movq %rax, 144(%rsi)\n" /* eflags   */89		"movq %rsp, 152(%rsi)\n" /* rsp      */90		"movq   $0, 160(%rsi)\n" /* ss       */91		"ret\n"92);93}94 95static void test_uretprobe_regs_equal(void)96{97	struct uprobe_syscall *skel = NULL;98	struct pt_regs before = {}, after = {};99	unsigned long *pb = (unsigned long *) &before;100	unsigned long *pa = (unsigned long *) &after;101	unsigned long *pp;102	unsigned int i, cnt;103	int err;104 105	skel = uprobe_syscall__open_and_load();106	if (!ASSERT_OK_PTR(skel, "uprobe_syscall__open_and_load"))107		goto cleanup;108 109	err = uprobe_syscall__attach(skel);110	if (!ASSERT_OK(err, "uprobe_syscall__attach"))111		goto cleanup;112 113	uretprobe_regs(&before, &after);114 115	pp = (unsigned long *) &skel->bss->regs;116	cnt = sizeof(before)/sizeof(*pb);117 118	for (i = 0; i < cnt; i++) {119		unsigned int offset = i * sizeof(unsigned long);120 121		/*122		 * Check register before and after uretprobe_regs_trigger call123		 * that triggers the uretprobe.124		 */125		switch (offset) {126		case offsetof(struct pt_regs, rax):127			ASSERT_EQ(pa[i], 0xdeadbeef, "return value");128			break;129		default:130			if (!ASSERT_EQ(pb[i], pa[i], "register before-after value check"))131				fprintf(stdout, "failed register offset %u\n", offset);132		}133 134		/*135		 * Check register seen from bpf program and register after136		 * uretprobe_regs_trigger call137		 */138		switch (offset) {139		/*140		 * These values will be different (not set in uretprobe_regs),141		 * we don't care.142		 */143		case offsetof(struct pt_regs, orig_rax):144		case offsetof(struct pt_regs, rip):145		case offsetof(struct pt_regs, cs):146		case offsetof(struct pt_regs, rsp):147		case offsetof(struct pt_regs, ss):148			break;149		default:150			if (!ASSERT_EQ(pp[i], pa[i], "register prog-after value check"))151				fprintf(stdout, "failed register offset %u\n", offset);152		}153	}154 155cleanup:156	uprobe_syscall__destroy(skel);157}158 159#define BPF_TESTMOD_UPROBE_TEST_FILE "/sys/kernel/bpf_testmod_uprobe"160 161static int write_bpf_testmod_uprobe(unsigned long offset)162{163	size_t n, ret;164	char buf[30];165	int fd;166 167	n = sprintf(buf, "%lu", offset);168 169	fd = open(BPF_TESTMOD_UPROBE_TEST_FILE, O_WRONLY);170	if (fd < 0)171		return -errno;172 173	ret = write(fd, buf, n);174	close(fd);175	return ret != n ? (int) ret : 0;176}177 178static void test_uretprobe_regs_change(void)179{180	struct pt_regs before = {}, after = {};181	unsigned long *pb = (unsigned long *) &before;182	unsigned long *pa = (unsigned long *) &after;183	unsigned long cnt = sizeof(before)/sizeof(*pb);184	unsigned int i, err, offset;185 186	offset = get_uprobe_offset(uretprobe_regs_trigger);187 188	err = write_bpf_testmod_uprobe(offset);189	if (!ASSERT_OK(err, "register_uprobe"))190		return;191 192	uretprobe_regs(&before, &after);193 194	err = write_bpf_testmod_uprobe(0);195	if (!ASSERT_OK(err, "unregister_uprobe"))196		return;197 198	for (i = 0; i < cnt; i++) {199		unsigned int offset = i * sizeof(unsigned long);200 201		switch (offset) {202		case offsetof(struct pt_regs, rax):203			ASSERT_EQ(pa[i], 0x12345678deadbeef, "rax");204			break;205		case offsetof(struct pt_regs, rcx):206			ASSERT_EQ(pa[i], 0x87654321feebdaed, "rcx");207			break;208		case offsetof(struct pt_regs, r11):209			ASSERT_EQ(pa[i], (__u64) -1, "r11");210			break;211		default:212			if (!ASSERT_EQ(pa[i], pb[i], "register before-after value check"))213				fprintf(stdout, "failed register offset %u\n", offset);214		}215	}216}217 218#ifndef __NR_uretprobe219#define __NR_uretprobe 335220#endif221 222__naked unsigned long uretprobe_syscall_call_1(void)223{224	/*225	 * Pretend we are uretprobe trampoline to trigger the return226	 * probe invocation in order to verify we get SIGILL.227	 */228	asm volatile (229		"pushq %rax\n"230		"pushq %rcx\n"231		"pushq %r11\n"232		"movq $" __stringify(__NR_uretprobe) ", %rax\n"233		"syscall\n"234		"popq %r11\n"235		"popq %rcx\n"236		"retq\n"237	);238}239 240__naked unsigned long uretprobe_syscall_call(void)241{242	asm volatile (243		"call uretprobe_syscall_call_1\n"244		"retq\n"245	);246}247 248static void test_uretprobe_syscall_call(void)249{250	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts,251		.retprobe = true,252	);253	struct uprobe_syscall_executed *skel;254	int pid, status, err, go[2], c;255 256	if (!ASSERT_OK(pipe(go), "pipe"))257		return;258 259	skel = uprobe_syscall_executed__open_and_load();260	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))261		goto cleanup;262 263	pid = fork();264	if (!ASSERT_GE(pid, 0, "fork"))265		goto cleanup;266 267	/* child */268	if (pid == 0) {269		close(go[1]);270 271		/* wait for parent's kick */272		err = read(go[0], &c, 1);273		if (err != 1)274			exit(-1);275 276		uretprobe_syscall_call();277		_exit(0);278	}279 280	skel->links.test = bpf_program__attach_uprobe_multi(skel->progs.test, pid,281							    "/proc/self/exe",282							    "uretprobe_syscall_call", &opts);283	if (!ASSERT_OK_PTR(skel->links.test, "bpf_program__attach_uprobe_multi"))284		goto cleanup;285 286	/* kick the child */287	write(go[1], &c, 1);288	err = waitpid(pid, &status, 0);289	ASSERT_EQ(err, pid, "waitpid");290 291	/* verify the child got killed with SIGILL */292	ASSERT_EQ(WIFSIGNALED(status), 1, "WIFSIGNALED");293	ASSERT_EQ(WTERMSIG(status), SIGILL, "WTERMSIG");294 295	/* verify the uretprobe program wasn't called */296	ASSERT_EQ(skel->bss->executed, 0, "executed");297 298cleanup:299	uprobe_syscall_executed__destroy(skel);300	close(go[1]);301	close(go[0]);302}303 304/*305 * Borrowed from tools/testing/selftests/x86/test_shadow_stack.c.306 *307 * For use in inline enablement of shadow stack.308 *309 * The program can't return from the point where shadow stack gets enabled310 * because there will be no address on the shadow stack. So it can't use311 * syscall() for enablement, since it is a function.312 *313 * Based on code from nolibc.h. Keep a copy here because this can't pull314 * in all of nolibc.h.315 */316#define ARCH_PRCTL(arg1, arg2)					\317({								\318	long _ret;						\319	register long _num  asm("eax") = __NR_arch_prctl;	\320	register long _arg1 asm("rdi") = (long)(arg1);		\321	register long _arg2 asm("rsi") = (long)(arg2);		\322								\323	asm volatile (						\324		"syscall\n"					\325		: "=a"(_ret)					\326		: "r"(_arg1), "r"(_arg2),			\327		  "0"(_num)					\328		: "rcx", "r11", "memory", "cc"			\329	);							\330	_ret;							\331})332 333#ifndef ARCH_SHSTK_ENABLE334#define ARCH_SHSTK_ENABLE	0x5001335#define ARCH_SHSTK_DISABLE	0x5002336#define ARCH_SHSTK_SHSTK	(1ULL <<  0)337#endif338 339static void test_uretprobe_shadow_stack(void)340{341	if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) {342		test__skip();343		return;344	}345 346	/* Run all of the uretprobe tests. */347	test_uretprobe_regs_equal();348	test_uretprobe_regs_change();349	test_uretprobe_syscall_call();350 351	ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);352}353#else354static void test_uretprobe_regs_equal(void)355{356	test__skip();357}358 359static void test_uretprobe_regs_change(void)360{361	test__skip();362}363 364static void test_uretprobe_syscall_call(void)365{366	test__skip();367}368 369static void test_uretprobe_shadow_stack(void)370{371	test__skip();372}373#endif374 375void test_uprobe_syscall(void)376{377	if (test__start_subtest("uretprobe_regs_equal"))378		test_uretprobe_regs_equal();379	if (test__start_subtest("uretprobe_regs_change"))380		test_uretprobe_regs_change();381	if (test__start_subtest("uretprobe_syscall_call"))382		test_uretprobe_syscall_call();383	if (test__start_subtest("uretprobe_shadow_stack"))384		test_uretprobe_shadow_stack();385}386