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

cgroup.c (11455B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
      2// Copyright (C) 2017 Facebook
      3// Author: Roman Gushchin <guro@fb.com>
      4
      5#define _XOPEN_SOURCE 500
      6#include <errno.h>
      7#include <fcntl.h>
      8#include <ftw.h>
      9#include <mntent.h>
     10#include <stdio.h>
     11#include <stdlib.h>
     12#include <string.h>
     13#include <sys/stat.h>
     14#include <sys/types.h>
     15#include <unistd.h>
     16
     17#include <bpf/bpf.h>
     18
     19#include "main.h"
     20
     21#define HELP_SPEC_ATTACH_FLAGS						\
     22	"ATTACH_FLAGS := { multi | override }"
     23
     24#define HELP_SPEC_ATTACH_TYPES						       \
     25	"       ATTACH_TYPE := { ingress | egress | sock_create |\n"	       \
     26	"                        sock_ops | device | bind4 | bind6 |\n"	       \
     27	"                        post_bind4 | post_bind6 | connect4 |\n"       \
     28	"                        connect6 | getpeername4 | getpeername6 |\n"   \
     29	"                        getsockname4 | getsockname6 | sendmsg4 |\n"   \
     30	"                        sendmsg6 | recvmsg4 | recvmsg6 |\n"           \
     31	"                        sysctl | getsockopt | setsockopt |\n"	       \
     32	"                        sock_release }"
     33
     34static unsigned int query_flags;
     35
     36static enum bpf_attach_type parse_attach_type(const char *str)
     37{
     38	enum bpf_attach_type type;
     39
     40	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
     41		if (attach_type_name[type] &&
     42		    is_prefix(str, attach_type_name[type]))
     43			return type;
     44	}
     45
     46	return __MAX_BPF_ATTACH_TYPE;
     47}
     48
     49static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
     50			 const char *attach_flags_str,
     51			 int level)
     52{
     53	char prog_name[MAX_PROG_FULL_NAME];
     54	struct bpf_prog_info info = {};
     55	__u32 info_len = sizeof(info);
     56	int prog_fd;
     57
     58	prog_fd = bpf_prog_get_fd_by_id(id);
     59	if (prog_fd < 0)
     60		return -1;
     61
     62	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
     63		close(prog_fd);
     64		return -1;
     65	}
     66
     67	get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
     68	if (json_output) {
     69		jsonw_start_object(json_wtr);
     70		jsonw_uint_field(json_wtr, "id", info.id);
     71		if (attach_type < ARRAY_SIZE(attach_type_name))
     72			jsonw_string_field(json_wtr, "attach_type",
     73					   attach_type_name[attach_type]);
     74		else
     75			jsonw_uint_field(json_wtr, "attach_type", attach_type);
     76		jsonw_string_field(json_wtr, "attach_flags",
     77				   attach_flags_str);
     78		jsonw_string_field(json_wtr, "name", prog_name);
     79		jsonw_end_object(json_wtr);
     80	} else {
     81		printf("%s%-8u ", level ? "    " : "", info.id);
     82		if (attach_type < ARRAY_SIZE(attach_type_name))
     83			printf("%-15s", attach_type_name[attach_type]);
     84		else
     85			printf("type %-10u", attach_type);
     86		printf(" %-15s %-15s\n", attach_flags_str, prog_name);
     87	}
     88
     89	close(prog_fd);
     90	return 0;
     91}
     92
     93static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
     94{
     95	__u32 prog_cnt = 0;
     96	int ret;
     97
     98	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
     99			     NULL, &prog_cnt);
    100	if (ret)
    101		return -1;
    102
    103	return prog_cnt;
    104}
    105
    106static int cgroup_has_attached_progs(int cgroup_fd)
    107{
    108	enum bpf_attach_type type;
    109	bool no_prog = true;
    110
    111	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
    112		int count = count_attached_bpf_progs(cgroup_fd, type);
    113
    114		if (count < 0 && errno != EINVAL)
    115			return -1;
    116
    117		if (count > 0) {
    118			no_prog = false;
    119			break;
    120		}
    121	}
    122
    123	return no_prog ? 0 : 1;
    124}
    125static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
    126				   int level)
    127{
    128	const char *attach_flags_str;
    129	__u32 prog_ids[1024] = {0};
    130	__u32 prog_cnt, iter;
    131	__u32 attach_flags;
    132	char buf[32];
    133	int ret;
    134
    135	prog_cnt = ARRAY_SIZE(prog_ids);
    136	ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags,
    137			     prog_ids, &prog_cnt);
    138	if (ret)
    139		return ret;
    140
    141	if (prog_cnt == 0)
    142		return 0;
    143
    144	switch (attach_flags) {
    145	case BPF_F_ALLOW_MULTI:
    146		attach_flags_str = "multi";
    147		break;
    148	case BPF_F_ALLOW_OVERRIDE:
    149		attach_flags_str = "override";
    150		break;
    151	case 0:
    152		attach_flags_str = "";
    153		break;
    154	default:
    155		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
    156		attach_flags_str = buf;
    157	}
    158
    159	for (iter = 0; iter < prog_cnt; iter++)
    160		show_bpf_prog(prog_ids[iter], type,
    161			      attach_flags_str, level);
    162
    163	return 0;
    164}
    165
    166static int do_show(int argc, char **argv)
    167{
    168	enum bpf_attach_type type;
    169	int has_attached_progs;
    170	const char *path;
    171	int cgroup_fd;
    172	int ret = -1;
    173
    174	query_flags = 0;
    175
    176	if (!REQ_ARGS(1))
    177		return -1;
    178	path = GET_ARG();
    179
    180	while (argc) {
    181		if (is_prefix(*argv, "effective")) {
    182			if (query_flags & BPF_F_QUERY_EFFECTIVE) {
    183				p_err("duplicated argument: %s", *argv);
    184				return -1;
    185			}
    186			query_flags |= BPF_F_QUERY_EFFECTIVE;
    187			NEXT_ARG();
    188		} else {
    189			p_err("expected no more arguments, 'effective', got: '%s'?",
    190			      *argv);
    191			return -1;
    192		}
    193	}
    194
    195	cgroup_fd = open(path, O_RDONLY);
    196	if (cgroup_fd < 0) {
    197		p_err("can't open cgroup %s", path);
    198		goto exit;
    199	}
    200
    201	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
    202	if (has_attached_progs < 0) {
    203		p_err("can't query bpf programs attached to %s: %s",
    204		      path, strerror(errno));
    205		goto exit_cgroup;
    206	} else if (!has_attached_progs) {
    207		ret = 0;
    208		goto exit_cgroup;
    209	}
    210
    211	if (json_output)
    212		jsonw_start_array(json_wtr);
    213	else
    214		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
    215		       "AttachFlags", "Name");
    216
    217	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
    218		/*
    219		 * Not all attach types may be supported, so it's expected,
    220		 * that some requests will fail.
    221		 * If we were able to get the show for at least one
    222		 * attach type, let's return 0.
    223		 */
    224		if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
    225			ret = 0;
    226	}
    227
    228	if (json_output)
    229		jsonw_end_array(json_wtr);
    230
    231exit_cgroup:
    232	close(cgroup_fd);
    233exit:
    234	return ret;
    235}
    236
    237/*
    238 * To distinguish nftw() errors and do_show_tree_fn() errors
    239 * and avoid duplicating error messages, let's return -2
    240 * from do_show_tree_fn() in case of error.
    241 */
    242#define NFTW_ERR		-1
    243#define SHOW_TREE_FN_ERR	-2
    244static int do_show_tree_fn(const char *fpath, const struct stat *sb,
    245			   int typeflag, struct FTW *ftw)
    246{
    247	enum bpf_attach_type type;
    248	int has_attached_progs;
    249	int cgroup_fd;
    250
    251	if (typeflag != FTW_D)
    252		return 0;
    253
    254	cgroup_fd = open(fpath, O_RDONLY);
    255	if (cgroup_fd < 0) {
    256		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
    257		return SHOW_TREE_FN_ERR;
    258	}
    259
    260	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
    261	if (has_attached_progs < 0) {
    262		p_err("can't query bpf programs attached to %s: %s",
    263		      fpath, strerror(errno));
    264		close(cgroup_fd);
    265		return SHOW_TREE_FN_ERR;
    266	} else if (!has_attached_progs) {
    267		close(cgroup_fd);
    268		return 0;
    269	}
    270
    271	if (json_output) {
    272		jsonw_start_object(json_wtr);
    273		jsonw_string_field(json_wtr, "cgroup", fpath);
    274		jsonw_name(json_wtr, "programs");
    275		jsonw_start_array(json_wtr);
    276	} else {
    277		printf("%s\n", fpath);
    278	}
    279
    280	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
    281		show_attached_bpf_progs(cgroup_fd, type, ftw->level);
    282
    283	if (errno == EINVAL)
    284		/* Last attach type does not support query.
    285		 * Do not report an error for this, especially because batch
    286		 * mode would stop processing commands.
    287		 */
    288		errno = 0;
    289
    290	if (json_output) {
    291		jsonw_end_array(json_wtr);
    292		jsonw_end_object(json_wtr);
    293	}
    294
    295	close(cgroup_fd);
    296
    297	return 0;
    298}
    299
    300static char *find_cgroup_root(void)
    301{
    302	struct mntent *mnt;
    303	FILE *f;
    304
    305	f = fopen("/proc/mounts", "r");
    306	if (f == NULL)
    307		return NULL;
    308
    309	while ((mnt = getmntent(f))) {
    310		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
    311			fclose(f);
    312			return strdup(mnt->mnt_dir);
    313		}
    314	}
    315
    316	fclose(f);
    317	return NULL;
    318}
    319
    320static int do_show_tree(int argc, char **argv)
    321{
    322	char *cgroup_root, *cgroup_alloced = NULL;
    323	int ret;
    324
    325	query_flags = 0;
    326
    327	if (!argc) {
    328		cgroup_alloced = find_cgroup_root();
    329		if (!cgroup_alloced) {
    330			p_err("cgroup v2 isn't mounted");
    331			return -1;
    332		}
    333		cgroup_root = cgroup_alloced;
    334	} else {
    335		cgroup_root = GET_ARG();
    336
    337		while (argc) {
    338			if (is_prefix(*argv, "effective")) {
    339				if (query_flags & BPF_F_QUERY_EFFECTIVE) {
    340					p_err("duplicated argument: %s", *argv);
    341					return -1;
    342				}
    343				query_flags |= BPF_F_QUERY_EFFECTIVE;
    344				NEXT_ARG();
    345			} else {
    346				p_err("expected no more arguments, 'effective', got: '%s'?",
    347				      *argv);
    348				return -1;
    349			}
    350		}
    351	}
    352
    353	if (json_output)
    354		jsonw_start_array(json_wtr);
    355	else
    356		printf("%s\n"
    357		       "%-8s %-15s %-15s %-15s\n",
    358		       "CgroupPath",
    359		       "ID", "AttachType", "AttachFlags", "Name");
    360
    361	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
    362	case NFTW_ERR:
    363		p_err("can't iterate over %s: %s", cgroup_root,
    364		      strerror(errno));
    365		ret = -1;
    366		break;
    367	case SHOW_TREE_FN_ERR:
    368		ret = -1;
    369		break;
    370	default:
    371		ret = 0;
    372	}
    373
    374	if (json_output)
    375		jsonw_end_array(json_wtr);
    376
    377	free(cgroup_alloced);
    378
    379	return ret;
    380}
    381
    382static int do_attach(int argc, char **argv)
    383{
    384	enum bpf_attach_type attach_type;
    385	int cgroup_fd, prog_fd;
    386	int attach_flags = 0;
    387	int ret = -1;
    388	int i;
    389
    390	if (argc < 4) {
    391		p_err("too few parameters for cgroup attach");
    392		goto exit;
    393	}
    394
    395	cgroup_fd = open(argv[0], O_RDONLY);
    396	if (cgroup_fd < 0) {
    397		p_err("can't open cgroup %s", argv[0]);
    398		goto exit;
    399	}
    400
    401	attach_type = parse_attach_type(argv[1]);
    402	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
    403		p_err("invalid attach type");
    404		goto exit_cgroup;
    405	}
    406
    407	argc -= 2;
    408	argv = &argv[2];
    409	prog_fd = prog_parse_fd(&argc, &argv);
    410	if (prog_fd < 0)
    411		goto exit_cgroup;
    412
    413	for (i = 0; i < argc; i++) {
    414		if (is_prefix(argv[i], "multi")) {
    415			attach_flags |= BPF_F_ALLOW_MULTI;
    416		} else if (is_prefix(argv[i], "override")) {
    417			attach_flags |= BPF_F_ALLOW_OVERRIDE;
    418		} else {
    419			p_err("unknown option: %s", argv[i]);
    420			goto exit_cgroup;
    421		}
    422	}
    423
    424	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
    425		p_err("failed to attach program");
    426		goto exit_prog;
    427	}
    428
    429	if (json_output)
    430		jsonw_null(json_wtr);
    431
    432	ret = 0;
    433
    434exit_prog:
    435	close(prog_fd);
    436exit_cgroup:
    437	close(cgroup_fd);
    438exit:
    439	return ret;
    440}
    441
    442static int do_detach(int argc, char **argv)
    443{
    444	enum bpf_attach_type attach_type;
    445	int prog_fd, cgroup_fd;
    446	int ret = -1;
    447
    448	if (argc < 4) {
    449		p_err("too few parameters for cgroup detach");
    450		goto exit;
    451	}
    452
    453	cgroup_fd = open(argv[0], O_RDONLY);
    454	if (cgroup_fd < 0) {
    455		p_err("can't open cgroup %s", argv[0]);
    456		goto exit;
    457	}
    458
    459	attach_type = parse_attach_type(argv[1]);
    460	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
    461		p_err("invalid attach type");
    462		goto exit_cgroup;
    463	}
    464
    465	argc -= 2;
    466	argv = &argv[2];
    467	prog_fd = prog_parse_fd(&argc, &argv);
    468	if (prog_fd < 0)
    469		goto exit_cgroup;
    470
    471	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
    472		p_err("failed to detach program");
    473		goto exit_prog;
    474	}
    475
    476	if (json_output)
    477		jsonw_null(json_wtr);
    478
    479	ret = 0;
    480
    481exit_prog:
    482	close(prog_fd);
    483exit_cgroup:
    484	close(cgroup_fd);
    485exit:
    486	return ret;
    487}
    488
    489static int do_help(int argc, char **argv)
    490{
    491	if (json_output) {
    492		jsonw_null(json_wtr);
    493		return 0;
    494	}
    495
    496	fprintf(stderr,
    497		"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
    498		"       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
    499		"       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
    500		"       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
    501		"       %1$s %2$s help\n"
    502		"\n"
    503		HELP_SPEC_ATTACH_TYPES "\n"
    504		"       " HELP_SPEC_ATTACH_FLAGS "\n"
    505		"       " HELP_SPEC_PROGRAM "\n"
    506		"       " HELP_SPEC_OPTIONS " |\n"
    507		"                    {-f|--bpffs} }\n"
    508		"",
    509		bin_name, argv[-2]);
    510
    511	return 0;
    512}
    513
    514static const struct cmd cmds[] = {
    515	{ "show",	do_show },
    516	{ "list",	do_show },
    517	{ "tree",       do_show_tree },
    518	{ "attach",	do_attach },
    519	{ "detach",	do_detach },
    520	{ "help",	do_help },
    521	{ 0 }
    522};
    523
    524int do_cgroup(int argc, char **argv)
    525{
    526	return cmd_select(cmds, argc, argv, do_help);
    527}