From e5598c17a97a6cbe90d958140b478d9e11573726 Mon Sep 17 00:00:00 2001 From: Jo Van Bulck Date: Sat, 2 Sep 2023 12:29:19 +0200 Subject: [PATCH] app/apic: Add APIC precision microbenchmark experiments As described in Appendix A of the AEX-Notify paper. --- app/apic/.gitignore | 4 ++ app/apic/Makefile | 98 +++++++++++++++++++++++++++++ app/apic/README.md | 30 +++++++++ app/apic/apic-hist.png | Bin 0 -> 68890 bytes app/apic/asm.S | 15 +++++ app/apic/config.h | 17 +++++ app/apic/irq_entry.S | 58 +++++++++++++++++ app/apic/main.c | 118 ++++++++++++++++++++++++++++++++++ app/apic/parse.py | 139 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 479 insertions(+) create mode 100644 app/apic/.gitignore create mode 100644 app/apic/Makefile create mode 100644 app/apic/README.md create mode 100644 app/apic/apic-hist.png create mode 100644 app/apic/asm.S create mode 100644 app/apic/config.h create mode 100644 app/apic/irq_entry.S create mode 100644 app/apic/main.c create mode 100755 app/apic/parse.py diff --git a/app/apic/.gitignore b/app/apic/.gitignore new file mode 100644 index 0000000..75d4672 --- /dev/null +++ b/app/apic/.gitignore @@ -0,0 +1,4 @@ +*.txt +*.pdf +log +copy_icx.sh diff --git a/app/apic/Makefile b/app/apic/Makefile new file mode 100644 index 0000000..06394f7 --- /dev/null +++ b/app/apic/Makefile @@ -0,0 +1,98 @@ +LIBSGXSTEP_DIR = ../.. +LIBSGXSTEP = $(LIBSGXSTEP_DIR)/libsgxstep +-include $(LIBSGXSTEP)/Makefile.config + +LIBSGXSTEP_SILENT = 1 + +ifeq ($(SGX_SDK),) + SGX_SDK = /opt/intel/sgxsdk +endif +export SGX_SDK +ifneq ($(SGX_SDK), /opt/intel/sgxsdk) + URTS_LD_LIBRARY_PATH = LD_LIBRARY_PATH=$(LIBSGXSTEP_DIR)/linux-sgx/psw/urts/linux +endif + +SUBDIRS = $(LIBSGXSTEP) + +CC = gcc +AS = gcc +LD = gcc + +ifeq ($(M32), 1) + ASFLAGS = -m32 -DM32=$(M32) + CFLAGS = -m32 -DM32=$(M32) + LDFLAGS = -m32 +else + LIB_SUFX = 64 +endif + +CFLAGS += -fPIC -fno-stack-protector -fno-builtin -fno-jump-tables \ + -fno-common -Wno-attributes -g -D_GNU_SOURCE -O0 +INCLUDE = -I$(SGX_SDK)/include/ -I$(LIBSGXSTEP_DIR) +LDFLAGS += -lsgx-step -lsgx_urts \ + -lsgx_uae_service -pthread $(SUBDIRS:%=-L %) -L$(SGX_SDK)/lib$(LIB_SUFX)/ \ + -L$(LIBSGXSTEP_DIR)/linux-sgx/psw/urts/linux + +C_SOURCES = $(shell ls *.c) +ASM_SOURCES = $(shell ls *.S) +C_OBJECTS = $(C_SOURCES:.c=.o) +ASM_OBJECTS = $(ASM_SOURCES:.S=.o) +OBJECTS = $(C_OBJECTS) $(ASM_OBJECTS) +OUTPUT = app + +BUILDDIRS = $(SUBDIRS:%=build-%) +CLEANDIRS = $(SUBDIRS:%=clean-%) + +ATTACK = 1 +PARSE = nop +ifeq ($(STRLEN), 1) + ATTACK = 2 + PARSE = strlen +endif +ifeq ($(ZIGZAG), 1) + ATTACK = 3 + PARSE = zz +endif + +ifeq ($(NUM),) + NUM = 100 +endif +export NUM + +CFLAGS += -DATTACK_SCENARIO=$(ATTACK) -DNUM_RUNS=$(NUM) + +MAKEFLAGS += --silent + +#.SILENT: +all: $(OUTPUT) + +run: clean all + sudo $(URTS_LD_LIBRARY_PATH) ./app > out.txt + cat out.txt + +parse: run + SGX_STEP_PLATFORM=$(SGX_STEP_PLATFORM) ./parse_$(PARSE).py $(NUM) + +$(OUTPUT): $(BUILDDIRS) $(OBJECTS) + echo "$(INDENT)[LD]" $(OBJECTS) $(LIBS) -o $(OUTPUT) + $(LD) $(OBJECTS) $(LDFLAGS) -o $(OUTPUT) + +%.o : %.c + echo "$(INDENT)[CC] " $< + $(CC) $(CFLAGS) $(INCLUDE) -c $< + +%.o : %.S + echo "$(INDENT)[AS] " $< + $(AS) $(ASFLAGS) $(INCLUDE) -c $< -o $@ + +clean: $(CLEANDIRS) + echo "$(INDENT)[RM]" $(OBJECTS) $(OUTPUT) + rm -f $(OBJECTS) $(OUTPUT) + +$(BUILDDIRS): + echo "$(INDENT)[===] $(@:build-%=%) [===]" + $(MAKE) -C $(@:build-%=%) INDENT+="$(INDENT_STEP)" M32=$(M32) curr-dir=$(curr-dir)/$(@:build-%=%) + +$(CLEANDIRS): + echo "$(INDENT)[===] $(@:clean-%=%) [===]" + $(MAKE) clean -C $(@:clean-%=%) INDENT+="$(INDENT_STEP)" curr-dir=$(curr-dir)/$(@:build-%=%) diff --git a/app/apic/README.md b/app/apic/README.md new file mode 100644 index 0000000..7dddf6c --- /dev/null +++ b/app/apic/README.md @@ -0,0 +1,30 @@ +# APIC Precision Microbenchmarks + +This directory contains the experiments to determine the accuracy of the local +APIC timer, as described in Appendix A of the [AEX-Notify +paper](https://jovanbulck.github.io/files/usenix23-aexnotify.pdf). + +The local APIC timer can be configured in one-shot or periodic mode to send an +interrupt when an MMIO counter register reaches zero, or in TSC-deadline mode +when the processor's timestamp counter exceeds a value specified in the +dedicated `IA32_TSC_DEADLINE_MSR` model-specific register. Depending on the +processor model, the APIC timer operates at the frequency of either the +processor’s bus clock or its core crystal clock. Neither time source is +synchronized with the core clock, which typically operates at a much higher +frequency (e.g., more than 100x higher). + +**TSC-Deadline Mode.** As a contribution of independent interest, we devised an +improved setup to considerably reduce the noise from additional kernel context +switches. For this, we registered a custom ring-0 interrupt gate in the +processor's interrupt-descriptor table. This allows to essentially bypass the +OS kernel altogether by directly invoking a minimal assembly handler that +programs a specified value in `IA32_TSC_DEADLINE_MSR` using the privileged +`WRMSR` instruction via a software interrupt. + +**Comparison Results.** We conclude that our novel interrupt-gate TSC-deadline +timer configuration technique yields a considerable improvement in terms of +standard deviation of elapsed cycles for both the existing one-shot technique +that SGX-Step currently uses, as well as naive kernel-level configuration +approaches (cf. as is also visually evident in the figure below) + +![APIC distribution histogram](apic-hist.png) diff --git a/app/apic/apic-hist.png b/app/apic/apic-hist.png new file mode 100644 index 0000000000000000000000000000000000000000..974e66b388503bb4edc954cace1d222cef206bc7 GIT binary patch literal 68890 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�O#n=aPAql1e*WYJUN| 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

