162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes
462306a36Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * On AMD CPUs, SYSRET can return with a valid SS descriptor with with
762306a36Sopenharmony_ci * the hidden attributes set to an unusable state.  Make sure the kernel
862306a36Sopenharmony_ci * doesn't let this happen.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define _GNU_SOURCE
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <stdlib.h>
1462306a36Sopenharmony_ci#include <unistd.h>
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <string.h>
1762306a36Sopenharmony_ci#include <sys/mman.h>
1862306a36Sopenharmony_ci#include <err.h>
1962306a36Sopenharmony_ci#include <stddef.h>
2062306a36Sopenharmony_ci#include <stdbool.h>
2162306a36Sopenharmony_ci#include <pthread.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void *threadproc(void *ctx)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	/*
2662306a36Sopenharmony_ci	 * Do our best to cause sleeps on this CPU to exit the kernel and
2762306a36Sopenharmony_ci	 * re-enter with SS = 0.
2862306a36Sopenharmony_ci	 */
2962306a36Sopenharmony_ci	while (true)
3062306a36Sopenharmony_ci		;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	return NULL;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef __x86_64__
3662306a36Sopenharmony_ciextern unsigned long call32_from_64(void *stack, void (*function)(void));
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciasm (".pushsection .text\n\t"
3962306a36Sopenharmony_ci     ".code32\n\t"
4062306a36Sopenharmony_ci     "test_ss:\n\t"
4162306a36Sopenharmony_ci     "pushl $0\n\t"
4262306a36Sopenharmony_ci     "popl %eax\n\t"
4362306a36Sopenharmony_ci     "ret\n\t"
4462306a36Sopenharmony_ci     ".code64");
4562306a36Sopenharmony_ciextern void test_ss(void);
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint main()
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	/*
5162306a36Sopenharmony_ci	 * Start a busy-looping thread on the same CPU we're on.
5262306a36Sopenharmony_ci	 * For simplicity, just stick everything to CPU 0.  This will
5362306a36Sopenharmony_ci	 * fail in some containers, but that's probably okay.
5462306a36Sopenharmony_ci	 */
5562306a36Sopenharmony_ci	cpu_set_t cpuset;
5662306a36Sopenharmony_ci	CPU_ZERO(&cpuset);
5762306a36Sopenharmony_ci	CPU_SET(0, &cpuset);
5862306a36Sopenharmony_ci	if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
5962306a36Sopenharmony_ci		printf("[WARN]\tsched_setaffinity failed\n");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	pthread_t thread;
6262306a36Sopenharmony_ci	if (pthread_create(&thread, 0, threadproc, 0) != 0)
6362306a36Sopenharmony_ci		err(1, "pthread_create");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#ifdef __x86_64__
6662306a36Sopenharmony_ci	unsigned char *stack32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
6762306a36Sopenharmony_ci				      MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE,
6862306a36Sopenharmony_ci				      -1, 0);
6962306a36Sopenharmony_ci	if (stack32 == MAP_FAILED)
7062306a36Sopenharmony_ci		err(1, "mmap");
7162306a36Sopenharmony_ci#endif
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	printf("[RUN]\tSyscalls followed by SS validation\n");
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	for (int i = 0; i < 1000; i++) {
7662306a36Sopenharmony_ci		/*
7762306a36Sopenharmony_ci		 * Go to sleep and return using sysret (if we're 64-bit
7862306a36Sopenharmony_ci		 * or we're 32-bit on AMD on a 64-bit kernel).  On AMD CPUs,
7962306a36Sopenharmony_ci		 * SYSRET doesn't fix up the cached SS descriptor, so the
8062306a36Sopenharmony_ci		 * kernel needs some kind of workaround to make sure that we
8162306a36Sopenharmony_ci		 * end the system call with a valid stack segment.  This
8262306a36Sopenharmony_ci		 * can be a confusing failure because the SS *selector*
8362306a36Sopenharmony_ci		 * is the same regardless.
8462306a36Sopenharmony_ci		 */
8562306a36Sopenharmony_ci		usleep(2);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#ifdef __x86_64__
8862306a36Sopenharmony_ci		/*
8962306a36Sopenharmony_ci		 * On 32-bit, just doing a syscall through glibc is enough
9062306a36Sopenharmony_ci		 * to cause a crash if our cached SS descriptor is invalid.
9162306a36Sopenharmony_ci		 * On 64-bit, it's not, so try extra hard.
9262306a36Sopenharmony_ci		 */
9362306a36Sopenharmony_ci		call32_from_64(stack32 + 4088, test_ss);
9462306a36Sopenharmony_ci#endif
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	printf("[OK]\tWe survived\n");
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#ifdef __x86_64__
10062306a36Sopenharmony_ci	munmap(stack32, 4096);
10162306a36Sopenharmony_ci#endif
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
105