[LTP] [PATCH] kallsyms01: Utilize ksymbol table for unauthorized address access

Cyril Hrubis chrubis@suse.cz
Mon Apr 29 16:42:02 CEST 2024


Hi!
> +struct kallsym {
> +	unsigned long addr;
> +	char type;
> +	char name[128];
> +};
> +
> +struct ksymstbl {
> +	struct kallsym symbol;
> +	struct ksymstbl *next;
> +};
> +
> +static struct ksymstbl *sym_table;
> +static unsigned int nr_symbols;
> +static sigjmp_buf jmpbuf;
> +volatile sig_atomic_t segv_caught = 0;
> +
> +static void segv_handler(int sig)
> +{
> +	if (sig == SIGSEGV)
> +		segv_caught++;
> +	else
> +		tst_res(TFAIL, "Unexpected signal %s", strsignal(sig));
> +
> +	siglongjmp(jmpbuf, 1);
> +}
> +
> +static struct ksymstbl *read_kallsyms(unsigned int *nr_symbols)
> +{
> +	FILE *stream;
> +	char *line = NULL;
> +	size_t len = 0;
> +	unsigned int nr_syms = 0;
> +	struct ksymstbl *head, *item, *i;
> +
> +	item = head = calloc(1, sizeof(*head));
> +	if (head == NULL)
> +		goto out;
> +
> +	stream = SAFE_FOPEN("/proc/kallsyms", "r");
> +
> +	while (getline(&line, &len, stream) != -1) {
> +		i = item;
> +
> +		sscanf(line, "%lx %c %s",
> +				&i->symbol.addr, &i->symbol.type, i->symbol.name);
> +
> +		item = calloc(1, sizeof(*i));
> +		if (item == NULL)
> +			tst_brk(TBROK, "In calloc[]");

We should add SAFE_CALLOC() to the test library. Or maybe
SAFE_ZEROED_MALLOC() because we actually mis-use the calloc() here to
get the memory initialized to zero.

Also it may be actually faster to read the file twice, first time only
to count the addresses and allocate an array of the symbols instead.
That way we avoid all the allocation and list traversal.

static unsigned int read_kallsyms(struct ksymbtl *table, unsigned int table_size)
{
	FILE *stream = SAFE_FOPEN("/proc/kallsyms", "r");
	unsigned int nr_syms = 0;

	while (getline(&line, &len, stream) != -1) {

		if (table && nr_syms < table_size) {
			sscanf(line, "%lx %c %s",
			       &table[nr_syms].addr,
			       ...);
		}

		nr_syms++;
	}

	return nr_syms;
}


static void setup(void)
{
	unsigned int nr_syms;

	nr_syms = read_kallsyms(NULL, 0);

	sym_table = SAFE_MALLOC(sizeof(*sym_table) * nr_syms);

	if (nr_syms != read_kallsyms(sym_table, nr_syms))
		tst_res(TWARN, "/proc/kallsyms changed size!?");
}

> +		i->next = item;
> +		nr_syms += 1;
> +	}
> +
> +	*nr_symbols = nr_syms;
> +	SAFE_FCLOSE(stream);
> +out:
> +	return head;
> +}
> +
> +static void setup(void)
> +{
> +	sym_table = read_kallsyms(&nr_symbols);
> +	if (!sym_table)
> +		tst_brk(TBROK, "Failed to read kernel symbols");
> +}
> +
> +static void access_ksymbols_address(struct ksymstbl *sym_table)
> +{
> +	if (sigsetjmp(jmpbuf, 1) == 0) {
> +		*(volatile unsigned long *)sym_table->symbol.addr = 0;
> +
> +		tst_res(TFAIL, "Successfully accessed kernel addr 0x%lx (%s)",
> +			sym_table->symbol.addr, sym_table->symbol.name);
> +	}
> +
> +}
> +
> +static void test_access_kernel_address(void)
> +{
> +	struct ksymstbl *current;
> +	struct sigaction sa;
> +
> +	memset(&sa, 0, sizeof(sa));
> +	sa.sa_handler = segv_handler;
> +	sigaction(SIGSEGV, &sa, NULL);
> +
> +	current = sym_table;
> +	while (current->next != NULL) {
> +		access_ksymbols_address(current);
> +		current = current->next;
> +	}
> +
> +	if (segv_caught == (sig_atomic_t)nr_symbols)
> +		tst_res(TPASS, "Caught %d times SIGSEGV in access ksymbols addr", segv_caught);
> +}
> +
> +static void cleanup(void)
> +{
> +	while (sym_table != NULL) {
> +		struct ksymstbl *temp = sym_table;
> +		sym_table = sym_table->next;
> +		free(temp);
> +	}
> +}
> +
> +static struct tst_test test = {
> +	.needs_root = 1,
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.max_runtime = 60,
> +	.test_all = test_access_kernel_address,
> +};

-- 
Cyril Hrubis
chrubis@suse.cz


More information about the ltp mailing list