LAbORc0Mq1@)fzK9rI1Cn8mO0L! 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, + * Raoul Strackx + * + * SGX-Step is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SGX-Step is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with SGX-Step. If not, see . + */ + +#include +#include +#include +#include "libsgxstep/apic.h" +#include "libsgxstep/cpu.h" +#include "libsgxstep/pt.h" +#include "libsgxstep/sched.h" +#include "libsgxstep/enclave.h" +#include "libsgxstep/debug.h" +#include "libsgxstep/config.h" +#include "libsgxstep/idt.h" +#include "libsgxstep/config.h" +#include "config.h" + +unsigned long long test_tsc_results[TEST_ITERATIONS]; +unsigned long long test_inc_results[TEST_ITERATIONS]; +unsigned long long test_deadline_results[TEST_ITERATIONS]; + +void incs(void); +void __apic_irq_handler_timer(void); +void __apic_priv_irq_gate(void); +extern unsigned long long __apic_irq_tsc; +extern unsigned long long __apic_deadline_tsc; +extern unsigned long long __apic_irq_rax; + +/* ================== ATTACKER INIT/SETUP ================= */ + +/* Configure and check attacker untrusted runtime environment. */ +void attacker_config_runtime(void) +{ + ASSERT( !claim_cpu(VICTIM_CPU) ); + ASSERT( !prepare_system_for_benchmark(PSTATE_PCT) ); + print_system_settings(); + + if (isatty(fileno(stdout))) + { + info("WARNING: interactive terminal detected; known to cause "); + info("unstable timer intervals! Use stdout file redirection for "); + info("precise single-stepping results..."); + } +} + +/* ================== ATTACKER MAIN ================= */ + +/* Untrusted main function to create/enter the trusted enclave. */ +int main( int argc, char **argv ) +{ + idt_t idt = {0}; + attacker_config_runtime(); + map_idt(&idt); + install_kernel_irq_handler(&idt, __apic_irq_handler_timer, IRQ_VECTOR); + + #if TSC_DEADLINE + const char *filename = "deadline_results.txt"; + apic_timer_deadline(IRQ_VECTOR); + + #if USE_IRQ_GATE + install_kernel_irq_handler(&idt, __apic_priv_irq_gate, IRQ_PRIV_VECTOR); + #endif + #else + const char *filename = "oneshot_results.txt"; + apic_timer_oneshot(IRQ_VECTOR); + #endif + + for (int i = 0; i < TEST_ITERATIONS; ++i) + { + __ss_irq_fired = 0; + unsigned long long begin_time = __rdtsc(); + + #if TSC_DEADLINE && USE_IRQ_GATE + __apic_deadline_tsc = begin_time + APIC_TSC_INTERVAL; + asm("int %0\n\t" ::"i"(IRQ_PRIV_VECTOR):); + #elif TSC_DEADLINE + __apic_deadline_tsc = begin_time + APIC_TSC_INTERVAL + WRMSR_ON_CPU_LATENCY; + wrmsr_on_cpu(IA32_TSC_DEADLINE_MSR, get_cpu(), __apic_deadline_tsc); + //unsigned long long wrmsr_time = __rdtsc(); + #else + apic_timer_irq(APIC_ONESHOT_TICKS); + #endif + + incs(); + ASSERT(__ss_irq_fired); + + test_tsc_results[i] = __apic_irq_tsc - begin_time; + test_inc_results[i] = __apic_irq_rax; + test_deadline_results[i] = __apic_irq_tsc - __apic_deadline_tsc; + } + + // record results + FILE *fp = fopen(filename, "w"); + fprintf(fp, "#tsc_diff,inc_count,tsc_deadline_drift\n"); + for (int i = 1; i < TEST_ITERATIONS; ++i) { + fprintf(fp, "%llu,%llu,%llu\n", test_tsc_results[i], test_inc_results[i], test_deadline_results[i]); + } + fclose(fp); + + return 0; +} diff --git a/app/apic/parse.py b/app/apic/parse.py new file mode 100755 index 0000000..5d40df5 --- /dev/null +++ b/app/apic/parse.py @@ -0,0 +1,139 @@ +#!/usr/bin/python3 + +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import matplotlib.font_manager as font_manager +import statistics + +# ensure no type 3 fonts +import matplotlib +matplotlib.rcParams['pdf.fonttype'] = 42 +matplotlib.rcParams['ps.fonttype'] = 42 + +# TSC deadline + IRQ gate; deadline diff +LATENCY_MIN = 1000 +LATENCY_MAX = 1500 + +# TSC deadline + IRQ gate; tsc diff +#LATENCY_MIN = 11000 +#LATENCY_MAX = 11500 + +# TSC deadline w/o IRQ gate; deadline diff +#LATENCY_MIN = 900 +#LATENCY_MAX = 1300 + +# TSC oneshot; tsc diff +LATENCY_MIN = 14600 +LATENCY_MAX = 15200 + +# ICX TSC oneshot; tsc diff +LATENCY_MIN = 10800 +LATENCY_MAX = 11200 + +pd.set_option('display.precision', 0) +np.set_printoptions(precision=0) + +def hist(latencies, labels=[], title='', filename='hist', xlabel='Latency (cycles)', legend_loc='best'): + plt.figure(figsize=(10,5)) + #colors = [] + + for i in range(0,len(labels)): + d = latencies[i] + labels[i] += f' (μ={int(np.mean(d))}, σ={int(np.std(d))})' #, M={int(np.median(d))})' + #colors.append(f'C{i}') + + plt.hist(latencies, + label=labels, + bins=500, + #histtype='step', + histtype='stepfilled', + alpha=0.5, + range=[LATENCY_MIN, LATENCY_MAX]) + + #for i in range(0,len(latencies)): + # x = latencies[i] + # plt.axvline(np.mean(x), color=colors[i], linestyle='dashed', linewidth=1) + + font = font_manager.FontProperties(family='monospace', size=10) #weight='bold', style='normal') + if len(labels) > 0: + plt.legend(prop=font,loc=legend_loc) + + plt.xlabel(xlabel) + plt.ylabel('Frequency') + plt.title(title) + + print(f".. writing histogram '{title}' to '{filename}.pdf'") + plt.savefig(filename + '.pdf', bbox_inches='tight') + plt.show() + +def reject_outliers(data, m=3): + stdev = np.std(data) + mean = np.mean(data) + mask_min = mean - stdev * m + mask_max = mean + stdev * m + + outliers = [d for d in data if d < mask_min or d > mask_max ] + print(f'Warning: removing {len(outliers)} outliers:') + print(outliers) + + return [d for d in data if d >= mask_min and d <= mask_max ] + +def reject_syscall_inc_outliers(data): + for i in range(0, len(data)): + if data[i] > 1000000: + print(f'Warning: removing outlier: {data[i]}') + data[i] = 0 + return data + +def load_data(file, col): + print(f".. loading data from '{file}'") + #data = np.loadtxt(file, dtype=int, skiprows=1) + d = pd.read_csv(file) + data = d[col] + #data = reject_outliers(data) + + #HACK: if the IRQ arrived before the slide the counter in RAX is invalid (zero) + if file == 'logs/icx/deadline_results_syscall.txt' and col == 'inc_count': + data = reject_syscall_inc_outliers(data) + + print('---------------------------------------------------------------------------') + s = pd.Series(data) + print(s.describe()) + print(f'med {int(np.median(data))}') + print('---------------------------------------------------------------------------') + return data + + +#LATENCY_MIN = 10700 +#LATENCY_MAX = 11500 +#s0 = load_data('oneshot_results.txt', col='#tsc_diff') +#hist([s0], ['oneshot']) +#exit() + +##s0 = load_data('deadline_results.txt', col='tsc_deadline_drift') + +#s0 = load_data('logs/icx/oneshot_results.txt', col='inc_count') +#s1 = load_data('logs/icx/deadline_results_syscall.txt', col='inc_count') +#s2 = load_data('logs/icx/deadline_results_irq_gate.txt', col='inc_count') +#LATENCY_MIN = 2500 +#LATENCY_MAX = 10000 +#hist([s2,s1,s0], ['TSC IRQ gate', 'TSC syscall', 'oneshot'], xlabel='ADD instructions executed before IRQ', legend_loc='upper left') + +## Appendix figure in paper +LATENCY_MIN = 10680 +LATENCY_MAX = 11120 +s0 = load_data('logs/icx/oneshot_results.txt', col='#tsc_diff') +s1 = load_data('logs/icx/deadline_results_syscall.txt', col='#tsc_diff') +s2 = load_data('logs/icx/deadline_results_irq_gate.txt', col='#tsc_diff') +hist([s2,s1,s0], ['TSC IRQ gate', 'TSC syscall', 'oneshot']) + +#s0 = load_data('oneshot_results.txt', col='#tsc_diff') +#hist([s0], ['oneshot'], 'APIC timer oneshot TSC distribution') + +#LATENCY_MIN = 650 +#LATENCY_MAX = 680 +##s0 = load_data('pt_results.txt', col='pte-not-a') +#s0 = load_data('pt_results.txt', col='pte-a') +#hist([s0], ['pte-not-a'], 'Assisted page-table walk latency distribution') +