cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

test_sock.c (13301B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2018 Facebook
      3
      4#include <stdio.h>
      5#include <unistd.h>
      6
      7#include <arpa/inet.h>
      8#include <sys/types.h>
      9#include <sys/socket.h>
     10
     11#include <linux/filter.h>
     12
     13#include <bpf/bpf.h>
     14
     15#include "cgroup_helpers.h"
     16#include <bpf/bpf_endian.h>
     17#include "bpf_util.h"
     18
     19#define CG_PATH		"/foo"
     20#define MAX_INSNS	512
     21
     22char bpf_log_buf[BPF_LOG_BUF_SIZE];
     23static bool verbose = false;
     24
     25struct sock_test {
     26	const char *descr;
     27	/* BPF prog properties */
     28	struct bpf_insn	insns[MAX_INSNS];
     29	enum bpf_attach_type expected_attach_type;
     30	enum bpf_attach_type attach_type;
     31	/* Socket properties */
     32	int domain;
     33	int type;
     34	/* Endpoint to bind() to */
     35	const char *ip;
     36	unsigned short port;
     37	unsigned short port_retry;
     38	/* Expected test result */
     39	enum {
     40		LOAD_REJECT,
     41		ATTACH_REJECT,
     42		BIND_REJECT,
     43		SUCCESS,
     44		RETRY_SUCCESS,
     45		RETRY_REJECT
     46	} result;
     47};
     48
     49static struct sock_test tests[] = {
     50	{
     51		.descr = "bind4 load with invalid access: src_ip6",
     52		.insns = {
     53			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
     54			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
     55				    offsetof(struct bpf_sock, src_ip6[0])),
     56			BPF_MOV64_IMM(BPF_REG_0, 1),
     57			BPF_EXIT_INSN(),
     58		},
     59		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
     60		.attach_type = BPF_CGROUP_INET4_POST_BIND,
     61		.result = LOAD_REJECT,
     62	},
     63	{
     64		.descr = "bind4 load with invalid access: mark",
     65		.insns = {
     66			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
     67			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
     68				    offsetof(struct bpf_sock, mark)),
     69			BPF_MOV64_IMM(BPF_REG_0, 1),
     70			BPF_EXIT_INSN(),
     71		},
     72		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
     73		.attach_type = BPF_CGROUP_INET4_POST_BIND,
     74		.result = LOAD_REJECT,
     75	},
     76	{
     77		.descr = "bind6 load with invalid access: src_ip4",
     78		.insns = {
     79			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
     80			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
     81				    offsetof(struct bpf_sock, src_ip4)),
     82			BPF_MOV64_IMM(BPF_REG_0, 1),
     83			BPF_EXIT_INSN(),
     84		},
     85		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
     86		.attach_type = BPF_CGROUP_INET6_POST_BIND,
     87		.result = LOAD_REJECT,
     88	},
     89	{
     90		.descr = "sock_create load with invalid access: src_port",
     91		.insns = {
     92			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
     93			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
     94				    offsetof(struct bpf_sock, src_port)),
     95			BPF_MOV64_IMM(BPF_REG_0, 1),
     96			BPF_EXIT_INSN(),
     97		},
     98		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
     99		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
    100		.result = LOAD_REJECT,
    101	},
    102	{
    103		.descr = "sock_create load w/o expected_attach_type (compat mode)",
    104		.insns = {
    105			BPF_MOV64_IMM(BPF_REG_0, 1),
    106			BPF_EXIT_INSN(),
    107		},
    108		.expected_attach_type = 0,
    109		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
    110		.domain = AF_INET,
    111		.type = SOCK_STREAM,
    112		.ip = "127.0.0.1",
    113		.port = 8097,
    114		.result = SUCCESS,
    115	},
    116	{
    117		.descr = "sock_create load w/ expected_attach_type",
    118		.insns = {
    119			BPF_MOV64_IMM(BPF_REG_0, 1),
    120			BPF_EXIT_INSN(),
    121		},
    122		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
    123		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
    124		.domain = AF_INET,
    125		.type = SOCK_STREAM,
    126		.ip = "127.0.0.1",
    127		.port = 8097,
    128		.result = SUCCESS,
    129	},
    130	{
    131		.descr = "attach type mismatch bind4 vs bind6",
    132		.insns = {
    133			BPF_MOV64_IMM(BPF_REG_0, 1),
    134			BPF_EXIT_INSN(),
    135		},
    136		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
    137		.attach_type = BPF_CGROUP_INET6_POST_BIND,
    138		.result = ATTACH_REJECT,
    139	},
    140	{
    141		.descr = "attach type mismatch bind6 vs bind4",
    142		.insns = {
    143			BPF_MOV64_IMM(BPF_REG_0, 1),
    144			BPF_EXIT_INSN(),
    145		},
    146		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
    147		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    148		.result = ATTACH_REJECT,
    149	},
    150	{
    151		.descr = "attach type mismatch default vs bind4",
    152		.insns = {
    153			BPF_MOV64_IMM(BPF_REG_0, 1),
    154			BPF_EXIT_INSN(),
    155		},
    156		.expected_attach_type = 0,
    157		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    158		.result = ATTACH_REJECT,
    159	},
    160	{
    161		.descr = "attach type mismatch bind6 vs sock_create",
    162		.insns = {
    163			BPF_MOV64_IMM(BPF_REG_0, 1),
    164			BPF_EXIT_INSN(),
    165		},
    166		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
    167		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
    168		.result = ATTACH_REJECT,
    169	},
    170	{
    171		.descr = "bind4 reject all",
    172		.insns = {
    173			BPF_MOV64_IMM(BPF_REG_0, 0),
    174			BPF_EXIT_INSN(),
    175		},
    176		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
    177		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    178		.domain = AF_INET,
    179		.type = SOCK_STREAM,
    180		.ip = "0.0.0.0",
    181		.result = BIND_REJECT,
    182	},
    183	{
    184		.descr = "bind6 reject all",
    185		.insns = {
    186			BPF_MOV64_IMM(BPF_REG_0, 0),
    187			BPF_EXIT_INSN(),
    188		},
    189		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
    190		.attach_type = BPF_CGROUP_INET6_POST_BIND,
    191		.domain = AF_INET6,
    192		.type = SOCK_STREAM,
    193		.ip = "::",
    194		.result = BIND_REJECT,
    195	},
    196	{
    197		.descr = "bind6 deny specific IP & port",
    198		.insns = {
    199			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
    200
    201			/* if (ip == expected && port == expected) */
    202			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    203				    offsetof(struct bpf_sock, src_ip6[3])),
    204			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
    205				    __bpf_constant_ntohl(0x00000001), 4),
    206			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    207				    offsetof(struct bpf_sock, src_port)),
    208			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
    209
    210			/* return DENY; */
    211			BPF_MOV64_IMM(BPF_REG_0, 0),
    212			BPF_JMP_A(1),
    213
    214			/* else return ALLOW; */
    215			BPF_MOV64_IMM(BPF_REG_0, 1),
    216			BPF_EXIT_INSN(),
    217		},
    218		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
    219		.attach_type = BPF_CGROUP_INET6_POST_BIND,
    220		.domain = AF_INET6,
    221		.type = SOCK_STREAM,
    222		.ip = "::1",
    223		.port = 8193,
    224		.result = BIND_REJECT,
    225	},
    226	{
    227		.descr = "bind4 allow specific IP & port",
    228		.insns = {
    229			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
    230
    231			/* if (ip == expected && port == expected) */
    232			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    233				    offsetof(struct bpf_sock, src_ip4)),
    234			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
    235				    __bpf_constant_ntohl(0x7F000001), 4),
    236			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    237				    offsetof(struct bpf_sock, src_port)),
    238			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
    239
    240			/* return ALLOW; */
    241			BPF_MOV64_IMM(BPF_REG_0, 1),
    242			BPF_JMP_A(1),
    243
    244			/* else return DENY; */
    245			BPF_MOV64_IMM(BPF_REG_0, 0),
    246			BPF_EXIT_INSN(),
    247		},
    248		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
    249		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    250		.domain = AF_INET,
    251		.type = SOCK_STREAM,
    252		.ip = "127.0.0.1",
    253		.port = 4098,
    254		.result = SUCCESS,
    255	},
    256	{
    257		.descr = "bind4 deny specific IP & port of TCP, and retry",
    258		.insns = {
    259			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
    260
    261			/* if (ip == expected && port == expected) */
    262			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    263				    offsetof(struct bpf_sock, src_ip4)),
    264			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
    265				    __bpf_constant_ntohl(0x7F000001), 4),
    266			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    267				    offsetof(struct bpf_sock, src_port)),
    268			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
    269
    270			/* return DENY; */
    271			BPF_MOV64_IMM(BPF_REG_0, 0),
    272			BPF_JMP_A(1),
    273
    274			/* else return ALLOW; */
    275			BPF_MOV64_IMM(BPF_REG_0, 1),
    276			BPF_EXIT_INSN(),
    277		},
    278		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
    279		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    280		.domain = AF_INET,
    281		.type = SOCK_STREAM,
    282		.ip = "127.0.0.1",
    283		.port = 4098,
    284		.port_retry = 5000,
    285		.result = RETRY_SUCCESS,
    286	},
    287	{
    288		.descr = "bind4 deny specific IP & port of UDP, and retry",
    289		.insns = {
    290			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
    291
    292			/* if (ip == expected && port == expected) */
    293			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    294				    offsetof(struct bpf_sock, src_ip4)),
    295			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
    296				    __bpf_constant_ntohl(0x7F000001), 4),
    297			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    298				    offsetof(struct bpf_sock, src_port)),
    299			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
    300
    301			/* return DENY; */
    302			BPF_MOV64_IMM(BPF_REG_0, 0),
    303			BPF_JMP_A(1),
    304
    305			/* else return ALLOW; */
    306			BPF_MOV64_IMM(BPF_REG_0, 1),
    307			BPF_EXIT_INSN(),
    308		},
    309		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
    310		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    311		.domain = AF_INET,
    312		.type = SOCK_DGRAM,
    313		.ip = "127.0.0.1",
    314		.port = 4098,
    315		.port_retry = 5000,
    316		.result = RETRY_SUCCESS,
    317	},
    318	{
    319		.descr = "bind6 deny specific IP & port, and retry",
    320		.insns = {
    321			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
    322
    323			/* if (ip == expected && port == expected) */
    324			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    325				    offsetof(struct bpf_sock, src_ip6[3])),
    326			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
    327				    __bpf_constant_ntohl(0x00000001), 4),
    328			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
    329				    offsetof(struct bpf_sock, src_port)),
    330			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
    331
    332			/* return DENY; */
    333			BPF_MOV64_IMM(BPF_REG_0, 0),
    334			BPF_JMP_A(1),
    335
    336			/* else return ALLOW; */
    337			BPF_MOV64_IMM(BPF_REG_0, 1),
    338			BPF_EXIT_INSN(),
    339		},
    340		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
    341		.attach_type = BPF_CGROUP_INET6_POST_BIND,
    342		.domain = AF_INET6,
    343		.type = SOCK_STREAM,
    344		.ip = "::1",
    345		.port = 8193,
    346		.port_retry = 9000,
    347		.result = RETRY_SUCCESS,
    348	},
    349	{
    350		.descr = "bind4 allow all",
    351		.insns = {
    352			BPF_MOV64_IMM(BPF_REG_0, 1),
    353			BPF_EXIT_INSN(),
    354		},
    355		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
    356		.attach_type = BPF_CGROUP_INET4_POST_BIND,
    357		.domain = AF_INET,
    358		.type = SOCK_STREAM,
    359		.ip = "0.0.0.0",
    360		.result = SUCCESS,
    361	},
    362	{
    363		.descr = "bind6 allow all",
    364		.insns = {
    365			BPF_MOV64_IMM(BPF_REG_0, 1),
    366			BPF_EXIT_INSN(),
    367		},
    368		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
    369		.attach_type = BPF_CGROUP_INET6_POST_BIND,
    370		.domain = AF_INET6,
    371		.type = SOCK_STREAM,
    372		.ip = "::",
    373		.result = SUCCESS,
    374	},
    375};
    376
    377static size_t probe_prog_length(const struct bpf_insn *fp)
    378{
    379	size_t len;
    380
    381	for (len = MAX_INSNS - 1; len > 0; --len)
    382		if (fp[len].code != 0 || fp[len].imm != 0)
    383			break;
    384	return len + 1;
    385}
    386
    387static int load_sock_prog(const struct bpf_insn *prog,
    388			  enum bpf_attach_type attach_type)
    389{
    390	LIBBPF_OPTS(bpf_prog_load_opts, opts);
    391	int ret, insn_cnt;
    392
    393	insn_cnt = probe_prog_length(prog);
    394
    395	opts.expected_attach_type = attach_type;
    396	opts.log_buf = bpf_log_buf;
    397	opts.log_size = BPF_LOG_BUF_SIZE;
    398	opts.log_level = 2;
    399
    400	ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts);
    401	if (verbose && ret < 0)
    402		fprintf(stderr, "%s\n", bpf_log_buf);
    403
    404	return ret;
    405}
    406
    407static int attach_sock_prog(int cgfd, int progfd,
    408			    enum bpf_attach_type attach_type)
    409{
    410	return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
    411}
    412
    413static int bind_sock(int domain, int type, const char *ip,
    414		     unsigned short port, unsigned short port_retry)
    415{
    416	struct sockaddr_storage addr;
    417	struct sockaddr_in6 *addr6;
    418	struct sockaddr_in *addr4;
    419	int sockfd = -1;
    420	socklen_t len;
    421	int res = SUCCESS;
    422
    423	sockfd = socket(domain, type, 0);
    424	if (sockfd < 0)
    425		goto err;
    426
    427	memset(&addr, 0, sizeof(addr));
    428
    429	if (domain == AF_INET) {
    430		len = sizeof(struct sockaddr_in);
    431		addr4 = (struct sockaddr_in *)&addr;
    432		addr4->sin_family = domain;
    433		addr4->sin_port = htons(port);
    434		if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
    435			goto err;
    436	} else if (domain == AF_INET6) {
    437		len = sizeof(struct sockaddr_in6);
    438		addr6 = (struct sockaddr_in6 *)&addr;
    439		addr6->sin6_family = domain;
    440		addr6->sin6_port = htons(port);
    441		if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
    442			goto err;
    443	} else {
    444		goto err;
    445	}
    446
    447	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
    448		/* sys_bind() may fail for different reasons, errno has to be
    449		 * checked to confirm that BPF program rejected it.
    450		 */
    451		if (errno != EPERM)
    452			goto err;
    453		if (port_retry)
    454			goto retry;
    455		res = BIND_REJECT;
    456		goto out;
    457	}
    458
    459	goto out;
    460retry:
    461	if (domain == AF_INET)
    462		addr4->sin_port = htons(port_retry);
    463	else
    464		addr6->sin6_port = htons(port_retry);
    465	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
    466		if (errno != EPERM)
    467			goto err;
    468		res = RETRY_REJECT;
    469	} else {
    470		res = RETRY_SUCCESS;
    471	}
    472	goto out;
    473err:
    474	res = -1;
    475out:
    476	close(sockfd);
    477	return res;
    478}
    479
    480static int run_test_case(int cgfd, const struct sock_test *test)
    481{
    482	int progfd = -1;
    483	int err = 0;
    484	int res;
    485
    486	printf("Test case: %s .. ", test->descr);
    487	progfd = load_sock_prog(test->insns, test->expected_attach_type);
    488	if (progfd < 0) {
    489		if (test->result == LOAD_REJECT)
    490			goto out;
    491		else
    492			goto err;
    493	}
    494
    495	if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) {
    496		if (test->result == ATTACH_REJECT)
    497			goto out;
    498		else
    499			goto err;
    500	}
    501
    502	res = bind_sock(test->domain, test->type, test->ip, test->port,
    503			test->port_retry);
    504	if (res > 0 && test->result == res)
    505		goto out;
    506
    507err:
    508	err = -1;
    509out:
    510	/* Detaching w/o checking return code: best effort attempt. */
    511	if (progfd != -1)
    512		bpf_prog_detach(cgfd, test->attach_type);
    513	close(progfd);
    514	printf("[%s]\n", err ? "FAIL" : "PASS");
    515	return err;
    516}
    517
    518static int run_tests(int cgfd)
    519{
    520	int passes = 0;
    521	int fails = 0;
    522	int i;
    523
    524	for (i = 0; i < ARRAY_SIZE(tests); ++i) {
    525		if (run_test_case(cgfd, &tests[i]))
    526			++fails;
    527		else
    528			++passes;
    529	}
    530	printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
    531	return fails ? -1 : 0;
    532}
    533
    534int main(int argc, char **argv)
    535{
    536	int cgfd = -1;
    537	int err = 0;
    538
    539	cgfd = cgroup_setup_and_join(CG_PATH);
    540	if (cgfd < 0)
    541		goto err;
    542
    543	/* Use libbpf 1.0 API mode */
    544	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
    545
    546	if (run_tests(cgfd))
    547		goto err;
    548
    549	goto out;
    550err:
    551	err = -1;
    552out:
    553	close(cgfd);
    554	cleanup_cgroup_environment();
    555	return err;
    556}