diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c243b24e..a7b6714e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -241,6 +241,72 @@ jobs: env: XAIOS_QEMU_SMOKE_TIMEOUT: "120" + parser-fuzz: + name: Parser Fuzzing + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Install toolchain + run: | + sudo apt-get update + sudo apt-get install -y clang lld llvm python3 + + # Every one of these parsers is reachable before authentication: the SSH + # binary packet layer, the DNS/DNSSEC response path, and the SFTP request + # decoder. The corpus is carried between runs so each campaign starts + # from the coverage the previous one reached instead of from one seed. + - name: Restore fuzzing corpus + uses: actions/cache@v5 + with: + path: build/fuzz + key: parser-fuzz-corpus-${{ github.sha }} + restore-keys: | + parser-fuzz-corpus- + + - name: Run bounded coverage-guided campaign + run: make parser-fuzz + env: + XAIOS_FUZZ_RUNS: "300000" + + - name: Upload crashes and corpus + if: always() + uses: actions/upload-artifact@v7 + with: + name: parser-fuzz-evidence + path: | + build/fuzz/*-corpus/** + build/fuzz/crash-* + build/fuzz/leak-* + build/fuzz/timeout-* + if-no-files-found: ignore + retention-days: 14 + + persistence-reboot: + name: Persistence Across Reboot + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Install toolchain + run: | + sudo apt-get update + sudo apt-get install -y clang lld llvm meson ninja-build qemu-system-arm qemu-efi-aarch64 mtools python3 python3-cryptography + + # Boots twice against one VirtIO volume and requires the second boot to + # reload the state the first wrote. Nothing else in CI covers durability + # across a restart, which is how a persistence subsystem that wrote to a + # snapshot-backed device went unnoticed. + - name: Verify snapshot state survives a reboot + run: make qemu-persistence-reboot + env: + XAIOS_QEMU_PERSISTENCE_TIMEOUT: "180" + model-storage-interoperability: name: ModelFS SFTP Interoperability runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 55b8d957..84630119 100644 --- a/Makefile +++ b/Makefile @@ -421,6 +421,11 @@ hosted-test: engine-cli -Ikernel/include kernel/fs/vfs.c tests/storage/test_vfs.c \ -o build/hosted/test-vfs ./build/hosted/test-vfs + $(HOST_CC) $(HOST_CFLAGS) \ + -Ikernel/include kernel/fs/mutable_fs.c kernel/dev/block_device.c \ + tests/storage/test_mutable_fs_mirror.c \ + -o build/hosted/test-mutable-fs-mirror + ./build/hosted/test-mutable-fs-mirror $(HOST_CC) $(HOST_CFLAGS) \ -Iuserspace/include -Iuserspace/sshd -Iuserspace/apps/terminal \ -Ikernel/include \ diff --git a/boot/uefi/include/uefi_min.h b/boot/uefi/include/uefi_min.h index 6709329f..8b02a4d0 100644 --- a/boot/uefi/include/uefi_min.h +++ b/boot/uefi/include/uefi_min.h @@ -228,8 +228,12 @@ struct efi_graphics_output_protocol_mode { }; struct efi_graphics_output_protocol { - void *query_mode; - void *set_mode; + efi_status_t(EFIAPI *query_mode)( + efi_graphics_output_protocol_t *self, uint32_t mode_number, + uint64_t *size_of_info, + efi_graphics_output_mode_information_t **info); + efi_status_t(EFIAPI *set_mode)(efi_graphics_output_protocol_t *self, + uint32_t mode_number); void *blt; efi_graphics_output_protocol_mode_t *mode; }; diff --git a/boot/uefi/loader_main.c b/boot/uefi/loader_main.c index 395c9574..8f7fcf22 100644 --- a/boot/uefi/loader_main.c +++ b/boot/uefi/loader_main.c @@ -158,6 +158,54 @@ static void collect_firmware_entropy(efi_system_table_t *system_table, boot_info->entropy_seed_size = XAIOS_BOOT_INFO_ENTROPY_SEED_BYTES; } +/* Firmware hands over whatever mode it happened to be in, which on VMware + Fusion is 1024x768. The console renders an 8x8 font into that, so the guest + looks like a DOS box on a modern display. Pick the largest mode the firmware + offers in a directly addressable 32-bit format, bounded so an unusually + large mode cannot produce a framebuffer the kernel will not map. */ +#define LOADER_MAX_DISPLAY_WIDTH UINT32_C(2560) +#define LOADER_MAX_DISPLAY_HEIGHT UINT32_C(1600) + +static void select_display_mode(efi_graphics_output_protocol_t *gop) { + if (gop == 0 || gop->query_mode == 0 || gop->set_mode == 0 || + gop->mode == 0 || gop->mode->max_mode == 0U) { + return; + } + uint32_t best_mode = gop->mode->mode; + uint64_t best_pixels = 0U; + if (gop->mode->info != 0) { + best_pixels = (uint64_t)gop->mode->info->horizontal_resolution * + (uint64_t)gop->mode->info->vertical_resolution; + } + for (uint32_t candidate = 0U; candidate < gop->mode->max_mode; ++candidate) { + efi_graphics_output_mode_information_t *info = 0; + uint64_t size_of_info = 0U; + if (is_error(gop->query_mode(gop, candidate, &size_of_info, &info)) || + info == 0) { + continue; + } + /* Only the two packed 32-bit formats are drawable by the kernel. */ + if (info->pixel_format > 1U) continue; + if (info->horizontal_resolution == 0U || info->vertical_resolution == 0U || + info->pixels_per_scan_line < info->horizontal_resolution) { + continue; + } + if (info->horizontal_resolution > LOADER_MAX_DISPLAY_WIDTH || + info->vertical_resolution > LOADER_MAX_DISPLAY_HEIGHT) { + continue; + } + uint64_t pixels = (uint64_t)info->horizontal_resolution * + (uint64_t)info->vertical_resolution; + if (pixels > best_pixels) { + best_pixels = pixels; + best_mode = candidate; + } + } + if (best_mode != gop->mode->mode) { + (void)gop->set_mode(gop, best_mode); + } +} + static void collect_framebuffer(efi_system_table_t *system_table, xaios_boot_info_t *boot_info) { if (system_table == 0 || system_table->boot_services == 0 || @@ -174,6 +222,7 @@ static void collect_framebuffer(efi_system_table_t *system_table, gop->mode->framebuffer_size == 0U) { return; } + select_display_mode(gop); const efi_graphics_output_mode_information_t *info = gop->mode->info; if (info->horizontal_resolution == 0U || info->vertical_resolution == 0U || info->pixels_per_scan_line < info->horizontal_resolution || diff --git a/contracts/qemu-rc-v1.json b/contracts/qemu-rc-v1.json index 2e41f761..0d55a297 100644 --- a/contracts/qemu-rc-v1.json +++ b/contracts/qemu-rc-v1.json @@ -6,97 +6,401 @@ "architecture": "aarch64+x86_64", "machine": "qemu-virt+q35", "firmware": "UEFI", - "accelerators": ["hvf", "tcg"], + "accelerators": [ + "hvf", + "tcg" + ], "benchmark_type": "qemu-correctness", "performance_claims_allowed": false }, "syscall_abi": { "version": 1, "syscalls": [ - {"number": 1, "name": "log", "capability": "XAIOS_CAP_LOG"}, - {"number": 2, "name": "exit", "capability": "XAIOS_CAP_EXIT"}, - {"number": 3, "name": "osctl", "capability": "XAIOS_CAP_OSCTL"}, - {"number": 4, "name": "read_service_descriptor", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 5, "name": "service_status", "capability": "XAIOS_CAP_SERVICE_CONTROL"}, - {"number": 6, "name": "service_start", "capability": "XAIOS_CAP_SERVICE_CONTROL"}, - {"number": 7, "name": "service_stop", "capability": "XAIOS_CAP_SERVICE_CONTROL"}, - {"number": 8, "name": "service_restart", "capability": "XAIOS_CAP_SERVICE_CONTROL"}, - {"number": 9, "name": "service_rollback", "capability": "XAIOS_CAP_SERVICE_ROLLBACK"}, - {"number": 10, "name": "service_update", "capability": "XAIOS_CAP_UPDATE"}, - {"number": 11, "name": "fs_open", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 12, "name": "fs_read", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 13, "name": "fs_write", "capability": "XAIOS_CAP_FS_WRITE"}, - {"number": 14, "name": "fs_close", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 15, "name": "fs_stat", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 16, "name": "fs_mkdir", "capability": "XAIOS_CAP_FS_WRITE"}, - {"number": 17, "name": "fs_delete", "capability": "XAIOS_CAP_FS_WRITE"}, - {"number": 18, "name": "fs_rename", "capability": "XAIOS_CAP_FS_WRITE"}, - {"number": 19, "name": "fs_list", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 20, "name": "clock_nanos", "capability": "XAIOS_CAP_TIME"}, - {"number": 21, "name": "net_udp_echo", "capability": "XAIOS_CAP_NET"}, - {"number": 22, "name": "net_tcp_connect", "capability": "XAIOS_CAP_NET"}, - {"number": 23, "name": "smp_run", "capability": "XAIOS_CAP_SMP"}, - {"number": 24, "name": "cpu_ai_decode", "capability": "XAIOS_CAP_CPU_AI"}, - {"number": 25, "name": "remote_login", "capability": "XAIOS_CAP_REMOTE_LOGIN"}, - {"number": 26, "name": "net_external_session", "capability": "XAIOS_CAP_NET"}, - {"number": 27, "name": "thread_group_run", "capability": "XAIOS_CAP_THREADS"}, - {"number": 28, "name": "ml_run", "capability": "XAIOS_CAP_ML"}, - {"number": 29, "name": "net_listen", "capability": "XAIOS_CAP_NET_SOCKET"}, - {"number": 30, "name": "net_accept", "capability": "XAIOS_CAP_NET_SOCKET"}, - {"number": 31, "name": "net_recv", "capability": "XAIOS_CAP_NET_SOCKET"}, - {"number": 32, "name": "net_send", "capability": "XAIOS_CAP_NET_SOCKET"}, - {"number": 33, "name": "net_close", "capability": "XAIOS_CAP_NET_SOCKET"}, - {"number": 34, "name": "agent_dispatch", "capability": "XAIOS_CAP_AGENT"}, - {"number": 35, "name": "random", "capability": "XAIOS_CAP_RANDOM"}, - {"number": 36, "name": "fs_seek", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 37, "name": "control_query", "capability": "XAIOS_CAP_CONTROL_QUERY"}, - {"number": 38, "name": "remote_login_session", "capability": "XAIOS_CAP_REMOTE_LOGIN"}, - {"number": 39, "name": "fs_pread", "capability": "XAIOS_CAP_FS_READ"}, - {"number": 40, "name": "fs_pwrite", "capability": "XAIOS_CAP_FS_WRITE"}, - {"number": 41, "name": "fs_fsync", "capability": "XAIOS_CAP_FS_WRITE"}, - {"number": 42, "name": "thread_create", "capability": "XAIOS_CAP_THREADS"}, - {"number": 43, "name": "thread_join", "capability": "XAIOS_CAP_THREADS"}, - {"number": 44, "name": "thread_cancel", "capability": "XAIOS_CAP_THREADS"}, - {"number": 45, "name": "thread_exit", "capability": "XAIOS_CAP_THREADS"}, - {"number": 46, "name": "net_resolve", "capability": "XAIOS_CAP_NET"}, - {"number": 47, "name": "console_read", "capability": "XAIOS_CAP_CONSOLE"}, - {"number": 48, "name": "console_write", "capability": "XAIOS_CAP_CONSOLE"}, - {"number": 49, "name": "net_local_ipv4", "capability": "XAIOS_CAP_NET"}, - {"number": 50, "name": "net_connect", "capability": "XAIOS_CAP_NET_SOCKET"} + { + "number": 1, + "name": "log", + "capability": "XAIOS_CAP_LOG" + }, + { + "number": 2, + "name": "exit", + "capability": "XAIOS_CAP_EXIT" + }, + { + "number": 3, + "name": "osctl", + "capability": "XAIOS_CAP_OSCTL" + }, + { + "number": 4, + "name": "read_service_descriptor", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 5, + "name": "service_status", + "capability": "XAIOS_CAP_SERVICE_CONTROL" + }, + { + "number": 6, + "name": "service_start", + "capability": "XAIOS_CAP_SERVICE_CONTROL" + }, + { + "number": 7, + "name": "service_stop", + "capability": "XAIOS_CAP_SERVICE_CONTROL" + }, + { + "number": 8, + "name": "service_restart", + "capability": "XAIOS_CAP_SERVICE_CONTROL" + }, + { + "number": 9, + "name": "service_rollback", + "capability": "XAIOS_CAP_SERVICE_ROLLBACK" + }, + { + "number": 10, + "name": "service_update", + "capability": "XAIOS_CAP_UPDATE" + }, + { + "number": 11, + "name": "fs_open", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 12, + "name": "fs_read", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 13, + "name": "fs_write", + "capability": "XAIOS_CAP_FS_WRITE" + }, + { + "number": 14, + "name": "fs_close", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 15, + "name": "fs_stat", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 16, + "name": "fs_mkdir", + "capability": "XAIOS_CAP_FS_WRITE" + }, + { + "number": 17, + "name": "fs_delete", + "capability": "XAIOS_CAP_FS_WRITE" + }, + { + "number": 18, + "name": "fs_rename", + "capability": "XAIOS_CAP_FS_WRITE" + }, + { + "number": 19, + "name": "fs_list", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 20, + "name": "clock_nanos", + "capability": "XAIOS_CAP_TIME" + }, + { + "number": 21, + "name": "net_udp_echo", + "capability": "XAIOS_CAP_NET" + }, + { + "number": 22, + "name": "net_tcp_connect", + "capability": "XAIOS_CAP_NET" + }, + { + "number": 23, + "name": "smp_run", + "capability": "XAIOS_CAP_SMP" + }, + { + "number": 24, + "name": "cpu_ai_decode", + "capability": "XAIOS_CAP_CPU_AI" + }, + { + "number": 25, + "name": "remote_login", + "capability": "XAIOS_CAP_REMOTE_LOGIN" + }, + { + "number": 26, + "name": "net_external_session", + "capability": "XAIOS_CAP_NET" + }, + { + "number": 27, + "name": "thread_group_run", + "capability": "XAIOS_CAP_THREADS" + }, + { + "number": 28, + "name": "ml_run", + "capability": "XAIOS_CAP_ML" + }, + { + "number": 29, + "name": "net_listen", + "capability": "XAIOS_CAP_NET_SOCKET" + }, + { + "number": 30, + "name": "net_accept", + "capability": "XAIOS_CAP_NET_SOCKET" + }, + { + "number": 31, + "name": "net_recv", + "capability": "XAIOS_CAP_NET_SOCKET" + }, + { + "number": 32, + "name": "net_send", + "capability": "XAIOS_CAP_NET_SOCKET" + }, + { + "number": 33, + "name": "net_close", + "capability": "XAIOS_CAP_NET_SOCKET" + }, + { + "number": 34, + "name": "agent_dispatch", + "capability": "XAIOS_CAP_AGENT" + }, + { + "number": 35, + "name": "random", + "capability": "XAIOS_CAP_RANDOM" + }, + { + "number": 36, + "name": "fs_seek", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 37, + "name": "control_query", + "capability": "XAIOS_CAP_CONTROL_QUERY" + }, + { + "number": 38, + "name": "remote_login_session", + "capability": "XAIOS_CAP_REMOTE_LOGIN" + }, + { + "number": 39, + "name": "fs_pread", + "capability": "XAIOS_CAP_FS_READ" + }, + { + "number": 40, + "name": "fs_pwrite", + "capability": "XAIOS_CAP_FS_WRITE" + }, + { + "number": 41, + "name": "fs_fsync", + "capability": "XAIOS_CAP_FS_WRITE" + }, + { + "number": 42, + "name": "thread_create", + "capability": "XAIOS_CAP_THREADS" + }, + { + "number": 43, + "name": "thread_join", + "capability": "XAIOS_CAP_THREADS" + }, + { + "number": 44, + "name": "thread_cancel", + "capability": "XAIOS_CAP_THREADS" + }, + { + "number": 45, + "name": "thread_exit", + "capability": "XAIOS_CAP_THREADS" + }, + { + "number": 46, + "name": "net_resolve", + "capability": "XAIOS_CAP_NET" + }, + { + "number": 47, + "name": "console_read", + "capability": "XAIOS_CAP_CONSOLE" + }, + { + "number": 48, + "name": "console_write", + "capability": "XAIOS_CAP_CONSOLE" + }, + { + "number": 49, + "name": "net_local_ipv4", + "capability": "XAIOS_CAP_NET" + }, + { + "number": 50, + "name": "net_connect", + "capability": "XAIOS_CAP_NET_SOCKET" + }, + { + "number": 51, + "name": "net_local_ipv6", + "capability": "XAIOS_CAP_NET" + } ], "capabilities": [ - {"bit": 1, "name": "XAIOS_CAP_LOG"}, - {"bit": 2, "name": "XAIOS_CAP_EXIT"}, - {"bit": 4, "name": "XAIOS_CAP_OSCTL"}, - {"bit": 8, "name": "XAIOS_CAP_SERVICE_ROLLBACK"}, - {"bit": 16, "name": "XAIOS_CAP_UPDATE"}, - {"bit": 32, "name": "XAIOS_CAP_FS_READ"}, - {"bit": 64, "name": "XAIOS_CAP_SERVICE_CONTROL"}, - {"bit": 128, "name": "XAIOS_CAP_ADMIN"}, - {"bit": 256, "name": "XAIOS_CAP_FS_WRITE"}, - {"bit": 512, "name": "XAIOS_CAP_TIME"}, - {"bit": 1024, "name": "XAIOS_CAP_NET"}, - {"bit": 2048, "name": "XAIOS_CAP_SMP"}, - {"bit": 4096, "name": "XAIOS_CAP_CPU_AI"}, - {"bit": 8192, "name": "XAIOS_CAP_REMOTE_LOGIN"}, - {"bit": 16384, "name": "XAIOS_CAP_THREADS"}, - {"bit": 32768, "name": "XAIOS_CAP_ML"}, - {"bit": 65536, "name": "XAIOS_CAP_NET_SOCKET"}, - {"bit": 131072, "name": "XAIOS_CAP_AGENT"}, - {"bit": 262144, "name": "XAIOS_CAP_RANDOM"}, - {"bit": 524288, "name": "XAIOS_CAP_CONTROL_QUERY"}, - {"bit": 1048576, "name": "XAIOS_CAP_CONTROL_ADMIN"}, - {"bit": 2097152, "name": "XAIOS_CAP_STORAGE_READ"}, - {"bit": 4194304, "name": "XAIOS_CAP_STORAGE_MOUNT"}, - {"bit": 8388608, "name": "XAIOS_CAP_STORAGE_FORMAT"}, - {"bit": 16777216, "name": "XAIOS_CAP_STORAGE_PARTITION"}, - {"bit": 33554432, "name": "XAIOS_CAP_STORAGE_REPAIR"}, - {"bit": 67108864, "name": "XAIOS_CAP_STORAGE_RESIZE"}, - {"bit": 134217728, "name": "XAIOS_CAP_STORAGE_TRIM"}, - {"bit": 268435456, "name": "XAIOS_CAP_MODEL_STAGE"}, - {"bit": 536870912, "name": "XAIOS_CAP_MODEL_ACTIVATE"}, - {"bit": 1073741824, "name": "XAIOS_CAP_CONSOLE"}, - {"bit": 2147483648, "name": "XAIOS_CAP_CREDENTIAL_READ"} + { + "bit": 1, + "name": "XAIOS_CAP_LOG" + }, + { + "bit": 2, + "name": "XAIOS_CAP_EXIT" + }, + { + "bit": 4, + "name": "XAIOS_CAP_OSCTL" + }, + { + "bit": 8, + "name": "XAIOS_CAP_SERVICE_ROLLBACK" + }, + { + "bit": 16, + "name": "XAIOS_CAP_UPDATE" + }, + { + "bit": 32, + "name": "XAIOS_CAP_FS_READ" + }, + { + "bit": 64, + "name": "XAIOS_CAP_SERVICE_CONTROL" + }, + { + "bit": 128, + "name": "XAIOS_CAP_ADMIN" + }, + { + "bit": 256, + "name": "XAIOS_CAP_FS_WRITE" + }, + { + "bit": 512, + "name": "XAIOS_CAP_TIME" + }, + { + "bit": 1024, + "name": "XAIOS_CAP_NET" + }, + { + "bit": 2048, + "name": "XAIOS_CAP_SMP" + }, + { + "bit": 4096, + "name": "XAIOS_CAP_CPU_AI" + }, + { + "bit": 8192, + "name": "XAIOS_CAP_REMOTE_LOGIN" + }, + { + "bit": 16384, + "name": "XAIOS_CAP_THREADS" + }, + { + "bit": 32768, + "name": "XAIOS_CAP_ML" + }, + { + "bit": 65536, + "name": "XAIOS_CAP_NET_SOCKET" + }, + { + "bit": 131072, + "name": "XAIOS_CAP_AGENT" + }, + { + "bit": 262144, + "name": "XAIOS_CAP_RANDOM" + }, + { + "bit": 524288, + "name": "XAIOS_CAP_CONTROL_QUERY" + }, + { + "bit": 1048576, + "name": "XAIOS_CAP_CONTROL_ADMIN" + }, + { + "bit": 2097152, + "name": "XAIOS_CAP_STORAGE_READ" + }, + { + "bit": 4194304, + "name": "XAIOS_CAP_STORAGE_MOUNT" + }, + { + "bit": 8388608, + "name": "XAIOS_CAP_STORAGE_FORMAT" + }, + { + "bit": 16777216, + "name": "XAIOS_CAP_STORAGE_PARTITION" + }, + { + "bit": 33554432, + "name": "XAIOS_CAP_STORAGE_REPAIR" + }, + { + "bit": 67108864, + "name": "XAIOS_CAP_STORAGE_RESIZE" + }, + { + "bit": 134217728, + "name": "XAIOS_CAP_STORAGE_TRIM" + }, + { + "bit": 268435456, + "name": "XAIOS_CAP_MODEL_STAGE" + }, + { + "bit": 536870912, + "name": "XAIOS_CAP_MODEL_ACTIVATE" + }, + { + "bit": 1073741824, + "name": "XAIOS_CAP_CONSOLE" + }, + { + "bit": 2147483648, + "name": "XAIOS_CAP_CREDENTIAL_READ" + } ] }, "control_protocol": { @@ -299,9 +603,14 @@ "max_files": 64, "path_max": 64, "data_offset": 2097152, - "flags": ["read_only"], + "flags": [ + "read_only" + ], "entry_type": "file", - "entry_flags": ["executable", "manifest"], + "entry_flags": [ + "executable", + "manifest" + ], "required_paths": [ "/init", "/bin/service-manager", @@ -405,7 +714,13 @@ "name": "XAIOS service descriptor", "encoding": "ascii key=value lines", "path": "/etc/services/source-index.svc", - "required_keys": ["name", "parent", "restart", "start", "status"], + "required_keys": [ + "name", + "parent", + "restart", + "start", + "status" + ], "expected_values": { "name": "/svc/source-index", "parent": "/init", @@ -505,17 +820,50 @@ "session_lifecycle_metadata" ], "independent_gates": [ - {"name": "fault_injection", "command": "make qemu-fault-injection"}, - {"name": "storage_crash", "command": "make qemu-storage-crash-test"}, - {"name": "smmuv3", "command": "make qemu-smmu-gate"}, - {"name": "nvme", "command": "make qemu-nvme-gate"}, - {"name": "outbound_fragmentation", "command": "make qemu-outbound-fragmentation-gate"}, - {"name": "network", "command": "make qemu-network-suite"}, - {"name": "high_core", "command": "make qemu-high-core-gate"}, - {"name": "x86_64", "command": "make qemu-x86_64-smoke"}, - {"name": "x86_64_cpu_matrix", "command": "make qemu-x86_64-cpu-matrix"}, - {"name": "x86_64_platform_matrix", "command": "make qemu-x86_64-platform-matrix"}, - {"name": "x86_64_network", "command": "XAIOS_QEMU_NETWORK_ARCH=x86_64 python3 tests/scripts/qemu-docker-network-suite.py"} + { + "name": "fault_injection", + "command": "make qemu-fault-injection" + }, + { + "name": "storage_crash", + "command": "make qemu-storage-crash-test" + }, + { + "name": "smmuv3", + "command": "make qemu-smmu-gate" + }, + { + "name": "nvme", + "command": "make qemu-nvme-gate" + }, + { + "name": "outbound_fragmentation", + "command": "make qemu-outbound-fragmentation-gate" + }, + { + "name": "network", + "command": "make qemu-network-suite" + }, + { + "name": "high_core", + "command": "make qemu-high-core-gate" + }, + { + "name": "x86_64", + "command": "make qemu-x86_64-smoke" + }, + { + "name": "x86_64_cpu_matrix", + "command": "make qemu-x86_64-cpu-matrix" + }, + { + "name": "x86_64_platform_matrix", + "command": "make qemu-x86_64-platform-matrix" + }, + { + "name": "x86_64_network", + "command": "XAIOS_QEMU_NETWORK_ARCH=x86_64 python3 tests/scripts/qemu-docker-network-suite.py" + } ], "x86_qemu_service_parity": true, "x86_physical_support": false @@ -523,35 +871,156 @@ "cpu_matrix": { "schema": "xaios.qemu.cpu_matrix.v1", "arm64_boot_tiers": [ - {"name": "fast-local-hvf-host", "cpu": "host", "accelerator": "hvf", "required": false, "run_if_env": "XAIOS_QEMU_RUN_OPTIONAL_HVF=1", "validation": "qemu-smoke-default"}, - {"name": "baseline-cortex-a53", "cpu": "cortex-a53", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "common-cortex-a72", "cpu": "cortex-a72", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "modern-cortex-a76", "cpu": "cortex-a76", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "newer-cortex-a710", "cpu": "cortex-a710", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "server-neoverse-n1", "cpu": "neoverse-n1", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "server-neoverse-n2", "cpu": "neoverse-n2", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "performance-neoverse-v1", "cpu": "neoverse-v1", "accelerator": "tcg", "validation": "qemu-boot-probe"}, - {"name": "future-max", "cpu": "max", "accelerator": "tcg", "validation": "qemu-boot-probe"} + { + "name": "fast-local-hvf-host", + "cpu": "host", + "accelerator": "hvf", + "required": false, + "run_if_env": "XAIOS_QEMU_RUN_OPTIONAL_HVF=1", + "validation": "qemu-smoke-default" + }, + { + "name": "baseline-cortex-a53", + "cpu": "cortex-a53", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "common-cortex-a72", + "cpu": "cortex-a72", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "modern-cortex-a76", + "cpu": "cortex-a76", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "newer-cortex-a710", + "cpu": "cortex-a710", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "server-neoverse-n1", + "cpu": "neoverse-n1", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "server-neoverse-n2", + "cpu": "neoverse-n2", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "performance-neoverse-v1", + "cpu": "neoverse-v1", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + }, + { + "name": "future-max", + "cpu": "max", + "accelerator": "tcg", + "validation": "qemu-boot-probe" + } ], "x86_64_command_tiers": [ - {"name": "intel-desktop-conservative", "cpu": "Skylake-Client", "validation": "qemu-smoke"}, - {"name": "intel-desktop-alderlake-compat", "cpu": "max", "validation": "qemu-smoke"}, - {"name": "intel-server-skylake", "cpu": "Skylake-Server", "validation": "qemu-smoke"}, - {"name": "intel-server-cascadelake", "cpu": "Cascadelake-Server", "validation": "qemu-smoke"}, - {"name": "intel-server-cooperlake", "cpu": "Cooperlake", "validation": "qemu-smoke"}, - {"name": "intel-server-icelake", "cpu": "Icelake-Server", "validation": "qemu-smoke"}, - {"name": "intel-server-sapphirerapids", "cpu": "SapphireRapids", "validation": "qemu-smoke"}, - {"name": "intel-server-graniterapids", "cpu": "GraniteRapids", "validation": "qemu-smoke"}, - {"name": "intel-server-diamondrapids", "cpu": "DiamondRapids", "required": false, "validation": "qemu-smoke"}, - {"name": "intel-server-sierraforest", "cpu": "SierraForest", "validation": "qemu-smoke"}, - {"name": "intel-server-clearwaterforest", "cpu": "ClearwaterForest", "validation": "qemu-smoke"}, - {"name": "intel-server-edge-denverton", "cpu": "Denverton", "validation": "qemu-smoke"}, - {"name": "intel-server-edge-snowridge", "cpu": "Snowridge", "validation": "qemu-smoke"}, - {"name": "amd-epyc", "cpu": "EPYC", "validation": "qemu-smoke"}, - {"name": "amd-epyc-rome", "cpu": "EPYC-Rome", "validation": "qemu-smoke"}, - {"name": "amd-epyc-milan", "cpu": "EPYC-Milan", "validation": "qemu-smoke"}, - {"name": "amd-epyc-genoa", "cpu": "EPYC-Genoa", "validation": "qemu-smoke"}, - {"name": "amd-epyc-turin", "cpu": "EPYC-Turin", "required": false, "validation": "qemu-smoke"} + { + "name": "intel-desktop-conservative", + "cpu": "Skylake-Client", + "validation": "qemu-smoke" + }, + { + "name": "intel-desktop-alderlake-compat", + "cpu": "max", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-skylake", + "cpu": "Skylake-Server", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-cascadelake", + "cpu": "Cascadelake-Server", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-cooperlake", + "cpu": "Cooperlake", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-icelake", + "cpu": "Icelake-Server", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-sapphirerapids", + "cpu": "SapphireRapids", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-graniterapids", + "cpu": "GraniteRapids", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-diamondrapids", + "cpu": "DiamondRapids", + "required": false, + "validation": "qemu-smoke" + }, + { + "name": "intel-server-sierraforest", + "cpu": "SierraForest", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-clearwaterforest", + "cpu": "ClearwaterForest", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-edge-denverton", + "cpu": "Denverton", + "validation": "qemu-smoke" + }, + { + "name": "intel-server-edge-snowridge", + "cpu": "Snowridge", + "validation": "qemu-smoke" + }, + { + "name": "amd-epyc", + "cpu": "EPYC", + "validation": "qemu-smoke" + }, + { + "name": "amd-epyc-rome", + "cpu": "EPYC-Rome", + "validation": "qemu-smoke" + }, + { + "name": "amd-epyc-milan", + "cpu": "EPYC-Milan", + "validation": "qemu-smoke" + }, + { + "name": "amd-epyc-genoa", + "cpu": "EPYC-Genoa", + "validation": "qemu-smoke" + }, + { + "name": "amd-epyc-turin", + "cpu": "EPYC-Turin", + "required": false, + "validation": "qemu-smoke" + } ] }, "out_of_scope_before_intel": [ diff --git a/deploy/xapt/Caddyfile b/deploy/xapt/Caddyfile index 0c11d59a..15227f77 100644 --- a/deploy/xapt/Caddyfile +++ b/deploy/xapt/Caddyfile @@ -1,9 +1,58 @@ -:8443 { - tls /etc/caddy/xapt-tls-cert.pem /etc/caddy/xapt-tls-key.pem +# The updater's own web server: its own binary, config, storage, user and unit. +# Nothing here is shared with any other project on the host. +# +# Deployed to /var/xaios_updater/caddy/Caddyfile and run by xaios-caddy.service. +# TLS is NOT terminated here — the master edge in /var/caddy owns :443 for +# xaios.91.99.176.243.nip.io and forwards to :8090 below. Reloading the shared +# `caddy` service from this project would take the other projects down with it, +# so publishing only ever reloads xaios-caddy. +{ + # The master terminates TLS and forwards here, so this instance never + # speaks TLS and never talks to an ACME server. + auto_https off + # Its own admin socket: two Caddy processes cannot share the default + # admin endpoint, and the second one to start would fail. + admin unix//var/xaios_updater/caddy/admin.sock + + servers { + # Behind the master, so the forwarded client address is trustworthy + # here. Without this every request logs as 127.0.0.1. + trusted_proxies static private_ranges + } +} + +:8090 { + # Not bound to loopback yet, deliberately. Devices provisioned before the + # master existed still fetch from http://91.99.176.243:8090/, and a + # device's base URL is configured on the device, so binding loopback would + # silently strand them. Integrity does not depend on the transport: + # catalog entries carry sha256, a signature and a key, so a tampered + # artifact is rejected by the device however it arrived. root * /var/xaios_updater + header { + -Server X-Content-Type-Options nosniff + Referrer-Policy "no-referrer" + X-Frame-Options "DENY" + # Applies to the hostname served through the master. HSTS is ignored + # for bare-IP requests, so this cannot strand a device still using + # http://91.99.176.243:8090/. + Strict-Transport-Security "max-age=31536000" Cache-Control "public, max-age=300" } - file_server + + file_server { + # The server's own files live inside the directory it serves, so they + # are hidden explicitly rather than being downloadable. + hide /var/xaios_updater/caddy /var/xaios_updater/bin + } + + log { + output file /var/xaios_updater/caddy/logs/updater.log { + roll_size 20MiB + roll_keep 10 + } + format json + } } diff --git a/docs/NETWORK-SSH-STATUS.md b/docs/NETWORK-SSH-STATUS.md index ad33b03e..834ffce4 100644 --- a/docs/NETWORK-SSH-STATUS.md +++ b/docs/NETWORK-SSH-STATUS.md @@ -183,7 +183,11 @@ analysis and physical deployment qualification remain required. Fresh randomness comes from a VirtIO RNG-backed ChaCha20 DRBG. SSH startup is fail-closed when secure entropy is unavailable. The host key is created once, stored on the persistent mutable filesystem, flushed to the block device, and -reused on reboot. Rekey works in both protocol directions; the Debian gate +reused on reboot. If it cannot be written, the service keeps the key it has +and reports that the key is ephemeral rather than refusing to start: an +unwritable filesystem is exactly when an operator needs remote access to +repair storage, and a key that clients report as changed is at least visible, +where an unreachable machine offers nothing to act on. Rekey works in both protocol directions; the Debian gate forces the client-initiated path. The SSH command boundary recognizes an exact `xaiosctl` prefix and invokes the diff --git a/kernel/core/boot_ui.c b/kernel/core/boot_ui.c index de9e1389..fe1204cf 100644 --- a/kernel/core/boot_ui.c +++ b/kernel/core/boot_ui.c @@ -14,9 +14,16 @@ #define FB_BAR_MAX_WIDTH UINT32_C(720) #define FB_GLYPH_WIDTH UINT32_C(8) #define FB_GLYPH_HEIGHT UINT32_C(8) -#define FB_GLYPH_X_SCALE UINT32_C(1) -#define FB_GLYPH_Y_SCALE UINT32_C(2) -#define FB_GLYPH_ADVANCE UINT32_C(9) +/* Glyph geometry follows the mode the firmware gave us. The bitmap font is + 8x8, so a fixed 1x2 scale that reads well at 1024x768 turns into unreadable + specks once the loader selects a 1920x1200 mode. Scale with the display so + text keeps roughly the same physical size instead. */ +static uint32_t g_glyph_x_scale = UINT32_C(1); +static uint32_t g_glyph_y_scale = UINT32_C(2); +static uint32_t g_glyph_advance = UINT32_C(9); +#define FB_GLYPH_X_SCALE g_glyph_x_scale +#define FB_GLYPH_Y_SCALE g_glyph_y_scale +#define FB_GLYPH_ADVANCE g_glyph_advance typedef struct boot_framebuffer { volatile uint32_t *pixels; @@ -615,6 +622,14 @@ static void fb_init(const xaios_boot_info_t *boot) { g_framebuffer.height = boot->framebuffer_height; g_framebuffer.stride = boot->framebuffer_pixels_per_scan_line; g_framebuffer.format = boot->framebuffer_format; + + /* Roughly 100-160 columns at any supported width. */ + uint32_t scale = g_framebuffer.width / UINT32_C(1024); + if (scale == 0U) scale = 1U; + if (scale > 3U) scale = 3U; + g_glyph_x_scale = scale; + g_glyph_y_scale = scale * 2U; + g_glyph_advance = (FB_GLYPH_WIDTH + 1U) * scale; } #if !XAIOS_BOOT_TEST_APPS && !XAIOS_BOOT_VERBOSE diff --git a/kernel/core/klog_ring.c b/kernel/core/klog_ring.c index 4bef46d8..72d8fb1a 100644 --- a/kernel/core/klog_ring.c +++ b/kernel/core/klog_ring.c @@ -20,6 +20,11 @@ typedef struct xaios_klog_ring { static xaios_klog_ring_t g_ring; static uint32_t g_ring_initialized; +/* Capturing to memory and being able to persist are separate capabilities. + Conflating them meant the ring stayed switched off until MutableFS was + mounted, so nothing from early boot was ever captured and a boot whose + persistent mount failed captured nothing at all. */ +static uint32_t g_persist_ready; static uint64_t g_persist_count; static uint64_t g_rotate_count; @@ -35,17 +40,27 @@ void klog_ring_init(void) { g_ring.total_written = 0; g_persist_count = 0; g_rotate_count = 0; + g_persist_ready = 0; - /* Ensure the persistent log path exists before enabling the ring. */ + /* In-memory capture depends on nothing but this buffer, so it starts here + and can be started long before storage exists. */ + g_ring_initialized = 1; + klog("klog_ring: capture enabled size=%u\n", XAIOS_KLOG_RING_SIZE); +} + +/* Enable the persistent path once MutableFS is mounted. Capture continues + regardless; only flushing depends on this. */ +xaios_status_t klog_ring_enable_persistence(void) { + if (g_ring_initialized == 0) return XAIOS_ERR_INVALID; if (mutable_fs_mkdir("/var") != XAIOS_OK || mutable_fs_mkdir("/var/log") != XAIOS_OK) { - g_ring_initialized = 0; - klog("klog_ring: initialization failed; persistent path unavailable\n"); - return; + g_persist_ready = 0; + klog("klog_ring: persistent path unavailable; capture continues\n"); + return XAIOS_ERR_IO; } - - g_ring_initialized = 1; - klog("klog_ring: initialized size=%u\n", XAIOS_KLOG_RING_SIZE); + g_persist_ready = 1; + klog("klog_ring: persistence enabled\n"); + return XAIOS_OK; } void klog_ring_write(const char *data, uint32_t length) { @@ -114,6 +129,30 @@ uint32_t klog_ring_snapshot(char *out, uint32_t max_len, return copied; } +/* Lock-free tail read for the panic path. + + Every other reader takes the ring lock, which a panic must not: interrupts + are already masked and another CPU may hold it, so waiting would replace a + readable diagnostic with a hang. Reading unlocked can tear against a + concurrent writer; a possibly-frayed last line beats no log at all, which + is what the panic screen showed before. */ +uint32_t klog_ring_panic_tail(char *out, uint32_t max_len) { + uint32_t available; + uint32_t take; + uint32_t start; + if (g_ring_initialized == 0 || out == 0 || max_len == 0) return 0; + available = g_ring.count; + if (available > XAIOS_KLOG_RING_SIZE) available = XAIOS_KLOG_RING_SIZE; + take = available < max_len ? available : max_len; + if (take == 0U) return 0; + start = (g_ring.write_pos + XAIOS_KLOG_RING_SIZE - take) % + XAIOS_KLOG_RING_SIZE; + for (uint32_t i = 0U; i < take; ++i) { + out[i] = g_ring.buffer[(start + i) % XAIOS_KLOG_RING_SIZE]; + } + return take; +} + void klog_ring_clear(void) { if (g_ring_initialized == 0) { return; @@ -156,7 +195,7 @@ uint64_t klog_ring_total_written(void) { } xaios_status_t klog_rotate(void) { - if (g_ring_initialized == 0) { + if (g_ring_initialized == 0 || g_persist_ready == 0) { return XAIOS_ERR_INVALID; } @@ -176,7 +215,7 @@ xaios_status_t klog_rotate(void) { xaios_status_t klog_flush(void) { uint64_t append_offset = 0; - if (g_ring_initialized == 0 || g_ring.count == 0) { + if (g_ring_initialized == 0 || g_persist_ready == 0 || g_ring.count == 0) { return XAIOS_OK; } diff --git a/kernel/core/kmain.c b/kernel/core/kmain.c index 349e7ad5..16380735 100644 --- a/kernel/core/kmain.c +++ b/kernel/core/kmain.c @@ -85,6 +85,10 @@ #define XAIOS_LIBC_TEST 0 #endif +/* Two NTP retransmits plus margin, well inside the client's own 10s + timeout, so a filtered UDP/123 costs a bounded pause and nothing more. */ +#define BOOT_NTP_DEADLINE_NS UINT64_C(6000000000) + static const char g_vmm_rodata_probe[] = "vmm-rodata"; static uint64_t g_vmm_data_probe; static virtio_block_handle_t *g_storage_admin_handle; @@ -156,6 +160,34 @@ static int run_user_app(const char *path, uint32_t pid, uint64_t capabilities) { return exit_code; } +/* Set the wall clock from NTP before any service starts. + + The clock is otherwise whatever the RTC reports, and QEMU's PL031 commonly + reports epoch zero, leaving the system in 1970. Anything that checks a + certificate validity window then sees every certificate as not-yet-valid, + which is how xapt fails against the updater's publicly issued certificate. + + Bounded and non-fatal. The default server is a bare address, so this needs + no DNS, but UDP/123 is filtered on some networks and a boot must not stall + waiting for a reply that will never arrive. */ +static void boot_sync_wall_clock(void) { + if (ntp_sync(0U) != XAIOS_ERR_BUSY) { + klog("kernel: boot ntp not started state=%u\n", + (unsigned)ntp_status().state); + return; + } + uint64_t deadline = timer_now_ns() + BOOT_NTP_DEADLINE_NS; + while (ntp_status().state == XAIOS_NTP_PENDING && + timer_now_ns() < deadline) { + network_poll_tick(); + xaios_cpu_relax(); + } + klog("kernel: boot ntp state=%u epoch_seconds=%lu source=%u\n", + (unsigned)ntp_status().state, + wall_time_now_ns() / UINT64_C(1000000000), + (unsigned)wall_time_source()); +} + static void map_mmio_range(uint64_t start, uint64_t size) { const uint64_t page_size = 4096; uint64_t page = start & ~(page_size - 1U); @@ -171,6 +203,12 @@ static void map_mmio_range(uint64_t start, uint64_t size) { void kmain(const xaios_boot_info_t *boot) { uint32_t persistent_network_ready = 0U; klog_init(boot); + /* Start capturing before any subsystem can fail. A normal boot redraws the + progress display over the serial console, so a failure explanation is + cleared from the screen moments before a panic replaces it; the ring is + what lets the panic screen say why, not just where. */ + klog_ring_init(); + klog_ring_self_test(); boot_ui_begin(boot); boot_ui_self_test(); boot_ui_update(25U, "hardware handoff", "CPU and interrupts", 5U); @@ -409,9 +447,8 @@ void kmain(const xaios_boot_info_t *boot) { provision_ephemeral_credential("/etc/xaios_ssh_client_identity"); admin_control_init(); admin_control_self_test(); - /* Initialize persistent log ring buffer */ - klog_ring_init(); - klog_ring_self_test(); + /* Capture already runs; this only adds the persistent flush path. */ + (void)klog_ring_enable_persistence(); /* Increment boot counter for recovery detection */ boot_counter_increment(); if (boot_in_recovery_mode()) { @@ -424,6 +461,9 @@ void kmain(const xaios_boot_info_t *boot) { } operations_init(persistent_status == XAIOS_OK ? 1U : 0U); kassert(vfs_mount_mutable_root() == XAIOS_OK); + /* Expose the boot image's /bin read-only, so the userspace ls that ships + as /bin/ls can list the directory it lives in. */ + kassert(vfs_mount_initramfs("/bin") == XAIOS_OK); klog("vfs: MutableFS mounted at /\n"); /* The boot loader selected this immutable system slot. Admit and validate @@ -754,6 +794,8 @@ void kmain(const xaios_boot_info_t *boot) { } } + boot_sync_wall_clock(); + klog("kernel: starting persistent /bin/sshd service\n"); int sshd_exit = run_user_app("/bin/sshd", XAIOS_BOOT_TEST_APPS ? 18U : 3U, sshd_caps); diff --git a/kernel/core/panic.c b/kernel/core/panic.c index cd5abaa3..392ec48e 100644 --- a/kernel/core/panic.c +++ b/kernel/core/panic.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -349,6 +350,41 @@ static void render_backtrace(const uint64_t *trace, uint32_t depth) { panic_puts("\r\n"); } +/* The last thing the kernel said before it died. + + Subsystems log why they fail, but a normal boot redraws the progress + display over the serial console, so on a non-verbose boot that explanation + is cleared off the screen a moment before the panic replaces it. Registers + and a backtrace then describe where the assertion fired without ever + saying what it found. Replaying the ring here keeps the reason attached to + the failure, which is what makes a rare boot panic diagnosable at all. */ +#define XAIOS_PANIC_LOG_BYTES 1536U + +static void render_recent_log(void) { + static char tail[XAIOS_PANIC_LOG_BYTES]; + uint32_t used = klog_ring_panic_tail(tail, sizeof(tail)); + uint32_t start = 0U; + if (used == 0U) return; + panic_puts("\r\n --- Recent Kernel Log ---\r\n"); + /* The first line is usually cut mid-way by the tail boundary; drop it so + the replay starts on a real line. */ + while (start < used && tail[start] != '\n') ++start; + if (start < used) ++start; + if (start >= used) start = 0U; + panic_puts(" "); + for (uint32_t i = start; i < used; ++i) { + char c = tail[i]; + if (c == '\n') { + panic_puts("\r\n "); + } else if (c == '\r') { + continue; + } else if (c >= 0x20 && c < 0x7f) { + panic_putc(c); + } + } + panic_puts("\r\n"); +} + static void render_halt(void) { panic_puts(" System halted. Manual reset required.\r\n"); panic_puts(" =====================================================\r\n"); @@ -394,6 +430,7 @@ void panic_at(const char *file, int line, const char *fmt, ...) { render_gp_regs(gp_regs); render_sys_regs(elr, esr, far, spsr, sp_el0, current_el); render_backtrace(stack_trace, trace_depth); + render_recent_log(); render_halt(); /* Halt forever — no auto-reboot so the operator can read diagnostics */ diff --git a/kernel/fs/initramfs.c b/kernel/fs/initramfs.c index b853b871..fdca91db 100644 --- a/kernel/fs/initramfs.c +++ b/kernel/fs/initramfs.c @@ -55,6 +55,12 @@ static char g_config_child_service_restart[INITFS_MODE_MAX]; static xaios_initramfs_config_t g_config; static uint32_t g_file_count; +static uint32_t str_len(const char *value) { + uint32_t length = 0U; + while (value[length] != '\0') ++length; + return length; +} + static int str_eq(const char *a, const char *b) { while (*a != '\0' && *b != '\0') { if (*a != *b) { @@ -253,6 +259,31 @@ static xaios_status_t validate_descriptor_target(void) { return XAIOS_ERR_NOT_FOUND; } +/* Bounded retry for boot-time block reads. + + A single transient sector read used to end the boot in a panic, with no + second attempt anywhere on the path. Retrying cannot smuggle in corruption: + the header is structurally validated and every file is checked against its + recorded hash below, so a bad read still fails. A retry that was actually + needed is logged, so a marginal device stays visible rather than silently + absorbed. */ +#define INITFS_READ_ATTEMPTS 3U + +static xaios_status_t read_sector_retrying(uint64_t sector, void *buffer) { + for (uint32_t attempt = 0U; attempt < INITFS_READ_ATTEMPTS; ++attempt) { + if (virtio_block_read_sector(sector, buffer, SECTOR_SIZE) == XAIOS_OK) { + if (attempt != 0U) { + klog("initramfs: sector=%lu read recovered on attempt %u\n", sector, + (unsigned)(attempt + 1U)); + } + return XAIOS_OK; + } + } + klog("initramfs: sector=%lu unreadable after %u attempts\n", sector, + (unsigned)INITFS_READ_ATTEMPTS); + return XAIOS_ERR_IO; +} + static xaios_status_t read_file_bytes(uint64_t offset, uint64_t size, void *buffer) { uint8_t sector[SECTOR_SIZE]; @@ -267,8 +298,7 @@ static xaios_status_t read_file_bytes(uint64_t offset, uint64_t size, if (chunk > size - copied) { chunk = size - copied; } - if (virtio_block_read_sector(sector_index, sector, sizeof(sector)) != - XAIOS_OK) { + if (read_sector_retrying(sector_index, sector) != XAIOS_OK) { return XAIOS_ERR_IO; } bytes_copy(out + copied, sector + sector_offset, chunk); @@ -319,9 +349,8 @@ static xaios_status_t validate_entry(const initfs_disk_header_t *header, xaios_status_t initramfs_init(void) { uint8_t header_bytes[INITFS_HEADER_BYTES]; for (uint64_t i = 0; i < INITFS_HEADER_BYTES / SECTOR_SIZE; ++i) { - if (virtio_block_read_sector(INITFS_SECTOR + i, - header_bytes + (i * SECTOR_SIZE), - SECTOR_SIZE) != XAIOS_OK) { + if (read_sector_retrying(INITFS_SECTOR + i, + header_bytes + (i * SECTOR_SIZE)) != XAIOS_OK) { klog("initramfs: failed to read header sector=%lu\n", INITFS_SECTOR + i); return XAIOS_ERR_IO; @@ -410,6 +439,53 @@ xaios_status_t initramfs_lookup(const char *path, return XAIOS_ERR_NOT_FOUND; } +uint32_t initramfs_file_count(void) { return g_file_count; } + +const xaios_initramfs_file_t *initramfs_file_at(uint32_t index) { + return index < g_file_count ? &g_files[index] : 0; +} + +/* The image stores flat absolute paths and no directory records, so + directory shape is derived: a directory exists when any file path extends + it, and its children are the first path components under it. */ +int initramfs_child_at(const char *directory, uint32_t index, char *name, + uint64_t name_capacity, int *is_directory) { + const xaios_initramfs_file_t *file = initramfs_file_at(index); + uint32_t dir_length = 0U; + if (directory == 0 || name == 0 || is_directory == 0 || file == 0 || + file->path == 0 || directory[0] != '/') { + return 0; + } + dir_length = str_len(directory); + if (dir_length > 1U) { + uint32_t i = 0U; + while (i < dir_length && file->path[i] == directory[i]) ++i; + if (i != dir_length || file->path[i] != '/') return 0; + } + const char *remainder = + file->path + (dir_length == 1U ? 1U : dir_length + 1U); + uint64_t component = 0U; + while (remainder[component] != '\0' && remainder[component] != '/') + ++component; + if (component == 0U || component + 1U > name_capacity) return 0; + for (uint64_t i = 0U; i < component; ++i) name[i] = remainder[i]; + name[component] = '\0'; + *is_directory = remainder[component] == '/'; + return 1; +} + +int initramfs_directory_exists(const char *directory) { + char name[64]; + int is_directory = 0; + if (directory == 0 || directory[0] != '/') return 0; + if (directory[1] == '\0') return 1; + for (uint32_t i = 0U; i < g_file_count; ++i) { + if (initramfs_child_at(directory, i, name, sizeof(name), &is_directory)) + return 1; + } + return 0; +} + const xaios_initramfs_config_t *initramfs_config(void) { return g_config.valid != 0 ? &g_config : 0; } diff --git a/kernel/fs/mutable_fs.c b/kernel/fs/mutable_fs.c index 727e9ec3..96e4cfd3 100644 --- a/kernel/fs/mutable_fs.c +++ b/kernel/fs/mutable_fs.c @@ -13,6 +13,27 @@ #define MFS_SECTOR_SIZE UINT64_C(512) #define MFS_START_SECTOR UINT64_C(3072) #define MFS_METADATA_SECTORS UINT64_C(16) +/* A/B metadata. + + The metadata region is written in place, so a write torn by power loss + leaves it neither valid nor blank. Mount then refuses to continue, because + formatting would destroy the volume, and the result is a filesystem that + cannot be mounted or repaired. A second copy removes that: writes alternate + between the two, so a tear can only ever damage the copy that is not + currently authoritative, and mount falls back to the survivor. + + The mirror lives past the data region, so every existing offset is + unchanged and an existing volume keeps mounting. The write sequence sits in + the slack at the end of the region for the same reason: appending a header + field would have shifted the bitmap and every node after it. A volume + written before this reads sequence 0, which simply makes it the older copy. + + Alternating rather than writing both copies each time keeps the write cost + identical to before. The trade is that a torn write falls back to the + previous commit instead of the one being written, which is the same + guarantee an interrupted commit already had. */ +#define MFS_METADATA_SLOTS 2U +#define MFS_SEQUENCE_TAIL_BYTES UINT64_C(16) #define MFS_JOURNAL_SECTORS UINT64_C(2) #define MFS_CHECKSUM_OFFSET UINT64_C(80) #define MFS_DATA_SECTORS 96U @@ -201,6 +222,11 @@ static uint64_t g_open_count; static uint64_t g_close_count; static uint64_t g_metadata_verified_checksum; +/* Slot the live metadata was loaded from; the next write targets the other. */ +static uint32_t g_metadata_slot; +static uint32_t g_metadata_mirror_enabled; +static uint64_t g_metadata_sequence; +static uint64_t g_metadata_mirror_recoveries; static uint8_t g_metadata_buffer[MFS_V5_METADATA_SECTORS * MFS_SECTOR_SIZE]; static xaios_spinlock_t g_mutable_fs_lock = XAIOS_SPINLOCK_INIT; @@ -297,6 +323,21 @@ static uint64_t active_data_start_sector(void) { return active_journal_header_sector() + MFS_JOURNAL_SECTORS; } +/* The mirror sits immediately after the data region, so nothing that an + existing volume already uses moves. */ +static uint64_t metadata_mirror_start_sector(void) { + return active_data_start_sector() + g_active_data_sectors; +} + +static uint64_t metadata_slot_start_sector(uint32_t slot) { + return slot == 0U ? MFS_START_SECTOR : metadata_mirror_start_sector(); +} + +static uint64_t metadata_sequence_offset(void) { + return (uint64_t)g_active_metadata_sectors * MFS_SECTOR_SIZE - + MFS_SEQUENCE_TAIL_BYTES; +} + static xaios_status_t blk_read(uint64_t sector, void *buf, uint64_t sz) { if (g_persistent_device != 0) { if (sector > UINT64_MAX / MFS_SECTOR_SIZE) return XAIOS_ERR_INVALID; @@ -663,9 +704,10 @@ static void export_legacy_node(xaios_mfs_node_v3_t *legacy, } } -static xaios_status_t read_metadata(void) { +static xaios_status_t read_metadata_slot(uint32_t slot) { uint8_t first_sector[MFS_SECTOR_SIZE]; - if (blk_read(MFS_START_SECTOR, first_sector, sizeof(first_sector)) != XAIOS_OK) { + uint64_t start = metadata_slot_start_sector(slot); + if (blk_read(start, first_sector, sizeof(first_sector)) != XAIOS_OK) { return XAIOS_ERR_IO; } uint32_t version = 0; @@ -683,12 +725,14 @@ static xaios_status_t read_metadata(void) { bytes_zero(g_metadata_buffer, sizeof(g_metadata_buffer)); bytes_copy(g_metadata_buffer, first_sector, MFS_SECTOR_SIZE); for (uint32_t i = 1; i < sectors; ++i) { - if (blk_read(MFS_START_SECTOR + i, + if (blk_read(start + i, g_metadata_buffer + i * MFS_SECTOR_SIZE, MFS_SECTOR_SIZE) != XAIOS_OK) { return XAIOS_ERR_IO; } } + bytes_copy(&g_metadata_sequence, + g_metadata_buffer + metadata_sequence_offset(), 8); uint64_t total_bytes = (uint64_t)sectors * MFS_SECTOR_SIZE; g_metadata_verified_checksum = mfs_checksum(g_metadata_buffer, total_bytes); bytes_zero(&g_mfs, sizeof(g_mfs)); @@ -736,6 +780,54 @@ static xaios_status_t read_metadata(void) { return XAIOS_OK; } +/* A slot is usable when it parses and its stored checksum matches what its + own bytes hash to. Structural validation stays with the caller, which + applies it to whichever slot wins. */ +static int metadata_slot_probe(uint32_t slot, uint64_t *out_sequence) { + if (read_metadata_slot(slot) != XAIOS_OK) return 0; + if (g_mfs.checksum != g_metadata_verified_checksum) return 0; + if (!bytes_eq(g_mfs.magic, MFS_MAGIC, MFS_MAGIC_LEN)) return 0; + *out_sequence = g_metadata_sequence; + return 1; +} + +/* Load the newer of the two copies that is intact. */ +static xaios_status_t read_metadata(void) { + uint64_t sequence[MFS_METADATA_SLOTS] = {0U, 0U}; + int usable[MFS_METADATA_SLOTS] = {0, 0}; + uint32_t chosen; + usable[0] = metadata_slot_probe(0U, &sequence[0]); + if (g_metadata_mirror_enabled != 0U) { + /* The mirror's position depends on the geometry, which normally comes + from the primary. A torn primary is exactly the case this exists for, + and its version field is unreadable then, so assume the only layout + that ever has a mirror rather than giving up on the copy that is + still intact. */ + if (usable[0] == 0) set_active_v5(); + if (g_active_version == MFS_V5_VERSION) { + usable[1] = metadata_slot_probe(1U, &sequence[1]); + } + } + if (usable[0] == 0 && usable[1] == 0) { + /* Neither copy is intact. Reload the primary so the caller sees the + original bytes and can apply its own blank-versus-damaged judgement. */ + g_metadata_slot = 0U; + return read_metadata_slot(0U); + } + if (usable[0] != 0 && usable[1] != 0) { + chosen = sequence[1] > sequence[0] ? 1U : 0U; + } else { + chosen = usable[0] != 0 ? 0U : 1U; + } + if (usable[chosen ^ 1U] == 0) { + ++g_metadata_mirror_recoveries; + klog("mutable-fs: metadata slot %u unusable; continuing from slot %u seq=%lu\n", + (unsigned)(chosen ^ 1U), (unsigned)chosen, sequence[chosen]); + } + g_metadata_slot = chosen; + return read_metadata_slot(chosen); +} + static xaios_status_t write_metadata(void) { bytes_zero(g_metadata_buffer, sizeof(g_metadata_buffer)); uint64_t p = 0; @@ -783,20 +875,36 @@ static xaios_status_t write_metadata(void) { ++g_reject_count; return XAIOS_ERR_NO_MEMORY; } + /* Stamp the write sequence before hashing so the checksum covers it; a + tear that damages the sequence therefore invalidates the copy too. */ + uint64_t next_sequence = g_metadata_sequence + 1U; + bytes_copy(g_metadata_buffer + metadata_sequence_offset(), &next_sequence, 8); g_mfs.checksum = mfs_checksum(g_metadata_buffer, total_bytes); bytes_copy(g_metadata_buffer + checksum_offset, &g_mfs.checksum, 8); + /* Alternate slots so the copy being overwritten is never the one mount + would currently choose. Without a mirror this degrades to the previous + in-place behaviour. */ + uint32_t target = g_metadata_mirror_enabled != 0U ? (g_metadata_slot ^ 1U) + : g_metadata_slot; + uint64_t start = metadata_slot_start_sector(target); uint8_t sector[MFS_SECTOR_SIZE]; for (uint32_t i = 0; i < g_active_metadata_sectors; ++i) { bytes_copy(sector, g_metadata_buffer + (uint64_t)i * MFS_SECTOR_SIZE, MFS_SECTOR_SIZE); - if (blk_write(MFS_START_SECTOR + i, sector, sizeof(sector)) != XAIOS_OK) { + if (blk_write(start + i, sector, sizeof(sector)) != XAIOS_OK) { klog("mutable-fs: metadata write failed sector=%lu capacity=%lu\n", - MFS_START_SECTOR + i, blk_capacity()); + start + i, blk_capacity()); ++g_reject_count; return XAIOS_ERR_IO; } } - return blk_flush(); + xaios_status_t flushed = blk_flush(); + if (flushed != XAIOS_OK) return flushed; + /* Only once the new copy is durable does it become the one to read, and + the other becomes the next target. */ + g_metadata_slot = target; + g_metadata_sequence = next_sequence; + return XAIOS_OK; } static xaios_status_t clear_journal(void) { @@ -932,9 +1040,17 @@ static xaios_status_t format_volume(void) { g_mfs.generation = 1; g_mfs.committed_generation = 0; ++g_format_count; + g_metadata_sequence = 0U; + g_metadata_slot = 0U; if (clear_journal() != XAIOS_OK) { return XAIOS_ERR_IO; } + /* Fill both copies at format time. Writing only one would leave a fresh + volume with a single valid copy until its second metadata write, which + is exactly the window this is meant to remove. The two writes alternate + slots, so both end up holding a complete, self-consistent image. */ + if (write_metadata() != XAIOS_OK) return XAIOS_ERR_IO; + if (g_metadata_mirror_enabled == 0U) return XAIOS_OK; return write_metadata(); } @@ -1030,6 +1146,12 @@ static xaios_status_t replay_journal(void) { static xaios_status_t mount_volume(uint32_t mount_flags) { set_active_v2(); + /* The in-image volume is sized to the boot image and has no room for a + mirror. Clear it explicitly: this global outlives a previous device + mount, and inheriting its setting here would send writes to a slot that + does not exist on this volume. */ + g_metadata_mirror_enabled = 0U; + g_metadata_slot = 0U; if (blk_capacity() < active_data_start_sector() + g_active_data_sectors) { ++g_reject_count; return XAIOS_ERR_IO; @@ -2070,6 +2192,10 @@ static xaios_status_t mutable_fs_close_locked(uint32_t fd) { } uint64_t mutable_fs_mount_count(void) { return g_mount_count; } +uint64_t mutable_fs_metadata_recoveries(void) { + return g_metadata_mirror_recoveries; +} + uint64_t mutable_fs_format_count(void) { return g_format_count; } uint64_t mutable_fs_boot_load_count(void) { return g_boot_load_count; } uint64_t mutable_fs_file_count(void) { return node_count_by_type(MFS_NODE_FILE); } @@ -2129,6 +2255,15 @@ static xaios_status_t mutable_fs_mount_device_locked(const char *identifier) { return XAIOS_ERR_UNSUPPORTED; } set_active_v5(); + g_metadata_slot = 0U; + g_metadata_sequence = 0U; + /* The mirror is optional: a volume sized exactly for the old layout keeps + working single-copy rather than being refused. */ + g_metadata_mirror_enabled = + info.capacity_bytes / MFS_SECTOR_SIZE >= + metadata_mirror_start_sector() + g_active_metadata_sectors + ? 1U + : 0U; if (info.capacity_bytes / MFS_SECTOR_SIZE < active_data_start_sector() + MFS_V5_DATA_SECTORS) { klog("mutable-fs: persistent disk too small capacity=%lu needed=%lu\n", @@ -2416,6 +2551,29 @@ xaios_status_t mutable_fs_mount_device(const char *identifier) { return result; } +/* Release a mounted device, flushing first. The slot bookkeeping is reset so + a later mount re-derives which copy is authoritative from the volume rather + than from whatever the previous mount happened to leave behind. */ +xaios_status_t mutable_fs_unmount(void) { + xaios_spin_lock(&g_mutable_fs_lock); + if (g_persistent_device == 0) { + xaios_spin_unlock(&g_mutable_fs_lock); + return XAIOS_ERR_INVALID; + } + (void)blk_flush(); + xaios_block_device_t *device = g_persistent_device; + g_persistent_device = 0; + g_mounted = 0; + g_mount_flags = 0; + g_metadata_slot = 0U; + g_metadata_sequence = 0U; + g_metadata_mirror_enabled = 0U; + set_active_v2(); + (void)block_device_close(device); + xaios_spin_unlock(&g_mutable_fs_lock); + return XAIOS_OK; +} + xaios_status_t mutable_fs_mount_persistent(uint32_t slot) { xaios_spin_lock(&g_mutable_fs_lock); xaios_status_t result = mutable_fs_mount_persistent_locked(slot); diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c index d8be4382..3171f218 100644 --- a/kernel/fs/vfs.c +++ b/kernel/fs/vfs.c @@ -1,4 +1,5 @@ #include +#include typedef struct vfs_mount_record { uint32_t active; @@ -21,6 +22,7 @@ typedef struct vfs_handle_record { } vfs_handle_record_t; static vfs_mount_record_t g_mounts[XAIOS_VFS_MAX_MOUNTS]; +static xaios_spinlock_t g_vfs_lock = XAIOS_SPINLOCK_INIT; static vfs_handle_record_t g_handles[XAIOS_VFS_MAX_HANDLES]; static uint64_t g_next_generation; @@ -204,9 +206,7 @@ xaios_status_t vfs_init(void) { return XAIOS_OK; } -xaios_status_t vfs_mount(const char *mount_path, - const xaios_vfs_backend_ops_t *ops, void *context, - uint32_t flags) { +static xaios_status_t vfs_mount_locked(const char *mount_path, const xaios_vfs_backend_ops_t *ops, void *context, uint32_t flags) { if (ops == 0 || ops->open == 0 || ops->close == 0 || ops->pread == 0 || ops->stat == 0 || flags & ~XAIOS_VFS_MOUNT_READ_ONLY) { return XAIOS_ERR_INVALID; @@ -238,7 +238,7 @@ xaios_status_t vfs_mount(const char *mount_path, return XAIOS_OK; } -xaios_status_t vfs_unmount(const char *mount_path) { +static xaios_status_t vfs_unmount_locked(const char *mount_path) { char normalized[XAIOS_VFS_PATH_MAX]; if (normalize_path(mount_path, normalized) != XAIOS_OK) { return XAIOS_ERR_INVALID; @@ -263,7 +263,7 @@ xaios_status_t vfs_resolve(const char *path, return resolve_normalized(normalized, resolution); } -int64_t vfs_open(const char *path, uint32_t flags, uint32_t owner_id) { +static int64_t vfs_open_locked(const char *path, uint32_t flags, uint32_t owner_id) { if (flags == 0U || flags & ~(XAIOS_VFS_OPEN_READ | XAIOS_VFS_OPEN_WRITE | XAIOS_VFS_OPEN_CREATE | XAIOS_VFS_OPEN_TRUNCATE) || @@ -304,7 +304,7 @@ int64_t vfs_open(const char *path, uint32_t flags, uint32_t owner_id) { return (int64_t)(index + 1U); } -xaios_status_t vfs_close(uint32_t fd, uint32_t owner_id) { +static xaios_status_t vfs_close_locked(uint32_t fd, uint32_t owner_id) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0) return XAIOS_ERR_INVALID; vfs_mount_record_t *mount = &g_mounts[handle->mount_index]; @@ -317,7 +317,7 @@ xaios_status_t vfs_close(uint32_t fd, uint32_t owner_id) { return XAIOS_OK; } -xaios_status_t vfs_release_owner(uint32_t owner_id) { +static xaios_status_t vfs_release_owner_locked(uint32_t owner_id) { if (owner_id == 0U) return XAIOS_ERR_INVALID; xaios_status_t result = XAIOS_OK; for (uint32_t index = 0U; index < XAIOS_VFS_MAX_HANDLES; ++index) { @@ -348,8 +348,7 @@ xaios_status_t vfs_release_owner(uint32_t owner_id) { return result; } -int64_t vfs_pread(uint32_t fd, uint32_t owner_id, void *buffer, - uint64_t length, uint64_t offset) { +static int64_t vfs_pread_locked(uint32_t fd, uint32_t owner_id, void *buffer, uint64_t length, uint64_t offset) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0 || buffer == 0 || (handle->flags & XAIOS_VFS_OPEN_READ) == 0U) { @@ -360,8 +359,7 @@ int64_t vfs_pread(uint32_t fd, uint32_t owner_id, void *buffer, length, offset); } -int64_t vfs_pwrite(uint32_t fd, uint32_t owner_id, const void *buffer, - uint64_t length, uint64_t offset) { +static int64_t vfs_pwrite_locked(uint32_t fd, uint32_t owner_id, const void *buffer, uint64_t length, uint64_t offset) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0 || buffer == 0 || (handle->flags & XAIOS_VFS_OPEN_WRITE) == 0U) { @@ -376,11 +374,11 @@ int64_t vfs_pwrite(uint32_t fd, uint32_t owner_id, const void *buffer, length, offset); } -int64_t vfs_read(uint32_t fd, uint32_t owner_id, void *buffer, - uint64_t length) { +static int64_t vfs_read_locked(uint32_t fd, uint32_t owner_id, void *buffer, uint64_t length) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0) return XAIOS_ERR_INVALID; - int64_t result = vfs_pread(fd, owner_id, buffer, length, handle->cursor); + int64_t result = vfs_pread_locked(fd, owner_id, buffer, length, + handle->cursor); if (result > 0) { if ((uint64_t)result > UINT64_MAX - handle->cursor) return XAIOS_ERR_INVALID; handle->cursor += (uint64_t)result; @@ -388,11 +386,11 @@ int64_t vfs_read(uint32_t fd, uint32_t owner_id, void *buffer, return result; } -int64_t vfs_write(uint32_t fd, uint32_t owner_id, const void *buffer, - uint64_t length) { +static int64_t vfs_write_locked(uint32_t fd, uint32_t owner_id, const void *buffer, uint64_t length) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0) return XAIOS_ERR_INVALID; - int64_t result = vfs_pwrite(fd, owner_id, buffer, length, handle->cursor); + int64_t result = vfs_pwrite_locked(fd, owner_id, buffer, length, + handle->cursor); if (result > 0) { if ((uint64_t)result > UINT64_MAX - handle->cursor) return XAIOS_ERR_INVALID; handle->cursor += (uint64_t)result; @@ -400,14 +398,14 @@ int64_t vfs_write(uint32_t fd, uint32_t owner_id, const void *buffer, return result; } -xaios_status_t vfs_seek(uint32_t fd, uint32_t owner_id, uint64_t offset) { +static xaios_status_t vfs_seek_locked(uint32_t fd, uint32_t owner_id, uint64_t offset) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0) return XAIOS_ERR_INVALID; handle->cursor = offset; return XAIOS_OK; } -xaios_status_t vfs_fsync(uint32_t fd, uint32_t owner_id) { +static xaios_status_t vfs_fsync_locked(uint32_t fd, uint32_t owner_id) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0) return XAIOS_ERR_INVALID; vfs_mount_record_t *mount = &g_mounts[handle->mount_index]; @@ -416,7 +414,7 @@ xaios_status_t vfs_fsync(uint32_t fd, uint32_t owner_id) { : XAIOS_ERR_UNSUPPORTED; } -xaios_status_t vfs_truncate(uint32_t fd, uint32_t owner_id, uint64_t size) { +static xaios_status_t vfs_truncate_locked(uint32_t fd, uint32_t owner_id, uint64_t size) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0 || (handle->flags & XAIOS_VFS_OPEN_WRITE) == 0U) { return XAIOS_ERR_INVALID; @@ -429,8 +427,7 @@ xaios_status_t vfs_truncate(uint32_t fd, uint32_t owner_id, uint64_t size) { return mount->ops->truncate(mount->context, handle->backend_handle, size); } -xaios_status_t vfs_fallocate(uint32_t fd, uint32_t owner_id, uint64_t offset, - uint64_t length) { +static xaios_status_t vfs_fallocate_locked(uint32_t fd, uint32_t owner_id, uint64_t offset, uint64_t length) { vfs_handle_record_t *handle = find_handle(fd, owner_id); if (handle == 0 || length == 0U || offset > UINT64_MAX - length || (handle->flags & XAIOS_VFS_OPEN_WRITE) == 0U) { @@ -454,7 +451,7 @@ static xaios_status_t resolve_operation(const char *path, return XAIOS_OK; } -xaios_status_t vfs_stat(const char *path, xaios_vfs_stat_t *stat) { +static xaios_status_t vfs_stat_locked(const char *path, xaios_vfs_stat_t *stat) { if (stat == 0) return XAIOS_ERR_INVALID; xaios_vfs_resolution_t resolution; vfs_mount_record_t *mount = 0; @@ -464,7 +461,7 @@ xaios_status_t vfs_stat(const char *path, xaios_vfs_stat_t *stat) { : status; } -xaios_status_t vfs_statfs(const char *path, xaios_vfs_statfs_t *statfs) { +static xaios_status_t vfs_statfs_locked(const char *path, xaios_vfs_statfs_t *statfs) { if (statfs == 0) return XAIOS_ERR_INVALID; xaios_vfs_resolution_t resolution; vfs_mount_record_t *mount = 0; @@ -494,36 +491,36 @@ static xaios_status_t path_mutation( return operation(mount->context, resolution.relative_path); } -xaios_status_t vfs_mkdir(const char *path) { +static xaios_status_t vfs_mkdir_locked(const char *path) { xaios_vfs_resolution_t resolution; vfs_mount_record_t *mount = 0; xaios_status_t status = resolve_operation(path, &resolution, &mount); return status == XAIOS_OK ? path_mutation(path, mount->ops->mkdir) : status; } -xaios_status_t vfs_rmdir(const char *path) { +static xaios_status_t vfs_rmdir_locked(const char *path) { xaios_vfs_resolution_t resolution; vfs_mount_record_t *mount = 0; xaios_status_t status = resolve_operation(path, &resolution, &mount); return status == XAIOS_OK ? path_mutation(path, mount->ops->rmdir) : status; } -xaios_status_t vfs_unlink(const char *path) { +static xaios_status_t vfs_unlink_locked(const char *path) { xaios_vfs_resolution_t resolution; vfs_mount_record_t *mount = 0; xaios_status_t status = resolve_operation(path, &resolution, &mount); return status == XAIOS_OK ? path_mutation(path, mount->ops->unlink) : status; } -xaios_status_t vfs_delete(const char *path) { +static xaios_status_t vfs_delete_locked(const char *path) { xaios_vfs_stat_t stat; - xaios_status_t status = vfs_stat(path, &stat); + xaios_status_t status = vfs_stat_locked(path, &stat); if (status != XAIOS_OK) return status; - return stat.type == XAIOS_VFS_TYPE_DIRECTORY ? vfs_rmdir(path) - : vfs_unlink(path); + return stat.type == XAIOS_VFS_TYPE_DIRECTORY ? vfs_rmdir_locked(path) + : vfs_unlink_locked(path); } -xaios_status_t vfs_rename(const char *old_path, const char *new_path) { +static xaios_status_t vfs_rename_locked(const char *old_path, const char *new_path) { xaios_vfs_resolution_t old_resolution; xaios_vfs_resolution_t new_resolution; if (vfs_resolve(old_path, &old_resolution) != XAIOS_OK || @@ -542,17 +539,219 @@ xaios_status_t vfs_rename(const char *old_path, const char *new_path) { new_resolution.relative_path); } -xaios_status_t vfs_list(const char *path, char *buffer, uint64_t capacity, - uint64_t *out_size) { +static xaios_status_t vfs_list_locked(const char *path, char *buffer, uint64_t capacity, uint64_t *out_size) { if (buffer == 0 || capacity == 0U || out_size == 0) { return XAIOS_ERR_INVALID; } xaios_vfs_resolution_t resolution; vfs_mount_record_t *mount = 0; + char normalized[XAIOS_VFS_PATH_MAX]; xaios_status_t status = resolve_operation(path, &resolution, &mount); if (status != XAIOS_OK || mount->ops->list == 0) { return status != XAIOS_OK ? status : XAIOS_ERR_UNSUPPORTED; } - return mount->ops->list(mount->context, resolution.relative_path, buffer, - capacity, out_size); + status = mount->ops->list(mount->context, resolution.relative_path, buffer, + capacity, out_size); + if (status != XAIOS_OK || normalize_path(path, normalized) != XAIOS_OK) { + return status; + } + /* A mount point is a directory entry of its parent, but the parent's + backend has never heard of it: without this, /bin is fully usable yet + absent from a listing of /. Append the basename of every mount rooted + one level below the listed directory, unless the backend already named + it. */ + uint64_t base_length = string_length(normalized); + if (base_length == 1U) base_length = 0U; + for (uint32_t index = 0U; index < XAIOS_VFS_MAX_MOUNTS; ++index) { + const vfs_mount_record_t *candidate = &g_mounts[index]; + const char *name; + uint64_t name_length = 0U; + if (candidate->active == 0U || candidate == mount) continue; + if (!mount_matches(normalized, candidate->path) || + string_length(candidate->path) <= base_length) { + continue; + } + name = candidate->path + base_length + 1U; + while (name[name_length] != '\0' && name[name_length] != '/') + ++name_length; + if (name_length == 0U || name[name_length] == '/') continue; + /* Skip when the backend listed the same name already. */ + { + uint64_t line = 0U; + int present = 0; + while (line < *out_size && present == 0) { + uint64_t end = line; + while (end < *out_size && buffer[end] != '\n') ++end; + if (end - line == name_length) { + uint64_t i = 0U; + while (i < name_length && buffer[line + i] == name[i]) ++i; + if (i == name_length) present = 1; + } + line = end + 1U; + } + if (present != 0) continue; + } + if (*out_size + name_length + 1U > capacity) return XAIOS_ERR_NO_MEMORY; + for (uint64_t i = 0U; i < name_length; ++i) + buffer[*out_size + i] = name[i]; + buffer[*out_size + name_length] = '\n'; + *out_size += name_length + 1U; + } + return XAIOS_OK; +} + + +/* Serialised public entry points. + The handle table and mount table are reached from every CPU through the + filesystem syscalls, and were mutated with no mutual exclusion: vfs_open + scanned for a free slot and filled it in separate steps while vfs_close and + vfs_release_owner cleared entries underneath it. Each entry point now runs + under one lock; the bodies above assume it is held and must not be called + directly. */ +xaios_status_t vfs_mount(const char *mount_path, const xaios_vfs_backend_ops_t *ops, void *context, uint32_t flags) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_mount_locked(mount_path, ops, context, flags); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_unmount(const char *mount_path) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_unmount_locked(mount_path); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +int64_t vfs_open(const char *path, uint32_t flags, uint32_t owner_id) { + xaios_spin_lock(&g_vfs_lock); + int64_t result = vfs_open_locked(path, flags, owner_id); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_close(uint32_t fd, uint32_t owner_id) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_close_locked(fd, owner_id); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_release_owner(uint32_t owner_id) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_release_owner_locked(owner_id); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +int64_t vfs_pread(uint32_t fd, uint32_t owner_id, void *buffer, uint64_t length, uint64_t offset) { + xaios_spin_lock(&g_vfs_lock); + int64_t result = vfs_pread_locked(fd, owner_id, buffer, length, offset); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +int64_t vfs_pwrite(uint32_t fd, uint32_t owner_id, const void *buffer, uint64_t length, uint64_t offset) { + xaios_spin_lock(&g_vfs_lock); + int64_t result = vfs_pwrite_locked(fd, owner_id, buffer, length, offset); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +int64_t vfs_read(uint32_t fd, uint32_t owner_id, void *buffer, uint64_t length) { + xaios_spin_lock(&g_vfs_lock); + int64_t result = vfs_read_locked(fd, owner_id, buffer, length); + xaios_spin_unlock(&g_vfs_lock); + return result; } + +int64_t vfs_write(uint32_t fd, uint32_t owner_id, const void *buffer, uint64_t length) { + xaios_spin_lock(&g_vfs_lock); + int64_t result = vfs_write_locked(fd, owner_id, buffer, length); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_seek(uint32_t fd, uint32_t owner_id, uint64_t offset) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_seek_locked(fd, owner_id, offset); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_fsync(uint32_t fd, uint32_t owner_id) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_fsync_locked(fd, owner_id); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_truncate(uint32_t fd, uint32_t owner_id, uint64_t size) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_truncate_locked(fd, owner_id, size); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_fallocate(uint32_t fd, uint32_t owner_id, uint64_t offset, uint64_t length) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_fallocate_locked(fd, owner_id, offset, length); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_stat(const char *path, xaios_vfs_stat_t *stat) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_stat_locked(path, stat); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_statfs(const char *path, xaios_vfs_statfs_t *statfs) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_statfs_locked(path, statfs); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_mkdir(const char *path) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_mkdir_locked(path); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_rmdir(const char *path) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_rmdir_locked(path); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_unlink(const char *path) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_unlink_locked(path); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_delete(const char *path) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_delete_locked(path); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_rename(const char *old_path, const char *new_path) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_rename_locked(old_path, new_path); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + +xaios_status_t vfs_list(const char *path, char *buffer, uint64_t capacity, uint64_t *out_size) { + xaios_spin_lock(&g_vfs_lock); + xaios_status_t result = vfs_list_locked(path, buffer, capacity, out_size); + xaios_spin_unlock(&g_vfs_lock); + return result; +} + diff --git a/kernel/fs/vfs_initramfs.c b/kernel/fs/vfs_initramfs.c new file mode 100644 index 00000000..309b2ee1 --- /dev/null +++ b/kernel/fs/vfs_initramfs.c @@ -0,0 +1,213 @@ +#include +#include + +/* Read-only VFS view of the boot image, so userspace utilities can list and + read what the kernel loads from it. Without this, /bin exists only as the + kernel's private launch table: the shell can run /bin/htop while ls shows + an empty directory, which reads as a broken system rather than a design. + + The image is immutable after initramfs_init and the mount is declared + read-only, so the adapter carries no locking and no mutation entry points. + Backend handles are the file's table index plus one, so zero stays the + "no handle" value. */ + +#define INITRAMFS_VFS_NAME_MAX 64U + +static char g_mount_prefix[XAIOS_VFS_PATH_MAX]; + +static xaios_status_t absolute_path(const char *relative, char *out, + uint64_t capacity) { + uint64_t used = 0U; + const char *prefix = g_mount_prefix; + if (relative == 0 || relative[0] != '/') return XAIOS_ERR_INVALID; + while (prefix[used] != '\0') { + if (used + 1U >= capacity) return XAIOS_ERR_INVALID; + out[used] = prefix[used]; + ++used; + } + /* The mount root itself arrives as "/": the prefix already names it. */ + if (relative[1] != '\0') { + for (uint64_t i = 0U; relative[i] != '\0'; ++i) { + if (used + 1U >= capacity) return XAIOS_ERR_INVALID; + out[used++] = relative[i]; + } + } + out[used] = '\0'; + return XAIOS_OK; +} + +static xaios_status_t find_file(const char *relative, uint32_t *out_index) { + char path[XAIOS_VFS_PATH_MAX]; + xaios_status_t status = absolute_path(relative, path, sizeof(path)); + if (status != XAIOS_OK) return status; + for (uint32_t i = 0U; i < initramfs_file_count(); ++i) { + const xaios_initramfs_file_t *file = initramfs_file_at(i); + uint64_t j = 0U; + while (file->path[j] != '\0' && file->path[j] == path[j]) ++j; + if (file->path[j] == '\0' && path[j] == '\0') { + *out_index = i; + return XAIOS_OK; + } + } + return XAIOS_ERR_NOT_FOUND; +} + +static xaios_status_t initramfs_vfs_open(void *context, const char *path, + uint32_t flags, uint64_t *handle) { + uint32_t index = 0U; + (void)context; + if (handle == 0 || + (flags & (XAIOS_VFS_OPEN_WRITE | XAIOS_VFS_OPEN_CREATE | + XAIOS_VFS_OPEN_TRUNCATE)) != 0U) { + return XAIOS_ERR_INVALID; + } + if (find_file(path, &index) != XAIOS_OK) return XAIOS_ERR_NOT_FOUND; + *handle = (uint64_t)index + 1U; + return XAIOS_OK; +} + +static xaios_status_t initramfs_vfs_close(void *context, uint64_t handle) { + (void)context; + return handle != 0U && handle <= initramfs_file_count() ? XAIOS_OK + : XAIOS_ERR_INVALID; +} + +static int64_t initramfs_vfs_pread(void *context, uint64_t handle, + void *buffer, uint64_t length, + uint64_t offset) { + const xaios_initramfs_file_t *file; + const uint8_t *base; + uint8_t *out = (uint8_t *)buffer; + (void)context; + if (buffer == 0 || handle == 0U || handle > initramfs_file_count()) { + return -1; + } + file = initramfs_file_at((uint32_t)(handle - 1U)); + if (file == 0 || file->base == 0) return -1; + if (offset >= file->size) return 0; + if (length > file->size - offset) length = file->size - offset; + if (length > INT64_MAX) return -1; + base = (const uint8_t *)file->base + offset; + for (uint64_t i = 0U; i < length; ++i) out[i] = base[i]; + return (int64_t)length; +} + +static xaios_status_t initramfs_vfs_stat(void *context, const char *path, + xaios_vfs_stat_t *stat) { + char absolute[XAIOS_VFS_PATH_MAX]; + uint32_t index = 0U; + (void)context; + if (stat == 0) return XAIOS_ERR_INVALID; + stat->block_count = 0U; + stat->generation = 0U; + stat->content_hash = 0U; + if (find_file(path, &index) == XAIOS_OK) { + const xaios_initramfs_file_t *file = initramfs_file_at(index); + stat->type = 2U; + stat->size = file->size; + stat->content_hash = file->content_hash; + return XAIOS_OK; + } + if (absolute_path(path, absolute, sizeof(absolute)) == XAIOS_OK && + initramfs_directory_exists(absolute) != 0) { + stat->type = 1U; + stat->size = 0U; + return XAIOS_OK; + } + return XAIOS_ERR_NOT_FOUND; +} + +static xaios_status_t initramfs_vfs_statfs(void *context, + xaios_vfs_statfs_t *statfs) { + uint64_t total = 0U; + (void)context; + if (statfs == 0) return XAIOS_ERR_INVALID; + for (uint32_t i = 0U; i < initramfs_file_count(); ++i) { + total += initramfs_file_at(i)->size; + } + statfs->total_bytes = total; + statfs->allocated_bytes = total; + statfs->free_bytes = 0U; + statfs->reserved_bytes = 0U; + statfs->file_count = initramfs_file_count(); + statfs->directory_count = 0U; + statfs->generation = 1U; + statfs->block_size = 1U; + statfs->read_only = 1U; + statfs->format_version = 1U; + return XAIOS_OK; +} + +static xaios_status_t initramfs_vfs_list(void *context, const char *path, + char *buffer, uint64_t capacity, + uint64_t *out_size) { + char absolute[XAIOS_VFS_PATH_MAX]; + uint64_t used = 0U; + int found = 0; + (void)context; + if (buffer == 0 || out_size == 0) return XAIOS_ERR_INVALID; + if (absolute_path(path, absolute, sizeof(absolute)) != XAIOS_OK) { + return XAIOS_ERR_INVALID; + } + if (initramfs_directory_exists(absolute) == 0) return XAIOS_ERR_NOT_FOUND; + for (uint32_t i = 0U; i < initramfs_file_count(); ++i) { + char name[INITRAMFS_VFS_NAME_MAX]; + int is_directory = 0; + if (initramfs_child_at(absolute, i, name, sizeof(name), &is_directory) == + 0) { + continue; + } + if (is_directory != 0) { + /* Subdirectories repeat for every file below them; keep the first. */ + char earlier[INITRAMFS_VFS_NAME_MAX]; + int earlier_directory = 0; + uint32_t seen = 0U; + for (uint32_t j = 0U; j < i && seen == 0U; ++j) { + if (initramfs_child_at(absolute, j, earlier, sizeof(earlier), + &earlier_directory) != 0 && + earlier_directory != 0) { + uint32_t k = 0U; + while (earlier[k] != '\0' && earlier[k] == name[k]) ++k; + if (earlier[k] == '\0' && name[k] == '\0') seen = 1U; + } + } + if (seen != 0U) continue; + } + for (uint64_t j = 0U; name[j] != '\0'; ++j) { + if (used + 2U > capacity) return XAIOS_ERR_NO_MEMORY; + buffer[used++] = name[j]; + } + if (used + 1U > capacity) return XAIOS_ERR_NO_MEMORY; + buffer[used++] = '\n'; + found = 1; + } + (void)found; + *out_size = used; + return XAIOS_OK; +} + +static const xaios_vfs_backend_ops_t k_initramfs_ops = { + initramfs_vfs_open, initramfs_vfs_close, initramfs_vfs_pread, + 0 /* pwrite */, 0 /* fsync */, 0 /* truncate */, + 0 /* fallocate */, initramfs_vfs_stat, initramfs_vfs_statfs, + 0 /* mkdir */, 0 /* rmdir */, 0 /* unlink */, + 0 /* rename */, initramfs_vfs_list, +}; + +xaios_status_t vfs_mount_initramfs(const char *mount_path) { + uint64_t i = 0U; + if (mount_path == 0 || mount_path[0] != '/' || mount_path[1] == '\0') { + return XAIOS_ERR_INVALID; + } + while (mount_path[i] != '\0') { + if (i + 1U >= sizeof(g_mount_prefix)) return XAIOS_ERR_INVALID; + g_mount_prefix[i] = mount_path[i]; + ++i; + } + g_mount_prefix[i] = '\0'; + if (initramfs_directory_exists(g_mount_prefix) == 0) { + return XAIOS_ERR_NOT_FOUND; + } + return vfs_mount(mount_path, &k_initramfs_ops, 0, + XAIOS_VFS_MOUNT_READ_ONLY); +} diff --git a/kernel/include/xaios/dns.h b/kernel/include/xaios/dns.h index 078339e0..91e92fa2 100644 --- a/kernel/include/xaios/dns.h +++ b/kernel/include/xaios/dns.h @@ -56,6 +56,9 @@ uint64_t dns_reject_count(void); uint64_t dns_timeout_count(void); uint64_t dns_tcp_fallback_count(void); uint64_t dns_authenticated_count(void); +/* Answers accepted from a proven-insecure zone. Deliberately not folded into + the authenticated count: the two carry different guarantees. */ +uint64_t dns_insecure_count(void); uint32_t dns_pending_count(void); /* Encode a DNS name (e.g., "www.google.com" -> 3www6google3com0) */ diff --git a/kernel/include/xaios/dnssec.h b/kernel/include/xaios/dnssec.h index 23cf84c5..2312637d 100644 --- a/kernel/include/xaios/dnssec.h +++ b/kernel/include/xaios/dnssec.h @@ -73,4 +73,21 @@ xaios_status_t dnssec_verify_nodata(const uint8_t *message, uint32_t length, const dnssec_keyset_t *keys, uint64_t wall_time_ns); +/* Prove a delegation carries no DS, which makes the child zone insecure + * rather than bogus. Accepts a signed NSEC3 matching the delegation with NS + * and without DS, or a signed opt-out NSEC3 whose hash range covers it. */ +xaios_status_t dnssec_verify_no_ds(const uint8_t *message, uint32_t length, + const char *child_zone, + const dnssec_keyset_t *keys, + uint64_t wall_time_ns); + +/* Read an address from an unsigned answer. Valid only after the chain has + * proven the zone insecure; owner and type are still matched. */ +xaios_status_t dnssec_extract_address_insecure(const uint8_t *message, + uint32_t length, + const char *hostname, + uint16_t type, + uint8_t *out_address, + uint32_t *out_ttl); + #endif diff --git a/kernel/include/xaios/initramfs.h b/kernel/include/xaios/initramfs.h index cb05c972..5eff8aeb 100644 --- a/kernel/include/xaios/initramfs.h +++ b/kernel/include/xaios/initramfs.h @@ -25,6 +25,21 @@ typedef struct xaios_initramfs_config { } xaios_initramfs_config_t; xaios_status_t initramfs_init(void); + +/* Read-only enumeration of the loaded image, for directory listings. The + table is immutable after initramfs_init, so no locking is involved. */ +uint32_t initramfs_file_count(void); +const xaios_initramfs_file_t *initramfs_file_at(uint32_t index); + +/* Derived directory shape over the image's flat path table. A directory + exists when any file path extends it; children are first components. */ +int initramfs_child_at(const char *directory, uint32_t index, char *name, + uint64_t name_capacity, int *is_directory); +int initramfs_directory_exists(const char *directory); + +/* Mount the image read-only into the VFS at the given directory, typically + "/bin", so userspace utilities can list and read what the kernel loads. */ +xaios_status_t vfs_mount_initramfs(const char *mount_path); xaios_status_t initramfs_lookup(const char *path, const xaios_initramfs_file_t **file); const xaios_initramfs_config_t *initramfs_config(void); diff --git a/kernel/include/xaios/klog_ring.h b/kernel/include/xaios/klog_ring.h index 60cef5a8..bcbfeebf 100644 --- a/kernel/include/xaios/klog_ring.h +++ b/kernel/include/xaios/klog_ring.h @@ -16,12 +16,20 @@ typedef enum xaios_log_level { #define XAIOS_KLOG_LINE_MAX UINT32_C(256) #define XAIOS_KLOG_FLUSH_MAX UINT32_C(8192) +/* Start in-memory capture. Safe to call as soon as klog works; depends on + no other subsystem. */ void klog_ring_init(void); +/* Enable the persistent flush path once MutableFS is mounted. Capture is + unaffected by this failing. */ +xaios_status_t klog_ring_enable_persistence(void); void klog_ring_write(const char *data, uint32_t length); uint32_t klog_ring_read(char *out, uint32_t max_len); uint32_t klog_ring_snapshot(char *out, uint32_t max_len, uint64_t since_cursor, uint64_t *start_cursor, uint64_t *next_cursor, uint64_t *latest_cursor); +/* Lock-free tail read, for the panic path only. May tear against a + concurrent writer; every other reader should use klog_ring_snapshot. */ +uint32_t klog_ring_panic_tail(char *out, uint32_t max_len); void klog_ring_clear(void); uint32_t klog_ring_count(void); uint32_t klog_ring_overflow_count(void); diff --git a/kernel/include/xaios/mutable_fs.h b/kernel/include/xaios/mutable_fs.h index d4815535..ebf6f964 100644 --- a/kernel/include/xaios/mutable_fs.h +++ b/kernel/include/xaios/mutable_fs.h @@ -34,6 +34,10 @@ typedef struct xaios_mfs_fsck_result { void mutable_fs_self_test(void); xaios_status_t mutable_fs_mount_device(const char *identifier); +/* Flush and release the mounted device. */ +xaios_status_t mutable_fs_unmount(void); +/* Metadata copies recovered from during mount, when one was unusable. */ +uint64_t mutable_fs_metadata_recoveries(void); xaios_status_t mutable_fs_mount_persistent(uint32_t slot); xaios_mfs_fsck_result_t mutable_fs_fsck(void); uint64_t mutable_fs_persistent_mount_count(void); diff --git a/kernel/include/xaios/panic.h b/kernel/include/xaios/panic.h index b6bcbb26..54a54be5 100644 --- a/kernel/include/xaios/panic.h +++ b/kernel/include/xaios/panic.h @@ -7,17 +7,14 @@ * On fatal error: dumps registers + stack trace to UART with ANSI cyan * background, then halts forever (no auto-reboot) so the operator can * read the diagnostics. + * + * noreturn is load-bearing beyond codegen: kassert(pointer != 0) guards + * every dereference that follows it only if the compiler and analyzers + * know a failed assertion never falls through. */ -void panic_at(const char *file, int line, const char *fmt, ...); - -#define panic(...) panic_at(__FILE__, __LINE__, __VA_ARGS__) - -#endif -#ifndef XAIOS_PANIC_H -#define XAIOS_PANIC_H - -void panic_at(const char *file, int line, const char *fmt, ...); +void panic_at(const char *file, int line, const char *fmt, ...) + __attribute__((noreturn, format(printf, 3, 4))); #define panic(...) panic_at(__FILE__, __LINE__, __VA_ARGS__) diff --git a/kernel/include/xaios/syscall.h b/kernel/include/xaios/syscall.h index c4e1b288..f04c2e1a 100644 --- a/kernel/include/xaios/syscall.h +++ b/kernel/include/xaios/syscall.h @@ -54,6 +54,7 @@ #define XAIOS_SYSCALL_CONSOLE_WRITE UINT64_C(48) #define XAIOS_SYSCALL_NET_LOCAL_IPV4 UINT64_C(49) #define XAIOS_SYSCALL_NET_CONNECT UINT64_C(50) +#define XAIOS_SYSCALL_NET_LOCAL_IPV6 UINT64_C(51) #define XAIOS_CLOCK_MONOTONIC UINT64_C(0) #define XAIOS_CLOCK_REALTIME UINT64_C(1) diff --git a/kernel/net/dns.c b/kernel/net/dns.c index ae12ae62..96f63561 100644 --- a/kernel/net/dns.c +++ b/kernel/net/dns.c @@ -55,6 +55,10 @@ typedef struct dns_pending { uint8_t family; uint8_t retransmits; uint8_t dnssec_stage; + /* DNSSEC has three outcomes. Set once a delegation is proven to carry no + DS: everything below it is unsigned, so the address answer arrives with + no RRSIG and must be accepted on the strength of that proof instead. */ + uint8_t dnssec_insecure; uint8_t zone_labels; uint8_t hostname_labels; uint16_t id; @@ -88,6 +92,7 @@ static uint64_t g_reject_count; static uint64_t g_timeout_count; static uint64_t g_tcp_fallback_count; static uint64_t g_authenticated_count; +static uint64_t g_insecure_count; static void complete_pending(xaios_status_t status) { g_pending.state = DNS_PENDING_COMPLETE; @@ -177,6 +182,7 @@ void dns_init(void) { g_timeout_count = 0U; g_tcp_fallback_count = 0U; g_authenticated_count = 0U; + g_insecure_count = 0U; dnssec_init(); } @@ -369,22 +375,27 @@ static uint8_t hostname_label_count(const char *hostname) { return count; } +/* Write the zone formed by the last `labels` labels of `hostname`. + + That zone starts just after the dot with `labels` labels to its right, so + the search needs one fewer dot than it might look. When the zone is the + hostname itself there is no such dot at all, which is the ordinary case for + any name whose apex is the name being resolved, such as example.com. */ static int child_zone_name(const char *hostname, uint8_t labels, char *out, uint32_t capacity) { if (labels == 0U || capacity == 0U) return -1; - uint8_t seen = 0U; + uint8_t total = hostname_label_count(hostname); + if (labels > total) return -1; uint32_t start = 0U; - for (uint32_t i = str_len(hostname); i > 0U; --i) { - if (hostname[i - 1U] == '.') { - if (++seen == labels) { start = i; break; } + if (labels < total) { + uint8_t seen = 0U; + for (uint32_t i = str_len(hostname); i > 0U; --i) { + if (hostname[i - 1U] == '.' && ++seen == labels) { + start = i; + break; + } } - } - if (labels == 1U) { - start = 0U; - for (uint32_t i = 0U; hostname[i] != '\0'; ++i) - if (hostname[i] == '.') start = i + 1U; - } else if (seen != labels) { - return -1; + if (seen != labels) return -1; } if (str_len(hostname + start) + 1U > capacity) return -1; str_copy(out, hostname + start, capacity); @@ -504,7 +515,8 @@ xaios_status_t dns_process_message(const uint8_t *message, uint32_t length, ++g_reject_count; return XAIOS_ERR_INVALID; } - position += 4U; + /* The stage verifiers re-parse the whole message themselves, so nothing + consumes an offset past the question section here. */ xaios_status_t status = XAIOS_ERR_INVALID; uint64_t wall_ns = wall_time_now_ns(); if (g_pending.dnssec_stage == DNSSEC_STAGE_ROOT_DNSKEY) { @@ -527,7 +539,14 @@ xaios_status_t dns_process_message(const uint8_t *message, uint32_t length, g_pending.dnssec_stage = DNSSEC_STAGE_CHILD_DNSKEY; } else if (dnssec_verify_nodata(message, length, g_pending.child_zone, DNS_TYPE_DS, &g_pending.validated_keys, - wall_ns) == XAIOS_OK) { + wall_ns) == XAIOS_OK || + dnssec_verify_no_ds(message, length, g_pending.child_zone, + &g_pending.validated_keys, + wall_ns) == XAIOS_OK) { + /* No DS at this delegation: the child is insecure, not bogus. Ask for + the address directly rather than walking further down a chain that + has no keys to offer. */ + g_pending.dnssec_insecure = 1U; status = start_query(&g_pending, g_pending.hostname, g_pending.family == XAIOS_IP_FAMILY_V4 ? XAIOS_DNS_TYPE_A : XAIOS_DNS_TYPE_AAAA, now_ns); g_pending.dnssec_stage = DNSSEC_STAGE_ADDRESS; @@ -554,13 +573,23 @@ xaios_status_t dns_process_message(const uint8_t *message, uint32_t length, } else if (g_pending.dnssec_stage == DNSSEC_STAGE_ADDRESS) { uint8_t bytes[16]; uint32_t ttl = 0U; uint16_t type = g_pending.family == XAIOS_IP_FAMILY_V4 ? XAIOS_DNS_TYPE_A : XAIOS_DNS_TYPE_AAAA; - status = dnssec_verify_address(message, length, g_pending.hostname, type, - &g_pending.validated_keys, wall_ns, bytes, &ttl); + status = g_pending.dnssec_insecure != 0U + ? dnssec_extract_address_insecure(message, length, + g_pending.hostname, type, + bytes, &ttl) + : dnssec_verify_address(message, length, g_pending.hostname, + type, &g_pending.validated_keys, + wall_ns, bytes, &ttl); if (status == XAIOS_OK) { xaios_ip_addr_t answer; xaios_ip_addr_zero(&answer); answer.family = g_pending.family; bytes_copy(answer.addr, bytes, g_pending.family == XAIOS_IP_FAMILY_V4 ? 4U : 16U); cache_insert(g_pending.hostname, &answer, ttl, now_ns); - ++g_authenticated_count; ++g_response_count; + /* Only a validated chain counts as authenticated. An insecure answer + is a separate, weaker result and is counted separately so the two + can never be read as the same thing. */ + if (g_pending.dnssec_insecure != 0U) ++g_insecure_count; + else ++g_authenticated_count; + ++g_response_count; if (g_pending.tcp_flow_id != 0U) (void)network_stack_tcp_close_flow(g_pending.tcp_flow_id); bytes_zero(&g_pending, sizeof(g_pending)); return XAIOS_OK; } @@ -683,6 +712,7 @@ uint64_t dns_reject_count(void) { return g_reject_count; } uint64_t dns_timeout_count(void) { return g_timeout_count; } uint64_t dns_tcp_fallback_count(void) { return g_tcp_fallback_count; } uint64_t dns_authenticated_count(void) { return g_authenticated_count; } +uint64_t dns_insecure_count(void) { return g_insecure_count; } uint32_t dns_pending_count(void) { return g_pending.state != DNS_PENDING_NONE && g_pending.state != DNS_PENDING_COMPLETE diff --git a/kernel/net/dnssec.c b/kernel/net/dnssec.c index 6f5a55b0..842657ea 100644 --- a/kernel/net/dnssec.c +++ b/kernel/net/dnssec.c @@ -9,6 +9,13 @@ #define DNSSEC_TYPE_DNSKEY 48U #define DNSSEC_TYPE_RRSIG 46U #define DNSSEC_TYPE_NSEC 47U +#define DNSSEC_TYPE_NSEC3 50U +#define DNSSEC_NSEC3_SHA1 1U +#define DNSSEC_NSEC3_OPT_OUT 0x01U +/* An iteration count is attacker-chosen work for the resolver. RFC 9276 puts + the sensible ceiling at zero; accept a small margin for zones that have not + caught up, and refuse the rest rather than burn boot time on them. */ +#define DNSSEC_NSEC3_MAX_ITERATIONS 150U #define DNSSEC_MIN_WALL_TIME UINT64_C(946684800000000000) #define DNSSEC_MAX_RECORDS 96U #define DNSSEC_MAX_RRSET 16U @@ -280,6 +287,173 @@ xaios_status_t dnssec_set_trust_anchors(const dnssec_ds_t *anchors, uint32_t cou copy_bytes(g_anchors, anchors, count * sizeof(g_anchors[0])); g_anchor_count = count; return XAIOS_OK; } +/* base32hex (RFC 4648 section 7), the encoding NSEC3 owner labels use. */ +static int base32hex_value(uint8_t c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'V') return c - 'A' + 10; + if (c >= 'a' && c <= 'v') return c - 'a' + 10; + return -1; +} + +/* Decode the first label of an NSEC3 owner name into its hash. */ +static int nsec3_owner_hash(const char *owner, uint8_t *out, uint32_t capacity, + uint32_t *out_length) { + uint32_t bits = 0U, accumulator = 0U, produced = 0U; + uint32_t i = 0U; + for (; owner[i] != '\0' && owner[i] != '.'; ++i) { + int value = base32hex_value((uint8_t)owner[i]); + if (value < 0) return -1; + accumulator = (accumulator << 5U) | (uint32_t)value; + bits += 5U; + if (bits >= 8U) { + bits -= 8U; + if (produced >= capacity) return -1; + out[produced++] = (uint8_t)(accumulator >> bits); + } + } + /* A partial group must be zero padding, never discarded data. */ + if (i == 0U || (accumulator & ((1U << bits) - 1U)) != 0U) return -1; + *out_length = produced; + return 0; +} + +/* H(name) = SHA1(wire_name || salt), then iterated over H || salt. */ +static int nsec3_hash(const char *name, const uint8_t *salt, uint32_t salt_length, + uint16_t iterations, uint8_t *out) { + uint8_t wire[256]; + uint32_t wire_length = 0U; + br_sha1_context ctx; + if (iterations > DNSSEC_NSEC3_MAX_ITERATIONS) return -1; + if (canonical_name(name, wire, sizeof(wire), &wire_length) != 0) return -1; + br_sha1_init(&ctx); + br_sha1_update(&ctx, wire, wire_length); + br_sha1_update(&ctx, salt, salt_length); + br_sha1_out(&ctx, out); + for (uint32_t i = 0U; i < iterations; ++i) { + br_sha1_init(&ctx); + br_sha1_update(&ctx, out, 20U); + br_sha1_update(&ctx, salt, salt_length); + br_sha1_out(&ctx, out); + } + return 0; +} + +static int hash_compare(const uint8_t *a, const uint8_t *b, uint32_t n) { + for (uint32_t i = 0U; i < n; ++i) if (a[i] != b[i]) return a[i] < b[i] ? -1 : 1; + return 0; +} + +/* True when target lies strictly between owner and next in NSEC3 hash order, + accounting for the wrap at the last record of the zone's ordered chain. */ +static int nsec3_covers(const uint8_t *owner, const uint8_t *next, + const uint8_t *target) { + int wrapped = hash_compare(owner, next, 20U) >= 0; + int above_owner = hash_compare(target, owner, 20U) > 0; + int below_next = hash_compare(target, next, 20U) < 0; + return wrapped ? (above_owner || below_next) : (above_owner && below_next); +} + +static int type_bitmap_has(const uint8_t *bitmap, uint32_t length, + uint16_t type) { + uint32_t p = 0U; + while (p + 2U <= length) { + uint8_t window = bitmap[p]; + uint8_t bytes = bitmap[p + 1U]; + p += 2U; + if (bytes == 0U || bytes > 32U || bytes > length - p) return -1; + if ((uint32_t)(type >> 8U) == window) { + uint32_t bit = type & 0xffU; + if (bit / 8U < bytes && + (bitmap[p + bit / 8U] & (uint8_t)(0x80U >> (bit & 7U))) != 0U) { + return 1; + } + } + p += bytes; + } + return p == length ? 0 : -1; +} + +/* Prove that a delegation carries no DS, which makes the child zone insecure + rather than bogus. Two shapes are accepted, both signed by the parent: + an NSEC3 matching the delegation exactly whose bitmap has NS without DS, + and an opt-out NSEC3 whose hash range covers the delegation. */ +xaios_status_t dnssec_verify_no_ds(const uint8_t *message, uint32_t length, + const char *child_zone, + const dnssec_keyset_t *keys, + uint64_t wall_time_ns) { + if (message == 0 || child_zone == 0 || keys == 0) return XAIOS_ERR_INVALID; + if (parse_records(message, length) != 0) return XAIOS_ERR_INVALID; + for (uint32_t i = 0U; i < g_record_count; ++i) { + const dnssec_rr_t *rr = &g_records[i]; + const uint8_t *r = rr->rdata; + uint8_t salt_length, hash_length; + uint16_t iterations; + uint8_t target[20], owner_hash[20]; + uint32_t owner_hash_length = 0U, bitmap_offset; + if (rr->type != DNSSEC_TYPE_NSEC3 || rr->rdata_length < 6U) continue; + if (r[0] != DNSSEC_NSEC3_SHA1) continue; + iterations = get_be16(r + 2U); + salt_length = r[4]; + if ((uint32_t)5U + salt_length + 1U > rr->rdata_length) continue; + hash_length = r[5U + salt_length]; + if (hash_length != 20U) continue; + bitmap_offset = 6U + (uint32_t)salt_length + hash_length; + if (bitmap_offset > rr->rdata_length) continue; + if (nsec3_hash(child_zone, r + 5U, salt_length, iterations, target) != 0) { + continue; + } + if (nsec3_owner_hash(rr->owner, owner_hash, sizeof(owner_hash), + &owner_hash_length) != 0 || + owner_hash_length != 20U) { + continue; + } + if (hash_compare(owner_hash, target, 20U) == 0) { + const uint8_t *bitmap = r + bitmap_offset; + uint32_t bitmap_length = rr->rdata_length - bitmap_offset; + int has_ds = type_bitmap_has(bitmap, bitmap_length, DNSSEC_TYPE_DS); + int has_ns = type_bitmap_has(bitmap, bitmap_length, 2U /* NS */); + if (has_ds != 0 || has_ns != 1) continue; + } else if ((r[1] & DNSSEC_NSEC3_OPT_OUT) != 0U && + nsec3_covers(owner_hash, r + 6U + salt_length, target) != 0) { + /* Opt-out: the span is unsigned, so the delegation is insecure. */ + } else { + continue; + } + if (verify_rrset(message, length, rr->owner, DNSSEC_TYPE_NSEC3, keys, + wall_time_ns)) { + return XAIOS_OK; + } + } + return XAIOS_ERR_NOT_FOUND; +} + +/* Read an address out of an unsigned answer. Only reached once the chain has + proven the zone insecure, so there is no signature to check; the owner and + type still have to match what was asked. */ +xaios_status_t dnssec_extract_address_insecure(const uint8_t *message, + uint32_t length, + const char *hostname, + uint16_t type, + uint8_t *out_address, + uint32_t *out_ttl) { + uint32_t want = type == XAIOS_DNS_TYPE_A ? 4U : 16U; + if (message == 0 || hostname == 0 || out_address == 0 || out_ttl == 0) { + return XAIOS_ERR_INVALID; + } + if (parse_records(message, length) != 0) return XAIOS_ERR_INVALID; + for (uint32_t i = 0U; i < g_record_count; ++i) { + const dnssec_rr_t *rr = &g_records[i]; + if (rr->type != type || rr->rr_class != XAIOS_DNS_CLASS_IN || + rr->rdata_length != want || !name_equal(rr->owner, hostname)) { + continue; + } + copy_bytes(out_address, rr->rdata, want); + *out_ttl = rr->ttl; + return XAIOS_OK; + } + return XAIOS_ERR_NOT_FOUND; +} + xaios_status_t dnssec_verify_dnskey(const uint8_t *message, uint32_t length, const char *zone, const dnssec_dsset_t *parent_ds, uint64_t wall_time_ns, dnssec_keyset_t *out) { if (zone == 0 || out == 0 || parse_records(message, length) != 0) return XAIOS_ERR_INVALID; zero_bytes(out, sizeof(*out)); uint32_t oi = 0U; while (oi + 1U < sizeof(out->owner) && zone[oi]) { out->owner[oi] = zone[oi]; ++oi; } out->owner[oi] = '\0'; diff --git a/kernel/runtime/control_protocol.c b/kernel/runtime/control_protocol.c index 8de1ba56..0c4db041 100644 --- a/kernel/runtime/control_protocol.c +++ b/kernel/runtime/control_protocol.c @@ -2149,9 +2149,14 @@ xaios_status_t control_protocol_dispatch( xaios_control_role_t authenticated_role) { xaios_control_request_header_t request; counter_increment(&g_control_requests); - if (response_bytes != 0) { - *response_bytes = 0U; + /* Every handler's success path writes the response and its size without + rechecking these, so the guarantee has to be made once here. Tolerating + a null response_bytes at entry while handlers dereference it was an + inconsistency waiting for a caller to find it. */ + if (response == 0 || response_bytes == 0) { + return XAIOS_ERR_INVALID; } + *response_bytes = 0U; if (request_bytes == 0 || request_size < sizeof(request)) { return write_error(response, response_capacity, response_bytes, 0U, 0U, XAIOS_CONTROL_STATUS_INVALID_REQUEST); diff --git a/kernel/runtime/remote_login.c b/kernel/runtime/remote_login.c index 29f70ed4..9fed190f 100644 --- a/kernel/runtime/remote_login.c +++ b/kernel/runtime/remote_login.c @@ -1127,9 +1127,24 @@ static xaios_status_t handle_ls(const char *args, char *output, explicit_path[0] == '\0' ? resolved : explicit_path, target_stat.size, target_stat.type, long_form); } + { + const xaios_initramfs_file_t *image_file = 0; + if (initramfs_lookup(resolved, &image_file) == XAIOS_OK && + initramfs_directory_exists(resolved) == 0) { + return append_ls_entry(output, output_capacity, output_bytes, + explicit_path[0] == '\0' ? resolved + : explicit_path, + image_file->size, 2U, long_form); + } + } + int image_directory = initramfs_directory_exists(resolved); xaios_status_t list_status = mutable_fs_list(resolved, listing, sizeof(listing), &listing_size); + if (image_directory != 0 && list_status != XAIOS_OK) { + list_status = XAIOS_OK; + listing_size = 0U; + } if ((list_status != XAIOS_OK && (list_status != XAIOS_ERR_NO_MEMORY || listing_size == 0U)) || listing_size > sizeof(listing)) { @@ -1179,6 +1194,39 @@ static xaios_status_t handle_ls(const char *args, char *output, } line_start = line_end + 1U; } + /* Merge the boot image's view of this directory. MutableFS took its turn + above, so anything it can stat is already listed and is skipped here; + synthetic subdirectories are deduplicated against earlier image files. */ + for (uint32_t i = 0U; i < initramfs_file_count(); ++i) { + char name[XAIOS_MFS_PATH_MAX]; + int is_directory = 0; + if (initramfs_child_at(resolved, i, name, sizeof(name), &is_directory) == 0) + continue; + if (!show_all && is_hidden_name(name) != 0) continue; + char child[XAIOS_MFS_PATH_MAX]; + xaios_mfs_stat_t child_stat; + if (path_join(child, sizeof(child), resolved, name) != XAIOS_OK) continue; + if (mutable_fs_stat(child, &child_stat) == XAIOS_OK) continue; + if (is_directory != 0) { + uint32_t seen = 0U; + for (uint32_t j = 0U; j < i && seen == 0U; ++j) { + char earlier[XAIOS_MFS_PATH_MAX]; + int earlier_dir = 0; + if (initramfs_child_at(resolved, j, earlier, sizeof(earlier), + &earlier_dir) != 0 && + earlier_dir != 0 && string_equal(earlier, name) == 1U) + seen = 1U; + } + if (seen != 0U) continue; + } + const xaios_initramfs_file_t *file = initramfs_file_at(i); + if (append_ls_entry(output, output_capacity, output_bytes, name, + is_directory != 0 ? 0U : file->size, + is_directory != 0 ? 1U : 2U, long_form) != XAIOS_OK) { + return command_fail(output, output_capacity, output_bytes, + "ls: output too large"); + } + } return XAIOS_OK; } @@ -1869,7 +1917,8 @@ static xaios_status_t handle_cd(const char *arg, char *output, output_append(output, output_capacity, output_bytes, "\n"); return XAIOS_OK; } - if (mutable_fs_stat(resolved, &stat) != XAIOS_OK || stat.type != 1U) { + if ((mutable_fs_stat(resolved, &stat) != XAIOS_OK || stat.type != 1U) && + initramfs_directory_exists(resolved) == 0) { return command_fail(output, output_capacity, output_bytes, "cd: not a directory"); } @@ -3904,9 +3953,14 @@ static const remote_app_definition_t g_remote_apps[] = { REMOTE_APP("hello", "/bin/hello", XAIOS_CAP_LOG | XAIOS_CAP_EXIT), REMOTE_APP("helloworldc99", "/bin/helloworldc99", XAIOS_CAP_CONSOLE | XAIOS_CAP_EXIT), + /* XAIOS_CAP_NET is what authorizes net_resolve. Without it xapt can open + sockets but cannot turn a name into an address, so a configured host + works only as a literal and every hostname fails identically whether or + not it is DNSSEC-signed. */ REMOTE_APP("xapt", "/bin/xapt", XAIOS_CAP_CONSOLE | XAIOS_CAP_EXIT | XAIOS_CAP_TIME | - XAIOS_CAP_FS_READ | XAIOS_CAP_FS_WRITE | XAIOS_CAP_NET_SOCKET | + XAIOS_CAP_FS_READ | XAIOS_CAP_FS_WRITE | + XAIOS_CAP_NET | XAIOS_CAP_NET_SOCKET | XAIOS_CAP_RANDOM | XAIOS_CAP_CONTROL_QUERY | XAIOS_CAP_CONTROL_ADMIN | XAIOS_CAP_UPDATE | XAIOS_CAP_ADMIN), diff --git a/kernel/user/syscall.c b/kernel/user/syscall.c index f768500c..8d8212af 100644 --- a/kernel/user/syscall.c +++ b/kernel/user/syscall.c @@ -89,6 +89,7 @@ static const xaios_syscall_entry_t g_syscall_table[] = { {XAIOS_SYSCALL_CONSOLE_WRITE, "console_write", XAIOS_CAP_CONSOLE}, {XAIOS_SYSCALL_NET_LOCAL_IPV4, "net_local_ipv4", XAIOS_CAP_NET}, {XAIOS_SYSCALL_NET_CONNECT, "net_connect", XAIOS_CAP_NET_SOCKET}, + {XAIOS_SYSCALL_NET_LOCAL_IPV6, "net_local_ipv6", XAIOS_CAP_NET}, }; static uint64_t control_operation_capability(uint16_t operation) { @@ -522,6 +523,21 @@ uint64_t syscall_dispatch(uint64_t syscall, uint64_t arg0, uint64_t arg1, return arg1; } + if (syscall == XAIOS_SYSCALL_NET_LOCAL_IPV6) { + xaios_ip_addr_t address; + if (arg1 != 16U || + vmm_validate_user_buffer(arg0, 16U, XAIOS_VMM_WRITABLE) != XAIOS_OK) { + return reject_syscall(syscall, arg0, arg1, "bad-ipv6-buffer"); + } + if (network_stack_local_public_ipv6(&address) != XAIOS_OK) { + user_process_note_syscall(0); + return 0U; + } + bytes_copy((void *)(uintptr_t)arg0, address.addr, 16U); + user_process_note_syscall(0); + return 1U; + } + if (syscall == XAIOS_SYSCALL_NET_LOCAL_IPV4) { user_process_note_syscall(0); return network_config_local_ipv4(); diff --git a/kernel/user/user.c b/kernel/user/user.c index 64d72a04..5e48734f 100644 --- a/kernel/user/user.c +++ b/kernel/user/user.c @@ -218,6 +218,9 @@ static uint64_t process_runtime_read(const xaios_user_process_t *process, do { before = __atomic_load_n(&process->runtime_sequence, __ATOMIC_ACQUIRE); if ((before & 1U) != 0U) { + /* A writer is mid-update. Force the retry through a defined value + rather than letting the loop test read an unwritten `after`. */ + after = before; continue; } runtime = process->runtime_ns; @@ -240,6 +243,8 @@ static uint64_t cpu_usage_read(const xaios_cpu_usage_record_t *usage, do { before = __atomic_load_n(&usage->sequence, __ATOMIC_ACQUIRE); if ((before & 1U) != 0U) { + /* Same defined-retry as the runtime reader above. */ + after = before; continue; } busy = usage->busy_ns; diff --git a/scripts/build-image.sh b/scripts/build-image.sh index fe79c30c..9495cd48 100755 --- a/scripts/build-image.sh +++ b/scripts/build-image.sh @@ -446,6 +446,7 @@ KERNEL_OBJECTS=" $KERNEL_BUILD_DIR/mutable_fs.o $KERNEL_BUILD_DIR/vfs.o $KERNEL_BUILD_DIR/vfs_mutable.o + $KERNEL_BUILD_DIR/vfs_initramfs.o $KERNEL_BUILD_DIR/vfs_model.o $KERNEL_BUILD_DIR/model_volume_admin.o $KERNEL_BUILD_DIR/service.o @@ -577,6 +578,7 @@ compile_kernel "$ROOT_DIR/kernel/fs/initramfs.c" "$KERNEL_BUILD_DIR/initramfs.o" compile_kernel "$ROOT_DIR/kernel/fs/mutable_fs.c" "$KERNEL_BUILD_DIR/mutable_fs.o" compile_kernel "$ROOT_DIR/kernel/fs/vfs.c" "$KERNEL_BUILD_DIR/vfs.o" compile_kernel "$ROOT_DIR/kernel/fs/vfs_mutable.c" "$KERNEL_BUILD_DIR/vfs_mutable.o" +compile_kernel "$ROOT_DIR/kernel/fs/vfs_initramfs.c" "$KERNEL_BUILD_DIR/vfs_initramfs.o" compile_kernel "$ROOT_DIR/kernel/fs/vfs_model.c" "$KERNEL_BUILD_DIR/vfs_model.o" compile_kernel "$ROOT_DIR/kernel/fs/model_volume_admin.c" "$KERNEL_BUILD_DIR/model_volume_admin.o" compile_kernel "$ROOT_DIR/kernel/user/service.c" "$KERNEL_BUILD_DIR/service.o" @@ -813,9 +815,19 @@ for app in $USER_APPS; do -I"$ROOT_DIR/userspace/include" -I"$ROOT_DIR/userspace/apps" \ -I"$ROOT_DIR/third_party/bearssl/inc" \ -c "$ROOT_DIR/userspace/apps/xapt_tls.c" -o "$XAPT_TLS_OBJ" + XAPT_TRUST_OBJ="$INIT_BUILD_DIR/xapt-trust-anchors.o" + "$CLANG" --target="$TARGET_TRIPLE" $USER_ARCH_CFLAGS -std=c99 \ + -ffreestanding -fno-stack-protector -fno-builtin -fno-pic -fno-pie \ + -Os -Wall -Wextra -Werror \ + -isystem "$BUILD_DIR/libc/$TARGET_ARCH/sysroot/include" \ + -I"$ROOT_DIR/userspace/include" -I"$ROOT_DIR/userspace/apps" \ + -I"$ROOT_DIR/third_party/bearssl/inc" \ + -c "$ROOT_DIR/userspace/apps/xapt_trust_anchors.c" \ + -o "$XAPT_TRUST_OBJ" "$LD_LLD" -nostdlib -T "$ROOT_DIR/userspace/init/linker.ld" \ -o "$app_elf" "$USER_START_OBJ" "$USER_LIB_OBJ" \ - "$USER_CONTROL_OBJ" "$app_obj" "$XAPT_TLS_OBJ" "$XAPT_BEARSSL" + "$USER_CONTROL_OBJ" "$app_obj" "$XAPT_TLS_OBJ" "$XAPT_TRUST_OBJ" \ + "$XAPT_BEARSSL" elif [ "$app" = "xaiosctl" ] || [ "$app" = "htop" ]; then "$LD_LLD" \ diff --git a/scripts/publish-xapt-repository.sh b/scripts/publish-xapt-repository.sh index 751267ff..53aa4fcd 100755 --- a/scripts/publish-xapt-repository.sh +++ b/scripts/publish-xapt-repository.sh @@ -1,49 +1,35 @@ #!/bin/sh set -eu +# Publish a verified xapt repository to the updater host. +# +# TLS is terminated by the master edge in /var/caddy, which also fronts the +# other projects on this host. This script therefore never writes /etc/caddy +# and never reloads the shared `caddy` service — doing so would take those +# projects down. It syncs the repository and reloads the updater's own +# instance, xaios-caddy, which serves plain HTTP on :8090 behind the master. + ROOT=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd) SOURCE=${1:-"$ROOT/build/xapt/repository"} HOST=${XAIOS_UPDATE_HOST:-root@91.99.176.243} DESTINATION=${XAIOS_UPDATE_ROOT:-/var/xaios_updater} -TLS_CERT=${XAIOS_XAPT_TLS_CERT:-} -TLS_KEY=${XAIOS_XAPT_TLS_KEY:-} - -if [ -z "$TLS_CERT" ] || [ -z "$TLS_KEY" ]; then - if [ "${XAIOS_ALLOW_TEST_TLS_FIXTURE:-0}" = 1 ]; then - TLS_CERT="$ROOT/tests/fixtures/xapt-tls-cert.pem" - TLS_KEY="$ROOT/tests/fixtures/xapt-tls-key.pem" - printf '%s\n' \ - 'warning: publishing the public test-only TLS identity; never use this endpoint in production' >&2 - else - printf '%s\n' \ - 'error: set XAIOS_XAPT_TLS_CERT and XAIOS_XAPT_TLS_KEY to an operator-managed TLS identity' >&2 - exit 2 - fi -fi -[ -r "$TLS_CERT" ] || { printf 'error: unreadable TLS certificate: %s\n' "$TLS_CERT" >&2; exit 2; } -[ -r "$TLS_KEY" ] || { printf 'error: unreadable TLS key: %s\n' "$TLS_KEY" >&2; exit 2; } +SERVICE=${XAIOS_UPDATE_SERVICE:-xaios-caddy} [ -d "$SOURCE" ] || { printf 'error: repository directory not found: %s\n' "$SOURCE" >&2 exit 1 } python3 "$ROOT/tools/xaios_xapt_repo.py" verify --repository "$SOURCE" + ssh "$HOST" "install -d -m 0755 '$DESTINATION'" -rsync -rlpt --delete-delay "$SOURCE/" "$HOST:$DESTINATION/" -scp "$ROOT/deploy/xapt/Caddyfile" "$HOST:/etc/caddy/Caddyfile.xapt-new" -scp "$TLS_CERT" \ - "$HOST:/etc/caddy/xapt-tls-cert.pem.new" -scp "$TLS_KEY" \ - "$HOST:/etc/caddy/xapt-tls-key.pem.new" -ssh "$HOST" "caddy validate --config /etc/caddy/Caddyfile.xapt-new && \ - install -m 0644 /etc/caddy/xapt-tls-cert.pem.new /etc/caddy/xapt-tls-cert.pem && \ - install -m 0600 /etc/caddy/xapt-tls-key.pem.new /etc/caddy/xapt-tls-key.pem && \ - install -m 0644 /etc/caddy/Caddyfile.xapt-new /etc/caddy/Caddyfile && \ - rm -f /etc/caddy/Caddyfile.xapt-new /etc/caddy/xapt-tls-cert.pem.new \ - /etc/caddy/xapt-tls-key.pem.new && \ - if systemctl is-active --quiet caddy; then \ - systemctl reload caddy; \ - else \ - systemctl enable --now caddy; \ - fi" +# The updater's own server lives inside the directory it serves; excluding it +# keeps --delete-delay from removing the binary and config out from under it. +rsync -rlpt --delete-delay \ + --exclude /caddy/ --exclude /bin/ \ + "$SOURCE/" "$HOST:$DESTINATION/" + +ssh "$HOST" "systemctl reload-or-restart '$SERVICE' && \ + systemctl is-active --quiet '$SERVICE'" + printf 'Published verified xapt repository to %s:%s\n' "$HOST" "$DESTINATION" +printf 'Reachable at https://xaios.91.99.176.243.nip.io/ via the master edge.\n' diff --git a/scripts/run-qemu-aarch64.sh b/scripts/run-qemu-aarch64.sh index 18a7ac8b..a222ee0c 100755 --- a/scripts/run-qemu-aarch64.sh +++ b/scripts/run-qemu-aarch64.sh @@ -152,10 +152,34 @@ case "$iommu" in ;; esac +# The virt machine only grew an "msi" property in newer QEMU releases. Older +# builds still route MSI through the ITS whenever one is instantiated, so ask +# the binary what it supports rather than passing an option that makes it +# refuse to start: on QEMU 8.2 the explicit form fails with +# "Property 'virt-8.2-machine.msi' not found" and the guest never boots. +if "$qemu" -machine virt,help 2>/dev/null | grep -q '^[[:space:]]*msi='; then + machine_msi_property=1 +else + machine_msi_property=0 +fi + case "$msi_controller" in auto) ;; - gicv2m) machine_options="$machine_options,msi=gicv2m" ;; - its) machine_options="$machine_options,its=on,msi=its" ;; + gicv2m) + if [ "$machine_msi_property" -eq 1 ]; then + machine_options="$machine_options,msi=gicv2m" + else + printf '%s\n' \ + "error: this QEMU cannot select GICv2m explicitly; it has no virt machine msi property" >&2 + exit 2 + fi + ;; + its) + machine_options="$machine_options,its=on" + if [ "$machine_msi_property" -eq 1 ]; then + machine_options="$machine_options,msi=its" + fi + ;; *) printf '%s\n' "error: XAIOS_QEMU_MSI_CONTROLLER must be auto, gicv2m, or its" >&2 exit 2 diff --git a/tests/fuzz/dns-corpus/0832a9a9ac5086bc9af775caa5170eba07c02d1b b/tests/fuzz/dns-corpus/0832a9a9ac5086bc9af775caa5170eba07c02d1b new file mode 100644 index 00000000..5d9d617e --- /dev/null +++ b/tests/fuzz/dns-corpus/0832a9a9ac5086bc9af775caa5170eba07c02d1b @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/08c421258fa435d5cee5f0a241472e1a66e2421b b/tests/fuzz/dns-corpus/08c421258fa435d5cee5f0a241472e1a66e2421b new file mode 100644 index 00000000..0a4b01c7 Binary files /dev/null and b/tests/fuzz/dns-corpus/08c421258fa435d5cee5f0a241472e1a66e2421b differ diff --git a/tests/fuzz/dns-corpus/0973198e06c44aad20a413e134bd7538a6989638 b/tests/fuzz/dns-corpus/0973198e06c44aad20a413e134bd7538a6989638 new file mode 100644 index 00000000..7a422449 --- /dev/null +++ b/tests/fuzz/dns-corpus/0973198e06c44aad20a413e134bd7538a6989638 @@ -0,0 +1 @@ +:!t# \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/0b013a0c91bd4804212acd07fc81d46830381344 b/tests/fuzz/dns-corpus/0b013a0c91bd4804212acd07fc81d46830381344 new file mode 100644 index 00000000..5116f5bd Binary files /dev/null and b/tests/fuzz/dns-corpus/0b013a0c91bd4804212acd07fc81d46830381344 differ diff --git a/tests/fuzz/dns-corpus/0c3da521fc1eeeb7b105accf96f5c427dfca3dc0 b/tests/fuzz/dns-corpus/0c3da521fc1eeeb7b105accf96f5c427dfca3dc0 new file mode 100644 index 00000000..6e58555e Binary files /dev/null and b/tests/fuzz/dns-corpus/0c3da521fc1eeeb7b105accf96f5c427dfca3dc0 differ diff --git a/tests/fuzz/dns-corpus/0f30339a46eba6cd2ddd3d07a5131f01ed939687 b/tests/fuzz/dns-corpus/0f30339a46eba6cd2ddd3d07a5131f01ed939687 new file mode 100644 index 00000000..bd9f3521 --- /dev/null +++ b/tests/fuzz/dns-corpus/0f30339a46eba6cd2ddd3d07a5131f01ed939687 @@ -0,0 +1 @@ +ÿÿ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/109a3c99f744fc841ebf337a890bc7bd7c9652a9 b/tests/fuzz/dns-corpus/109a3c99f744fc841ebf337a890bc7bd7c9652a9 new file mode 100644 index 00000000..c23eaf48 Binary files /dev/null and b/tests/fuzz/dns-corpus/109a3c99f744fc841ebf337a890bc7bd7c9652a9 differ diff --git a/tests/fuzz/dns-corpus/10c9a0c64d26dc0103956a51c4d32be4e1f40997 b/tests/fuzz/dns-corpus/10c9a0c64d26dc0103956a51c4d32be4e1f40997 new file mode 100644 index 00000000..ea097b68 Binary files /dev/null and b/tests/fuzz/dns-corpus/10c9a0c64d26dc0103956a51c4d32be4e1f40997 differ diff --git a/tests/fuzz/dns-corpus/111c1e4415defd5209ba7ebef3e74c1beb56ccc0 b/tests/fuzz/dns-corpus/111c1e4415defd5209ba7ebef3e74c1beb56ccc0 new file mode 100644 index 00000000..03b48ee7 Binary files /dev/null and b/tests/fuzz/dns-corpus/111c1e4415defd5209ba7ebef3e74c1beb56ccc0 differ diff --git a/tests/fuzz/dns-corpus/115d300d95e9362933f252a6228da2d841303d3d b/tests/fuzz/dns-corpus/115d300d95e9362933f252a6228da2d841303d3d new file mode 100644 index 00000000..62a5292d Binary files /dev/null and b/tests/fuzz/dns-corpus/115d300d95e9362933f252a6228da2d841303d3d differ diff --git a/tests/fuzz/dns-corpus/122435e3d0c83f1be4ff2d6878ccff5e88e4fcb8 b/tests/fuzz/dns-corpus/122435e3d0c83f1be4ff2d6878ccff5e88e4fcb8 new file mode 100644 index 00000000..83dc5eff --- /dev/null +++ b/tests/fuzz/dns-corpus/122435e3d0c83f1be4ff2d6878ccff5e88e4fcb8 @@ -0,0 +1 @@ +ÿÿÿÅÿÿÿÍ diff --git a/tests/fuzz/dns-corpus/13b859b2cf7e59ae115fb4b84e4c7007cadde369 b/tests/fuzz/dns-corpus/13b859b2cf7e59ae115fb4b84e4c7007cadde369 new file mode 100644 index 00000000..ac5fa9c7 Binary files /dev/null and b/tests/fuzz/dns-corpus/13b859b2cf7e59ae115fb4b84e4c7007cadde369 differ diff --git a/tests/fuzz/dns-corpus/13eca931b5a402bb1cefc17c04f0417d6a8b4b95 b/tests/fuzz/dns-corpus/13eca931b5a402bb1cefc17c04f0417d6a8b4b95 new file mode 100644 index 00000000..f826a689 Binary files /dev/null and b/tests/fuzz/dns-corpus/13eca931b5a402bb1cefc17c04f0417d6a8b4b95 differ diff --git a/tests/fuzz/dns-corpus/1580bf67d44f74b1da01786a7baf00e99ec04c57 b/tests/fuzz/dns-corpus/1580bf67d44f74b1da01786a7baf00e99ec04c57 new file mode 100644 index 00000000..7cd1008a Binary files /dev/null and b/tests/fuzz/dns-corpus/1580bf67d44f74b1da01786a7baf00e99ec04c57 differ diff --git a/tests/fuzz/dns-corpus/17ad6754a1df76838b7c9262d22fb42a8d0968dc b/tests/fuzz/dns-corpus/17ad6754a1df76838b7c9262d22fb42a8d0968dc new file mode 100644 index 00000000..81943cfb Binary files /dev/null and b/tests/fuzz/dns-corpus/17ad6754a1df76838b7c9262d22fb42a8d0968dc differ diff --git a/tests/fuzz/dns-corpus/17dcefd52f0cf8b0a4c3433f6192353952e9dcf5 b/tests/fuzz/dns-corpus/17dcefd52f0cf8b0a4c3433f6192353952e9dcf5 new file mode 100644 index 00000000..82cad9a7 Binary files /dev/null and b/tests/fuzz/dns-corpus/17dcefd52f0cf8b0a4c3433f6192353952e9dcf5 differ diff --git a/tests/fuzz/dns-corpus/199bcdd5dfc73ede125126b22758bd4d877c024b b/tests/fuzz/dns-corpus/199bcdd5dfc73ede125126b22758bd4d877c024b new file mode 100644 index 00000000..2aec9a34 --- /dev/null +++ b/tests/fuzz/dns-corpus/199bcdd5dfc73ede125126b22758bd4d877c024b @@ -0,0 +1 @@ +w\x\}pe° \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/1aeda6594a9d633d937806bb28d2769931361885 b/tests/fuzz/dns-corpus/1aeda6594a9d633d937806bb28d2769931361885 new file mode 100644 index 00000000..e875f342 Binary files /dev/null and b/tests/fuzz/dns-corpus/1aeda6594a9d633d937806bb28d2769931361885 differ diff --git a/tests/fuzz/dns-corpus/1c34cbbcf60f224c4307d487e3e1753d850bf5f7 b/tests/fuzz/dns-corpus/1c34cbbcf60f224c4307d487e3e1753d850bf5f7 new file mode 100644 index 00000000..34fa164c Binary files /dev/null and b/tests/fuzz/dns-corpus/1c34cbbcf60f224c4307d487e3e1753d850bf5f7 differ diff --git a/tests/fuzz/dns-corpus/1d3a9baf24157af7eb732b066d54de89817594c5 b/tests/fuzz/dns-corpus/1d3a9baf24157af7eb732b066d54de89817594c5 new file mode 100644 index 00000000..65f1d0f5 Binary files /dev/null and b/tests/fuzz/dns-corpus/1d3a9baf24157af7eb732b066d54de89817594c5 differ diff --git a/tests/fuzz/dns-corpus/1d58425e43abd654b30a9caa53d017f2d496c37f b/tests/fuzz/dns-corpus/1d58425e43abd654b30a9caa53d017f2d496c37f new file mode 100644 index 00000000..b8f9d659 Binary files /dev/null and b/tests/fuzz/dns-corpus/1d58425e43abd654b30a9caa53d017f2d496c37f differ diff --git a/tests/fuzz/dns-corpus/1e0bfd59e32618944ee2d274486060e2c977026d b/tests/fuzz/dns-corpus/1e0bfd59e32618944ee2d274486060e2c977026d new file mode 100644 index 00000000..d915a005 Binary files /dev/null and b/tests/fuzz/dns-corpus/1e0bfd59e32618944ee2d274486060e2c977026d differ diff --git a/tests/fuzz/dns-corpus/1ff534e40d49a195e7efa0f0abccbcddfae2c175 b/tests/fuzz/dns-corpus/1ff534e40d49a195e7efa0f0abccbcddfae2c175 new file mode 100644 index 00000000..a72c8753 Binary files /dev/null and b/tests/fuzz/dns-corpus/1ff534e40d49a195e7efa0f0abccbcddfae2c175 differ diff --git a/tests/fuzz/dns-corpus/203c21c063b1474bdddcf23549bdb91480316de7 b/tests/fuzz/dns-corpus/203c21c063b1474bdddcf23549bdb91480316de7 new file mode 100644 index 00000000..91c3e6fd Binary files /dev/null and b/tests/fuzz/dns-corpus/203c21c063b1474bdddcf23549bdb91480316de7 differ diff --git a/tests/fuzz/dns-corpus/2159792965bfa279b8b4128c5fda2ed325c78fc9 b/tests/fuzz/dns-corpus/2159792965bfa279b8b4128c5fda2ed325c78fc9 new file mode 100644 index 00000000..8a25aad6 Binary files /dev/null and b/tests/fuzz/dns-corpus/2159792965bfa279b8b4128c5fda2ed325c78fc9 differ diff --git a/tests/fuzz/dns-corpus/233cc95c1ef7e425eaadaea4105b88cd1e6b9972 b/tests/fuzz/dns-corpus/233cc95c1ef7e425eaadaea4105b88cd1e6b9972 new file mode 100644 index 00000000..ec11a328 Binary files /dev/null and b/tests/fuzz/dns-corpus/233cc95c1ef7e425eaadaea4105b88cd1e6b9972 differ diff --git a/tests/fuzz/dns-corpus/2349a23896383c74eab468c2ca73916fd50de5ac b/tests/fuzz/dns-corpus/2349a23896383c74eab468c2ca73916fd50de5ac new file mode 100644 index 00000000..803e3826 Binary files /dev/null and b/tests/fuzz/dns-corpus/2349a23896383c74eab468c2ca73916fd50de5ac differ diff --git a/tests/fuzz/dns-corpus/237044e0370d888f31aa9811010d4da2d5a32ef1 b/tests/fuzz/dns-corpus/237044e0370d888f31aa9811010d4da2d5a32ef1 new file mode 100644 index 00000000..838bb40d Binary files /dev/null and b/tests/fuzz/dns-corpus/237044e0370d888f31aa9811010d4da2d5a32ef1 differ diff --git a/tests/fuzz/dns-corpus/24c14b151f990ac5dd2847a2e8030eb6226796fe b/tests/fuzz/dns-corpus/24c14b151f990ac5dd2847a2e8030eb6226796fe new file mode 100644 index 00000000..328f6ec0 Binary files /dev/null and b/tests/fuzz/dns-corpus/24c14b151f990ac5dd2847a2e8030eb6226796fe differ diff --git a/tests/fuzz/dns-corpus/253fa1826be261d676f25897a6bf36ca0a5a7fbe b/tests/fuzz/dns-corpus/253fa1826be261d676f25897a6bf36ca0a5a7fbe new file mode 100644 index 00000000..e7f239eb Binary files /dev/null and b/tests/fuzz/dns-corpus/253fa1826be261d676f25897a6bf36ca0a5a7fbe differ diff --git a/tests/fuzz/dns-corpus/29ad60729fff57ce0860ae0f87d155aaaf428f02 b/tests/fuzz/dns-corpus/29ad60729fff57ce0860ae0f87d155aaaf428f02 new file mode 100644 index 00000000..23d3ef8b Binary files /dev/null and b/tests/fuzz/dns-corpus/29ad60729fff57ce0860ae0f87d155aaaf428f02 differ diff --git a/tests/fuzz/dns-corpus/29e66ad3d83b342164cd33519c79e7771a6ea165 b/tests/fuzz/dns-corpus/29e66ad3d83b342164cd33519c79e7771a6ea165 new file mode 100644 index 00000000..e45cafc4 Binary files /dev/null and b/tests/fuzz/dns-corpus/29e66ad3d83b342164cd33519c79e7771a6ea165 differ diff --git a/tests/fuzz/dns-corpus/2a0882cf16552e66576f2cb3e981eb127d4e1570 b/tests/fuzz/dns-corpus/2a0882cf16552e66576f2cb3e981eb127d4e1570 new file mode 100644 index 00000000..97c798ae --- /dev/null +++ b/tests/fuzz/dns-corpus/2a0882cf16552e66576f2cb3e981eb127d4e1570 @@ -0,0 +1 @@ +ÿú \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/2c97080a0e1bb54c106965edac5592b215c0a5f3 b/tests/fuzz/dns-corpus/2c97080a0e1bb54c106965edac5592b215c0a5f3 new file mode 100644 index 00000000..f4daabec Binary files /dev/null and b/tests/fuzz/dns-corpus/2c97080a0e1bb54c106965edac5592b215c0a5f3 differ diff --git a/tests/fuzz/dns-corpus/2c9c8df39868c1aa70c3142f17ac134ee437e71b b/tests/fuzz/dns-corpus/2c9c8df39868c1aa70c3142f17ac134ee437e71b new file mode 100644 index 00000000..d8ef1e7e Binary files /dev/null and b/tests/fuzz/dns-corpus/2c9c8df39868c1aa70c3142f17ac134ee437e71b differ diff --git a/tests/fuzz/dns-corpus/2eb72323a56b6f4367d9b91406776c99786adfcc b/tests/fuzz/dns-corpus/2eb72323a56b6f4367d9b91406776c99786adfcc new file mode 100644 index 00000000..e272a618 Binary files /dev/null and b/tests/fuzz/dns-corpus/2eb72323a56b6f4367d9b91406776c99786adfcc differ diff --git a/tests/fuzz/dns-corpus/30f90f5b014b2998b4c7204eaa8fab40b5be0824 b/tests/fuzz/dns-corpus/30f90f5b014b2998b4c7204eaa8fab40b5be0824 new file mode 100644 index 00000000..b33962ac Binary files /dev/null and b/tests/fuzz/dns-corpus/30f90f5b014b2998b4c7204eaa8fab40b5be0824 differ diff --git a/tests/fuzz/dns-corpus/328ecc4f0ea167e6704c3f0762ad29a0dddf4d2a b/tests/fuzz/dns-corpus/328ecc4f0ea167e6704c3f0762ad29a0dddf4d2a new file mode 100644 index 00000000..a9ba7848 Binary files /dev/null and b/tests/fuzz/dns-corpus/328ecc4f0ea167e6704c3f0762ad29a0dddf4d2a differ diff --git a/tests/fuzz/dns-corpus/32fc661c90093060e46c7cc421990fde453f6109 b/tests/fuzz/dns-corpus/32fc661c90093060e46c7cc421990fde453f6109 new file mode 100644 index 00000000..59c0543d Binary files /dev/null and b/tests/fuzz/dns-corpus/32fc661c90093060e46c7cc421990fde453f6109 differ diff --git a/tests/fuzz/dns-corpus/34a8662d53e3a99956128877f744fd4e29c242fe b/tests/fuzz/dns-corpus/34a8662d53e3a99956128877f744fd4e29c242fe new file mode 100644 index 00000000..31dec365 Binary files /dev/null and b/tests/fuzz/dns-corpus/34a8662d53e3a99956128877f744fd4e29c242fe differ diff --git a/tests/fuzz/dns-corpus/34fba317e4ccebd5c014b04858172baddae7c07e b/tests/fuzz/dns-corpus/34fba317e4ccebd5c014b04858172baddae7c07e new file mode 100644 index 00000000..3f1b4b75 Binary files /dev/null and b/tests/fuzz/dns-corpus/34fba317e4ccebd5c014b04858172baddae7c07e differ diff --git a/tests/fuzz/dns-corpus/381e8b6177462d74ef9bd6d2b3a9545345fda1e5 b/tests/fuzz/dns-corpus/381e8b6177462d74ef9bd6d2b3a9545345fda1e5 new file mode 100644 index 00000000..c1245327 Binary files /dev/null and b/tests/fuzz/dns-corpus/381e8b6177462d74ef9bd6d2b3a9545345fda1e5 differ diff --git a/tests/fuzz/dns-corpus/3916fd15d4c912f9567d0dd0befba4c65cf796c3 b/tests/fuzz/dns-corpus/3916fd15d4c912f9567d0dd0befba4c65cf796c3 new file mode 100644 index 00000000..0f1ef963 Binary files /dev/null and b/tests/fuzz/dns-corpus/3916fd15d4c912f9567d0dd0befba4c65cf796c3 differ diff --git a/tests/fuzz/dns-corpus/3bbde2cc3d76799060eec0bf6a12d1d3cb2fd3a0 b/tests/fuzz/dns-corpus/3bbde2cc3d76799060eec0bf6a12d1d3cb2fd3a0 new file mode 100644 index 00000000..43f15740 --- /dev/null +++ b/tests/fuzz/dns-corpus/3bbde2cc3d76799060eec0bf6a12d1d3cb2fd3a0 @@ -0,0 +1 @@ +Aÿ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/3e25be2c84e1650b4810de54249fa2884f7f3ad4 b/tests/fuzz/dns-corpus/3e25be2c84e1650b4810de54249fa2884f7f3ad4 new file mode 100644 index 00000000..dda89c46 Binary files /dev/null and b/tests/fuzz/dns-corpus/3e25be2c84e1650b4810de54249fa2884f7f3ad4 differ diff --git a/tests/fuzz/dns-corpus/402ce6c4ec4e5ce132435511bb6d44d727b6d7bb b/tests/fuzz/dns-corpus/402ce6c4ec4e5ce132435511bb6d44d727b6d7bb new file mode 100644 index 00000000..664d5c35 Binary files /dev/null and b/tests/fuzz/dns-corpus/402ce6c4ec4e5ce132435511bb6d44d727b6d7bb differ diff --git a/tests/fuzz/dns-corpus/40dac1e15a7c998bfa643bd2b1f17f32fbc079af b/tests/fuzz/dns-corpus/40dac1e15a7c998bfa643bd2b1f17f32fbc079af new file mode 100644 index 00000000..04fac2ae --- /dev/null +++ b/tests/fuzz/dns-corpus/40dac1e15a7c998bfa643bd2b1f17f32fbc079af @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/40e53188844f19e7c92bf120c5c6cebbbfd16da8 b/tests/fuzz/dns-corpus/40e53188844f19e7c92bf120c5c6cebbbfd16da8 new file mode 100644 index 00000000..b1797cae --- /dev/null +++ b/tests/fuzz/dns-corpus/40e53188844f19e7c92bf120c5c6cebbbfd16da8 @@ -0,0 +1 @@ +Íö~ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/4181673749d8559ce46b580dc2fc91692404568a b/tests/fuzz/dns-corpus/4181673749d8559ce46b580dc2fc91692404568a new file mode 100644 index 00000000..99cdeb84 Binary files /dev/null and b/tests/fuzz/dns-corpus/4181673749d8559ce46b580dc2fc91692404568a differ diff --git a/tests/fuzz/dns-corpus/4273c30ab32f1fe3279015cbb875eb81b030eadb b/tests/fuzz/dns-corpus/4273c30ab32f1fe3279015cbb875eb81b030eadb new file mode 100644 index 00000000..eefc6c79 Binary files /dev/null and b/tests/fuzz/dns-corpus/4273c30ab32f1fe3279015cbb875eb81b030eadb differ diff --git a/tests/fuzz/dns-corpus/45210c455c626b00ca5f720f175e13659979bcd6 b/tests/fuzz/dns-corpus/45210c455c626b00ca5f720f175e13659979bcd6 new file mode 100644 index 00000000..66641020 Binary files /dev/null and b/tests/fuzz/dns-corpus/45210c455c626b00ca5f720f175e13659979bcd6 differ diff --git a/tests/fuzz/dns-corpus/46dc4257c8f6fdf1678133531c94c95548da0b2c b/tests/fuzz/dns-corpus/46dc4257c8f6fdf1678133531c94c95548da0b2c new file mode 100644 index 00000000..a26fac96 Binary files /dev/null and b/tests/fuzz/dns-corpus/46dc4257c8f6fdf1678133531c94c95548da0b2c differ diff --git a/tests/fuzz/dns-corpus/46ef9075fd5df2a8a7abf77a7d60fcb6584e3b5e b/tests/fuzz/dns-corpus/46ef9075fd5df2a8a7abf77a7d60fcb6584e3b5e new file mode 100644 index 00000000..521456e6 Binary files /dev/null and b/tests/fuzz/dns-corpus/46ef9075fd5df2a8a7abf77a7d60fcb6584e3b5e differ diff --git a/tests/fuzz/dns-corpus/4871374f7e63da473ddc19ebca5915a3e1edcdf3 b/tests/fuzz/dns-corpus/4871374f7e63da473ddc19ebca5915a3e1edcdf3 new file mode 100644 index 00000000..bb8a3f18 --- /dev/null +++ b/tests/fuzz/dns-corpus/4871374f7e63da473ddc19ebca5915a3e1edcdf3 @@ -0,0 +1 @@ +ÀÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/4a580cca79dab74fbe4397c293545a23cfb7c7aa b/tests/fuzz/dns-corpus/4a580cca79dab74fbe4397c293545a23cfb7c7aa new file mode 100644 index 00000000..ebcb1a95 Binary files /dev/null and b/tests/fuzz/dns-corpus/4a580cca79dab74fbe4397c293545a23cfb7c7aa differ diff --git a/tests/fuzz/dns-corpus/4b7baaa905c5ffb2d25bab161b454a695653d9e0 b/tests/fuzz/dns-corpus/4b7baaa905c5ffb2d25bab161b454a695653d9e0 new file mode 100644 index 00000000..c5fe97e0 Binary files /dev/null and b/tests/fuzz/dns-corpus/4b7baaa905c5ffb2d25bab161b454a695653d9e0 differ diff --git a/tests/fuzz/dns-corpus/4cd13b6a9f0df27b0da5363c9293e43d14e5920e b/tests/fuzz/dns-corpus/4cd13b6a9f0df27b0da5363c9293e43d14e5920e new file mode 100644 index 00000000..91ce2872 --- /dev/null +++ b/tests/fuzz/dns-corpus/4cd13b6a9f0df27b0da5363c9293e43d14e5920e @@ -0,0 +1 @@ +ÀÀÿÿ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/4ce8d17cc857a2b3077a43da01de5d54b8a34c7f b/tests/fuzz/dns-corpus/4ce8d17cc857a2b3077a43da01de5d54b8a34c7f new file mode 100644 index 00000000..b62a966c --- /dev/null +++ b/tests/fuzz/dns-corpus/4ce8d17cc857a2b3077a43da01de5d54b8a34c7f @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/4d78030a76043e9f51bdcee5ee888a3d4a9f3006 b/tests/fuzz/dns-corpus/4d78030a76043e9f51bdcee5ee888a3d4a9f3006 new file mode 100644 index 00000000..f133d460 Binary files /dev/null and b/tests/fuzz/dns-corpus/4d78030a76043e9f51bdcee5ee888a3d4a9f3006 differ diff --git a/tests/fuzz/dns-corpus/4dc7c9ec434ed06502767136789763ec11d2c4b7 b/tests/fuzz/dns-corpus/4dc7c9ec434ed06502767136789763ec11d2c4b7 new file mode 100644 index 00000000..1d2f0149 --- /dev/null +++ b/tests/fuzz/dns-corpus/4dc7c9ec434ed06502767136789763ec11d2c4b7 @@ -0,0 +1 @@ +r \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/5191aa04a42fadbda859a175e1669d36e999eaae b/tests/fuzz/dns-corpus/5191aa04a42fadbda859a175e1669d36e999eaae new file mode 100644 index 00000000..b9ed798d --- /dev/null +++ b/tests/fuzz/dns-corpus/5191aa04a42fadbda859a175e1669d36e999eaae @@ -0,0 +1 @@ +‹  \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/51ac5a8995e6a1ab01674decfada6c1b33d09b2b b/tests/fuzz/dns-corpus/51ac5a8995e6a1ab01674decfada6c1b33d09b2b new file mode 100644 index 00000000..29ff4aed --- /dev/null +++ b/tests/fuzz/dns-corpus/51ac5a8995e6a1ab01674decfada6c1b33d09b2b @@ -0,0 +1 @@ +ˆˆw \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/524323d127d19f5d656a726f9d7e548367d6335f b/tests/fuzz/dns-corpus/524323d127d19f5d656a726f9d7e548367d6335f new file mode 100644 index 00000000..f38a12f6 Binary files /dev/null and b/tests/fuzz/dns-corpus/524323d127d19f5d656a726f9d7e548367d6335f differ diff --git a/tests/fuzz/dns-corpus/551813a038bec86e240b43b8db8c8119debd75d7 b/tests/fuzz/dns-corpus/551813a038bec86e240b43b8db8c8119debd75d7 new file mode 100644 index 00000000..2593bf76 --- /dev/null +++ b/tests/fuzz/dns-corpus/551813a038bec86e240b43b8db8c8119debd75d7 @@ -0,0 +1 @@ +À \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/57b3d33d0a953abac9ec63a58f79f38d6de2c336 b/tests/fuzz/dns-corpus/57b3d33d0a953abac9ec63a58f79f38d6de2c336 new file mode 100644 index 00000000..ef91ea98 Binary files /dev/null and b/tests/fuzz/dns-corpus/57b3d33d0a953abac9ec63a58f79f38d6de2c336 differ diff --git a/tests/fuzz/dns-corpus/5827001c056c9fdfbeccec3c4b214488cbf567c7 b/tests/fuzz/dns-corpus/5827001c056c9fdfbeccec3c4b214488cbf567c7 new file mode 100644 index 00000000..766f49ad Binary files /dev/null and b/tests/fuzz/dns-corpus/5827001c056c9fdfbeccec3c4b214488cbf567c7 differ diff --git a/tests/fuzz/dns-corpus/5980f8e77ff320aba4221dabb4e23218f1730a9d b/tests/fuzz/dns-corpus/5980f8e77ff320aba4221dabb4e23218f1730a9d new file mode 100644 index 00000000..ddb6bb93 --- /dev/null +++ b/tests/fuzz/dns-corpus/5980f8e77ff320aba4221dabb4e23218f1730a9d @@ -0,0 +1 @@ +_] \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/59a43ac5f29851ded3296a5ee6a8dd55f0a9caa0 b/tests/fuzz/dns-corpus/59a43ac5f29851ded3296a5ee6a8dd55f0a9caa0 new file mode 100644 index 00000000..24ac4324 Binary files /dev/null and b/tests/fuzz/dns-corpus/59a43ac5f29851ded3296a5ee6a8dd55f0a9caa0 differ diff --git a/tests/fuzz/dns-corpus/59ef36609df8c4f1d116191659ae5a4772fed346 b/tests/fuzz/dns-corpus/59ef36609df8c4f1d116191659ae5a4772fed346 new file mode 100644 index 00000000..b0921b79 Binary files /dev/null and b/tests/fuzz/dns-corpus/59ef36609df8c4f1d116191659ae5a4772fed346 differ diff --git a/tests/fuzz/dns-corpus/5bb7e364d03fe94e0e98f4344776cfe96d2fda7e b/tests/fuzz/dns-corpus/5bb7e364d03fe94e0e98f4344776cfe96d2fda7e new file mode 100644 index 00000000..4883fa7e Binary files /dev/null and b/tests/fuzz/dns-corpus/5bb7e364d03fe94e0e98f4344776cfe96d2fda7e differ diff --git a/tests/fuzz/dns-corpus/5c1e31789ceb92d68d87aa2a77fbbb3fa95b85d1 b/tests/fuzz/dns-corpus/5c1e31789ceb92d68d87aa2a77fbbb3fa95b85d1 new file mode 100644 index 00000000..c26a21a4 Binary files /dev/null and b/tests/fuzz/dns-corpus/5c1e31789ceb92d68d87aa2a77fbbb3fa95b85d1 differ diff --git a/tests/fuzz/dns-corpus/5c7dda5d2b869ca2c7ca20882429f9b927659d46 b/tests/fuzz/dns-corpus/5c7dda5d2b869ca2c7ca20882429f9b927659d46 new file mode 100644 index 00000000..a7420377 Binary files /dev/null and b/tests/fuzz/dns-corpus/5c7dda5d2b869ca2c7ca20882429f9b927659d46 differ diff --git a/tests/fuzz/dns-corpus/5ddb2113394f9d52469203b9c2a8ef057731d291 b/tests/fuzz/dns-corpus/5ddb2113394f9d52469203b9c2a8ef057731d291 new file mode 100644 index 00000000..e3a85930 Binary files /dev/null and b/tests/fuzz/dns-corpus/5ddb2113394f9d52469203b9c2a8ef057731d291 differ diff --git a/tests/fuzz/dns-corpus/5e70893f029b0f826298e5beebaa5a074944421c b/tests/fuzz/dns-corpus/5e70893f029b0f826298e5beebaa5a074944421c new file mode 100644 index 00000000..86bf8ba8 Binary files /dev/null and b/tests/fuzz/dns-corpus/5e70893f029b0f826298e5beebaa5a074944421c differ diff --git a/tests/fuzz/dns-corpus/5e7c9ac0a1d9a6882cc18f266e08b0b57a50f030 b/tests/fuzz/dns-corpus/5e7c9ac0a1d9a6882cc18f266e08b0b57a50f030 new file mode 100644 index 00000000..9f5d44f8 Binary files /dev/null and b/tests/fuzz/dns-corpus/5e7c9ac0a1d9a6882cc18f266e08b0b57a50f030 differ diff --git a/tests/fuzz/dns-corpus/5f60a8da2d2bb3bda481d0a1882e8e296949d0e4 b/tests/fuzz/dns-corpus/5f60a8da2d2bb3bda481d0a1882e8e296949d0e4 new file mode 100644 index 00000000..5884f3eb Binary files /dev/null and b/tests/fuzz/dns-corpus/5f60a8da2d2bb3bda481d0a1882e8e296949d0e4 differ diff --git a/tests/fuzz/dns-corpus/6303d30f649cfbdc6ebce818e5852b5d06f38e91 b/tests/fuzz/dns-corpus/6303d30f649cfbdc6ebce818e5852b5d06f38e91 new file mode 100644 index 00000000..3829bf88 Binary files /dev/null and b/tests/fuzz/dns-corpus/6303d30f649cfbdc6ebce818e5852b5d06f38e91 differ diff --git a/tests/fuzz/dns-corpus/65e2587041fff833f73367ebb8b96f2ec4a4758d b/tests/fuzz/dns-corpus/65e2587041fff833f73367ebb8b96f2ec4a4758d new file mode 100644 index 00000000..d169a84e Binary files /dev/null and b/tests/fuzz/dns-corpus/65e2587041fff833f73367ebb8b96f2ec4a4758d differ diff --git a/tests/fuzz/dns-corpus/66bfec37d0c773fe40a17002ce3facf26da8ba7d b/tests/fuzz/dns-corpus/66bfec37d0c773fe40a17002ce3facf26da8ba7d new file mode 100644 index 00000000..4e420df1 Binary files /dev/null and b/tests/fuzz/dns-corpus/66bfec37d0c773fe40a17002ce3facf26da8ba7d differ diff --git a/tests/fuzz/dns-corpus/66ca5c3c974d7bc74f18be2826e5bd5ae056b64b b/tests/fuzz/dns-corpus/66ca5c3c974d7bc74f18be2826e5bd5ae056b64b new file mode 100644 index 00000000..4744e3a4 Binary files /dev/null and b/tests/fuzz/dns-corpus/66ca5c3c974d7bc74f18be2826e5bd5ae056b64b differ diff --git a/tests/fuzz/dns-corpus/674be032fea169e5defddacd902079a2318e6155 b/tests/fuzz/dns-corpus/674be032fea169e5defddacd902079a2318e6155 new file mode 100644 index 00000000..482681d5 Binary files /dev/null and b/tests/fuzz/dns-corpus/674be032fea169e5defddacd902079a2318e6155 differ diff --git a/tests/fuzz/dns-corpus/67df1a85fb936605e5508a0e955f35366d5918c9 b/tests/fuzz/dns-corpus/67df1a85fb936605e5508a0e955f35366d5918c9 new file mode 100644 index 00000000..f109982e Binary files /dev/null and b/tests/fuzz/dns-corpus/67df1a85fb936605e5508a0e955f35366d5918c9 differ diff --git a/tests/fuzz/dns-corpus/68108d42dc3fb33305ed05d3c2c776054b98d474 b/tests/fuzz/dns-corpus/68108d42dc3fb33305ed05d3c2c776054b98d474 new file mode 100644 index 00000000..bc54361f Binary files /dev/null and b/tests/fuzz/dns-corpus/68108d42dc3fb33305ed05d3c2c776054b98d474 differ diff --git a/tests/fuzz/dns-corpus/6817738dee8844bd18a06db679676dcd5dcb522f b/tests/fuzz/dns-corpus/6817738dee8844bd18a06db679676dcd5dcb522f new file mode 100644 index 00000000..b7bc8855 Binary files /dev/null and b/tests/fuzz/dns-corpus/6817738dee8844bd18a06db679676dcd5dcb522f differ diff --git a/tests/fuzz/dns-corpus/6850cc827dbe063e2812c633fcec974d9ee85a0e b/tests/fuzz/dns-corpus/6850cc827dbe063e2812c633fcec974d9ee85a0e new file mode 100644 index 00000000..2d7ff905 Binary files /dev/null and b/tests/fuzz/dns-corpus/6850cc827dbe063e2812c633fcec974d9ee85a0e differ diff --git a/tests/fuzz/dns-corpus/6951a4e98f45d4e147fe74f88c424b64cbf1d6e0 b/tests/fuzz/dns-corpus/6951a4e98f45d4e147fe74f88c424b64cbf1d6e0 new file mode 100644 index 00000000..7bd4d4ce Binary files /dev/null and b/tests/fuzz/dns-corpus/6951a4e98f45d4e147fe74f88c424b64cbf1d6e0 differ diff --git a/tests/fuzz/dns-corpus/69d3c805efff5f106e737e7e607ed06e4dee5160 b/tests/fuzz/dns-corpus/69d3c805efff5f106e737e7e607ed06e4dee5160 new file mode 100644 index 00000000..6fb2ba1b Binary files /dev/null and b/tests/fuzz/dns-corpus/69d3c805efff5f106e737e7e607ed06e4dee5160 differ diff --git a/tests/fuzz/dns-corpus/6a008d95bd18090fdab93bcad3956d1e3f57a409 b/tests/fuzz/dns-corpus/6a008d95bd18090fdab93bcad3956d1e3f57a409 new file mode 100644 index 00000000..0ca749f5 Binary files /dev/null and b/tests/fuzz/dns-corpus/6a008d95bd18090fdab93bcad3956d1e3f57a409 differ diff --git a/tests/fuzz/dns-corpus/6acd4153cef83304567e6ebd43260f8124b682be b/tests/fuzz/dns-corpus/6acd4153cef83304567e6ebd43260f8124b682be new file mode 100644 index 00000000..7f0cded7 Binary files /dev/null and b/tests/fuzz/dns-corpus/6acd4153cef83304567e6ebd43260f8124b682be differ diff --git a/tests/fuzz/dns-corpus/6bf9165037832d222aa8b59087e6d3963ab125db b/tests/fuzz/dns-corpus/6bf9165037832d222aa8b59087e6d3963ab125db new file mode 100644 index 00000000..47c65c39 Binary files /dev/null and b/tests/fuzz/dns-corpus/6bf9165037832d222aa8b59087e6d3963ab125db differ diff --git a/tests/fuzz/dns-corpus/6d016e29b8a6a4f711a03d13ac261a3a630b8361 b/tests/fuzz/dns-corpus/6d016e29b8a6a4f711a03d13ac261a3a630b8361 new file mode 100644 index 00000000..300d83ed Binary files /dev/null and b/tests/fuzz/dns-corpus/6d016e29b8a6a4f711a03d13ac261a3a630b8361 differ diff --git a/tests/fuzz/dns-corpus/6d4ab2d108123c62bc92f9ac43e2162a58a4293c b/tests/fuzz/dns-corpus/6d4ab2d108123c62bc92f9ac43e2162a58a4293c new file mode 100644 index 00000000..06c53c0e Binary files /dev/null and b/tests/fuzz/dns-corpus/6d4ab2d108123c62bc92f9ac43e2162a58a4293c differ diff --git a/tests/fuzz/dns-corpus/6ed6d1ca9263d46c619010dcfa94cee44f71fb59 b/tests/fuzz/dns-corpus/6ed6d1ca9263d46c619010dcfa94cee44f71fb59 new file mode 100644 index 00000000..536ab7a3 Binary files /dev/null and b/tests/fuzz/dns-corpus/6ed6d1ca9263d46c619010dcfa94cee44f71fb59 differ diff --git a/tests/fuzz/dns-corpus/6f3fcad4b8b187afa5a81c50fcf16c45812d628d b/tests/fuzz/dns-corpus/6f3fcad4b8b187afa5a81c50fcf16c45812d628d new file mode 100644 index 00000000..9b60f3d4 Binary files /dev/null and b/tests/fuzz/dns-corpus/6f3fcad4b8b187afa5a81c50fcf16c45812d628d differ diff --git a/tests/fuzz/dns-corpus/7029cf28bddb8d39c50632d10a88b047205ed8c0 b/tests/fuzz/dns-corpus/7029cf28bddb8d39c50632d10a88b047205ed8c0 new file mode 100644 index 00000000..7bf91d5c Binary files /dev/null and b/tests/fuzz/dns-corpus/7029cf28bddb8d39c50632d10a88b047205ed8c0 differ diff --git a/tests/fuzz/dns-corpus/713080ce2865caa58645db9cc67546269523bc78 b/tests/fuzz/dns-corpus/713080ce2865caa58645db9cc67546269523bc78 new file mode 100644 index 00000000..39e822a3 Binary files /dev/null and b/tests/fuzz/dns-corpus/713080ce2865caa58645db9cc67546269523bc78 differ diff --git a/tests/fuzz/dns-corpus/72adf5e24f79a74f4dd8a513eb4eab986e4b3b6a b/tests/fuzz/dns-corpus/72adf5e24f79a74f4dd8a513eb4eab986e4b3b6a new file mode 100644 index 00000000..40ea0e20 Binary files /dev/null and b/tests/fuzz/dns-corpus/72adf5e24f79a74f4dd8a513eb4eab986e4b3b6a differ diff --git a/tests/fuzz/dns-corpus/73bce018710c615acab6762db7e21d8732241978 b/tests/fuzz/dns-corpus/73bce018710c615acab6762db7e21d8732241978 new file mode 100644 index 00000000..a1bfa460 Binary files /dev/null and b/tests/fuzz/dns-corpus/73bce018710c615acab6762db7e21d8732241978 differ diff --git a/tests/fuzz/dns-corpus/7747525cf1cd9c1b91af483e0bbc0c4b5efcda01 b/tests/fuzz/dns-corpus/7747525cf1cd9c1b91af483e0bbc0c4b5efcda01 new file mode 100644 index 00000000..a5a40b88 Binary files /dev/null and b/tests/fuzz/dns-corpus/7747525cf1cd9c1b91af483e0bbc0c4b5efcda01 differ diff --git a/tests/fuzz/dns-corpus/77b1f432121d31c0d86af77a060f5c3944bacf63 b/tests/fuzz/dns-corpus/77b1f432121d31c0d86af77a060f5c3944bacf63 new file mode 100644 index 00000000..9c0d1196 Binary files /dev/null and b/tests/fuzz/dns-corpus/77b1f432121d31c0d86af77a060f5c3944bacf63 differ diff --git a/tests/fuzz/dns-corpus/7a05871957657160ad8382ad73fbd3955db44e49 b/tests/fuzz/dns-corpus/7a05871957657160ad8382ad73fbd3955db44e49 new file mode 100644 index 00000000..8cf83a14 Binary files /dev/null and b/tests/fuzz/dns-corpus/7a05871957657160ad8382ad73fbd3955db44e49 differ diff --git a/tests/fuzz/dns-corpus/7c19b63371b35cad4c1c43d9ba7df6fc1ded7201 b/tests/fuzz/dns-corpus/7c19b63371b35cad4c1c43d9ba7df6fc1ded7201 new file mode 100644 index 00000000..6144b76f --- /dev/null +++ b/tests/fuzz/dns-corpus/7c19b63371b35cad4c1c43d9ba7df6fc1ded7201 @@ -0,0 +1 @@ +�À \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/7c371449d243a95162017a1a65d9f9cf058c3ec7 b/tests/fuzz/dns-corpus/7c371449d243a95162017a1a65d9f9cf058c3ec7 new file mode 100644 index 00000000..60b84045 --- /dev/null +++ b/tests/fuzz/dns-corpus/7c371449d243a95162017a1a65d9f9cf058c3ec7 @@ -0,0 +1 @@ +������������������������������� \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/7d7550756fd7ee81fd015ec4f15dde27d5605ddf b/tests/fuzz/dns-corpus/7d7550756fd7ee81fd015ec4f15dde27d5605ddf new file mode 100644 index 00000000..f3b1e76b Binary files /dev/null and b/tests/fuzz/dns-corpus/7d7550756fd7ee81fd015ec4f15dde27d5605ddf differ diff --git a/tests/fuzz/dns-corpus/84064830dbb22be8cecd07adeb2b576866b19c79 b/tests/fuzz/dns-corpus/84064830dbb22be8cecd07adeb2b576866b19c79 new file mode 100644 index 00000000..41353890 Binary files /dev/null and b/tests/fuzz/dns-corpus/84064830dbb22be8cecd07adeb2b576866b19c79 differ diff --git a/tests/fuzz/dns-corpus/845b0a4f74111d04681448278ade7adbacb9a6d6 b/tests/fuzz/dns-corpus/845b0a4f74111d04681448278ade7adbacb9a6d6 new file mode 100644 index 00000000..7753d886 Binary files /dev/null and b/tests/fuzz/dns-corpus/845b0a4f74111d04681448278ade7adbacb9a6d6 differ diff --git a/tests/fuzz/dns-corpus/866aa47950fcb272aff86996edd9e54bf44fafba b/tests/fuzz/dns-corpus/866aa47950fcb272aff86996edd9e54bf44fafba new file mode 100644 index 00000000..2fe9c153 --- /dev/null +++ b/tests/fuzz/dns-corpus/866aa47950fcb272aff86996edd9e54bf44fafba @@ -0,0 +1 @@ +ÿÿÿÑÿÿÑÿÑÑÿÿÿÿÑÿe \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/87f7d848e3d1efd406262bbaac50a6318a7d45ce b/tests/fuzz/dns-corpus/87f7d848e3d1efd406262bbaac50a6318a7d45ce new file mode 100644 index 00000000..f2a822b1 Binary files /dev/null and b/tests/fuzz/dns-corpus/87f7d848e3d1efd406262bbaac50a6318a7d45ce differ diff --git a/tests/fuzz/dns-corpus/89f0403865a685eab3831c406205bbfe40f946d4 b/tests/fuzz/dns-corpus/89f0403865a685eab3831c406205bbfe40f946d4 new file mode 100644 index 00000000..c6e0f350 --- /dev/null +++ b/tests/fuzz/dns-corpus/89f0403865a685eab3831c406205bbfe40f946d4 @@ -0,0 +1 @@ +$$ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/8cece9d808f46dabfa5c6055a82f20082d51440d b/tests/fuzz/dns-corpus/8cece9d808f46dabfa5c6055a82f20082d51440d new file mode 100644 index 00000000..a9ed54d7 Binary files /dev/null and b/tests/fuzz/dns-corpus/8cece9d808f46dabfa5c6055a82f20082d51440d differ diff --git a/tests/fuzz/dns-corpus/8e825a20197e5c3f5c225ae5332ff72053f1ab67 b/tests/fuzz/dns-corpus/8e825a20197e5c3f5c225ae5332ff72053f1ab67 new file mode 100644 index 00000000..77776515 --- /dev/null +++ b/tests/fuzz/dns-corpus/8e825a20197e5c3f5c225ae5332ff72053f1ab67 @@ -0,0 +1 @@ +ÀÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/8f160fb4aeaf0f2aee4c35fffbf91cc57e7065d9 b/tests/fuzz/dns-corpus/8f160fb4aeaf0f2aee4c35fffbf91cc57e7065d9 new file mode 100644 index 00000000..9190491c Binary files /dev/null and b/tests/fuzz/dns-corpus/8f160fb4aeaf0f2aee4c35fffbf91cc57e7065d9 differ diff --git a/tests/fuzz/dns-corpus/8f5474c5d095ebde097bff54c0af1d8794db4536 b/tests/fuzz/dns-corpus/8f5474c5d095ebde097bff54c0af1d8794db4536 new file mode 100644 index 00000000..8b4cf3f3 Binary files /dev/null and b/tests/fuzz/dns-corpus/8f5474c5d095ebde097bff54c0af1d8794db4536 differ diff --git a/tests/fuzz/dns-corpus/8f92abf2f1dcb974956285d869e9861c5f9d8bf3 b/tests/fuzz/dns-corpus/8f92abf2f1dcb974956285d869e9861c5f9d8bf3 new file mode 100644 index 00000000..b8f22ece Binary files /dev/null and b/tests/fuzz/dns-corpus/8f92abf2f1dcb974956285d869e9861c5f9d8bf3 differ diff --git a/tests/fuzz/dns-corpus/9010b258d6da931a6c846322440015a38563af11 b/tests/fuzz/dns-corpus/9010b258d6da931a6c846322440015a38563af11 new file mode 100644 index 00000000..7c0c82fd Binary files /dev/null and b/tests/fuzz/dns-corpus/9010b258d6da931a6c846322440015a38563af11 differ diff --git a/tests/fuzz/dns-corpus/903c13904fa0b973533f09f81e497453d114afc7 b/tests/fuzz/dns-corpus/903c13904fa0b973533f09f81e497453d114afc7 new file mode 100644 index 00000000..084fc254 --- /dev/null +++ b/tests/fuzz/dns-corpus/903c13904fa0b973533f09f81e497453d114afc7 @@ -0,0 +1 @@ +ÀÀÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/9069ca78e7450a285173431b3e52c5c25299e473 b/tests/fuzz/dns-corpus/9069ca78e7450a285173431b3e52c5c25299e473 new file mode 100644 index 00000000..593f4708 Binary files /dev/null and b/tests/fuzz/dns-corpus/9069ca78e7450a285173431b3e52c5c25299e473 differ diff --git a/tests/fuzz/dns-corpus/90ebdcd9256d081c052854eefcba5131155214cb b/tests/fuzz/dns-corpus/90ebdcd9256d081c052854eefcba5131155214cb new file mode 100644 index 00000000..49c37eaf Binary files /dev/null and b/tests/fuzz/dns-corpus/90ebdcd9256d081c052854eefcba5131155214cb differ diff --git a/tests/fuzz/dns-corpus/91d9b9bbd23ae177ebed4d5b2a97e1139306e629 b/tests/fuzz/dns-corpus/91d9b9bbd23ae177ebed4d5b2a97e1139306e629 new file mode 100644 index 00000000..0fc74b6f --- /dev/null +++ b/tests/fuzz/dns-corpus/91d9b9bbd23ae177ebed4d5b2a97e1139306e629 @@ -0,0 +1 @@ +/A \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/92b697de44a4db107ea9c2e5e2c48784091e8542 b/tests/fuzz/dns-corpus/92b697de44a4db107ea9c2e5e2c48784091e8542 new file mode 100644 index 00000000..c6a25f7f Binary files /dev/null and b/tests/fuzz/dns-corpus/92b697de44a4db107ea9c2e5e2c48784091e8542 differ diff --git a/tests/fuzz/dns-corpus/92e960a181304ff8e7ea5aad500a4a05af647d2b b/tests/fuzz/dns-corpus/92e960a181304ff8e7ea5aad500a4a05af647d2b new file mode 100644 index 00000000..7ae411c0 --- /dev/null +++ b/tests/fuzz/dns-corpus/92e960a181304ff8e7ea5aad500a4a05af647d2b @@ -0,0 +1,24 @@ + +S + + + + + + + + + + + + + + + + + + + +` +û +ÀÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/92f68fa015ea5e2d35b532b75f3113327f4757ae b/tests/fuzz/dns-corpus/92f68fa015ea5e2d35b532b75f3113327f4757ae new file mode 100644 index 00000000..02060779 Binary files /dev/null and b/tests/fuzz/dns-corpus/92f68fa015ea5e2d35b532b75f3113327f4757ae differ diff --git a/tests/fuzz/dns-corpus/93618150426380681421d8cfdacbbf22511f4d2a b/tests/fuzz/dns-corpus/93618150426380681421d8cfdacbbf22511f4d2a new file mode 100644 index 00000000..937c301b Binary files /dev/null and b/tests/fuzz/dns-corpus/93618150426380681421d8cfdacbbf22511f4d2a differ diff --git a/tests/fuzz/dns-corpus/9368ecb56d044baa768ce296532ba79fe3b7ac5f b/tests/fuzz/dns-corpus/9368ecb56d044baa768ce296532ba79fe3b7ac5f new file mode 100644 index 00000000..db17e340 Binary files /dev/null and b/tests/fuzz/dns-corpus/9368ecb56d044baa768ce296532ba79fe3b7ac5f differ diff --git a/tests/fuzz/dns-corpus/949c087fa6cea334361d3aa142b0fad0b1a0be43 b/tests/fuzz/dns-corpus/949c087fa6cea334361d3aa142b0fad0b1a0be43 new file mode 100644 index 00000000..c795ef2b Binary files /dev/null and b/tests/fuzz/dns-corpus/949c087fa6cea334361d3aa142b0fad0b1a0be43 differ diff --git a/tests/fuzz/dns-corpus/95d06784356054c00ae9fef99ac1ba6768de35fa b/tests/fuzz/dns-corpus/95d06784356054c00ae9fef99ac1ba6768de35fa new file mode 100644 index 00000000..e06b1a4e --- /dev/null +++ b/tests/fuzz/dns-corpus/95d06784356054c00ae9fef99ac1ba6768de35fa @@ -0,0 +1 @@ +<<<#<<<<<<<<<8 \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/989ed13e667e7158fa7304ee75b028265b76ef1a b/tests/fuzz/dns-corpus/989ed13e667e7158fa7304ee75b028265b76ef1a new file mode 100644 index 00000000..94d04789 Binary files /dev/null and b/tests/fuzz/dns-corpus/989ed13e667e7158fa7304ee75b028265b76ef1a differ diff --git a/tests/fuzz/dns-corpus/9996775d939dffae8700364f008789b70fa88fc8 b/tests/fuzz/dns-corpus/9996775d939dffae8700364f008789b70fa88fc8 new file mode 100644 index 00000000..5f446f4b Binary files /dev/null and b/tests/fuzz/dns-corpus/9996775d939dffae8700364f008789b70fa88fc8 differ diff --git a/tests/fuzz/dns-corpus/99e0e32a7b0ae951b73eb4e926309da3eda30a27 b/tests/fuzz/dns-corpus/99e0e32a7b0ae951b73eb4e926309da3eda30a27 new file mode 100644 index 00000000..25cda5ce --- /dev/null +++ b/tests/fuzz/dns-corpus/99e0e32a7b0ae951b73eb4e926309da3eda30a27 @@ -0,0 +1 @@ +`` \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/9bc545d430f98b5ad9875106c6e0d9123af9d1a8 b/tests/fuzz/dns-corpus/9bc545d430f98b5ad9875106c6e0d9123af9d1a8 new file mode 100644 index 00000000..c17332b6 Binary files /dev/null and b/tests/fuzz/dns-corpus/9bc545d430f98b5ad9875106c6e0d9123af9d1a8 differ diff --git a/tests/fuzz/dns-corpus/9cead994fd0edbab02a5bd6cd1f8905db773d543 b/tests/fuzz/dns-corpus/9cead994fd0edbab02a5bd6cd1f8905db773d543 new file mode 100644 index 00000000..ef9e634e Binary files /dev/null and b/tests/fuzz/dns-corpus/9cead994fd0edbab02a5bd6cd1f8905db773d543 differ diff --git a/tests/fuzz/dns-corpus/9e64e01898498b506c8427666fea6fb0dec6f5b2 b/tests/fuzz/dns-corpus/9e64e01898498b506c8427666fea6fb0dec6f5b2 new file mode 100644 index 00000000..9ee9bf42 Binary files /dev/null and b/tests/fuzz/dns-corpus/9e64e01898498b506c8427666fea6fb0dec6f5b2 differ diff --git a/tests/fuzz/dns-corpus/a0cccd8d7c26d06f25083fa6e2637d734d1f0f8b b/tests/fuzz/dns-corpus/a0cccd8d7c26d06f25083fa6e2637d734d1f0f8b new file mode 100644 index 00000000..36affafc Binary files /dev/null and b/tests/fuzz/dns-corpus/a0cccd8d7c26d06f25083fa6e2637d734d1f0f8b differ diff --git a/tests/fuzz/dns-corpus/a1dec82218861bc3fdbc49db1162a038989d679c b/tests/fuzz/dns-corpus/a1dec82218861bc3fdbc49db1162a038989d679c new file mode 100644 index 00000000..2679049f Binary files /dev/null and b/tests/fuzz/dns-corpus/a1dec82218861bc3fdbc49db1162a038989d679c differ diff --git a/tests/fuzz/dns-corpus/a240e7291da67f422f1335bf95c6a42ba0560307 b/tests/fuzz/dns-corpus/a240e7291da67f422f1335bf95c6a42ba0560307 new file mode 100644 index 00000000..4a1eef3b Binary files /dev/null and b/tests/fuzz/dns-corpus/a240e7291da67f422f1335bf95c6a42ba0560307 differ diff --git a/tests/fuzz/dns-corpus/a4d74774e71d9dc55029a5272c60d0855ac78216 b/tests/fuzz/dns-corpus/a4d74774e71d9dc55029a5272c60d0855ac78216 new file mode 100644 index 00000000..8e5a9a26 Binary files /dev/null and b/tests/fuzz/dns-corpus/a4d74774e71d9dc55029a5272c60d0855ac78216 differ diff --git a/tests/fuzz/dns-corpus/a8595afccfbd271481785b83b3190f9f3026906f b/tests/fuzz/dns-corpus/a8595afccfbd271481785b83b3190f9f3026906f new file mode 100644 index 00000000..223aa71f Binary files /dev/null and b/tests/fuzz/dns-corpus/a8595afccfbd271481785b83b3190f9f3026906f differ diff --git a/tests/fuzz/dns-corpus/a8904edfc06e1d3290eb1979e3b5879b635e50b4 b/tests/fuzz/dns-corpus/a8904edfc06e1d3290eb1979e3b5879b635e50b4 new file mode 100644 index 00000000..a42a19fb Binary files /dev/null and b/tests/fuzz/dns-corpus/a8904edfc06e1d3290eb1979e3b5879b635e50b4 differ diff --git a/tests/fuzz/dns-corpus/a8ab985fefa5c7123d21bffc210481e1e3767aa4 b/tests/fuzz/dns-corpus/a8ab985fefa5c7123d21bffc210481e1e3767aa4 new file mode 100644 index 00000000..52b026dd Binary files /dev/null and b/tests/fuzz/dns-corpus/a8ab985fefa5c7123d21bffc210481e1e3767aa4 differ diff --git a/tests/fuzz/dns-corpus/a9554d4a3c6f25ce9ddfc4758c76953f63225cdb b/tests/fuzz/dns-corpus/a9554d4a3c6f25ce9ddfc4758c76953f63225cdb new file mode 100644 index 00000000..7593bd9e Binary files /dev/null and b/tests/fuzz/dns-corpus/a9554d4a3c6f25ce9ddfc4758c76953f63225cdb differ diff --git a/tests/fuzz/dns-corpus/abd547e11e412bd9cb8df9bd4539de3caa77c048 b/tests/fuzz/dns-corpus/abd547e11e412bd9cb8df9bd4539de3caa77c048 new file mode 100644 index 00000000..d4f4a7bc --- /dev/null +++ b/tests/fuzz/dns-corpus/abd547e11e412bd9cb8df9bd4539de3caa77c048 @@ -0,0 +1 @@ +À2w...........2.....$.................+................. \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/ad155f8bb95b555621fe6e45f926812471352a95 b/tests/fuzz/dns-corpus/ad155f8bb95b555621fe6e45f926812471352a95 new file mode 100644 index 00000000..0802e956 --- /dev/null +++ b/tests/fuzz/dns-corpus/ad155f8bb95b555621fe6e45f926812471352a95 @@ -0,0 +1 @@ +[wx@ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/b08e5a574787d762dad68ba70274cc646d247758 b/tests/fuzz/dns-corpus/b08e5a574787d762dad68ba70274cc646d247758 new file mode 100644 index 00000000..df33fdef --- /dev/null +++ b/tests/fuzz/dns-corpus/b08e5a574787d762dad68ba70274cc646d247758 @@ -0,0 +1 @@ +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/b3cfffa086dab5f1245dd09a518e1511bc97c1e5 b/tests/fuzz/dns-corpus/b3cfffa086dab5f1245dd09a518e1511bc97c1e5 new file mode 100644 index 00000000..d86a4b2f Binary files /dev/null and b/tests/fuzz/dns-corpus/b3cfffa086dab5f1245dd09a518e1511bc97c1e5 differ diff --git a/tests/fuzz/dns-corpus/b4be53eeb1b006477d40e4889a5fb503bba7155d b/tests/fuzz/dns-corpus/b4be53eeb1b006477d40e4889a5fb503bba7155d new file mode 100644 index 00000000..ec0a5424 Binary files /dev/null and b/tests/fuzz/dns-corpus/b4be53eeb1b006477d40e4889a5fb503bba7155d differ diff --git a/tests/fuzz/dns-corpus/b4d9c0edc1f404b811f904dfc08ba10f521a47b9 b/tests/fuzz/dns-corpus/b4d9c0edc1f404b811f904dfc08ba10f521a47b9 new file mode 100644 index 00000000..63604e90 Binary files /dev/null and b/tests/fuzz/dns-corpus/b4d9c0edc1f404b811f904dfc08ba10f521a47b9 differ diff --git a/tests/fuzz/dns-corpus/b52e7809912dc1a4b5620de3192abb734517ab95 b/tests/fuzz/dns-corpus/b52e7809912dc1a4b5620de3192abb734517ab95 new file mode 100644 index 00000000..c3c7f55f Binary files /dev/null and b/tests/fuzz/dns-corpus/b52e7809912dc1a4b5620de3192abb734517ab95 differ diff --git a/tests/fuzz/dns-corpus/b754cf773ab1d150bd29a5c2e65d9202341bc70c b/tests/fuzz/dns-corpus/b754cf773ab1d150bd29a5c2e65d9202341bc70c new file mode 100644 index 00000000..0443f151 Binary files /dev/null and b/tests/fuzz/dns-corpus/b754cf773ab1d150bd29a5c2e65d9202341bc70c differ diff --git a/tests/fuzz/dns-corpus/b87d89946ec4a5fae85d27a100a161fa90e3d327 b/tests/fuzz/dns-corpus/b87d89946ec4a5fae85d27a100a161fa90e3d327 new file mode 100644 index 00000000..b6c0215d Binary files /dev/null and b/tests/fuzz/dns-corpus/b87d89946ec4a5fae85d27a100a161fa90e3d327 differ diff --git a/tests/fuzz/dns-corpus/b8e761372d4400e5254dafe1c059c481245d1c8b b/tests/fuzz/dns-corpus/b8e761372d4400e5254dafe1c059c481245d1c8b new file mode 100644 index 00000000..40ecdcf0 Binary files /dev/null and b/tests/fuzz/dns-corpus/b8e761372d4400e5254dafe1c059c481245d1c8b differ diff --git a/tests/fuzz/dns-corpus/b9c8ff005dc5f05c1a505d2859b9e58969de1c4c b/tests/fuzz/dns-corpus/b9c8ff005dc5f05c1a505d2859b9e58969de1c4c new file mode 100644 index 00000000..b05e35fb Binary files /dev/null and b/tests/fuzz/dns-corpus/b9c8ff005dc5f05c1a505d2859b9e58969de1c4c differ diff --git a/tests/fuzz/dns-corpus/bcbada26e1a125322f39de0b731314292ae9eef4 b/tests/fuzz/dns-corpus/bcbada26e1a125322f39de0b731314292ae9eef4 new file mode 100644 index 00000000..958d8c9a Binary files /dev/null and b/tests/fuzz/dns-corpus/bcbada26e1a125322f39de0b731314292ae9eef4 differ diff --git a/tests/fuzz/dns-corpus/bf2b6d6cdc2e263698a275185e994ade20970977 b/tests/fuzz/dns-corpus/bf2b6d6cdc2e263698a275185e994ade20970977 new file mode 100644 index 00000000..47d916bd Binary files /dev/null and b/tests/fuzz/dns-corpus/bf2b6d6cdc2e263698a275185e994ade20970977 differ diff --git a/tests/fuzz/dns-corpus/bf8b4530d8d246dd74ac53a13471bba17941dff7 b/tests/fuzz/dns-corpus/bf8b4530d8d246dd74ac53a13471bba17941dff7 new file mode 100644 index 00000000..6b2aaa76 --- /dev/null +++ b/tests/fuzz/dns-corpus/bf8b4530d8d246dd74ac53a13471bba17941dff7 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/c0702dd8306a22efc40e0bbc3a43ab6f25004ac7 b/tests/fuzz/dns-corpus/c0702dd8306a22efc40e0bbc3a43ab6f25004ac7 new file mode 100644 index 00000000..595275fd Binary files /dev/null and b/tests/fuzz/dns-corpus/c0702dd8306a22efc40e0bbc3a43ab6f25004ac7 differ diff --git a/tests/fuzz/dns-corpus/c0c3eaa3e8b508c9f91b7d24d5a24f850f62788c b/tests/fuzz/dns-corpus/c0c3eaa3e8b508c9f91b7d24d5a24f850f62788c new file mode 100644 index 00000000..fd1445ac Binary files /dev/null and b/tests/fuzz/dns-corpus/c0c3eaa3e8b508c9f91b7d24d5a24f850f62788c differ diff --git a/tests/fuzz/dns-corpus/c0c866f1b289f1044317e537ad74066230790c88 b/tests/fuzz/dns-corpus/c0c866f1b289f1044317e537ad74066230790c88 new file mode 100644 index 00000000..4624cd55 --- /dev/null +++ b/tests/fuzz/dns-corpus/c0c866f1b289f1044317e537ad74066230790c88 @@ -0,0 +1 @@ +À?(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((ÀÀÀÀÀÀÀÀ((((((((((((((((((((((((((((((((((((( \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/c398a10cff6ce3f8b5abd073f2c6c5d7c167c6d5 b/tests/fuzz/dns-corpus/c398a10cff6ce3f8b5abd073f2c6c5d7c167c6d5 new file mode 100644 index 00000000..593edf9e --- /dev/null +++ b/tests/fuzz/dns-corpus/c398a10cff6ce3f8b5abd073f2c6c5d7c167c6d5 @@ -0,0 +1 @@ +ú \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/c4fe9aac53ba096ec252bcf1773a3f31e9378b95 b/tests/fuzz/dns-corpus/c4fe9aac53ba096ec252bcf1773a3f31e9378b95 new file mode 100644 index 00000000..6d134541 Binary files /dev/null and b/tests/fuzz/dns-corpus/c4fe9aac53ba096ec252bcf1773a3f31e9378b95 differ diff --git a/tests/fuzz/dns-corpus/c65dfc51a0a8f65b6308b155ec55359354fdc23d b/tests/fuzz/dns-corpus/c65dfc51a0a8f65b6308b155ec55359354fdc23d new file mode 100644 index 00000000..099164e4 Binary files /dev/null and b/tests/fuzz/dns-corpus/c65dfc51a0a8f65b6308b155ec55359354fdc23d differ diff --git a/tests/fuzz/dns-corpus/c71bf45ca4266270774a8a26c19484ee9d2759da b/tests/fuzz/dns-corpus/c71bf45ca4266270774a8a26c19484ee9d2759da new file mode 100644 index 00000000..d90a8b7b --- /dev/null +++ b/tests/fuzz/dns-corpus/c71bf45ca4266270774a8a26c19484ee9d2759da @@ -0,0 +1 @@ +Ò£' \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/c9d6bd19f3c6f101c338898cf2956adc9fc540fd b/tests/fuzz/dns-corpus/c9d6bd19f3c6f101c338898cf2956adc9fc540fd new file mode 100644 index 00000000..c57d36ec --- /dev/null +++ b/tests/fuzz/dns-corpus/c9d6bd19f3c6f101c338898cf2956adc9fc540fd @@ -0,0 +1 @@ +01'4'0-1 \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/cbd83236be8892e743a8e89efd0415eb7e09c208 b/tests/fuzz/dns-corpus/cbd83236be8892e743a8e89efd0415eb7e09c208 new file mode 100644 index 00000000..6bb59f19 Binary files /dev/null and b/tests/fuzz/dns-corpus/cbd83236be8892e743a8e89efd0415eb7e09c208 differ diff --git a/tests/fuzz/dns-corpus/cc429040108fc7532e6212c0574fd9f93a8e1b7d b/tests/fuzz/dns-corpus/cc429040108fc7532e6212c0574fd9f93a8e1b7d new file mode 100644 index 00000000..6dc77bce Binary files /dev/null and b/tests/fuzz/dns-corpus/cc429040108fc7532e6212c0574fd9f93a8e1b7d differ diff --git a/tests/fuzz/dns-corpus/cc92c7a3b96ca18aaf4ecf3236b0219d9ae517dd b/tests/fuzz/dns-corpus/cc92c7a3b96ca18aaf4ecf3236b0219d9ae517dd new file mode 100644 index 00000000..9b27b52f Binary files /dev/null and b/tests/fuzz/dns-corpus/cc92c7a3b96ca18aaf4ecf3236b0219d9ae517dd differ diff --git a/tests/fuzz/dns-corpus/ccb5db9584979e0da5f525caf20b482dbd14e9a4 b/tests/fuzz/dns-corpus/ccb5db9584979e0da5f525caf20b482dbd14e9a4 new file mode 100644 index 00000000..7a87a3d1 Binary files /dev/null and b/tests/fuzz/dns-corpus/ccb5db9584979e0da5f525caf20b482dbd14e9a4 differ diff --git a/tests/fuzz/dns-corpus/cefe6ac071102ca2d96f1d8101b7b2b8f003def0 b/tests/fuzz/dns-corpus/cefe6ac071102ca2d96f1d8101b7b2b8f003def0 new file mode 100644 index 00000000..e1060d00 Binary files /dev/null and b/tests/fuzz/dns-corpus/cefe6ac071102ca2d96f1d8101b7b2b8f003def0 differ diff --git a/tests/fuzz/dns-corpus/cf856029645f96664eea8961f9c2eb4fcf5fdd45 b/tests/fuzz/dns-corpus/cf856029645f96664eea8961f9c2eb4fcf5fdd45 new file mode 100644 index 00000000..0a13b738 Binary files /dev/null and b/tests/fuzz/dns-corpus/cf856029645f96664eea8961f9c2eb4fcf5fdd45 differ diff --git a/tests/fuzz/dns-corpus/d18a0d943787dd22c989b62ecf3df7678598bebb b/tests/fuzz/dns-corpus/d18a0d943787dd22c989b62ecf3df7678598bebb new file mode 100644 index 00000000..7d0fdc5a --- /dev/null +++ b/tests/fuzz/dns-corpus/d18a0d943787dd22c989b62ecf3df7678598bebb @@ -0,0 +1 @@ +/.! \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/d349943058665adfae75c64f518d3eee5d5f522f b/tests/fuzz/dns-corpus/d349943058665adfae75c64f518d3eee5d5f522f new file mode 100644 index 00000000..bd79fb65 Binary files /dev/null and b/tests/fuzz/dns-corpus/d349943058665adfae75c64f518d3eee5d5f522f differ diff --git a/tests/fuzz/dns-corpus/d6d3d0f54557988c35a52cd2ce127d1e434ba536 b/tests/fuzz/dns-corpus/d6d3d0f54557988c35a52cd2ce127d1e434ba536 new file mode 100644 index 00000000..534f77c9 Binary files /dev/null and b/tests/fuzz/dns-corpus/d6d3d0f54557988c35a52cd2ce127d1e434ba536 differ diff --git a/tests/fuzz/dns-corpus/d8f44205ba008dc18afb2994437d474d3557761b b/tests/fuzz/dns-corpus/d8f44205ba008dc18afb2994437d474d3557761b new file mode 100644 index 00000000..ad24736a Binary files /dev/null and b/tests/fuzz/dns-corpus/d8f44205ba008dc18afb2994437d474d3557761b differ diff --git a/tests/fuzz/dns-corpus/da38d797d64cd4c27ade4606614390c0859c9dfc b/tests/fuzz/dns-corpus/da38d797d64cd4c27ade4606614390c0859c9dfc new file mode 100644 index 00000000..fc1a53e6 --- /dev/null +++ b/tests/fuzz/dns-corpus/da38d797d64cd4c27ade4606614390c0859c9dfc @@ -0,0 +1 @@ +ÀÀÀÀÀÀÀÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/daa5fbdc59ebfb06a455c0292222460cae5a6c3a b/tests/fuzz/dns-corpus/daa5fbdc59ebfb06a455c0292222460cae5a6c3a new file mode 100644 index 00000000..d00dc3e2 --- /dev/null +++ b/tests/fuzz/dns-corpus/daa5fbdc59ebfb06a455c0292222460cae5a6c3a @@ -0,0 +1 @@ +ÀÀÀÀÀ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/dbb90dba04770e93f8b6b220d7a1dd192f4e44ef b/tests/fuzz/dns-corpus/dbb90dba04770e93f8b6b220d7a1dd192f4e44ef new file mode 100644 index 00000000..bd5d4ba3 --- /dev/null +++ b/tests/fuzz/dns-corpus/dbb90dba04770e93f8b6b220d7a1dd192f4e44ef @@ -0,0 +1 @@ +0''''''''EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE*EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDEEEEEEEEEEEEEEEEEEEEE \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/dc2f02791dd61c4e1b36d545e74276940c97cf31 b/tests/fuzz/dns-corpus/dc2f02791dd61c4e1b36d545e74276940c97cf31 new file mode 100644 index 00000000..33abd3bb Binary files /dev/null and b/tests/fuzz/dns-corpus/dc2f02791dd61c4e1b36d545e74276940c97cf31 differ diff --git a/tests/fuzz/dns-corpus/df43895d582b0750bcc8555126c117ba7908d737 b/tests/fuzz/dns-corpus/df43895d582b0750bcc8555126c117ba7908d737 new file mode 100644 index 00000000..ad6b9ae9 Binary files /dev/null and b/tests/fuzz/dns-corpus/df43895d582b0750bcc8555126c117ba7908d737 differ diff --git a/tests/fuzz/dns-corpus/df5d37b895bf2738b61f8bce1bb34884c91ffdb7 b/tests/fuzz/dns-corpus/df5d37b895bf2738b61f8bce1bb34884c91ffdb7 new file mode 100644 index 00000000..c60caa8d Binary files /dev/null and b/tests/fuzz/dns-corpus/df5d37b895bf2738b61f8bce1bb34884c91ffdb7 differ diff --git a/tests/fuzz/dns-corpus/e84f6980abe18c9b75cd14974382e7a22cc1835e b/tests/fuzz/dns-corpus/e84f6980abe18c9b75cd14974382e7a22cc1835e new file mode 100644 index 00000000..e3fa523d Binary files /dev/null and b/tests/fuzz/dns-corpus/e84f6980abe18c9b75cd14974382e7a22cc1835e differ diff --git a/tests/fuzz/dns-corpus/e8aa3364a26a71dd217232a8b76f28a6d877fda0 b/tests/fuzz/dns-corpus/e8aa3364a26a71dd217232a8b76f28a6d877fda0 new file mode 100644 index 00000000..20af54f1 --- /dev/null +++ b/tests/fuzz/dns-corpus/e8aa3364a26a71dd217232a8b76f28a6d877fda0 @@ -0,0 +1 @@ +EŒw£££££££ple\§o \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/e8ea96105f45d1b00eac15af6baf048dbb3750d5 b/tests/fuzz/dns-corpus/e8ea96105f45d1b00eac15af6baf048dbb3750d5 new file mode 100644 index 00000000..80555954 Binary files /dev/null and b/tests/fuzz/dns-corpus/e8ea96105f45d1b00eac15af6baf048dbb3750d5 differ diff --git a/tests/fuzz/dns-corpus/e93d59e00c18060f8f0f052e7ede057b4f9b8283 b/tests/fuzz/dns-corpus/e93d59e00c18060f8f0f052e7ede057b4f9b8283 new file mode 100644 index 00000000..6f4d6b4b Binary files /dev/null and b/tests/fuzz/dns-corpus/e93d59e00c18060f8f0f052e7ede057b4f9b8283 differ diff --git a/tests/fuzz/dns-corpus/ee1c4fb4a2611a22269ecfd7c0f6e5a5ed667133 b/tests/fuzz/dns-corpus/ee1c4fb4a2611a22269ecfd7c0f6e5a5ed667133 new file mode 100644 index 00000000..64bb68d0 --- /dev/null +++ b/tests/fuzz/dns-corpus/ee1c4fb4a2611a22269ecfd7c0f6e5a5ed667133 @@ -0,0 +1 @@ +ÀÀ À \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/ef3a9d6d7d2fd2352c1ca4ff82fcd05dd2ab95cb b/tests/fuzz/dns-corpus/ef3a9d6d7d2fd2352c1ca4ff82fcd05dd2ab95cb new file mode 100644 index 00000000..c0a31ba0 Binary files /dev/null and b/tests/fuzz/dns-corpus/ef3a9d6d7d2fd2352c1ca4ff82fcd05dd2ab95cb differ diff --git a/tests/fuzz/dns-corpus/efaa64d30b467d97d7dc44dbe849767963e9fffa b/tests/fuzz/dns-corpus/efaa64d30b467d97d7dc44dbe849767963e9fffa new file mode 100644 index 00000000..11235953 Binary files /dev/null and b/tests/fuzz/dns-corpus/efaa64d30b467d97d7dc44dbe849767963e9fffa differ diff --git a/tests/fuzz/dns-corpus/f12d432129497e842012f850fed6c5a274f9b644 b/tests/fuzz/dns-corpus/f12d432129497e842012f850fed6c5a274f9b644 new file mode 100644 index 00000000..a8a1e10a --- /dev/null +++ b/tests/fuzz/dns-corpus/f12d432129497e842012f850fed6c5a274f9b644 @@ -0,0 +1 @@ +0327;;;;;;;;;;;;;?2';;;;;;;;;;; \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/f1b40b337c3c9500a811a0f839ba1395d3f2dee7 b/tests/fuzz/dns-corpus/f1b40b337c3c9500a811a0f839ba1395d3f2dee7 new file mode 100644 index 00000000..188393a2 --- /dev/null +++ b/tests/fuzz/dns-corpus/f1b40b337c3c9500a811a0f839ba1395d3f2dee7 @@ -0,0 +1 @@ +,],0 \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/f1d8225bc09bf197e010671746eff8adb2e6690f b/tests/fuzz/dns-corpus/f1d8225bc09bf197e010671746eff8adb2e6690f new file mode 100644 index 00000000..7a29b044 Binary files /dev/null and b/tests/fuzz/dns-corpus/f1d8225bc09bf197e010671746eff8adb2e6690f differ diff --git a/tests/fuzz/dns-corpus/f280fbe3d6dfdb5d046d0265733f85354f8a0c8e b/tests/fuzz/dns-corpus/f280fbe3d6dfdb5d046d0265733f85354f8a0c8e new file mode 100644 index 00000000..84883fc9 --- /dev/null +++ b/tests/fuzz/dns-corpus/f280fbe3d6dfdb5d046d0265733f85354f8a0c8e @@ -0,0 +1 @@ +`ÿö \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/f3c338d19daee8a2a98d0a2a6faaddde641d02b6 b/tests/fuzz/dns-corpus/f3c338d19daee8a2a98d0a2a6faaddde641d02b6 new file mode 100644 index 00000000..3bc2b315 Binary files /dev/null and b/tests/fuzz/dns-corpus/f3c338d19daee8a2a98d0a2a6faaddde641d02b6 differ diff --git a/tests/fuzz/dns-corpus/f421200710cda39c6c731477f7c2eba740ee0b18 b/tests/fuzz/dns-corpus/f421200710cda39c6c731477f7c2eba740ee0b18 new file mode 100644 index 00000000..3cda35db Binary files /dev/null and b/tests/fuzz/dns-corpus/f421200710cda39c6c731477f7c2eba740ee0b18 differ diff --git a/tests/fuzz/dns-corpus/f5e60c9d34e0574ab5456eeed6958b5902344a4b b/tests/fuzz/dns-corpus/f5e60c9d34e0574ab5456eeed6958b5902344a4b new file mode 100644 index 00000000..5f95595f Binary files /dev/null and b/tests/fuzz/dns-corpus/f5e60c9d34e0574ab5456eeed6958b5902344a4b differ diff --git a/tests/fuzz/dns-corpus/f66b9def72fde1ef60d28912d5707ce1fb515496 b/tests/fuzz/dns-corpus/f66b9def72fde1ef60d28912d5707ce1fb515496 new file mode 100644 index 00000000..ac90ba26 Binary files /dev/null and b/tests/fuzz/dns-corpus/f66b9def72fde1ef60d28912d5707ce1fb515496 differ diff --git a/tests/fuzz/dns-corpus/fae28f9b63d4916e8e99a2f385cb430edab64ecb b/tests/fuzz/dns-corpus/fae28f9b63d4916e8e99a2f385cb430edab64ecb new file mode 100644 index 00000000..7f2504d1 Binary files /dev/null and b/tests/fuzz/dns-corpus/fae28f9b63d4916e8e99a2f385cb430edab64ecb differ diff --git a/tests/fuzz/dns-corpus/fb387ef20626ef7b9d704c39fed2b46fde1d129b b/tests/fuzz/dns-corpus/fb387ef20626ef7b9d704c39fed2b46fde1d129b new file mode 100644 index 00000000..21cba2ac --- /dev/null +++ b/tests/fuzz/dns-corpus/fb387ef20626ef7b9d704c39fed2b46fde1d129b @@ -0,0 +1 @@ +À \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/fb80f1cefae3223472bbaeac01b031f7ba22cd05 b/tests/fuzz/dns-corpus/fb80f1cefae3223472bbaeac01b031f7ba22cd05 new file mode 100644 index 00000000..f984f36a Binary files /dev/null and b/tests/fuzz/dns-corpus/fb80f1cefae3223472bbaeac01b031f7ba22cd05 differ diff --git a/tests/fuzz/dns-corpus/fba76b72ff410022e48eb413d42c21a2eecb0743 b/tests/fuzz/dns-corpus/fba76b72ff410022e48eb413d42c21a2eecb0743 new file mode 100644 index 00000000..998069f3 Binary files /dev/null and b/tests/fuzz/dns-corpus/fba76b72ff410022e48eb413d42c21a2eecb0743 differ diff --git a/tests/fuzz/dns-corpus/fc039896a5a30151f46d9aed1de4b68736719c17 b/tests/fuzz/dns-corpus/fc039896a5a30151f46d9aed1de4b68736719c17 new file mode 100644 index 00000000..adecd184 --- /dev/null +++ b/tests/fuzz/dns-corpus/fc039896a5a30151f46d9aed1de4b68736719c17 @@ -0,0 +1 @@ +úþÀ& \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/fe882e37e272344980928e652fabf14a79d85713 b/tests/fuzz/dns-corpus/fe882e37e272344980928e652fabf14a79d85713 new file mode 100644 index 00000000..fd14b3e8 Binary files /dev/null and b/tests/fuzz/dns-corpus/fe882e37e272344980928e652fabf14a79d85713 differ diff --git a/tests/fuzz/dns-corpus/ff67843334eb60d5cfe627de91fde313c893453c b/tests/fuzz/dns-corpus/ff67843334eb60d5cfe627de91fde313c893453c new file mode 100644 index 00000000..d22e6698 --- /dev/null +++ b/tests/fuzz/dns-corpus/ff67843334eb60d5cfe627de91fde313c893453c @@ -0,0 +1 @@ +üÿÿÿ \ No newline at end of file diff --git a/tests/fuzz/dns-corpus/ffcc47fb5bacddd70402aec3b5d5df9068553c42 b/tests/fuzz/dns-corpus/ffcc47fb5bacddd70402aec3b5d5df9068553c42 new file mode 100644 index 00000000..e4d187bb Binary files /dev/null and b/tests/fuzz/dns-corpus/ffcc47fb5bacddd70402aec3b5d5df9068553c42 differ diff --git a/tests/fuzz/sftp-corpus/0218e164bc3fdfeab85db9d5d1dbc10c4dda8e7f b/tests/fuzz/sftp-corpus/0218e164bc3fdfeab85db9d5d1dbc10c4dda8e7f new file mode 100644 index 00000000..e653284a Binary files /dev/null and b/tests/fuzz/sftp-corpus/0218e164bc3fdfeab85db9d5d1dbc10c4dda8e7f differ diff --git a/tests/fuzz/sftp-corpus/02a214a8a141b4ad47349e2543f31f697cc82b08 b/tests/fuzz/sftp-corpus/02a214a8a141b4ad47349e2543f31f697cc82b08 new file mode 100644 index 00000000..6e99c37c Binary files /dev/null and b/tests/fuzz/sftp-corpus/02a214a8a141b4ad47349e2543f31f697cc82b08 differ diff --git a/tests/fuzz/sftp-corpus/03e09b4feb904c8e963b54b55b01bd333b8b1826 b/tests/fuzz/sftp-corpus/03e09b4feb904c8e963b54b55b01bd333b8b1826 new file mode 100644 index 00000000..fcb4faf7 --- /dev/null +++ b/tests/fuzz/sftp-corpus/03e09b4feb904c8e963b54b55b01bd333b8b1826 @@ -0,0 +1 @@ +\001 \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/04ad740c7207790a04b2a4e77cdf271da8015222 b/tests/fuzz/sftp-corpus/04ad740c7207790a04b2a4e77cdf271da8015222 new file mode 100644 index 00000000..1131ab4c Binary files /dev/null and b/tests/fuzz/sftp-corpus/04ad740c7207790a04b2a4e77cdf271da8015222 differ diff --git a/tests/fuzz/sftp-corpus/051edbbd23ea63081b6746046b10404aa914315c b/tests/fuzz/sftp-corpus/051edbbd23ea63081b6746046b10404aa914315c new file mode 100644 index 00000000..35935e37 Binary files /dev/null and b/tests/fuzz/sftp-corpus/051edbbd23ea63081b6746046b10404aa914315c differ diff --git a/tests/fuzz/sftp-corpus/05bc52029a86abf8001ae6c93fe28e4b8744ebe8 b/tests/fuzz/sftp-corpus/05bc52029a86abf8001ae6c93fe28e4b8744ebe8 new file mode 100644 index 00000000..d2af63c0 Binary files /dev/null and b/tests/fuzz/sftp-corpus/05bc52029a86abf8001ae6c93fe28e4b8744ebe8 differ diff --git a/tests/fuzz/sftp-corpus/060d7272e2116cc8b2f687894f9652d7f9408522 b/tests/fuzz/sftp-corpus/060d7272e2116cc8b2f687894f9652d7f9408522 new file mode 100644 index 00000000..f3f10d1f Binary files /dev/null and b/tests/fuzz/sftp-corpus/060d7272e2116cc8b2f687894f9652d7f9408522 differ diff --git a/tests/fuzz/sftp-corpus/0624354256e38adfd478a2e7b2da11a8d9df4da2 b/tests/fuzz/sftp-corpus/0624354256e38adfd478a2e7b2da11a8d9df4da2 new file mode 100644 index 00000000..564a1613 Binary files /dev/null and b/tests/fuzz/sftp-corpus/0624354256e38adfd478a2e7b2da11a8d9df4da2 differ diff --git a/tests/fuzz/sftp-corpus/067d5096f219c64b53bb1c7d5e3754285b565a47 b/tests/fuzz/sftp-corpus/067d5096f219c64b53bb1c7d5e3754285b565a47 new file mode 100644 index 00000000..2725bca0 --- /dev/null +++ b/tests/fuzz/sftp-corpus/067d5096f219c64b53bb1c7d5e3754285b565a47 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/0735683cd44fae1e933edec1b1f083982addc12f b/tests/fuzz/sftp-corpus/0735683cd44fae1e933edec1b1f083982addc12f new file mode 100644 index 00000000..5ffec567 Binary files /dev/null and b/tests/fuzz/sftp-corpus/0735683cd44fae1e933edec1b1f083982addc12f differ diff --git a/tests/fuzz/sftp-corpus/0768c01f83c69795939e82659982782f5505edbc b/tests/fuzz/sftp-corpus/0768c01f83c69795939e82659982782f5505edbc new file mode 100644 index 00000000..8a7ac541 Binary files /dev/null and b/tests/fuzz/sftp-corpus/0768c01f83c69795939e82659982782f5505edbc differ diff --git a/tests/fuzz/sftp-corpus/0c61e54e2cd868a4b657a606f2bbcb5073c26974 b/tests/fuzz/sftp-corpus/0c61e54e2cd868a4b657a606f2bbcb5073c26974 new file mode 100644 index 00000000..b77ece2e Binary files /dev/null and b/tests/fuzz/sftp-corpus/0c61e54e2cd868a4b657a606f2bbcb5073c26974 differ diff --git a/tests/fuzz/sftp-corpus/0c8786079d1c824cc8b5a1fe8ed6c75b1ee85c04 b/tests/fuzz/sftp-corpus/0c8786079d1c824cc8b5a1fe8ed6c75b1ee85c04 new file mode 100644 index 00000000..fb44aed9 Binary files /dev/null and b/tests/fuzz/sftp-corpus/0c8786079d1c824cc8b5a1fe8ed6c75b1ee85c04 differ diff --git a/tests/fuzz/sftp-corpus/0c89e2aa8eafa6e0c738a2bc3270e9edd97d3631 b/tests/fuzz/sftp-corpus/0c89e2aa8eafa6e0c738a2bc3270e9edd97d3631 new file mode 100644 index 00000000..80e9077c Binary files /dev/null and b/tests/fuzz/sftp-corpus/0c89e2aa8eafa6e0c738a2bc3270e9edd97d3631 differ diff --git a/tests/fuzz/sftp-corpus/0eeae9ce7f6f84ebbbe5a148260bfe60997cdd73 b/tests/fuzz/sftp-corpus/0eeae9ce7f6f84ebbbe5a148260bfe60997cdd73 new file mode 100644 index 00000000..b177acae Binary files /dev/null and b/tests/fuzz/sftp-corpus/0eeae9ce7f6f84ebbbe5a148260bfe60997cdd73 differ diff --git a/tests/fuzz/sftp-corpus/0f20fa243c75aeec4d60292be7483382487f9c3a b/tests/fuzz/sftp-corpus/0f20fa243c75aeec4d60292be7483382487f9c3a new file mode 100644 index 00000000..17c1b13e Binary files /dev/null and b/tests/fuzz/sftp-corpus/0f20fa243c75aeec4d60292be7483382487f9c3a differ diff --git a/tests/fuzz/sftp-corpus/10d5a39837ec84fc825176bc4bcb1198ff01521f b/tests/fuzz/sftp-corpus/10d5a39837ec84fc825176bc4bcb1198ff01521f new file mode 100644 index 00000000..56a704b5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/10d5a39837ec84fc825176bc4bcb1198ff01521f differ diff --git a/tests/fuzz/sftp-corpus/11b07148a611b9dffb8ce604bcb0833b67f63dc0 b/tests/fuzz/sftp-corpus/11b07148a611b9dffb8ce604bcb0833b67f63dc0 new file mode 100644 index 00000000..fff78c4d Binary files /dev/null and b/tests/fuzz/sftp-corpus/11b07148a611b9dffb8ce604bcb0833b67f63dc0 differ diff --git a/tests/fuzz/sftp-corpus/11f4de6b8b45cf8051b1d17fa4cde9ad935cea41 b/tests/fuzz/sftp-corpus/11f4de6b8b45cf8051b1d17fa4cde9ad935cea41 new file mode 100644 index 00000000..67c32976 --- /dev/null +++ b/tests/fuzz/sftp-corpus/11f4de6b8b45cf8051b1d17fa4cde9ad935cea41 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/16f8437e89e0ee4d90048244ddaed2805f03cae9 b/tests/fuzz/sftp-corpus/16f8437e89e0ee4d90048244ddaed2805f03cae9 new file mode 100644 index 00000000..8e68ade8 Binary files /dev/null and b/tests/fuzz/sftp-corpus/16f8437e89e0ee4d90048244ddaed2805f03cae9 differ diff --git a/tests/fuzz/sftp-corpus/186e680da206f48c0874803fdef3d144a40ed63d b/tests/fuzz/sftp-corpus/186e680da206f48c0874803fdef3d144a40ed63d new file mode 100644 index 00000000..1512a322 Binary files /dev/null and b/tests/fuzz/sftp-corpus/186e680da206f48c0874803fdef3d144a40ed63d differ diff --git a/tests/fuzz/sftp-corpus/1893370551ef88787ba27a3d08becb71b6825b0d b/tests/fuzz/sftp-corpus/1893370551ef88787ba27a3d08becb71b6825b0d new file mode 100644 index 00000000..3a5c7af0 Binary files /dev/null and b/tests/fuzz/sftp-corpus/1893370551ef88787ba27a3d08becb71b6825b0d differ diff --git a/tests/fuzz/sftp-corpus/196f6abf1b916a45ce3b4463436f093d23535b81 b/tests/fuzz/sftp-corpus/196f6abf1b916a45ce3b4463436f093d23535b81 new file mode 100644 index 00000000..03e4679d Binary files /dev/null and b/tests/fuzz/sftp-corpus/196f6abf1b916a45ce3b4463436f093d23535b81 differ diff --git a/tests/fuzz/sftp-corpus/1a6f75f517158b28073cc22457c6442f4f8f3912 b/tests/fuzz/sftp-corpus/1a6f75f517158b28073cc22457c6442f4f8f3912 new file mode 100644 index 00000000..08e98491 Binary files /dev/null and b/tests/fuzz/sftp-corpus/1a6f75f517158b28073cc22457c6442f4f8f3912 differ diff --git a/tests/fuzz/sftp-corpus/1b6453892473a467d07372d45eb05abc2031647a b/tests/fuzz/sftp-corpus/1b6453892473a467d07372d45eb05abc2031647a new file mode 100644 index 00000000..bf0d87ab --- /dev/null +++ b/tests/fuzz/sftp-corpus/1b6453892473a467d07372d45eb05abc2031647a @@ -0,0 +1 @@ +4 \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/1b9adac07eac275395e8feb8e29b0ecd2fa0d190 b/tests/fuzz/sftp-corpus/1b9adac07eac275395e8feb8e29b0ecd2fa0d190 new file mode 100644 index 00000000..326208f9 Binary files /dev/null and b/tests/fuzz/sftp-corpus/1b9adac07eac275395e8feb8e29b0ecd2fa0d190 differ diff --git a/tests/fuzz/sftp-corpus/1bd29f695a08d119ab65506fb935c438d7d56b7b b/tests/fuzz/sftp-corpus/1bd29f695a08d119ab65506fb935c438d7d56b7b new file mode 100644 index 00000000..501b1caa Binary files /dev/null and b/tests/fuzz/sftp-corpus/1bd29f695a08d119ab65506fb935c438d7d56b7b differ diff --git a/tests/fuzz/sftp-corpus/1e32e3c360501a0ede378bc45a24420dc2e53fba b/tests/fuzz/sftp-corpus/1e32e3c360501a0ede378bc45a24420dc2e53fba new file mode 100644 index 00000000..8214d0ee --- /dev/null +++ b/tests/fuzz/sftp-corpus/1e32e3c360501a0ede378bc45a24420dc2e53fba @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/1eb6fb731869ac526e54678fdddad0c87ff28c37 b/tests/fuzz/sftp-corpus/1eb6fb731869ac526e54678fdddad0c87ff28c37 new file mode 100644 index 00000000..d995d106 Binary files /dev/null and b/tests/fuzz/sftp-corpus/1eb6fb731869ac526e54678fdddad0c87ff28c37 differ diff --git a/tests/fuzz/sftp-corpus/205f5f5a581f65e06464533e5c4624187f3e44fc b/tests/fuzz/sftp-corpus/205f5f5a581f65e06464533e5c4624187f3e44fc new file mode 100644 index 00000000..a2bea508 Binary files /dev/null and b/tests/fuzz/sftp-corpus/205f5f5a581f65e06464533e5c4624187f3e44fc differ diff --git a/tests/fuzz/sftp-corpus/20998d1d6d26f915244912104ef5b722c56824aa b/tests/fuzz/sftp-corpus/20998d1d6d26f915244912104ef5b722c56824aa new file mode 100644 index 00000000..bad7f010 Binary files /dev/null and b/tests/fuzz/sftp-corpus/20998d1d6d26f915244912104ef5b722c56824aa differ diff --git a/tests/fuzz/sftp-corpus/21aa2b6b2f622c1e2069956b3a247850c187a110 b/tests/fuzz/sftp-corpus/21aa2b6b2f622c1e2069956b3a247850c187a110 new file mode 100644 index 00000000..186a6de6 Binary files /dev/null and b/tests/fuzz/sftp-corpus/21aa2b6b2f622c1e2069956b3a247850c187a110 differ diff --git a/tests/fuzz/sftp-corpus/21f1e9ccf3eede7a7390eca0ae08bee3b7ebb9bc b/tests/fuzz/sftp-corpus/21f1e9ccf3eede7a7390eca0ae08bee3b7ebb9bc new file mode 100644 index 00000000..5c5b12bc --- /dev/null +++ b/tests/fuzz/sftp-corpus/21f1e9ccf3eede7a7390eca0ae08bee3b7ebb9bc @@ -0,0 +1 @@ +//Ü \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/22d54c34f70e32cd83604d335d38e2efea6c7daf b/tests/fuzz/sftp-corpus/22d54c34f70e32cd83604d335d38e2efea6c7daf new file mode 100644 index 00000000..ea533968 Binary files /dev/null and b/tests/fuzz/sftp-corpus/22d54c34f70e32cd83604d335d38e2efea6c7daf differ diff --git a/tests/fuzz/sftp-corpus/22f7608a8e55e0f9a9b3df3c18bdf66c70ee6573 b/tests/fuzz/sftp-corpus/22f7608a8e55e0f9a9b3df3c18bdf66c70ee6573 new file mode 100644 index 00000000..b4727172 Binary files /dev/null and b/tests/fuzz/sftp-corpus/22f7608a8e55e0f9a9b3df3c18bdf66c70ee6573 differ diff --git a/tests/fuzz/sftp-corpus/230e89eb169c7e6a158ebd5fcab8fdd1dc1a1a77 b/tests/fuzz/sftp-corpus/230e89eb169c7e6a158ebd5fcab8fdd1dc1a1a77 new file mode 100644 index 00000000..5479961a Binary files /dev/null and b/tests/fuzz/sftp-corpus/230e89eb169c7e6a158ebd5fcab8fdd1dc1a1a77 differ diff --git a/tests/fuzz/sftp-corpus/2341042633e3f89604a2b27bde9c4ca53caa4f32 b/tests/fuzz/sftp-corpus/2341042633e3f89604a2b27bde9c4ca53caa4f32 new file mode 100644 index 00000000..693d7b62 --- /dev/null +++ b/tests/fuzz/sftp-corpus/2341042633e3f89604a2b27bde9c4ca53caa4f32 @@ -0,0 +1 @@ +00Aÿÿÿÿ \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/23d4202b430edff9b192a2b8121ed10949d05fe3 b/tests/fuzz/sftp-corpus/23d4202b430edff9b192a2b8121ed10949d05fe3 new file mode 100644 index 00000000..2186f68c Binary files /dev/null and b/tests/fuzz/sftp-corpus/23d4202b430edff9b192a2b8121ed10949d05fe3 differ diff --git a/tests/fuzz/sftp-corpus/2496922eeef997ae254b76045acecfcaadf30492 b/tests/fuzz/sftp-corpus/2496922eeef997ae254b76045acecfcaadf30492 new file mode 100644 index 00000000..6dd7ec23 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2496922eeef997ae254b76045acecfcaadf30492 differ diff --git a/tests/fuzz/sftp-corpus/27f32be7268a1c483fc2370df26dc838349f6d63 b/tests/fuzz/sftp-corpus/27f32be7268a1c483fc2370df26dc838349f6d63 new file mode 100644 index 00000000..649c3079 Binary files /dev/null and b/tests/fuzz/sftp-corpus/27f32be7268a1c483fc2370df26dc838349f6d63 differ diff --git a/tests/fuzz/sftp-corpus/27fa040b2b626622e41539eef593cbbb76ea8da3 b/tests/fuzz/sftp-corpus/27fa040b2b626622e41539eef593cbbb76ea8da3 new file mode 100644 index 00000000..166ea3b4 Binary files /dev/null and b/tests/fuzz/sftp-corpus/27fa040b2b626622e41539eef593cbbb76ea8da3 differ diff --git a/tests/fuzz/sftp-corpus/29647b5a05100dea70b7ce870dc69e039da262ff b/tests/fuzz/sftp-corpus/29647b5a05100dea70b7ce870dc69e039da262ff new file mode 100644 index 00000000..c3902381 Binary files /dev/null and b/tests/fuzz/sftp-corpus/29647b5a05100dea70b7ce870dc69e039da262ff differ diff --git a/tests/fuzz/sftp-corpus/2967bed3f6077aaf4f335712b4155dd557dec985 b/tests/fuzz/sftp-corpus/2967bed3f6077aaf4f335712b4155dd557dec985 new file mode 100644 index 00000000..39ff0020 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2967bed3f6077aaf4f335712b4155dd557dec985 differ diff --git a/tests/fuzz/sftp-corpus/2a32c5807d96e50f1825fc3398a139dddba0b22d b/tests/fuzz/sftp-corpus/2a32c5807d96e50f1825fc3398a139dddba0b22d new file mode 100644 index 00000000..d938dbd3 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2a32c5807d96e50f1825fc3398a139dddba0b22d differ diff --git a/tests/fuzz/sftp-corpus/2ad40adb6e6e07c9a47469c6e9b0a161c36c973a b/tests/fuzz/sftp-corpus/2ad40adb6e6e07c9a47469c6e9b0a161c36c973a new file mode 100644 index 00000000..5f69aa85 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2ad40adb6e6e07c9a47469c6e9b0a161c36c973a differ diff --git a/tests/fuzz/sftp-corpus/2b0ac5f49662c27ed49aafeeef1a0f45c5948f4e b/tests/fuzz/sftp-corpus/2b0ac5f49662c27ed49aafeeef1a0f45c5948f4e new file mode 100644 index 00000000..5d07ba25 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2b0ac5f49662c27ed49aafeeef1a0f45c5948f4e differ diff --git a/tests/fuzz/sftp-corpus/2b7082e93543d17a3cd5c0a56f65c4bfc14d1443 b/tests/fuzz/sftp-corpus/2b7082e93543d17a3cd5c0a56f65c4bfc14d1443 new file mode 100644 index 00000000..86be39c3 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2b7082e93543d17a3cd5c0a56f65c4bfc14d1443 differ diff --git a/tests/fuzz/sftp-corpus/2bc778fc9d30ae0884e27f9604adba3f9704c98c b/tests/fuzz/sftp-corpus/2bc778fc9d30ae0884e27f9604adba3f9704c98c new file mode 100644 index 00000000..d53de19c Binary files /dev/null and b/tests/fuzz/sftp-corpus/2bc778fc9d30ae0884e27f9604adba3f9704c98c differ diff --git a/tests/fuzz/sftp-corpus/2d0134ed3b9de132c720fe697b532b4c232ff9fe b/tests/fuzz/sftp-corpus/2d0134ed3b9de132c720fe697b532b4c232ff9fe new file mode 100644 index 00000000..f8fa5a23 --- /dev/null +++ b/tests/fuzz/sftp-corpus/2d0134ed3b9de132c720fe697b532b4c232ff9fe @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/2ded9cd86f5015263fd8954e2b0e2007366238bc b/tests/fuzz/sftp-corpus/2ded9cd86f5015263fd8954e2b0e2007366238bc new file mode 100644 index 00000000..687cff71 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2ded9cd86f5015263fd8954e2b0e2007366238bc differ diff --git a/tests/fuzz/sftp-corpus/2e1272706b911cf8b0056f9ad7717413df85182e b/tests/fuzz/sftp-corpus/2e1272706b911cf8b0056f9ad7717413df85182e new file mode 100644 index 00000000..66bbebd9 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2e1272706b911cf8b0056f9ad7717413df85182e differ diff --git a/tests/fuzz/sftp-corpus/2eee0e8d607ab9cc6bb9a8f4b7ec9b8a60a8cbe3 b/tests/fuzz/sftp-corpus/2eee0e8d607ab9cc6bb9a8f4b7ec9b8a60a8cbe3 new file mode 100644 index 00000000..bce4bf61 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2eee0e8d607ab9cc6bb9a8f4b7ec9b8a60a8cbe3 differ diff --git a/tests/fuzz/sftp-corpus/2f1ba53b15e6ec7532358b55d9d1b06a42dbcda1 b/tests/fuzz/sftp-corpus/2f1ba53b15e6ec7532358b55d9d1b06a42dbcda1 new file mode 100644 index 00000000..e96a680a Binary files /dev/null and b/tests/fuzz/sftp-corpus/2f1ba53b15e6ec7532358b55d9d1b06a42dbcda1 differ diff --git a/tests/fuzz/sftp-corpus/2fb946fd8fad5d57b31457b899536856e725f6cd b/tests/fuzz/sftp-corpus/2fb946fd8fad5d57b31457b899536856e725f6cd new file mode 100644 index 00000000..a6e90a5e Binary files /dev/null and b/tests/fuzz/sftp-corpus/2fb946fd8fad5d57b31457b899536856e725f6cd differ diff --git a/tests/fuzz/sftp-corpus/2fc50e3d3342d4610a4a08a70c4a013ceab2f874 b/tests/fuzz/sftp-corpus/2fc50e3d3342d4610a4a08a70c4a013ceab2f874 new file mode 100644 index 00000000..1d2dedd6 Binary files /dev/null and b/tests/fuzz/sftp-corpus/2fc50e3d3342d4610a4a08a70c4a013ceab2f874 differ diff --git a/tests/fuzz/sftp-corpus/300f761b6d19640e91db829d1cbfa8b025914ca4 b/tests/fuzz/sftp-corpus/300f761b6d19640e91db829d1cbfa8b025914ca4 new file mode 100644 index 00000000..41a5b5de Binary files /dev/null and b/tests/fuzz/sftp-corpus/300f761b6d19640e91db829d1cbfa8b025914ca4 differ diff --git a/tests/fuzz/sftp-corpus/30c84d37575a4521c145b2b60a7e4202cd2f4f97 b/tests/fuzz/sftp-corpus/30c84d37575a4521c145b2b60a7e4202cd2f4f97 new file mode 100644 index 00000000..a9762161 Binary files /dev/null and b/tests/fuzz/sftp-corpus/30c84d37575a4521c145b2b60a7e4202cd2f4f97 differ diff --git a/tests/fuzz/sftp-corpus/30d88c894380b4ee74353d60904560c7bfe26c6c b/tests/fuzz/sftp-corpus/30d88c894380b4ee74353d60904560c7bfe26c6c new file mode 100644 index 00000000..6ba12dad Binary files /dev/null and b/tests/fuzz/sftp-corpus/30d88c894380b4ee74353d60904560c7bfe26c6c differ diff --git a/tests/fuzz/sftp-corpus/31caece29debee6c1b4e4217cc0b252ad362efd1 b/tests/fuzz/sftp-corpus/31caece29debee6c1b4e4217cc0b252ad362efd1 new file mode 100644 index 00000000..c9bb102d Binary files /dev/null and b/tests/fuzz/sftp-corpus/31caece29debee6c1b4e4217cc0b252ad362efd1 differ diff --git a/tests/fuzz/sftp-corpus/320355ced694aa69924f6bb82e7b74f420303fd9 b/tests/fuzz/sftp-corpus/320355ced694aa69924f6bb82e7b74f420303fd9 new file mode 100644 index 00000000..9280c0d3 --- /dev/null +++ b/tests/fuzz/sftp-corpus/320355ced694aa69924f6bb82e7b74f420303fd9 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/3507d854de2458f33376dbd727071bb7ee93bf9b b/tests/fuzz/sftp-corpus/3507d854de2458f33376dbd727071bb7ee93bf9b new file mode 100644 index 00000000..5da8aac5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/3507d854de2458f33376dbd727071bb7ee93bf9b differ diff --git a/tests/fuzz/sftp-corpus/35b02ec6b51d5f7fcc99bf76ccd617d0eb323ce2 b/tests/fuzz/sftp-corpus/35b02ec6b51d5f7fcc99bf76ccd617d0eb323ce2 new file mode 100644 index 00000000..eb006a89 --- /dev/null +++ b/tests/fuzz/sftp-corpus/35b02ec6b51d5f7fcc99bf76ccd617d0eb323ce2 @@ -0,0 +1 @@ +T10 \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/37cce52e89d666350991820485c6aec4bdbcd4a9 b/tests/fuzz/sftp-corpus/37cce52e89d666350991820485c6aec4bdbcd4a9 new file mode 100644 index 00000000..84d1ed79 Binary files /dev/null and b/tests/fuzz/sftp-corpus/37cce52e89d666350991820485c6aec4bdbcd4a9 differ diff --git a/tests/fuzz/sftp-corpus/3857110167557685286d8a979724a09f5b098211 b/tests/fuzz/sftp-corpus/3857110167557685286d8a979724a09f5b098211 new file mode 100644 index 00000000..97a6d7f8 Binary files /dev/null and b/tests/fuzz/sftp-corpus/3857110167557685286d8a979724a09f5b098211 differ diff --git a/tests/fuzz/sftp-corpus/38994f4163e46aa52d97af1075f8dcc605ce5ac1 b/tests/fuzz/sftp-corpus/38994f4163e46aa52d97af1075f8dcc605ce5ac1 new file mode 100644 index 00000000..50971e9d Binary files /dev/null and b/tests/fuzz/sftp-corpus/38994f4163e46aa52d97af1075f8dcc605ce5ac1 differ diff --git a/tests/fuzz/sftp-corpus/39594f602b0345c3cf13b1271c8470f94d821061 b/tests/fuzz/sftp-corpus/39594f602b0345c3cf13b1271c8470f94d821061 new file mode 100644 index 00000000..bb26a3cb Binary files /dev/null and b/tests/fuzz/sftp-corpus/39594f602b0345c3cf13b1271c8470f94d821061 differ diff --git a/tests/fuzz/sftp-corpus/3a2092aff2749c4a939e475d2a31b8bb08a4aeed b/tests/fuzz/sftp-corpus/3a2092aff2749c4a939e475d2a31b8bb08a4aeed new file mode 100644 index 00000000..63cfe7f8 Binary files /dev/null and b/tests/fuzz/sftp-corpus/3a2092aff2749c4a939e475d2a31b8bb08a4aeed differ diff --git a/tests/fuzz/sftp-corpus/3bf260084777d0d1947ac40a33328902350459c8 b/tests/fuzz/sftp-corpus/3bf260084777d0d1947ac40a33328902350459c8 new file mode 100644 index 00000000..2f000140 Binary files /dev/null and b/tests/fuzz/sftp-corpus/3bf260084777d0d1947ac40a33328902350459c8 differ diff --git a/tests/fuzz/sftp-corpus/3bf4364543031a14f4d35c9f6a58e6e488aa4d55 b/tests/fuzz/sftp-corpus/3bf4364543031a14f4d35c9f6a58e6e488aa4d55 new file mode 100644 index 00000000..81c2cedd Binary files /dev/null and b/tests/fuzz/sftp-corpus/3bf4364543031a14f4d35c9f6a58e6e488aa4d55 differ diff --git a/tests/fuzz/sftp-corpus/3cf07f46232ec39af15a3ea28bef7f953da1ded7 b/tests/fuzz/sftp-corpus/3cf07f46232ec39af15a3ea28bef7f953da1ded7 new file mode 100644 index 00000000..1a2fbce7 Binary files /dev/null and b/tests/fuzz/sftp-corpus/3cf07f46232ec39af15a3ea28bef7f953da1ded7 differ diff --git a/tests/fuzz/sftp-corpus/3d6f4a63fbc1d2b6517803f2c0e02643daca26c1 b/tests/fuzz/sftp-corpus/3d6f4a63fbc1d2b6517803f2c0e02643daca26c1 new file mode 100644 index 00000000..33cc9283 Binary files /dev/null and b/tests/fuzz/sftp-corpus/3d6f4a63fbc1d2b6517803f2c0e02643daca26c1 differ diff --git a/tests/fuzz/sftp-corpus/3fe348064472fa143c9ffbbd22418d58d72c6c81 b/tests/fuzz/sftp-corpus/3fe348064472fa143c9ffbbd22418d58d72c6c81 new file mode 100644 index 00000000..ded3491e Binary files /dev/null and b/tests/fuzz/sftp-corpus/3fe348064472fa143c9ffbbd22418d58d72c6c81 differ diff --git a/tests/fuzz/sftp-corpus/4059c8c6e0d6d7cc498d7bb6db655e1fc588f5a9 b/tests/fuzz/sftp-corpus/4059c8c6e0d6d7cc498d7bb6db655e1fc588f5a9 new file mode 100644 index 00000000..aa3a5e93 Binary files /dev/null and b/tests/fuzz/sftp-corpus/4059c8c6e0d6d7cc498d7bb6db655e1fc588f5a9 differ diff --git a/tests/fuzz/sftp-corpus/40c05fd0c26bcc6469fc95989538d78a38949b4c b/tests/fuzz/sftp-corpus/40c05fd0c26bcc6469fc95989538d78a38949b4c new file mode 100644 index 00000000..aaa8b23d Binary files /dev/null and b/tests/fuzz/sftp-corpus/40c05fd0c26bcc6469fc95989538d78a38949b4c differ diff --git a/tests/fuzz/sftp-corpus/40cca6b80f2590bf0c8ee79930f2306e9a5f488b b/tests/fuzz/sftp-corpus/40cca6b80f2590bf0c8ee79930f2306e9a5f488b new file mode 100644 index 00000000..f99ba5e4 Binary files /dev/null and b/tests/fuzz/sftp-corpus/40cca6b80f2590bf0c8ee79930f2306e9a5f488b differ diff --git a/tests/fuzz/sftp-corpus/425e39635cb3ff6c746e049040e32b8ae91c3f98 b/tests/fuzz/sftp-corpus/425e39635cb3ff6c746e049040e32b8ae91c3f98 new file mode 100644 index 00000000..3a1b18bd Binary files /dev/null and b/tests/fuzz/sftp-corpus/425e39635cb3ff6c746e049040e32b8ae91c3f98 differ diff --git a/tests/fuzz/sftp-corpus/4309aa279959612211076df160c24f405f6fb916 b/tests/fuzz/sftp-corpus/4309aa279959612211076df160c24f405f6fb916 new file mode 100644 index 00000000..4d89dc5c Binary files /dev/null and b/tests/fuzz/sftp-corpus/4309aa279959612211076df160c24f405f6fb916 differ diff --git a/tests/fuzz/sftp-corpus/43a4cadad6ad815b460922d2f209637a9b41cb38 b/tests/fuzz/sftp-corpus/43a4cadad6ad815b460922d2f209637a9b41cb38 new file mode 100644 index 00000000..7192fdf4 Binary files /dev/null and b/tests/fuzz/sftp-corpus/43a4cadad6ad815b460922d2f209637a9b41cb38 differ diff --git a/tests/fuzz/sftp-corpus/4480d7e57896f5b6ce18f9782501b03fc44605f5 b/tests/fuzz/sftp-corpus/4480d7e57896f5b6ce18f9782501b03fc44605f5 new file mode 100644 index 00000000..c0759568 Binary files /dev/null and b/tests/fuzz/sftp-corpus/4480d7e57896f5b6ce18f9782501b03fc44605f5 differ diff --git a/tests/fuzz/sftp-corpus/450a780e731fee1d4a381f350fd397d5215d4305 b/tests/fuzz/sftp-corpus/450a780e731fee1d4a381f350fd397d5215d4305 new file mode 100644 index 00000000..d4eacbee Binary files /dev/null and b/tests/fuzz/sftp-corpus/450a780e731fee1d4a381f350fd397d5215d4305 differ diff --git a/tests/fuzz/sftp-corpus/455f99affc8426be3e50c7f1c7fea628d509b640 b/tests/fuzz/sftp-corpus/455f99affc8426be3e50c7f1c7fea628d509b640 new file mode 100644 index 00000000..a6ca4a90 Binary files /dev/null and b/tests/fuzz/sftp-corpus/455f99affc8426be3e50c7f1c7fea628d509b640 differ diff --git a/tests/fuzz/sftp-corpus/464f7563905b70a37713eaea50178dfd1a942b65 b/tests/fuzz/sftp-corpus/464f7563905b70a37713eaea50178dfd1a942b65 new file mode 100644 index 00000000..9014b1e3 Binary files /dev/null and b/tests/fuzz/sftp-corpus/464f7563905b70a37713eaea50178dfd1a942b65 differ diff --git a/tests/fuzz/sftp-corpus/47f7acd9da7ef2e518eb9614900efed19916d4f7 b/tests/fuzz/sftp-corpus/47f7acd9da7ef2e518eb9614900efed19916d4f7 new file mode 100644 index 00000000..c6f22efb Binary files /dev/null and b/tests/fuzz/sftp-corpus/47f7acd9da7ef2e518eb9614900efed19916d4f7 differ diff --git a/tests/fuzz/sftp-corpus/483a2db6e9aa2f4ccc36a9948d840369ab335c80 b/tests/fuzz/sftp-corpus/483a2db6e9aa2f4ccc36a9948d840369ab335c80 new file mode 100644 index 00000000..b3aba210 Binary files /dev/null and b/tests/fuzz/sftp-corpus/483a2db6e9aa2f4ccc36a9948d840369ab335c80 differ diff --git a/tests/fuzz/sftp-corpus/4b370760968ddceb194aca1da46987c36e8040fb b/tests/fuzz/sftp-corpus/4b370760968ddceb194aca1da46987c36e8040fb new file mode 100644 index 00000000..213eebb4 Binary files /dev/null and b/tests/fuzz/sftp-corpus/4b370760968ddceb194aca1da46987c36e8040fb differ diff --git a/tests/fuzz/sftp-corpus/4c2b04f376725a3726c2d1fe34eae9b721c7ad67 b/tests/fuzz/sftp-corpus/4c2b04f376725a3726c2d1fe34eae9b721c7ad67 new file mode 100644 index 00000000..056e5f73 Binary files /dev/null and b/tests/fuzz/sftp-corpus/4c2b04f376725a3726c2d1fe34eae9b721c7ad67 differ diff --git a/tests/fuzz/sftp-corpus/4cb9cb8b755e22d245531fddc8453dc9c61381fa b/tests/fuzz/sftp-corpus/4cb9cb8b755e22d245531fddc8453dc9c61381fa new file mode 100644 index 00000000..898c0d8d Binary files /dev/null and b/tests/fuzz/sftp-corpus/4cb9cb8b755e22d245531fddc8453dc9c61381fa differ diff --git a/tests/fuzz/sftp-corpus/4dffb01f63b278856e9871f736b9c27b1260a31f b/tests/fuzz/sftp-corpus/4dffb01f63b278856e9871f736b9c27b1260a31f new file mode 100644 index 00000000..cb781a9d Binary files /dev/null and b/tests/fuzz/sftp-corpus/4dffb01f63b278856e9871f736b9c27b1260a31f differ diff --git a/tests/fuzz/sftp-corpus/4fb430791a89b8a5e295171d8a718c44dcb55993 b/tests/fuzz/sftp-corpus/4fb430791a89b8a5e295171d8a718c44dcb55993 new file mode 100644 index 00000000..1df447c0 Binary files /dev/null and b/tests/fuzz/sftp-corpus/4fb430791a89b8a5e295171d8a718c44dcb55993 differ diff --git a/tests/fuzz/sftp-corpus/502fb1ce4d60456f1ff463b96176e957f6c2d192 b/tests/fuzz/sftp-corpus/502fb1ce4d60456f1ff463b96176e957f6c2d192 new file mode 100644 index 00000000..86520c60 Binary files /dev/null and b/tests/fuzz/sftp-corpus/502fb1ce4d60456f1ff463b96176e957f6c2d192 differ diff --git a/tests/fuzz/sftp-corpus/5034c72661c63e98573503a83e5bd7b12d2520af b/tests/fuzz/sftp-corpus/5034c72661c63e98573503a83e5bd7b12d2520af new file mode 100644 index 00000000..311586ae Binary files /dev/null and b/tests/fuzz/sftp-corpus/5034c72661c63e98573503a83e5bd7b12d2520af differ diff --git a/tests/fuzz/sftp-corpus/512a6a0c167e1e35a3cf1e96290168bd7677c8c8 b/tests/fuzz/sftp-corpus/512a6a0c167e1e35a3cf1e96290168bd7677c8c8 new file mode 100644 index 00000000..c2b42130 Binary files /dev/null and b/tests/fuzz/sftp-corpus/512a6a0c167e1e35a3cf1e96290168bd7677c8c8 differ diff --git a/tests/fuzz/sftp-corpus/51f5147b369c9a95c1181486b32dbe5ee6491ab8 b/tests/fuzz/sftp-corpus/51f5147b369c9a95c1181486b32dbe5ee6491ab8 new file mode 100644 index 00000000..f31ec48c Binary files /dev/null and b/tests/fuzz/sftp-corpus/51f5147b369c9a95c1181486b32dbe5ee6491ab8 differ diff --git a/tests/fuzz/sftp-corpus/524e8766c2f23ed7877940d7734e317e43f86015 b/tests/fuzz/sftp-corpus/524e8766c2f23ed7877940d7734e317e43f86015 new file mode 100644 index 00000000..086962ed Binary files /dev/null and b/tests/fuzz/sftp-corpus/524e8766c2f23ed7877940d7734e317e43f86015 differ diff --git a/tests/fuzz/sftp-corpus/52edfcf1aa24d39c47a2e7fc1dab7a06736f82dd b/tests/fuzz/sftp-corpus/52edfcf1aa24d39c47a2e7fc1dab7a06736f82dd new file mode 100644 index 00000000..a86a0cad Binary files /dev/null and b/tests/fuzz/sftp-corpus/52edfcf1aa24d39c47a2e7fc1dab7a06736f82dd differ diff --git a/tests/fuzz/sftp-corpus/53d1460e7dfa78c97dbea6a9807abfd7c4dafaf9 b/tests/fuzz/sftp-corpus/53d1460e7dfa78c97dbea6a9807abfd7c4dafaf9 new file mode 100644 index 00000000..a70155dd Binary files /dev/null and b/tests/fuzz/sftp-corpus/53d1460e7dfa78c97dbea6a9807abfd7c4dafaf9 differ diff --git a/tests/fuzz/sftp-corpus/541641c126e5f3d4586c73ffee8c787895dc1922 b/tests/fuzz/sftp-corpus/541641c126e5f3d4586c73ffee8c787895dc1922 new file mode 100644 index 00000000..81a8cb15 Binary files /dev/null and b/tests/fuzz/sftp-corpus/541641c126e5f3d4586c73ffee8c787895dc1922 differ diff --git a/tests/fuzz/sftp-corpus/545d0e901e918a1aad109d422a59836e8c58c255 b/tests/fuzz/sftp-corpus/545d0e901e918a1aad109d422a59836e8c58c255 new file mode 100644 index 00000000..a05d1dbb Binary files /dev/null and b/tests/fuzz/sftp-corpus/545d0e901e918a1aad109d422a59836e8c58c255 differ diff --git a/tests/fuzz/sftp-corpus/5516dca960817d2f7a24add6f069f3610d9f0b29 b/tests/fuzz/sftp-corpus/5516dca960817d2f7a24add6f069f3610d9f0b29 new file mode 100644 index 00000000..43bb22d3 Binary files /dev/null and b/tests/fuzz/sftp-corpus/5516dca960817d2f7a24add6f069f3610d9f0b29 differ diff --git a/tests/fuzz/sftp-corpus/551d27015c720a5e2ed7c046f835d9566890db78 b/tests/fuzz/sftp-corpus/551d27015c720a5e2ed7c046f835d9566890db78 new file mode 100644 index 00000000..bd88327c Binary files /dev/null and b/tests/fuzz/sftp-corpus/551d27015c720a5e2ed7c046f835d9566890db78 differ diff --git a/tests/fuzz/sftp-corpus/5697c9c17e20eea18f42e2cf47e383f6900e3bd8 b/tests/fuzz/sftp-corpus/5697c9c17e20eea18f42e2cf47e383f6900e3bd8 new file mode 100644 index 00000000..73439c4f Binary files /dev/null and b/tests/fuzz/sftp-corpus/5697c9c17e20eea18f42e2cf47e383f6900e3bd8 differ diff --git a/tests/fuzz/sftp-corpus/56a91971a81f758a80f8985e1a01e724c393b053 b/tests/fuzz/sftp-corpus/56a91971a81f758a80f8985e1a01e724c393b053 new file mode 100644 index 00000000..de2ffc86 Binary files /dev/null and b/tests/fuzz/sftp-corpus/56a91971a81f758a80f8985e1a01e724c393b053 differ diff --git a/tests/fuzz/sftp-corpus/577968610a60f563926e957a73abb65b3538152e b/tests/fuzz/sftp-corpus/577968610a60f563926e957a73abb65b3538152e new file mode 100644 index 00000000..0b9cd126 Binary files /dev/null and b/tests/fuzz/sftp-corpus/577968610a60f563926e957a73abb65b3538152e differ diff --git a/tests/fuzz/sftp-corpus/578f936354c54e17ba180ed7fe218ce25f050a79 b/tests/fuzz/sftp-corpus/578f936354c54e17ba180ed7fe218ce25f050a79 new file mode 100644 index 00000000..c31ebbde Binary files /dev/null and b/tests/fuzz/sftp-corpus/578f936354c54e17ba180ed7fe218ce25f050a79 differ diff --git a/tests/fuzz/sftp-corpus/58356fbaf0a833a4e4b89e7638f6ce05a3f41aae b/tests/fuzz/sftp-corpus/58356fbaf0a833a4e4b89e7638f6ce05a3f41aae new file mode 100644 index 00000000..91e299cf Binary files /dev/null and b/tests/fuzz/sftp-corpus/58356fbaf0a833a4e4b89e7638f6ce05a3f41aae differ diff --git a/tests/fuzz/sftp-corpus/586098392a60a8009627765d4fec39ec85435d14 b/tests/fuzz/sftp-corpus/586098392a60a8009627765d4fec39ec85435d14 new file mode 100644 index 00000000..db1cc338 Binary files /dev/null and b/tests/fuzz/sftp-corpus/586098392a60a8009627765d4fec39ec85435d14 differ diff --git a/tests/fuzz/sftp-corpus/59c8d38ec35ad55b438e34bbbf1af35dd9439e7c b/tests/fuzz/sftp-corpus/59c8d38ec35ad55b438e34bbbf1af35dd9439e7c new file mode 100644 index 00000000..feecc2fe --- /dev/null +++ b/tests/fuzz/sftp-corpus/59c8d38ec35ad55b438e34bbbf1af35dd9439e7c @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/5b1c7ac5562ed91dbc88858c9681f92ea91a9bf0 b/tests/fuzz/sftp-corpus/5b1c7ac5562ed91dbc88858c9681f92ea91a9bf0 new file mode 100644 index 00000000..6a3c2b8a Binary files /dev/null and b/tests/fuzz/sftp-corpus/5b1c7ac5562ed91dbc88858c9681f92ea91a9bf0 differ diff --git a/tests/fuzz/sftp-corpus/5bbdab3597adec6ee21aa3a7b25de96f394c58df b/tests/fuzz/sftp-corpus/5bbdab3597adec6ee21aa3a7b25de96f394c58df new file mode 100644 index 00000000..c98204c5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/5bbdab3597adec6ee21aa3a7b25de96f394c58df differ diff --git a/tests/fuzz/sftp-corpus/5ccd1aa8868474fb1ec4041869b95b4508c69097 b/tests/fuzz/sftp-corpus/5ccd1aa8868474fb1ec4041869b95b4508c69097 new file mode 100644 index 00000000..2a1b7c18 Binary files /dev/null and b/tests/fuzz/sftp-corpus/5ccd1aa8868474fb1ec4041869b95b4508c69097 differ diff --git a/tests/fuzz/sftp-corpus/5d1be7e9dda1ee8896be5b7e34a85ee16452a7b4 b/tests/fuzz/sftp-corpus/5d1be7e9dda1ee8896be5b7e34a85ee16452a7b4 new file mode 100644 index 00000000..303e398c --- /dev/null +++ b/tests/fuzz/sftp-corpus/5d1be7e9dda1ee8896be5b7e34a85ee16452a7b4 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/5d5d85324c853aaaf7430aa14d52dcd7dd5c2012 b/tests/fuzz/sftp-corpus/5d5d85324c853aaaf7430aa14d52dcd7dd5c2012 new file mode 100644 index 00000000..fed5dd6c Binary files /dev/null and b/tests/fuzz/sftp-corpus/5d5d85324c853aaaf7430aa14d52dcd7dd5c2012 differ diff --git a/tests/fuzz/sftp-corpus/5e6deb229ac0d578b5a4dc65be25e95369c1a868 b/tests/fuzz/sftp-corpus/5e6deb229ac0d578b5a4dc65be25e95369c1a868 new file mode 100644 index 00000000..4a2c0bbe Binary files /dev/null and b/tests/fuzz/sftp-corpus/5e6deb229ac0d578b5a4dc65be25e95369c1a868 differ diff --git a/tests/fuzz/sftp-corpus/6085ad0b48a21df555b3d523579d67eec38afe8d b/tests/fuzz/sftp-corpus/6085ad0b48a21df555b3d523579d67eec38afe8d new file mode 100644 index 00000000..39b038cd Binary files /dev/null and b/tests/fuzz/sftp-corpus/6085ad0b48a21df555b3d523579d67eec38afe8d differ diff --git a/tests/fuzz/sftp-corpus/60a3d7275709094b101917a88ed75b83da0b4ce2 b/tests/fuzz/sftp-corpus/60a3d7275709094b101917a88ed75b83da0b4ce2 new file mode 100644 index 00000000..85d2d108 Binary files /dev/null and b/tests/fuzz/sftp-corpus/60a3d7275709094b101917a88ed75b83da0b4ce2 differ diff --git a/tests/fuzz/sftp-corpus/627ab152900b2ebdb54d64e879ad96d2fff3f04b b/tests/fuzz/sftp-corpus/627ab152900b2ebdb54d64e879ad96d2fff3f04b new file mode 100644 index 00000000..f79590ba Binary files /dev/null and b/tests/fuzz/sftp-corpus/627ab152900b2ebdb54d64e879ad96d2fff3f04b differ diff --git a/tests/fuzz/sftp-corpus/6390ca80cc1279e487cfce07eed9a18948d48e0a b/tests/fuzz/sftp-corpus/6390ca80cc1279e487cfce07eed9a18948d48e0a new file mode 100644 index 00000000..bac21552 Binary files /dev/null and b/tests/fuzz/sftp-corpus/6390ca80cc1279e487cfce07eed9a18948d48e0a differ diff --git a/tests/fuzz/sftp-corpus/65d00f8f2bf28a0c19a064cce0dd866efbf70cb1 b/tests/fuzz/sftp-corpus/65d00f8f2bf28a0c19a064cce0dd866efbf70cb1 new file mode 100644 index 00000000..a35a610f Binary files /dev/null and b/tests/fuzz/sftp-corpus/65d00f8f2bf28a0c19a064cce0dd866efbf70cb1 differ diff --git a/tests/fuzz/sftp-corpus/6734c22834adb777d0896832f4258d6b2bdd8f52 b/tests/fuzz/sftp-corpus/6734c22834adb777d0896832f4258d6b2bdd8f52 new file mode 100644 index 00000000..aad4d399 Binary files /dev/null and b/tests/fuzz/sftp-corpus/6734c22834adb777d0896832f4258d6b2bdd8f52 differ diff --git a/tests/fuzz/sftp-corpus/6a002d9903b90d8e5aa45ee8a178177f2dc827a6 b/tests/fuzz/sftp-corpus/6a002d9903b90d8e5aa45ee8a178177f2dc827a6 new file mode 100644 index 00000000..31416425 Binary files /dev/null and b/tests/fuzz/sftp-corpus/6a002d9903b90d8e5aa45ee8a178177f2dc827a6 differ diff --git a/tests/fuzz/sftp-corpus/6aea28b93b39048e554525f24c03cc8df20d4b96 b/tests/fuzz/sftp-corpus/6aea28b93b39048e554525f24c03cc8df20d4b96 new file mode 100644 index 00000000..513387c0 Binary files /dev/null and b/tests/fuzz/sftp-corpus/6aea28b93b39048e554525f24c03cc8df20d4b96 differ diff --git a/tests/fuzz/sftp-corpus/6ccd9d6b32093fe954b3ee97d8794f0b6d264933 b/tests/fuzz/sftp-corpus/6ccd9d6b32093fe954b3ee97d8794f0b6d264933 new file mode 100644 index 00000000..8c0e3db5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/6ccd9d6b32093fe954b3ee97d8794f0b6d264933 differ diff --git a/tests/fuzz/sftp-corpus/6dd3baddbf0766ea68ea921baa6db5c76859796c b/tests/fuzz/sftp-corpus/6dd3baddbf0766ea68ea921baa6db5c76859796c new file mode 100644 index 00000000..df8380a3 Binary files /dev/null and b/tests/fuzz/sftp-corpus/6dd3baddbf0766ea68ea921baa6db5c76859796c differ diff --git a/tests/fuzz/sftp-corpus/6e14a407faae939957b80e641a836735bbdcad5a b/tests/fuzz/sftp-corpus/6e14a407faae939957b80e641a836735bbdcad5a new file mode 100644 index 00000000..31f442a2 --- /dev/null +++ b/tests/fuzz/sftp-corpus/6e14a407faae939957b80e641a836735bbdcad5a @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/71bf6b6296546665f79c9fdc21fa4294ea5ca762 b/tests/fuzz/sftp-corpus/71bf6b6296546665f79c9fdc21fa4294ea5ca762 new file mode 100644 index 00000000..31880572 Binary files /dev/null and b/tests/fuzz/sftp-corpus/71bf6b6296546665f79c9fdc21fa4294ea5ca762 differ diff --git a/tests/fuzz/sftp-corpus/768c697e679607f2e10272a1242f3b011e032e10 b/tests/fuzz/sftp-corpus/768c697e679607f2e10272a1242f3b011e032e10 new file mode 100644 index 00000000..5502de1a Binary files /dev/null and b/tests/fuzz/sftp-corpus/768c697e679607f2e10272a1242f3b011e032e10 differ diff --git a/tests/fuzz/sftp-corpus/789f725eebafd61d940382d15931ae0db897531d b/tests/fuzz/sftp-corpus/789f725eebafd61d940382d15931ae0db897531d new file mode 100644 index 00000000..cd526e6c Binary files /dev/null and b/tests/fuzz/sftp-corpus/789f725eebafd61d940382d15931ae0db897531d differ diff --git a/tests/fuzz/sftp-corpus/78af68212800e1c30c4e488b493f948740658013 b/tests/fuzz/sftp-corpus/78af68212800e1c30c4e488b493f948740658013 new file mode 100644 index 00000000..3b5a4450 Binary files /dev/null and b/tests/fuzz/sftp-corpus/78af68212800e1c30c4e488b493f948740658013 differ diff --git a/tests/fuzz/sftp-corpus/7c250f383c79fb2057bb726c1870d8ce6b12b677 b/tests/fuzz/sftp-corpus/7c250f383c79fb2057bb726c1870d8ce6b12b677 new file mode 100644 index 00000000..886d8f0f Binary files /dev/null and b/tests/fuzz/sftp-corpus/7c250f383c79fb2057bb726c1870d8ce6b12b677 differ diff --git a/tests/fuzz/sftp-corpus/7c4e794af39a08f0a429b9a32553978b4a4d0250 b/tests/fuzz/sftp-corpus/7c4e794af39a08f0a429b9a32553978b4a4d0250 new file mode 100644 index 00000000..6c1a7a69 Binary files /dev/null and b/tests/fuzz/sftp-corpus/7c4e794af39a08f0a429b9a32553978b4a4d0250 differ diff --git a/tests/fuzz/sftp-corpus/817b4a359b807c6af19f862ef1759ce7a32c59ee b/tests/fuzz/sftp-corpus/817b4a359b807c6af19f862ef1759ce7a32c59ee new file mode 100644 index 00000000..12a17e05 Binary files /dev/null and b/tests/fuzz/sftp-corpus/817b4a359b807c6af19f862ef1759ce7a32c59ee differ diff --git a/tests/fuzz/sftp-corpus/8243c48ba8d2870f3007fcc1e1dbc5d640d89c89 b/tests/fuzz/sftp-corpus/8243c48ba8d2870f3007fcc1e1dbc5d640d89c89 new file mode 100644 index 00000000..40d28f3a Binary files /dev/null and b/tests/fuzz/sftp-corpus/8243c48ba8d2870f3007fcc1e1dbc5d640d89c89 differ diff --git a/tests/fuzz/sftp-corpus/82837f7c443b66885aa03b859c60949078e4e3ae b/tests/fuzz/sftp-corpus/82837f7c443b66885aa03b859c60949078e4e3ae new file mode 100644 index 00000000..05b9a573 Binary files /dev/null and b/tests/fuzz/sftp-corpus/82837f7c443b66885aa03b859c60949078e4e3ae differ diff --git a/tests/fuzz/sftp-corpus/835008c2f57047a9082a3573a5937d8afb6926cb b/tests/fuzz/sftp-corpus/835008c2f57047a9082a3573a5937d8afb6926cb new file mode 100644 index 00000000..ecdbfc1d Binary files /dev/null and b/tests/fuzz/sftp-corpus/835008c2f57047a9082a3573a5937d8afb6926cb differ diff --git a/tests/fuzz/sftp-corpus/837c41995d1666ae11937f501591804e4d8d3aea b/tests/fuzz/sftp-corpus/837c41995d1666ae11937f501591804e4d8d3aea new file mode 100644 index 00000000..79155bcb Binary files /dev/null and b/tests/fuzz/sftp-corpus/837c41995d1666ae11937f501591804e4d8d3aea differ diff --git a/tests/fuzz/sftp-corpus/83dee3c79e7371aecff01fa92465557bbfc4713b b/tests/fuzz/sftp-corpus/83dee3c79e7371aecff01fa92465557bbfc4713b new file mode 100644 index 00000000..af79f3b7 Binary files /dev/null and b/tests/fuzz/sftp-corpus/83dee3c79e7371aecff01fa92465557bbfc4713b differ diff --git a/tests/fuzz/sftp-corpus/854fca7a34eb871fa101f171517dcf790ad56802 b/tests/fuzz/sftp-corpus/854fca7a34eb871fa101f171517dcf790ad56802 new file mode 100644 index 00000000..316f4b64 Binary files /dev/null and b/tests/fuzz/sftp-corpus/854fca7a34eb871fa101f171517dcf790ad56802 differ diff --git a/tests/fuzz/sftp-corpus/86c790e576d84604190d2c8fe6df729824a25d45 b/tests/fuzz/sftp-corpus/86c790e576d84604190d2c8fe6df729824a25d45 new file mode 100644 index 00000000..32e8d6b0 --- /dev/null +++ b/tests/fuzz/sftp-corpus/86c790e576d84604190d2c8fe6df729824a25d45 @@ -0,0 +1 @@ +ù < \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/87996e22d70b0b8dc5bbba019add15eaebf1cbfa b/tests/fuzz/sftp-corpus/87996e22d70b0b8dc5bbba019add15eaebf1cbfa new file mode 100644 index 00000000..f7eba941 Binary files /dev/null and b/tests/fuzz/sftp-corpus/87996e22d70b0b8dc5bbba019add15eaebf1cbfa differ diff --git a/tests/fuzz/sftp-corpus/889c73159bc726514772a3e41941cc92389d2dac b/tests/fuzz/sftp-corpus/889c73159bc726514772a3e41941cc92389d2dac new file mode 100644 index 00000000..f0268091 Binary files /dev/null and b/tests/fuzz/sftp-corpus/889c73159bc726514772a3e41941cc92389d2dac differ diff --git a/tests/fuzz/sftp-corpus/88c58523a1e0d3c3cb2124b17e951fb6888eb55b b/tests/fuzz/sftp-corpus/88c58523a1e0d3c3cb2124b17e951fb6888eb55b new file mode 100644 index 00000000..b409d545 Binary files /dev/null and b/tests/fuzz/sftp-corpus/88c58523a1e0d3c3cb2124b17e951fb6888eb55b differ diff --git a/tests/fuzz/sftp-corpus/89dc00dadcd7c952663a68893460456d048a7a38 b/tests/fuzz/sftp-corpus/89dc00dadcd7c952663a68893460456d048a7a38 new file mode 100644 index 00000000..0364b2a4 Binary files /dev/null and b/tests/fuzz/sftp-corpus/89dc00dadcd7c952663a68893460456d048a7a38 differ diff --git a/tests/fuzz/sftp-corpus/8ba2e8e067f8cdf84a6fd3ac8af7913aebb18370 b/tests/fuzz/sftp-corpus/8ba2e8e067f8cdf84a6fd3ac8af7913aebb18370 new file mode 100644 index 00000000..99cd83f1 --- /dev/null +++ b/tests/fuzz/sftp-corpus/8ba2e8e067f8cdf84a6fd3ac8af7913aebb18370 @@ -0,0 +1 @@ +·; \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/8c1e6ab4270792c51304ea06f47dc20ce51ba57b b/tests/fuzz/sftp-corpus/8c1e6ab4270792c51304ea06f47dc20ce51ba57b new file mode 100644 index 00000000..195ee381 --- /dev/null +++ b/tests/fuzz/sftp-corpus/8c1e6ab4270792c51304ea06f47dc20ce51ba57b @@ -0,0 +1 @@ +È \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/8cfc77ce6f11c7d6e7e1938e1d39fb02a3fc51d1 b/tests/fuzz/sftp-corpus/8cfc77ce6f11c7d6e7e1938e1d39fb02a3fc51d1 new file mode 100644 index 00000000..dc70c571 Binary files /dev/null and b/tests/fuzz/sftp-corpus/8cfc77ce6f11c7d6e7e1938e1d39fb02a3fc51d1 differ diff --git a/tests/fuzz/sftp-corpus/8d883f1577ca8c334b7c6d75ccb71209d71ced13 b/tests/fuzz/sftp-corpus/8d883f1577ca8c334b7c6d75ccb71209d71ced13 new file mode 100644 index 00000000..5a77f058 --- /dev/null +++ b/tests/fuzz/sftp-corpus/8d883f1577ca8c334b7c6d75ccb71209d71ced13 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/8dc00598417d4eb788a77ac6ccef3cb484905d8b b/tests/fuzz/sftp-corpus/8dc00598417d4eb788a77ac6ccef3cb484905d8b new file mode 100644 index 00000000..b0b2b1c8 --- /dev/null +++ b/tests/fuzz/sftp-corpus/8dc00598417d4eb788a77ac6ccef3cb484905d8b @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/9166d0999f92c20f325fd29b7faab06fbf44e29a b/tests/fuzz/sftp-corpus/9166d0999f92c20f325fd29b7faab06fbf44e29a new file mode 100644 index 00000000..eeaf11a3 Binary files /dev/null and b/tests/fuzz/sftp-corpus/9166d0999f92c20f325fd29b7faab06fbf44e29a differ diff --git a/tests/fuzz/sftp-corpus/9198bae0a5489db59f5d7285e404beb7726d532f b/tests/fuzz/sftp-corpus/9198bae0a5489db59f5d7285e404beb7726d532f new file mode 100644 index 00000000..514b70e0 Binary files /dev/null and b/tests/fuzz/sftp-corpus/9198bae0a5489db59f5d7285e404beb7726d532f differ diff --git a/tests/fuzz/sftp-corpus/95a8b7d543019355b2cdd74b1bb676b5a29bc5a4 b/tests/fuzz/sftp-corpus/95a8b7d543019355b2cdd74b1bb676b5a29bc5a4 new file mode 100644 index 00000000..0766c645 Binary files /dev/null and b/tests/fuzz/sftp-corpus/95a8b7d543019355b2cdd74b1bb676b5a29bc5a4 differ diff --git a/tests/fuzz/sftp-corpus/95cc6eb0fc7a3f4f9710de848cb15007ab8b950a b/tests/fuzz/sftp-corpus/95cc6eb0fc7a3f4f9710de848cb15007ab8b950a new file mode 100644 index 00000000..918c51dd Binary files /dev/null and b/tests/fuzz/sftp-corpus/95cc6eb0fc7a3f4f9710de848cb15007ab8b950a differ diff --git a/tests/fuzz/sftp-corpus/96b666b912f4834b427791313834b68fd2dc2161 b/tests/fuzz/sftp-corpus/96b666b912f4834b427791313834b68fd2dc2161 new file mode 100644 index 00000000..85f26a52 Binary files /dev/null and b/tests/fuzz/sftp-corpus/96b666b912f4834b427791313834b68fd2dc2161 differ diff --git a/tests/fuzz/sftp-corpus/977a51d4b9e9908a468851d13927ee5bfeb1ba2e b/tests/fuzz/sftp-corpus/977a51d4b9e9908a468851d13927ee5bfeb1ba2e new file mode 100644 index 00000000..dc50e98d Binary files /dev/null and b/tests/fuzz/sftp-corpus/977a51d4b9e9908a468851d13927ee5bfeb1ba2e differ diff --git a/tests/fuzz/sftp-corpus/97a3254991cf76a5b57619a1f0b0d621c3156039 b/tests/fuzz/sftp-corpus/97a3254991cf76a5b57619a1f0b0d621c3156039 new file mode 100644 index 00000000..36e19128 Binary files /dev/null and b/tests/fuzz/sftp-corpus/97a3254991cf76a5b57619a1f0b0d621c3156039 differ diff --git a/tests/fuzz/sftp-corpus/9842926af7ca0a8cca12604f945414f07b01e13d b/tests/fuzz/sftp-corpus/9842926af7ca0a8cca12604f945414f07b01e13d new file mode 100644 index 00000000..fc2b5693 --- /dev/null +++ b/tests/fuzz/sftp-corpus/9842926af7ca0a8cca12604f945414f07b01e13d @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/98e86f35a726d9cb537c2d9c13c78cb7f2beb6dd b/tests/fuzz/sftp-corpus/98e86f35a726d9cb537c2d9c13c78cb7f2beb6dd new file mode 100644 index 00000000..73b42789 Binary files /dev/null and b/tests/fuzz/sftp-corpus/98e86f35a726d9cb537c2d9c13c78cb7f2beb6dd differ diff --git a/tests/fuzz/sftp-corpus/99700ed980eee6403ec5cb65c7eab58cbff25e39 b/tests/fuzz/sftp-corpus/99700ed980eee6403ec5cb65c7eab58cbff25e39 new file mode 100644 index 00000000..0c1a4016 Binary files /dev/null and b/tests/fuzz/sftp-corpus/99700ed980eee6403ec5cb65c7eab58cbff25e39 differ diff --git a/tests/fuzz/sftp-corpus/99e14fc133cbaca666b6525a72b430184acc8945 b/tests/fuzz/sftp-corpus/99e14fc133cbaca666b6525a72b430184acc8945 new file mode 100644 index 00000000..3643ded8 Binary files /dev/null and b/tests/fuzz/sftp-corpus/99e14fc133cbaca666b6525a72b430184acc8945 differ diff --git a/tests/fuzz/sftp-corpus/99e9ed356525c84eec450a863545bdea9c121e17 b/tests/fuzz/sftp-corpus/99e9ed356525c84eec450a863545bdea9c121e17 new file mode 100644 index 00000000..1e91aea2 Binary files /dev/null and b/tests/fuzz/sftp-corpus/99e9ed356525c84eec450a863545bdea9c121e17 differ diff --git a/tests/fuzz/sftp-corpus/9ab2611cdd8e5b1dc71ebf5c88ef2d9c18f2b5c2 b/tests/fuzz/sftp-corpus/9ab2611cdd8e5b1dc71ebf5c88ef2d9c18f2b5c2 new file mode 100644 index 00000000..84eefe21 Binary files /dev/null and b/tests/fuzz/sftp-corpus/9ab2611cdd8e5b1dc71ebf5c88ef2d9c18f2b5c2 differ diff --git a/tests/fuzz/sftp-corpus/9be547651566eccc263604d9064c1c14702fb9cd b/tests/fuzz/sftp-corpus/9be547651566eccc263604d9064c1c14702fb9cd new file mode 100644 index 00000000..b19fa557 Binary files /dev/null and b/tests/fuzz/sftp-corpus/9be547651566eccc263604d9064c1c14702fb9cd differ diff --git a/tests/fuzz/sftp-corpus/9bf8c39ea8e210ce4ea8de6724d813dee1e97d27 b/tests/fuzz/sftp-corpus/9bf8c39ea8e210ce4ea8de6724d813dee1e97d27 new file mode 100644 index 00000000..ecc5dcb2 Binary files /dev/null and b/tests/fuzz/sftp-corpus/9bf8c39ea8e210ce4ea8de6724d813dee1e97d27 differ diff --git a/tests/fuzz/sftp-corpus/9cf0c05423f5eab37cfeff6f5e89469c362cda21 b/tests/fuzz/sftp-corpus/9cf0c05423f5eab37cfeff6f5e89469c362cda21 new file mode 100644 index 00000000..f897fb3b Binary files /dev/null and b/tests/fuzz/sftp-corpus/9cf0c05423f5eab37cfeff6f5e89469c362cda21 differ diff --git a/tests/fuzz/sftp-corpus/a183bd04033558c87c30c5e8c673f2e7084eb587 b/tests/fuzz/sftp-corpus/a183bd04033558c87c30c5e8c673f2e7084eb587 new file mode 100644 index 00000000..b8c57343 Binary files /dev/null and b/tests/fuzz/sftp-corpus/a183bd04033558c87c30c5e8c673f2e7084eb587 differ diff --git a/tests/fuzz/sftp-corpus/a226b456f772adb909f236df9e22a1e734f34238 b/tests/fuzz/sftp-corpus/a226b456f772adb909f236df9e22a1e734f34238 new file mode 100644 index 00000000..89bcf1f6 Binary files /dev/null and b/tests/fuzz/sftp-corpus/a226b456f772adb909f236df9e22a1e734f34238 differ diff --git a/tests/fuzz/sftp-corpus/a24bb6244531f23a2ad4c902ee9512cc38f7fdf4 b/tests/fuzz/sftp-corpus/a24bb6244531f23a2ad4c902ee9512cc38f7fdf4 new file mode 100644 index 00000000..93746289 Binary files /dev/null and b/tests/fuzz/sftp-corpus/a24bb6244531f23a2ad4c902ee9512cc38f7fdf4 differ diff --git a/tests/fuzz/sftp-corpus/a347d02f9c329a6ea24b8722a1ec39e6a3a179cf b/tests/fuzz/sftp-corpus/a347d02f9c329a6ea24b8722a1ec39e6a3a179cf new file mode 100644 index 00000000..e98a8cf8 --- /dev/null +++ b/tests/fuzz/sftp-corpus/a347d02f9c329a6ea24b8722a1ec39e6a3a179cf @@ -0,0 +1 @@ +;6 \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/a42c6cf1de3abfdea9b95f34687cbbe92b9a7383 b/tests/fuzz/sftp-corpus/a42c6cf1de3abfdea9b95f34687cbbe92b9a7383 new file mode 100644 index 00000000..45a8ca02 --- /dev/null +++ b/tests/fuzz/sftp-corpus/a42c6cf1de3abfdea9b95f34687cbbe92b9a7383 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/a56643c855a730e6373d32ce88af6887049e24ae b/tests/fuzz/sftp-corpus/a56643c855a730e6373d32ce88af6887049e24ae new file mode 100644 index 00000000..539c25a0 Binary files /dev/null and b/tests/fuzz/sftp-corpus/a56643c855a730e6373d32ce88af6887049e24ae differ diff --git a/tests/fuzz/sftp-corpus/a5ebef01cfcd36dcc045b0649991baed09d1f97c b/tests/fuzz/sftp-corpus/a5ebef01cfcd36dcc045b0649991baed09d1f97c new file mode 100644 index 00000000..0e95cd97 Binary files /dev/null and b/tests/fuzz/sftp-corpus/a5ebef01cfcd36dcc045b0649991baed09d1f97c differ diff --git a/tests/fuzz/sftp-corpus/a60922518f7a0d93fc61b13916ef2dc45026bdd6 b/tests/fuzz/sftp-corpus/a60922518f7a0d93fc61b13916ef2dc45026bdd6 new file mode 100644 index 00000000..d961f804 Binary files /dev/null and b/tests/fuzz/sftp-corpus/a60922518f7a0d93fc61b13916ef2dc45026bdd6 differ diff --git a/tests/fuzz/sftp-corpus/a74fa143d2c3fdd5e920e1aa2d124effb0838689 b/tests/fuzz/sftp-corpus/a74fa143d2c3fdd5e920e1aa2d124effb0838689 new file mode 100644 index 00000000..388da7bd Binary files /dev/null and b/tests/fuzz/sftp-corpus/a74fa143d2c3fdd5e920e1aa2d124effb0838689 differ diff --git a/tests/fuzz/sftp-corpus/a8abd012eb59b862bf9bc1ea443d2f35a1a2e222 b/tests/fuzz/sftp-corpus/a8abd012eb59b862bf9bc1ea443d2f35a1a2e222 new file mode 100644 index 00000000..3f1695f1 --- /dev/null +++ b/tests/fuzz/sftp-corpus/a8abd012eb59b862bf9bc1ea443d2f35a1a2e222 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/aa85c6adb0c4e9bda0e1b3bfeea6aa2449bfcc66 b/tests/fuzz/sftp-corpus/aa85c6adb0c4e9bda0e1b3bfeea6aa2449bfcc66 new file mode 100644 index 00000000..5dccbcae Binary files /dev/null and b/tests/fuzz/sftp-corpus/aa85c6adb0c4e9bda0e1b3bfeea6aa2449bfcc66 differ diff --git a/tests/fuzz/sftp-corpus/aa8fa2a4455098466a0aaf00411637a678f1fa68 b/tests/fuzz/sftp-corpus/aa8fa2a4455098466a0aaf00411637a678f1fa68 new file mode 100644 index 00000000..63e06033 Binary files /dev/null and b/tests/fuzz/sftp-corpus/aa8fa2a4455098466a0aaf00411637a678f1fa68 differ diff --git a/tests/fuzz/sftp-corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc b/tests/fuzz/sftp-corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/fuzz/sftp-corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc @@ -0,0 +1 @@ + diff --git a/tests/fuzz/sftp-corpus/af9e40dff4ea90df093b032c69e6bb3c8f4ab555 b/tests/fuzz/sftp-corpus/af9e40dff4ea90df093b032c69e6bb3c8f4ab555 new file mode 100644 index 00000000..debb2a85 Binary files /dev/null and b/tests/fuzz/sftp-corpus/af9e40dff4ea90df093b032c69e6bb3c8f4ab555 differ diff --git a/tests/fuzz/sftp-corpus/afb32e5be22a295a494adb7cda4ce7568b25bc14 b/tests/fuzz/sftp-corpus/afb32e5be22a295a494adb7cda4ce7568b25bc14 new file mode 100644 index 00000000..d41a566a Binary files /dev/null and b/tests/fuzz/sftp-corpus/afb32e5be22a295a494adb7cda4ce7568b25bc14 differ diff --git a/tests/fuzz/sftp-corpus/b05a91f0797b82bffe91d30a964ac15330baa26f b/tests/fuzz/sftp-corpus/b05a91f0797b82bffe91d30a964ac15330baa26f new file mode 100644 index 00000000..3713ba7a Binary files /dev/null and b/tests/fuzz/sftp-corpus/b05a91f0797b82bffe91d30a964ac15330baa26f differ diff --git a/tests/fuzz/sftp-corpus/b15b207bc1a8b5ddabd5749445cdd79e07e19312 b/tests/fuzz/sftp-corpus/b15b207bc1a8b5ddabd5749445cdd79e07e19312 new file mode 100644 index 00000000..90305026 Binary files /dev/null and b/tests/fuzz/sftp-corpus/b15b207bc1a8b5ddabd5749445cdd79e07e19312 differ diff --git a/tests/fuzz/sftp-corpus/b3915e3dc662f112d5ce728b4933dcdd4f7a17b9 b/tests/fuzz/sftp-corpus/b3915e3dc662f112d5ce728b4933dcdd4f7a17b9 new file mode 100644 index 00000000..eb63e3d9 --- /dev/null +++ b/tests/fuzz/sftp-corpus/b3915e3dc662f112d5ce728b4933dcdd4f7a17b9 @@ -0,0 +1,2 @@ +  +  \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/b40477d0dc849acd7b58fafb5a5eca20d72c59b5 b/tests/fuzz/sftp-corpus/b40477d0dc849acd7b58fafb5a5eca20d72c59b5 new file mode 100644 index 00000000..317cec27 Binary files /dev/null and b/tests/fuzz/sftp-corpus/b40477d0dc849acd7b58fafb5a5eca20d72c59b5 differ diff --git a/tests/fuzz/sftp-corpus/b9be97902843b7e75a2b0a86743dccb37e2be5a4 b/tests/fuzz/sftp-corpus/b9be97902843b7e75a2b0a86743dccb37e2be5a4 new file mode 100644 index 00000000..3e2b3829 Binary files /dev/null and b/tests/fuzz/sftp-corpus/b9be97902843b7e75a2b0a86743dccb37e2be5a4 differ diff --git a/tests/fuzz/sftp-corpus/bab33aa786059b87d29c24bc701f148708a9c00c b/tests/fuzz/sftp-corpus/bab33aa786059b87d29c24bc701f148708a9c00c new file mode 100644 index 00000000..d010565f Binary files /dev/null and b/tests/fuzz/sftp-corpus/bab33aa786059b87d29c24bc701f148708a9c00c differ diff --git a/tests/fuzz/sftp-corpus/bb8555952c37d9b359e4ed4beb01a01e6739a2d8 b/tests/fuzz/sftp-corpus/bb8555952c37d9b359e4ed4beb01a01e6739a2d8 new file mode 100644 index 00000000..ff536168 Binary files /dev/null and b/tests/fuzz/sftp-corpus/bb8555952c37d9b359e4ed4beb01a01e6739a2d8 differ diff --git a/tests/fuzz/sftp-corpus/bc32bcebf40fbdd63176d680db6e0223ca411216 b/tests/fuzz/sftp-corpus/bc32bcebf40fbdd63176d680db6e0223ca411216 new file mode 100644 index 00000000..aa5ceafb Binary files /dev/null and b/tests/fuzz/sftp-corpus/bc32bcebf40fbdd63176d680db6e0223ca411216 differ diff --git a/tests/fuzz/sftp-corpus/bd4b399f1be2d45e3a7f4be72dfcd8e16f516970 b/tests/fuzz/sftp-corpus/bd4b399f1be2d45e3a7f4be72dfcd8e16f516970 new file mode 100644 index 00000000..ec3257e7 Binary files /dev/null and b/tests/fuzz/sftp-corpus/bd4b399f1be2d45e3a7f4be72dfcd8e16f516970 differ diff --git a/tests/fuzz/sftp-corpus/bf8b4530d8d246dd74ac53a13471bba17941dff7 b/tests/fuzz/sftp-corpus/bf8b4530d8d246dd74ac53a13471bba17941dff7 new file mode 100644 index 00000000..6b2aaa76 --- /dev/null +++ b/tests/fuzz/sftp-corpus/bf8b4530d8d246dd74ac53a13471bba17941dff7 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/bfec9e9688dd3c8af1d4a21f4c4dc25580c76b67 b/tests/fuzz/sftp-corpus/bfec9e9688dd3c8af1d4a21f4c4dc25580c76b67 new file mode 100644 index 00000000..b00ffdd5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/bfec9e9688dd3c8af1d4a21f4c4dc25580c76b67 differ diff --git a/tests/fuzz/sftp-corpus/c0e79972f1be5a8105c2c2a92aa14697e884de9f b/tests/fuzz/sftp-corpus/c0e79972f1be5a8105c2c2a92aa14697e884de9f new file mode 100644 index 00000000..a853ae30 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c0e79972f1be5a8105c2c2a92aa14697e884de9f differ diff --git a/tests/fuzz/sftp-corpus/c1259f589b0998ce095b155318657e4d05b86a4c b/tests/fuzz/sftp-corpus/c1259f589b0998ce095b155318657e4d05b86a4c new file mode 100644 index 00000000..bb7854a5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c1259f589b0998ce095b155318657e4d05b86a4c differ diff --git a/tests/fuzz/sftp-corpus/c135d5f539e5c1c7293de4651f273290bba2d12a b/tests/fuzz/sftp-corpus/c135d5f539e5c1c7293de4651f273290bba2d12a new file mode 100644 index 00000000..6e1a31a2 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c135d5f539e5c1c7293de4651f273290bba2d12a differ diff --git a/tests/fuzz/sftp-corpus/c137ab0017f4f581299dafd3fb48949a7ef7bbc6 b/tests/fuzz/sftp-corpus/c137ab0017f4f581299dafd3fb48949a7ef7bbc6 new file mode 100644 index 00000000..19911feb Binary files /dev/null and b/tests/fuzz/sftp-corpus/c137ab0017f4f581299dafd3fb48949a7ef7bbc6 differ diff --git a/tests/fuzz/sftp-corpus/c20d0981edf727d6d9baaafec6dfcb524a09492f b/tests/fuzz/sftp-corpus/c20d0981edf727d6d9baaafec6dfcb524a09492f new file mode 100644 index 00000000..ccf09ec5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c20d0981edf727d6d9baaafec6dfcb524a09492f differ diff --git a/tests/fuzz/sftp-corpus/c24f2dc588d558697fdf019f4799d69611349a13 b/tests/fuzz/sftp-corpus/c24f2dc588d558697fdf019f4799d69611349a13 new file mode 100644 index 00000000..52c0f78d Binary files /dev/null and b/tests/fuzz/sftp-corpus/c24f2dc588d558697fdf019f4799d69611349a13 differ diff --git a/tests/fuzz/sftp-corpus/c25042331808b88cf8c48c8de62ab3dc38202713 b/tests/fuzz/sftp-corpus/c25042331808b88cf8c48c8de62ab3dc38202713 new file mode 100644 index 00000000..aa752e44 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c25042331808b88cf8c48c8de62ab3dc38202713 differ diff --git a/tests/fuzz/sftp-corpus/c437caec2f80f0db3a3114e900737894ef70b02a b/tests/fuzz/sftp-corpus/c437caec2f80f0db3a3114e900737894ef70b02a new file mode 100644 index 00000000..115ec707 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c437caec2f80f0db3a3114e900737894ef70b02a differ diff --git a/tests/fuzz/sftp-corpus/c4f87a6290aee1acfc1f26083974ce94621fca64 b/tests/fuzz/sftp-corpus/c4f87a6290aee1acfc1f26083974ce94621fca64 new file mode 100644 index 00000000..a4ceb359 --- /dev/null +++ b/tests/fuzz/sftp-corpus/c4f87a6290aee1acfc1f26083974ce94621fca64 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/c5b788b72af278fc8d2e932fd6475950b9c643b9 b/tests/fuzz/sftp-corpus/c5b788b72af278fc8d2e932fd6475950b9c643b9 new file mode 100644 index 00000000..5836126a Binary files /dev/null and b/tests/fuzz/sftp-corpus/c5b788b72af278fc8d2e932fd6475950b9c643b9 differ diff --git a/tests/fuzz/sftp-corpus/c6a8a65ca197980287cd7552222d358f242dd291 b/tests/fuzz/sftp-corpus/c6a8a65ca197980287cd7552222d358f242dd291 new file mode 100644 index 00000000..b702a883 Binary files /dev/null and b/tests/fuzz/sftp-corpus/c6a8a65ca197980287cd7552222d358f242dd291 differ diff --git a/tests/fuzz/sftp-corpus/c6ed189128b8aea136b71f237bd7dbd1fe4ac274 b/tests/fuzz/sftp-corpus/c6ed189128b8aea136b71f237bd7dbd1fe4ac274 new file mode 100644 index 00000000..233ff47b Binary files /dev/null and b/tests/fuzz/sftp-corpus/c6ed189128b8aea136b71f237bd7dbd1fe4ac274 differ diff --git a/tests/fuzz/sftp-corpus/c7255dc48b42d44f6c0676d6009051b7e1aa885b b/tests/fuzz/sftp-corpus/c7255dc48b42d44f6c0676d6009051b7e1aa885b new file mode 100644 index 00000000..c30d0581 --- /dev/null +++ b/tests/fuzz/sftp-corpus/c7255dc48b42d44f6c0676d6009051b7e1aa885b @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/c827978b7b51f7099ff741e5f1c1eac22b9a233a b/tests/fuzz/sftp-corpus/c827978b7b51f7099ff741e5f1c1eac22b9a233a new file mode 100644 index 00000000..c502ab3a Binary files /dev/null and b/tests/fuzz/sftp-corpus/c827978b7b51f7099ff741e5f1c1eac22b9a233a differ diff --git a/tests/fuzz/sftp-corpus/ca68f8a04d6c142929be9eef86ece8ec11cfae7b b/tests/fuzz/sftp-corpus/ca68f8a04d6c142929be9eef86ece8ec11cfae7b new file mode 100644 index 00000000..e24200bd Binary files /dev/null and b/tests/fuzz/sftp-corpus/ca68f8a04d6c142929be9eef86ece8ec11cfae7b differ diff --git a/tests/fuzz/sftp-corpus/ca9ec7d8d365fe163b1c8ec42963ae6b60f581a9 b/tests/fuzz/sftp-corpus/ca9ec7d8d365fe163b1c8ec42963ae6b60f581a9 new file mode 100644 index 00000000..2a9d5dea Binary files /dev/null and b/tests/fuzz/sftp-corpus/ca9ec7d8d365fe163b1c8ec42963ae6b60f581a9 differ diff --git a/tests/fuzz/sftp-corpus/cb8357675ec9519fcd37453d46cf158970c80ad4 b/tests/fuzz/sftp-corpus/cb8357675ec9519fcd37453d46cf158970c80ad4 new file mode 100644 index 00000000..159f82ce Binary files /dev/null and b/tests/fuzz/sftp-corpus/cb8357675ec9519fcd37453d46cf158970c80ad4 differ diff --git a/tests/fuzz/sftp-corpus/cc4eaf337a6e26215ddd062e1d541228dab72848 b/tests/fuzz/sftp-corpus/cc4eaf337a6e26215ddd062e1d541228dab72848 new file mode 100644 index 00000000..359b9a74 Binary files /dev/null and b/tests/fuzz/sftp-corpus/cc4eaf337a6e26215ddd062e1d541228dab72848 differ diff --git a/tests/fuzz/sftp-corpus/ccd0473957ed803fad7c77b847092606f3f710f6 b/tests/fuzz/sftp-corpus/ccd0473957ed803fad7c77b847092606f3f710f6 new file mode 100644 index 00000000..2394a24c Binary files /dev/null and b/tests/fuzz/sftp-corpus/ccd0473957ed803fad7c77b847092606f3f710f6 differ diff --git a/tests/fuzz/sftp-corpus/cdf222b3c2ea49d30763bd2bb2c9d18605de0bc6 b/tests/fuzz/sftp-corpus/cdf222b3c2ea49d30763bd2bb2c9d18605de0bc6 new file mode 100644 index 00000000..4a62e5b9 Binary files /dev/null and b/tests/fuzz/sftp-corpus/cdf222b3c2ea49d30763bd2bb2c9d18605de0bc6 differ diff --git a/tests/fuzz/sftp-corpus/ce2ae1dbdf84290c8e8fca3e43a250ee6be30625 b/tests/fuzz/sftp-corpus/ce2ae1dbdf84290c8e8fca3e43a250ee6be30625 new file mode 100644 index 00000000..a9cdcfba Binary files /dev/null and b/tests/fuzz/sftp-corpus/ce2ae1dbdf84290c8e8fca3e43a250ee6be30625 differ diff --git a/tests/fuzz/sftp-corpus/ce38c92a02b901ec58fb573a9f469b8d899cdfa6 b/tests/fuzz/sftp-corpus/ce38c92a02b901ec58fb573a9f469b8d899cdfa6 new file mode 100644 index 00000000..ef444f6b Binary files /dev/null and b/tests/fuzz/sftp-corpus/ce38c92a02b901ec58fb573a9f469b8d899cdfa6 differ diff --git a/tests/fuzz/sftp-corpus/cecdeb40caec896ac0600443f3c3834d1a9e3443 b/tests/fuzz/sftp-corpus/cecdeb40caec896ac0600443f3c3834d1a9e3443 new file mode 100644 index 00000000..86524d0f Binary files /dev/null and b/tests/fuzz/sftp-corpus/cecdeb40caec896ac0600443f3c3834d1a9e3443 differ diff --git a/tests/fuzz/sftp-corpus/cf1634a87ec1e2f916b2d8b74a60942b6b9f7222 b/tests/fuzz/sftp-corpus/cf1634a87ec1e2f916b2d8b74a60942b6b9f7222 new file mode 100644 index 00000000..e6ea2551 Binary files /dev/null and b/tests/fuzz/sftp-corpus/cf1634a87ec1e2f916b2d8b74a60942b6b9f7222 differ diff --git a/tests/fuzz/sftp-corpus/cfae9b5ee2b64427b8a63ffb8ea1fd5172a79d8b b/tests/fuzz/sftp-corpus/cfae9b5ee2b64427b8a63ffb8ea1fd5172a79d8b new file mode 100644 index 00000000..5c93b657 Binary files /dev/null and b/tests/fuzz/sftp-corpus/cfae9b5ee2b64427b8a63ffb8ea1fd5172a79d8b differ diff --git a/tests/fuzz/sftp-corpus/d01f7cbbd404f94459d962d84b4b5fd3168bfaf1 b/tests/fuzz/sftp-corpus/d01f7cbbd404f94459d962d84b4b5fd3168bfaf1 new file mode 100644 index 00000000..6edeee1f Binary files /dev/null and b/tests/fuzz/sftp-corpus/d01f7cbbd404f94459d962d84b4b5fd3168bfaf1 differ diff --git a/tests/fuzz/sftp-corpus/d0dd7f221a6d39b8184d686772157c854891c677 b/tests/fuzz/sftp-corpus/d0dd7f221a6d39b8184d686772157c854891c677 new file mode 100644 index 00000000..4ea70fe7 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d0dd7f221a6d39b8184d686772157c854891c677 differ diff --git a/tests/fuzz/sftp-corpus/d1dccc4d9d3fdec52f99e8952c0e9d98080a07e3 b/tests/fuzz/sftp-corpus/d1dccc4d9d3fdec52f99e8952c0e9d98080a07e3 new file mode 100644 index 00000000..dac656df Binary files /dev/null and b/tests/fuzz/sftp-corpus/d1dccc4d9d3fdec52f99e8952c0e9d98080a07e3 differ diff --git a/tests/fuzz/sftp-corpus/d1f8c090ae6b532382bd13e4467ca86c38207fca b/tests/fuzz/sftp-corpus/d1f8c090ae6b532382bd13e4467ca86c38207fca new file mode 100644 index 00000000..03054b69 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d1f8c090ae6b532382bd13e4467ca86c38207fca differ diff --git a/tests/fuzz/sftp-corpus/d373d9943a77b723fb105c007a69a1002a07d251 b/tests/fuzz/sftp-corpus/d373d9943a77b723fb105c007a69a1002a07d251 new file mode 100644 index 00000000..26b022a7 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d373d9943a77b723fb105c007a69a1002a07d251 differ diff --git a/tests/fuzz/sftp-corpus/d3a786f9fb2d3e93b8b52f3f3bb3e97c90215a3f b/tests/fuzz/sftp-corpus/d3a786f9fb2d3e93b8b52f3f3bb3e97c90215a3f new file mode 100644 index 00000000..6af0704d Binary files /dev/null and b/tests/fuzz/sftp-corpus/d3a786f9fb2d3e93b8b52f3f3bb3e97c90215a3f differ diff --git a/tests/fuzz/sftp-corpus/d445354f08cb28695e96ee56232469ab2d0d2a54 b/tests/fuzz/sftp-corpus/d445354f08cb28695e96ee56232469ab2d0d2a54 new file mode 100644 index 00000000..e83af181 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d445354f08cb28695e96ee56232469ab2d0d2a54 differ diff --git a/tests/fuzz/sftp-corpus/d585b0a92c38827c7307dbc7991b83a2f89eaed2 b/tests/fuzz/sftp-corpus/d585b0a92c38827c7307dbc7991b83a2f89eaed2 new file mode 100644 index 00000000..9684069e Binary files /dev/null and b/tests/fuzz/sftp-corpus/d585b0a92c38827c7307dbc7991b83a2f89eaed2 differ diff --git a/tests/fuzz/sftp-corpus/d5dbf54ceac80fa41f6419c49714ecd5268c2f41 b/tests/fuzz/sftp-corpus/d5dbf54ceac80fa41f6419c49714ecd5268c2f41 new file mode 100644 index 00000000..b0697bad Binary files /dev/null and b/tests/fuzz/sftp-corpus/d5dbf54ceac80fa41f6419c49714ecd5268c2f41 differ diff --git a/tests/fuzz/sftp-corpus/d6d9a45a7dacdc2d9505c325b424122a07a801fb b/tests/fuzz/sftp-corpus/d6d9a45a7dacdc2d9505c325b424122a07a801fb new file mode 100644 index 00000000..53347046 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d6d9a45a7dacdc2d9505c325b424122a07a801fb differ diff --git a/tests/fuzz/sftp-corpus/d724e314a279e70a3e00c536fe4f5604434d140e b/tests/fuzz/sftp-corpus/d724e314a279e70a3e00c536fe4f5604434d140e new file mode 100644 index 00000000..0756c223 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d724e314a279e70a3e00c536fe4f5604434d140e differ diff --git a/tests/fuzz/sftp-corpus/d886ba3a14eecaa5e37cc6252a079bc825f2d08e b/tests/fuzz/sftp-corpus/d886ba3a14eecaa5e37cc6252a079bc825f2d08e new file mode 100644 index 00000000..8938eb5e Binary files /dev/null and b/tests/fuzz/sftp-corpus/d886ba3a14eecaa5e37cc6252a079bc825f2d08e differ diff --git a/tests/fuzz/sftp-corpus/d98310823926e66de1bc407a9180128de78e49ca b/tests/fuzz/sftp-corpus/d98310823926e66de1bc407a9180128de78e49ca new file mode 100644 index 00000000..5d937c92 Binary files /dev/null and b/tests/fuzz/sftp-corpus/d98310823926e66de1bc407a9180128de78e49ca differ diff --git a/tests/fuzz/sftp-corpus/db916524f30d23812d0b79742b0c6a1913016bcd b/tests/fuzz/sftp-corpus/db916524f30d23812d0b79742b0c6a1913016bcd new file mode 100644 index 00000000..d5d3d50a Binary files /dev/null and b/tests/fuzz/sftp-corpus/db916524f30d23812d0b79742b0c6a1913016bcd differ diff --git a/tests/fuzz/sftp-corpus/deba0818e59e38b20cdda6baf370b75629af727e b/tests/fuzz/sftp-corpus/deba0818e59e38b20cdda6baf370b75629af727e new file mode 100644 index 00000000..6230cdb1 Binary files /dev/null and b/tests/fuzz/sftp-corpus/deba0818e59e38b20cdda6baf370b75629af727e differ diff --git a/tests/fuzz/sftp-corpus/dfc69802de0242559b7b60d6d9b44be9d0397665 b/tests/fuzz/sftp-corpus/dfc69802de0242559b7b60d6d9b44be9d0397665 new file mode 100644 index 00000000..17f7c079 Binary files /dev/null and b/tests/fuzz/sftp-corpus/dfc69802de0242559b7b60d6d9b44be9d0397665 differ diff --git a/tests/fuzz/sftp-corpus/e040e1c72b5773eb10800d74c816b6ee3336bcb0 b/tests/fuzz/sftp-corpus/e040e1c72b5773eb10800d74c816b6ee3336bcb0 new file mode 100644 index 00000000..f191086d Binary files /dev/null and b/tests/fuzz/sftp-corpus/e040e1c72b5773eb10800d74c816b6ee3336bcb0 differ diff --git a/tests/fuzz/sftp-corpus/e1c1682b5edba8d3d993306da764fc4e25de6035 b/tests/fuzz/sftp-corpus/e1c1682b5edba8d3d993306da764fc4e25de6035 new file mode 100644 index 00000000..f415db35 --- /dev/null +++ b/tests/fuzz/sftp-corpus/e1c1682b5edba8d3d993306da764fc4e25de6035 @@ -0,0 +1 @@ + 0A \ No newline at end of file diff --git a/tests/fuzz/sftp-corpus/e1fbfe7552a4d379cf01c54e59a3dc8fbeb18ff2 b/tests/fuzz/sftp-corpus/e1fbfe7552a4d379cf01c54e59a3dc8fbeb18ff2 new file mode 100644 index 00000000..13924634 Binary files /dev/null and b/tests/fuzz/sftp-corpus/e1fbfe7552a4d379cf01c54e59a3dc8fbeb18ff2 differ diff --git a/tests/fuzz/sftp-corpus/e3e15898cb2d35dfd7a3b440f71d3006dfd097ee b/tests/fuzz/sftp-corpus/e3e15898cb2d35dfd7a3b440f71d3006dfd097ee new file mode 100644 index 00000000..deda6c4e Binary files /dev/null and b/tests/fuzz/sftp-corpus/e3e15898cb2d35dfd7a3b440f71d3006dfd097ee differ diff --git a/tests/fuzz/sftp-corpus/e44ed304ed48883b19441eede8bd9664803c0ac3 b/tests/fuzz/sftp-corpus/e44ed304ed48883b19441eede8bd9664803c0ac3 new file mode 100644 index 00000000..fdb084b8 Binary files /dev/null and b/tests/fuzz/sftp-corpus/e44ed304ed48883b19441eede8bd9664803c0ac3 differ diff --git a/tests/fuzz/sftp-corpus/e4eefa7b2ffa044ccfb9f9057b7c02726c060367 b/tests/fuzz/sftp-corpus/e4eefa7b2ffa044ccfb9f9057b7c02726c060367 new file mode 100644 index 00000000..e1803f36 Binary files /dev/null and b/tests/fuzz/sftp-corpus/e4eefa7b2ffa044ccfb9f9057b7c02726c060367 differ diff --git a/tests/fuzz/sftp-corpus/e591b380040d6b395768cb1a3678bcfd34b8b90a b/tests/fuzz/sftp-corpus/e591b380040d6b395768cb1a3678bcfd34b8b90a new file mode 100644 index 00000000..f5e15bdd Binary files /dev/null and b/tests/fuzz/sftp-corpus/e591b380040d6b395768cb1a3678bcfd34b8b90a differ diff --git a/tests/fuzz/sftp-corpus/e7db786784ed017c7bff59cae28ed8c26fadac4b b/tests/fuzz/sftp-corpus/e7db786784ed017c7bff59cae28ed8c26fadac4b new file mode 100644 index 00000000..cce598d6 Binary files /dev/null and b/tests/fuzz/sftp-corpus/e7db786784ed017c7bff59cae28ed8c26fadac4b differ diff --git a/tests/fuzz/sftp-corpus/eb18afe0e724f2c05fc421bdbd2bd52bc94ab9eb b/tests/fuzz/sftp-corpus/eb18afe0e724f2c05fc421bdbd2bd52bc94ab9eb new file mode 100644 index 00000000..463a6a2b Binary files /dev/null and b/tests/fuzz/sftp-corpus/eb18afe0e724f2c05fc421bdbd2bd52bc94ab9eb differ diff --git a/tests/fuzz/sftp-corpus/ee275c9f32bce9de252933ffa591838a7d233a5f b/tests/fuzz/sftp-corpus/ee275c9f32bce9de252933ffa591838a7d233a5f new file mode 100644 index 00000000..c8e9e064 Binary files /dev/null and b/tests/fuzz/sftp-corpus/ee275c9f32bce9de252933ffa591838a7d233a5f differ diff --git a/tests/fuzz/sftp-corpus/ee36a5215bd209a32c58da812672a1cd9028fe16 b/tests/fuzz/sftp-corpus/ee36a5215bd209a32c58da812672a1cd9028fe16 new file mode 100644 index 00000000..47523658 Binary files /dev/null and b/tests/fuzz/sftp-corpus/ee36a5215bd209a32c58da812672a1cd9028fe16 differ diff --git a/tests/fuzz/sftp-corpus/ee40d3c7e952bfe9d1b03406ff7a2acc4448ba4c b/tests/fuzz/sftp-corpus/ee40d3c7e952bfe9d1b03406ff7a2acc4448ba4c new file mode 100644 index 00000000..5b0c0d5f Binary files /dev/null and b/tests/fuzz/sftp-corpus/ee40d3c7e952bfe9d1b03406ff7a2acc4448ba4c differ diff --git a/tests/fuzz/sftp-corpus/ef09d0f1b392f86b16823f601b7e43d05f81a394 b/tests/fuzz/sftp-corpus/ef09d0f1b392f86b16823f601b7e43d05f81a394 new file mode 100644 index 00000000..6594a7cf Binary files /dev/null and b/tests/fuzz/sftp-corpus/ef09d0f1b392f86b16823f601b7e43d05f81a394 differ diff --git a/tests/fuzz/sftp-corpus/f0379f54d742c9d9e1187f8661c77b05a70fa9f5 b/tests/fuzz/sftp-corpus/f0379f54d742c9d9e1187f8661c77b05a70fa9f5 new file mode 100644 index 00000000..282f75be Binary files /dev/null and b/tests/fuzz/sftp-corpus/f0379f54d742c9d9e1187f8661c77b05a70fa9f5 differ diff --git a/tests/fuzz/sftp-corpus/f0715009aeaf8cf6da0d9e9677598d1824880da5 b/tests/fuzz/sftp-corpus/f0715009aeaf8cf6da0d9e9677598d1824880da5 new file mode 100644 index 00000000..1311f679 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f0715009aeaf8cf6da0d9e9677598d1824880da5 differ diff --git a/tests/fuzz/sftp-corpus/f14c6ab53785a6ee7b4614ac309108ca2a005c25 b/tests/fuzz/sftp-corpus/f14c6ab53785a6ee7b4614ac309108ca2a005c25 new file mode 100644 index 00000000..a65004a5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f14c6ab53785a6ee7b4614ac309108ca2a005c25 differ diff --git a/tests/fuzz/sftp-corpus/f1755fe58cbfab2a87381ff8696c40dc948b5b79 b/tests/fuzz/sftp-corpus/f1755fe58cbfab2a87381ff8696c40dc948b5b79 new file mode 100644 index 00000000..cac48885 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f1755fe58cbfab2a87381ff8696c40dc948b5b79 differ diff --git a/tests/fuzz/sftp-corpus/f52937d5469703e91bce16207b971ca5134daae4 b/tests/fuzz/sftp-corpus/f52937d5469703e91bce16207b971ca5134daae4 new file mode 100644 index 00000000..5537de51 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f52937d5469703e91bce16207b971ca5134daae4 differ diff --git a/tests/fuzz/sftp-corpus/f566012827e68482a9e74b28d16ba4d93657d6f7 b/tests/fuzz/sftp-corpus/f566012827e68482a9e74b28d16ba4d93657d6f7 new file mode 100644 index 00000000..24c36c40 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f566012827e68482a9e74b28d16ba4d93657d6f7 differ diff --git a/tests/fuzz/sftp-corpus/f5a5ec60825f59406bb8b77e4f0bccb3980f88aa b/tests/fuzz/sftp-corpus/f5a5ec60825f59406bb8b77e4f0bccb3980f88aa new file mode 100644 index 00000000..8db3f715 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f5a5ec60825f59406bb8b77e4f0bccb3980f88aa differ diff --git a/tests/fuzz/sftp-corpus/f7a368896a3a1a2ae54a580fa89aa65561a7704b b/tests/fuzz/sftp-corpus/f7a368896a3a1a2ae54a580fa89aa65561a7704b new file mode 100644 index 00000000..ff9b3285 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f7a368896a3a1a2ae54a580fa89aa65561a7704b differ diff --git a/tests/fuzz/sftp-corpus/f7de4f8d46c0226c588d17fd26f99119cfcc1125 b/tests/fuzz/sftp-corpus/f7de4f8d46c0226c588d17fd26f99119cfcc1125 new file mode 100644 index 00000000..d0643cbb Binary files /dev/null and b/tests/fuzz/sftp-corpus/f7de4f8d46c0226c588d17fd26f99119cfcc1125 differ diff --git a/tests/fuzz/sftp-corpus/f83a6add0a0e56ae759d5daa6bcf0943c829052c b/tests/fuzz/sftp-corpus/f83a6add0a0e56ae759d5daa6bcf0943c829052c new file mode 100644 index 00000000..7e275287 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f83a6add0a0e56ae759d5daa6bcf0943c829052c differ diff --git a/tests/fuzz/sftp-corpus/f929580b5b5dece30bf16a93a6f2409a7c5418e0 b/tests/fuzz/sftp-corpus/f929580b5b5dece30bf16a93a6f2409a7c5418e0 new file mode 100644 index 00000000..bbc0a2be Binary files /dev/null and b/tests/fuzz/sftp-corpus/f929580b5b5dece30bf16a93a6f2409a7c5418e0 differ diff --git a/tests/fuzz/sftp-corpus/f9c477b39d700a18d5a6f523007a37c3ea1c7d7a b/tests/fuzz/sftp-corpus/f9c477b39d700a18d5a6f523007a37c3ea1c7d7a new file mode 100644 index 00000000..896980a5 Binary files /dev/null and b/tests/fuzz/sftp-corpus/f9c477b39d700a18d5a6f523007a37c3ea1c7d7a differ diff --git a/tests/fuzz/sftp-corpus/fc8f99eeffb875c78806af46376635650fe58a33 b/tests/fuzz/sftp-corpus/fc8f99eeffb875c78806af46376635650fe58a33 new file mode 100644 index 00000000..ec41c3f8 Binary files /dev/null and b/tests/fuzz/sftp-corpus/fc8f99eeffb875c78806af46376635650fe58a33 differ diff --git a/tests/fuzz/sftp-corpus/fd314dedbcc28cb457e1aeb6114fa58b8cc4e6c8 b/tests/fuzz/sftp-corpus/fd314dedbcc28cb457e1aeb6114fa58b8cc4e6c8 new file mode 100644 index 00000000..3ae11060 Binary files /dev/null and b/tests/fuzz/sftp-corpus/fd314dedbcc28cb457e1aeb6114fa58b8cc4e6c8 differ diff --git a/tests/fuzz/sftp-corpus/fd60501f2f0189b0a05f0f9e7053eb7b10401f0f b/tests/fuzz/sftp-corpus/fd60501f2f0189b0a05f0f9e7053eb7b10401f0f new file mode 100644 index 00000000..584f0997 Binary files /dev/null and b/tests/fuzz/sftp-corpus/fd60501f2f0189b0a05f0f9e7053eb7b10401f0f differ diff --git a/tests/fuzz/sftp-corpus/fda31647598a53a4fcce5a3f431347a9b885380a b/tests/fuzz/sftp-corpus/fda31647598a53a4fcce5a3f431347a9b885380a new file mode 100644 index 00000000..e5b92cee Binary files /dev/null and b/tests/fuzz/sftp-corpus/fda31647598a53a4fcce5a3f431347a9b885380a differ diff --git a/tests/fuzz/ssh-packet-corpus/02c97b82a92acdf62134a896c5889c7f3a5742a0 b/tests/fuzz/ssh-packet-corpus/02c97b82a92acdf62134a896c5889c7f3a5742a0 new file mode 100644 index 00000000..ca2fffbc Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/02c97b82a92acdf62134a896c5889c7f3a5742a0 differ diff --git a/tests/fuzz/ssh-packet-corpus/10afcd1e6acc3603568c463d4894c5e75cce93ce b/tests/fuzz/ssh-packet-corpus/10afcd1e6acc3603568c463d4894c5e75cce93ce new file mode 100644 index 00000000..4b3e6a8e Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/10afcd1e6acc3603568c463d4894c5e75cce93ce differ diff --git a/tests/fuzz/ssh-packet-corpus/1971217ec3dbd181cde5e26a96b6127998975597 b/tests/fuzz/ssh-packet-corpus/1971217ec3dbd181cde5e26a96b6127998975597 new file mode 100644 index 00000000..1b77cc68 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/1971217ec3dbd181cde5e26a96b6127998975597 differ diff --git a/tests/fuzz/ssh-packet-corpus/23187ed5a27cb0ed1b1ff7ade57a2a868cfd853b b/tests/fuzz/ssh-packet-corpus/23187ed5a27cb0ed1b1ff7ade57a2a868cfd853b new file mode 100644 index 00000000..7e49f456 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/23187ed5a27cb0ed1b1ff7ade57a2a868cfd853b differ diff --git a/tests/fuzz/ssh-packet-corpus/26922e2e4072a635974d2e1e920be7ef619f0d62 b/tests/fuzz/ssh-packet-corpus/26922e2e4072a635974d2e1e920be7ef619f0d62 new file mode 100644 index 00000000..96ab2256 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/26922e2e4072a635974d2e1e920be7ef619f0d62 differ diff --git a/tests/fuzz/ssh-packet-corpus/3fb9d18b4f09bbec54aff1dd054f8ab15faefaee b/tests/fuzz/ssh-packet-corpus/3fb9d18b4f09bbec54aff1dd054f8ab15faefaee new file mode 100644 index 00000000..d8fb9482 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/3fb9d18b4f09bbec54aff1dd054f8ab15faefaee differ diff --git a/tests/fuzz/ssh-packet-corpus/4ef27c4dff97d88b1d35cb2ff860af9d3f35e6bf b/tests/fuzz/ssh-packet-corpus/4ef27c4dff97d88b1d35cb2ff860af9d3f35e6bf new file mode 100644 index 00000000..77180d06 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/4ef27c4dff97d88b1d35cb2ff860af9d3f35e6bf differ diff --git a/tests/fuzz/ssh-packet-corpus/5d8cd1bef791b8111200c6fe5b118dc9f2d1b1a3 b/tests/fuzz/ssh-packet-corpus/5d8cd1bef791b8111200c6fe5b118dc9f2d1b1a3 new file mode 100644 index 00000000..78b3b1c5 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/5d8cd1bef791b8111200c6fe5b118dc9f2d1b1a3 differ diff --git a/tests/fuzz/ssh-packet-corpus/5e8eb0627853298689ab1273e9f830bdd9c1fd04 b/tests/fuzz/ssh-packet-corpus/5e8eb0627853298689ab1273e9f830bdd9c1fd04 new file mode 100644 index 00000000..72d19b31 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/5e8eb0627853298689ab1273e9f830bdd9c1fd04 differ diff --git a/tests/fuzz/ssh-packet-corpus/6acace559e7af791cea6ba8556ac8740a1b1be45 b/tests/fuzz/ssh-packet-corpus/6acace559e7af791cea6ba8556ac8740a1b1be45 new file mode 100644 index 00000000..a7766813 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/6acace559e7af791cea6ba8556ac8740a1b1be45 differ diff --git a/tests/fuzz/ssh-packet-corpus/6c5bc7b11a212e4f2e2309052c1d23da97d29812 b/tests/fuzz/ssh-packet-corpus/6c5bc7b11a212e4f2e2309052c1d23da97d29812 new file mode 100644 index 00000000..a8dbd5d2 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/6c5bc7b11a212e4f2e2309052c1d23da97d29812 differ diff --git a/tests/fuzz/ssh-packet-corpus/83f700c39509c5114d93fb775c67800a8fc32211 b/tests/fuzz/ssh-packet-corpus/83f700c39509c5114d93fb775c67800a8fc32211 new file mode 100644 index 00000000..3964f5e0 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/83f700c39509c5114d93fb775c67800a8fc32211 differ diff --git a/tests/fuzz/ssh-packet-corpus/8c40d8856fa8d66dd069083977517bc52aede4c4 b/tests/fuzz/ssh-packet-corpus/8c40d8856fa8d66dd069083977517bc52aede4c4 new file mode 100644 index 00000000..f9b62a92 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/8c40d8856fa8d66dd069083977517bc52aede4c4 differ diff --git a/tests/fuzz/ssh-packet-corpus/a10909c2cdcaf5adb7e6b092a4faba558b62bd96 b/tests/fuzz/ssh-packet-corpus/a10909c2cdcaf5adb7e6b092a4faba558b62bd96 new file mode 100644 index 00000000..40b450dd Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/a10909c2cdcaf5adb7e6b092a4faba558b62bd96 differ diff --git a/tests/fuzz/ssh-packet-corpus/bcf9e2f02a2f224646cb7fa9514c9e4cf5acb6ea b/tests/fuzz/ssh-packet-corpus/bcf9e2f02a2f224646cb7fa9514c9e4cf5acb6ea new file mode 100644 index 00000000..5cf2ece6 Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/bcf9e2f02a2f224646cb7fa9514c9e4cf5acb6ea differ diff --git a/tests/fuzz/ssh-packet-corpus/ca73ab65568cd125c2d27a22bbd9e863c10b675d b/tests/fuzz/ssh-packet-corpus/ca73ab65568cd125c2d27a22bbd9e863c10b675d new file mode 100644 index 00000000..b4158c40 --- /dev/null +++ b/tests/fuzz/ssh-packet-corpus/ca73ab65568cd125c2d27a22bbd9e863c10b675d @@ -0,0 +1 @@ +I \ No newline at end of file diff --git a/tests/fuzz/ssh-packet-corpus/f15a8ee24a54644f0d37907186741416814f45ea b/tests/fuzz/ssh-packet-corpus/f15a8ee24a54644f0d37907186741416814f45ea new file mode 100644 index 00000000..e1d3fb95 --- /dev/null +++ b/tests/fuzz/ssh-packet-corpus/f15a8ee24a54644f0d37907186741416814f45ea @@ -0,0 +1 @@ +\;A0\ \ No newline at end of file diff --git a/tests/fuzz/ssh-packet-corpus/f8a9c1cab64e766ad90980d3294e2afc4d759273 b/tests/fuzz/ssh-packet-corpus/f8a9c1cab64e766ad90980d3294e2afc4d759273 new file mode 100644 index 00000000..8f6e960c Binary files /dev/null and b/tests/fuzz/ssh-packet-corpus/f8a9c1cab64e766ad90980d3294e2afc4d759273 differ diff --git a/tests/fuzz/ssh_protocol_stubs.c b/tests/fuzz/ssh_protocol_stubs.c index 858cb3dd..838a5e83 100644 --- a/tests/fuzz/ssh_protocol_stubs.c +++ b/tests/fuzz/ssh_protocol_stubs.c @@ -23,19 +23,20 @@ int ssh_conn_recv(ssh_connection_t *conn, uint8_t *data, u64 length, return -1; } -int xaios_net_recv(uint64_t socket, void *buffer, uint64_t size, - uint64_t *received) { +/* Match xaios_user.h exactly. uint64_t is unsigned long long on macOS but + unsigned long on Linux, so spelling these with uint64_t compiled on a + development host and conflicted on CI. */ +int xaios_net_recv(u64 socket, void *buffer, u64 size, u64 *received) { (void)socket; (void)buffer; (void)size; (void)received; return -1; } -int xaios_net_send(uint64_t socket, const void *buffer, uint64_t size, - uint64_t *sent) { +int xaios_net_send(u64 socket, const void *buffer, u64 size, u64 *sent) { (void)socket; (void)buffer; (void)size; (void)sent; return -1; } -uint64_t xaios_clock_nanos(void) { return 0U; } +u64 xaios_clock_nanos(void) { return 0U; } void xaios_log(const char *message) { (void)message; } int crypto_random_bytes(uint8_t *output, uint32_t len) { (void)output; (void)len; return -1; diff --git a/tests/libc/c99-requirements.json b/tests/libc/c99-requirements.json index f00f364d..a98facf4 100644 --- a/tests/libc/c99-requirements.json +++ b/tests/libc/c99-requirements.json @@ -30,7 +30,10 @@ "wchar.h", "wctype.h" ], - "architecture_gates": ["aarch64", "x86_64"], + "architecture_gates": [ + "aarch64", + "x86_64" + ], "required_macros": { "__STDC_HOSTED__": "1", "__STDC_VERSION__": "199901L" @@ -48,13 +51,39 @@ "strings.h" ], "forbidden_public_identifiers": [ - "accept", "asprintf", "close", "connect", "execve", "fdopen", - "fileno", "fmemopen", "fork", "gettimeofday", "lseek", "mmap", - "munmap", "nanosleep", "open", "poll", "pthread_create", "read", - "setbuffer", "setlinebuf", "socket", "stat", "strfromd", - "timespec_get", "unlink", "valloc", "vasprintf", "write" + "accept", + "asprintf", + "close", + "connect", + "execve", + "fdopen", + "fileno", + "fmemopen", + "fork", + "gettimeofday", + "lseek", + "mmap", + "munmap", + "nanosleep", + "open", + "poll", + "pthread_create", + "read", + "setbuffer", + "setlinebuf", + "socket", + "stat", + "strfromd", + "timespec_get", + "unlink", + "valloc", + "vasprintf", + "write" + ], + "forbidden_public_macros": [ + "SIGUSR1", + "TIME_UTC" ], - "forbidden_public_macros": ["SIGUSR1", "TIME_UTC"], "runtime_markers": [ "C99-LANGUAGE-PASS", "C99-TYPES-MACROS-PASS", @@ -76,8 +105,8 @@ "kernel: /bin/c99-thread-context returned to kernel exit_code=0" ], "syscall_budget": { - "baseline_count": 50, - "maximum_identifier": 50, + "baseline_count": 51, + "maximum_identifier": 51, "libc_specific_additions": 0 } } diff --git a/tests/scripts/check-libc-contract.py b/tests/scripts/check-libc-contract.py index a5620b60..d3f825a1 100755 --- a/tests/scripts/check-libc-contract.py +++ b/tests/scripts/check-libc-contract.py @@ -206,7 +206,8 @@ def main() -> int: check_sysroot(arch) print( "libc-contract: PASS: 24 headers, 464 functions, exact strict-C99 " - "namespace, pinned source, non-POSIX surface, 50-syscall budget" + f"namespace, pinned source, non-POSIX surface, " + f"{REQUIREMENTS['syscall_budget']['baseline_count']}-syscall budget" ) return 0 diff --git a/tests/scripts/qemu-core-os-rc.py b/tests/scripts/qemu-core-os-rc.py index bd53c24d..7a3fba23 100644 --- a/tests/scripts/qemu-core-os-rc.py +++ b/tests/scripts/qemu-core-os-rc.py @@ -56,8 +56,13 @@ "ipv4: fragmentation/reassembly self-test passed", "ipv6: fragmentation/reassembly passed", ], + # The aggregate boots the XAIOS_BOOT_TEST_APPS image, where nettest drives + # the deterministic DNS fixture. The "resolve/cache" wording only exists in + # the non-test build that resolves a live name, so requiring it here could + # never be satisfied. Live resolution is covered by the network suite and + # the external interoperability gates, not by this boot. "userspace_dns": [ - "/bin/nettest: userspace DNS resolve/cache path passed", + "/bin/nettest: userspace DNS fixture path passed", ], "arm_fp_neon_context": [ "scheduler: SIMD/FP interrupt preservation passed", @@ -149,7 +154,9 @@ "smmuv3", [ "SMMU: translated DMA self-test passed", - "authorized=1 forbidden=1 stale_mapping=blocked faults=1", + # Cumulative SMMU fault total, not this test's count: unrelated + # streams fault first, so the number varies by boot. + "authorized=1 forbidden=1 stale_mapping=blocked faults=", "qemu-smmu-gate: translated DMA isolation passed", ], ), @@ -158,7 +165,7 @@ [ "nvme: async self-test passed namespaces=1", "rounds=8 async=38 cancelled=1", - "qemu-nvme-gate: AArch64/x86_64 async four-queue PRP/SGL direct I/O", + "qemu-nvme-gate: aarch64/x86_64 async four-queue PRP/SGL direct I/O", ], ), "outbound_fragmentation": ( diff --git a/tests/scripts/qemu-smmu-gate.py b/tests/scripts/qemu-smmu-gate.py index cf233660..c268d0db 100644 --- a/tests/scripts/qemu-smmu-gate.py +++ b/tests/scripts/qemu-smmu-gate.py @@ -2,6 +2,7 @@ """Prove QEMU SMMUv3 translation, revocation, and stream teardown.""" import json +import re import os import select import signal @@ -19,7 +20,12 @@ "SMMU: translated DMA result=0x0", "SMMU: event type=0x10", "SMMU: translated DMA self-test passed", - "authorized=1 forbidden=1 stale_mapping=blocked faults=1", + # The fault counter is cumulative across the whole SMMU, not a count of + # this test's faults: unrelated streams that reach the SMMU without a + # configured entry raise C_BAD_STE first, so the total is whatever the + # boot happened to accumulate. Assert the outcome and that at least one + # fault was recorded, not an exact running total. + "authorized=1 forbidden=1 stale_mapping=blocked faults=", ] PANIC_MARKERS = ["CYAN SCREEN OF DEATH", "System halted. Manual reset required"] @@ -118,6 +124,13 @@ def main() -> int: missing = [marker for marker in MARKERS if marker not in text] panics = [marker for marker in PANIC_MARKERS if marker in text] failures = [f"missing marker: {marker}" for marker in missing] + # The marker above only proves the summary line was printed. Check the + # fault total separately so a run that recorded no fault at all still + # fails, without pinning the assertion to one exact cumulative value. + fault_totals = [int(value) for value in + re.findall(r"stale_mapping=blocked faults=(\d+)", text)] + if fault_totals and max(fault_totals) < 1: + failures.append("SMMU recorded no translation faults") failures.extend(f"panic marker present: {marker}" for marker in panics) if not passed and not failures: failures.append(f"QEMU exited before SMMU evidence, code={process.returncode}") diff --git a/tests/scripts/qemu-smoke.py b/tests/scripts/qemu-smoke.py index b36b0167..72534a50 100644 --- a/tests/scripts/qemu-smoke.py +++ b/tests/scripts/qemu-smoke.py @@ -64,7 +64,7 @@ "initramfs: child service=/svc/source-index parent=/init restart=never", "initramfs: mounted rofs version=2 files=", "initramfs: rofs metadata/config self-test passed", - "syscall: table self-test passed entries=50", + "syscall: table self-test passed entries=51", "virtio-rng: entropy delivery self-test passed", "user: process table initialized slots=1024", "user: process lifecycle invalid/failed transition self-test passed", diff --git a/tests/scripts/run-parser-fuzz.py b/tests/scripts/run-parser-fuzz.py index fa819fd7..d16f10d9 100644 --- a/tests/scripts/run-parser-fuzz.py +++ b/tests/scripts/run-parser-fuzz.py @@ -25,6 +25,13 @@ def main() -> int: "clang", "-std=c99", "-O1", "-g", "-fno-omit-frame-pointer", "-fsanitize=fuzzer,address,undefined", "-Wall", "-Wextra", "-Werror", "-Wno-unused-function", + # BearSSL calls getentropy(), which glibc hides under -std=c99 unless + # _DEFAULT_SOURCE is defined. macOS declares it either way, so without + # this the DNS target only builds on a development host. + "-D_DEFAULT_SOURCE", + # openbsd-compat uses __nonstring__, unknown to clang before 21, the + # same reason scripts/build-image.sh passes this for those files. + "-Wno-unknown-attributes", ] targets = { "ssh-packet": [ @@ -52,12 +59,20 @@ def main() -> int: run([*common, *sources, "-o", str(binary)]) seed_corpus = ROOT / "tests" / "fuzz" / f"{name}-corpus" corpus = BUILD / f"{name}-corpus" - if corpus.exists(): - shutil.rmtree(corpus) - shutil.copytree(seed_corpus, corpus) + # Merge the checked-in seeds into whatever the corpus already holds + # rather than resetting it. A campaign that starts from one seed every + # time relearns the same shallow coverage; carrying the corpus forward + # is what lets successive runs reach deeper states. + corpus.mkdir(parents=True, exist_ok=True) + for seed in seed_corpus.iterdir(): + if seed.is_file(): + target_path = corpus / seed.name + if not target_path.exists(): + shutil.copy2(seed, target_path) run([ str(binary), str(corpus), f"-runs={RUNS}", "-timeout=5", "-rss_limit_mb=1024", "-print_final_stats=1", + f"-artifact_prefix={BUILD}/", ]) print(f"parser-fuzz: PASS targets={len(targets)} runs_per_target={RUNS}") return 0 diff --git a/tests/storage/test_mutable_fs_mirror.c b/tests/storage/test_mutable_fs_mirror.c new file mode 100644 index 00000000..dc693524 --- /dev/null +++ b/tests/storage/test_mutable_fs_mirror.c @@ -0,0 +1,150 @@ +/* A/B metadata recovery. + * + * The metadata region is rewritten in place, so a write interrupted by power + * loss leaves it neither valid nor blank, and mount refuses to continue + * rather than format over a volume that might still hold data. That made an + * interrupted metadata write unrecoverable. A second copy is kept past the + * data region and writes alternate between the two, so mount can fall back. + * + * These tests damage a copy the way a torn write would and require the volume + * to keep mounting with its contents intact. + */ +#include +#include +#include + +#include +#include + +/* Kernel dependencies the filesystem pulls in, reduced to what a hosted run + needs. The virtio path is unreachable here: this test mounts a registered + block device, never the in-image volume. */ +void klog(const char *fmt, ...) { (void)fmt; } +uint32_t smp_online_count(void) { return 1U; } +void panic_at(const char *file, int line, const char *fmt, ...) { + (void)fmt; + fprintf(stderr, "panic at %s:%d\n", file, line); + __builtin_trap(); +} +xaios_status_t virtio_block_read_sector(uint64_t s, void *b, uint64_t n) { + (void)s; (void)b; (void)n; return XAIOS_ERR_IO; +} +xaios_status_t virtio_block_write_sector(uint64_t s, const void *b, uint64_t n) { + (void)s; (void)b; (void)n; return XAIOS_ERR_IO; +} +xaios_status_t virtio_block_flush(void) { return XAIOS_ERR_IO; } +uint64_t virtio_block_capacity_sectors(void) { return 0U; } + +#define SECTOR 512U +/* Room for metadata + journal + data + the mirror that follows them. */ +#define DISK_SECTORS (3072U + 1280U + 2U + 8192U + 1280U + 64U) + +static uint8_t g_disk[(uint64_t)DISK_SECTORS * SECTOR]; +static xaios_block_device_t g_device; + +static xaios_status_t disk_read(void *context, uint64_t offset, void *buffer, + uint64_t length) { + (void)context; + if (offset + length > sizeof(g_disk)) return XAIOS_ERR_INVALID; + memcpy(buffer, g_disk + offset, (size_t)length); + return XAIOS_OK; +} + +static xaios_status_t disk_write(void *context, uint64_t offset, + const void *buffer, uint64_t length) { + (void)context; + if (offset + length > sizeof(g_disk)) return XAIOS_ERR_INVALID; + memcpy(g_disk + offset, buffer, (size_t)length); + return XAIOS_OK; +} + +static xaios_status_t disk_flush(void *context) { + (void)context; + return XAIOS_OK; +} + +static const xaios_block_backend_ops_t k_ops = { + disk_read, disk_write, disk_flush, 0, 0, +}; + +static void register_disk(void) { + xaios_block_device_info_t info; + memset(&info, 0, sizeof(info)); + snprintf(info.identifier, sizeof(info.identifier), "/dev/mirror0"); + snprintf(info.backend, sizeof(info.backend), "test"); + info.capacity_bytes = sizeof(g_disk); + info.capacity_logical_sectors = DISK_SECTORS; + info.logical_sector_size = SECTOR; + info.physical_block_size = SECTOR; + info.max_transfer_bytes = SECTOR; + info.flush_supported = 1U; + info.read_only = 0U; + assert(block_device_register(&g_device, &info, &k_ops, 0) == XAIOS_OK); +} + +/* Overwrite a metadata copy the way an interrupted write would: some sectors + * carry the new image, the rest still hold the old one, so the region is + * self-inconsistent without being blank. */ +static void tear_slot(uint64_t start_sector) { + for (uint32_t i = 0U; i < 8U; ++i) { + memset(g_disk + ((uint64_t)start_sector + i) * SECTOR, 0xA5, SECTOR); + } +} + +static uint64_t primary_start(void) { return 3072U; } +static uint64_t mirror_start(void) { return 3072U + 1280U + 2U + 8192U; } + +static void mount_fresh(void) { + assert(mutable_fs_mount_device("/dev/mirror0") == XAIOS_OK); +} + +int main(void) { + register_disk(); + + /* Create a volume with real content and commit it. */ + mount_fresh(); + assert(mutable_fs_mkdir("/state") == XAIOS_OK); + assert(mutable_fs_write("/state/keep", "durable", 7U) == XAIOS_OK); + assert(mutable_fs_commit("initial") == XAIOS_OK); + /* Several writes, so the slots have alternated and both hold real images. */ + for (uint32_t i = 0U; i < 4U; ++i) { + assert(mutable_fs_write("/state/keep", "durable", 7U) == XAIOS_OK); + } + mutable_fs_unmount(); + + /* A torn primary must not stop the volume mounting. */ + tear_slot(primary_start()); + mount_fresh(); + char buffer[32]; + uint64_t read_bytes = 0U; + assert(mutable_fs_read("/state/keep", buffer, sizeof(buffer), &read_bytes) == + XAIOS_OK); + assert(read_bytes == 7U && memcmp(buffer, "durable", 7U) == 0); + printf("mutable-fs-mirror: torn primary recovered, contents intact\n"); + + /* Writing after that recovery must restore a good second copy, so the + volume is not left one tear away from being unmountable. */ + assert(mutable_fs_write("/state/keep", "durable", 7U) == XAIOS_OK); + assert(mutable_fs_commit("after-recovery") == XAIOS_OK); + mutable_fs_unmount(); + + /* Now tear the other copy and require the same outcome. */ + tear_slot(mirror_start()); + mount_fresh(); + read_bytes = 0U; + assert(mutable_fs_read("/state/keep", buffer, sizeof(buffer), &read_bytes) == + XAIOS_OK); + assert(read_bytes == 7U && memcmp(buffer, "durable", 7U) == 0); + printf("mutable-fs-mirror: torn mirror recovered, contents intact\n"); + mutable_fs_unmount(); + + /* Both copies damaged must still refuse rather than format over the + volume: falling back is a recovery, not a licence to discard data. */ + tear_slot(primary_start()); + tear_slot(mirror_start()); + assert(mutable_fs_mount_device("/dev/mirror0") != XAIOS_OK); + printf("mutable-fs-mirror: both copies damaged still refuses to format\n"); + + printf("mutable-fs-mirror: all A/B metadata recovery tests passed\n"); + return 0; +} diff --git a/tests/storage/test_vfs.c b/tests/storage/test_vfs.c index 88b04a6c..b90f7fd0 100644 --- a/tests/storage/test_vfs.c +++ b/tests/storage/test_vfs.c @@ -162,6 +162,11 @@ static const xaios_vfs_backend_ops_t k_mock_ops = { mock_path_ok, mock_path_ok, mock_rename, mock_list, }; +/* vfs.c serialises its handle table with the kernel ticket lock, whose + single-CPU fast path asks how many CPUs are online. The hosted test links + only the filesystem translation unit, so provide the one symbol it needs. */ +uint32_t smp_online_count(void) { return 1U; } + int main(void) { mock_fs_t root; mock_fs_t models; diff --git a/tools/gen_xapt_trust_anchors.py b/tools/gen_xapt_trust_anchors.py new file mode 100644 index 00000000..682f127a --- /dev/null +++ b/tools/gen_xapt_trust_anchors.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Generate the BearSSL trust anchors xapt validates the updater chain against. + +xapt used to pin the updater's exact leaf RSA key. A publicly issued +certificate is renewed every few months with a fresh key, so a leaf pin has to +be re-cut on every renewal or the updater stops working. Anchoring on the +issuing roots instead survives renewal, and the update payload keeps its own +signature, so TLS here only has to establish a trustworthy transport. + +The roots come from a trusted local CA store, never from the server being +validated. Regenerate after replacing a PEM in userspace/apps/trust: + + python3 tools/gen_xapt_trust_anchors.py +""" + +import pathlib +import sys + +from cryptography import x509 +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.hazmat.primitives.serialization import ( + Encoding, + PublicFormat, +) + +ROOT = pathlib.Path(__file__).resolve().parent.parent +TRUST_DIR = ROOT / "userspace/apps/trust" +OUTPUT = ROOT / "userspace/apps/xapt_trust_anchors.c" + +# BearSSL curve identifiers (br_ec_*), from inc/bearssl_ec.h. +CURVE_IDS = {"secp256r1": 23, "secp384r1": 24, "secp521r1": 25} + + +def emit_bytes(name, data): + lines = [f"static const unsigned char {name}[] = {{"] + for offset in range(0, len(data), 12): + chunk = data[offset : offset + 12] + lines.append(" " + " ".join(f"0x{b:02X}," for b in chunk)) + lines.append("};") + return "\n".join(lines) + + +def integer_bytes(value): + return value.to_bytes((value.bit_length() + 7) // 8, "big") + + +def main(): + pems = sorted(TRUST_DIR.glob("*.pem")) + if not pems: + sys.exit(f"no root certificates found in {TRUST_DIR}") + + blocks = [] + entries = [] + for index, pem in enumerate(pems): + cert = x509.load_pem_x509_certificate(pem.read_bytes()) + public_key = cert.public_key() + dn = cert.subject.public_bytes() + blocks.append(f"/* {cert.subject.rfc4514_string()} ({pem.name}) */") + blocks.append(emit_bytes(f"TA{index}_DN", dn)) + + if isinstance(public_key, rsa.RSAPublicKey): + numbers = public_key.public_numbers() + blocks.append(emit_bytes(f"TA{index}_N", integer_bytes(numbers.n))) + blocks.append(emit_bytes(f"TA{index}_E", integer_bytes(numbers.e))) + key = ( + f"BR_KEYTYPE_RSA,\n" + f" {{ .rsa = {{ (unsigned char *)TA{index}_N, sizeof TA{index}_N,\n" + f" (unsigned char *)TA{index}_E, sizeof TA{index}_E }} }}" + ) + elif isinstance(public_key, ec.EllipticCurvePublicKey): + curve = public_key.curve.name + if curve not in CURVE_IDS: + sys.exit(f"{pem.name}: unsupported curve {curve}") + point = public_key.public_bytes( + Encoding.X962, PublicFormat.UncompressedPoint + ) + blocks.append(emit_bytes(f"TA{index}_Q", point)) + key = ( + f"BR_KEYTYPE_EC,\n" + f" {{ .ec = {{ {CURVE_IDS[curve]}, (unsigned char *)TA{index}_Q,\n" + f" sizeof TA{index}_Q }} }}" + ) + else: + sys.exit(f"{pem.name}: unsupported key type {type(public_key).__name__}") + + entries.append( + f" {{ {{ (unsigned char *)TA{index}_DN, sizeof TA{index}_DN }},\n" + f" BR_X509_TA_CA,\n" + f" {{ {key} }} }}," + ) + blocks.append("") + + body = "\n".join(blocks) + table = "\n".join(entries) + OUTPUT.write_text( + "/* Generated by tools/gen_xapt_trust_anchors.py. Do not edit. */\n" + '#include "xapt_trust_anchors.h"\n\n' + f"{body}\n" + "const br_x509_trust_anchor XAPT_TRUST_ANCHORS[] = {\n" + f"{table}\n" + "};\n\n" + "const size_t XAPT_TRUST_ANCHORS_COUNT =\n" + " sizeof XAPT_TRUST_ANCHORS / sizeof XAPT_TRUST_ANCHORS[0];\n" + ) + print(f"wrote {OUTPUT.relative_to(ROOT)} with {len(pems)} anchors") + + +if __name__ == "__main__": + main() diff --git a/userspace/apps/trust/ISRG_Root_X1.pem b/userspace/apps/trust/ISRG_Root_X1.pem new file mode 100644 index 00000000..b85c8037 --- /dev/null +++ b/userspace/apps/trust/ISRG_Root_X1.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/userspace/apps/trust/ISRG_Root_X2.pem b/userspace/apps/trust/ISRG_Root_X2.pem new file mode 100644 index 00000000..7d903edc --- /dev/null +++ b/userspace/apps/trust/ISRG_Root_X2.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- diff --git a/userspace/apps/xapt.c b/userspace/apps/xapt.c index f652de6f..3a9306ad 100644 --- a/userspace/apps/xapt.c +++ b/userspace/apps/xapt.c @@ -15,6 +15,11 @@ typedef struct xapt_config { char host[XAPT_HOST_BYTES]; + /* Optional literal to dial instead of resolving `host`. The name still + identifies the origin: it is what goes in the Host header and what any + certificate is checked against. Set this to reach an origin whose name + the resolver cannot yet return, and drop it once it can. */ + char address[XAPT_HOST_BYTES]; char base[64]; u64 port; u32 tls_required; @@ -228,7 +233,8 @@ static int http_get(const xapt_config_t *config, const char *catalog_path, g_http_error = 1U; return -1; } - if (resolve_host(config->host, &address) != 0) { + if (resolve_host(config->address[0] != '\0' ? config->address : config->host, + &address) != 0) { g_http_error = 2U; return -1; } @@ -277,7 +283,11 @@ static int http_get(const xapt_config_t *config, const char *catalog_path, u64 cursor = 0U; if (header_complete == 0U) { while (cursor < received && header_complete == 0U) { - if (header_used + 1U >= sizeof(g_buffer)) { + /* Headers accumulate in g_catalog before the body claims it, so the + cap must hold against that buffer, not g_buffer: the old test only + worked because g_buffer happens to be the smaller of the two. */ + if (header_used + 1U >= sizeof(g_buffer) || + header_used + 1U >= sizeof(g_catalog)) { g_http_error = 5U; (void)xaios_net_close(socket); return -1; @@ -371,6 +381,11 @@ static int load_config(xapt_config_t *config) { if (bytes <= 0) bytes = xaios_read_file("/etc/xapt.conf", g_buffer, sizeof(g_buffer)); if (bytes <= 0) return -1; + /* The key matches below compare fixed prefixes against the raw buffer, so + the content must end inside it: a full buffer is either truncated or + leaves a final unterminated line for a prefix compare to walk past. */ + if ((u64)bytes >= sizeof(g_buffer)) return -1; + g_buffer[bytes] = '\0'; u64 cursor = 0U; while (cursor < (u64)bytes) { u64 start = cursor; @@ -380,6 +395,10 @@ static int load_config(xapt_config_t *config) { if (copy_text(config->host, sizeof(config->host), g_buffer + start + 5U, length - 5U) != 0) return -1; + } else if (text_starts(g_buffer + start, "address=")) { + if (copy_text(config->address, sizeof(config->address), + g_buffer + start + 8U, length - 8U) != 0) + return -1; } else if (text_starts(g_buffer + start, "base=")) { if (copy_text(config->base, sizeof(config->base), g_buffer + start + 5U, length - 5U) != 0) @@ -404,7 +423,7 @@ static int load_config(xapt_config_t *config) { } return config->host[0] != '\0' && config->port > 0U && config->port <= 65535U && - (config->tls_required == 0U || + (xaios_strlen(config->tls_rsa_modulus) == 0U || xaios_strlen(config->tls_rsa_modulus) == XAPT_TLS_MODULUS_HEX_BYTES) && (config->base[0] == '\0' || path_valid(config->base)) diff --git a/userspace/apps/xapt_tls.c b/userspace/apps/xapt_tls.c index 447cd538..b0399768 100644 --- a/userspace/apps/xapt_tls.c +++ b/userspace/apps/xapt_tls.c @@ -2,11 +2,23 @@ #include #include "xapt_tls.h" +#include "xapt_trust_anchors.h" #define XAPT_TLS_RSA_BYTES 256U +/* br_x509_minimal_set_time counts days from year 0; the realtime clock counts + from 1970. This is the offset between the two in the Gregorian calendar. */ +#define XAPT_TLS_EPOCH_DAYS UINT32_C(719528) +#define XAPT_TLS_SECONDS_PER_DAY UINT64_C(86400) + +/* An unset clock reads as 1970, which would make every certificate look + not-yet-valid. Refuse instead of reporting a confusing expiry failure; + 2025-01-01 is comfortably before any certificate this client will meet. */ +#define XAPT_TLS_MIN_EPOCH_SECONDS UINT64_C(1735689600) + static br_ssl_client_context g_client; -static br_x509_knownkey_context g_validator; +static br_x509_minimal_context g_minimal; +static br_x509_knownkey_context g_knownkey; static br_sslio_context g_io; static unsigned char g_io_buffer[BR_SSL_BUFSIZE_BIDI]; static unsigned char g_modulus[XAPT_TLS_RSA_BYTES]; @@ -59,14 +71,65 @@ static int socket_write(void *context, const unsigned char *data, return offset == size ? (int)size : -1; } +/* Validate the presented chain against the compiled-in roots, including the + server name and the validity window. Used for publicly issued certificates, + which are reissued with a fresh key long before an image is rebuilt. */ +static int use_chain_validation(void) { + u64 epoch_seconds = + xaios_clock_nanos_kind(XAIOS_CLOCK_REALTIME) / UINT64_C(1000000000); + if (epoch_seconds < XAPT_TLS_MIN_EPOCH_SECONDS) { + g_error = 83; + return -1; + } + br_x509_minimal_init(&g_minimal, &br_sha256_vtable, XAPT_TRUST_ANCHORS, + XAPT_TRUST_ANCHORS_COUNT); + br_x509_minimal_set_hash(&g_minimal, br_sha256_ID, &br_sha256_vtable); + br_x509_minimal_set_hash(&g_minimal, br_sha384_ID, &br_sha384_vtable); + br_x509_minimal_set_hash(&g_minimal, br_sha512_ID, &br_sha512_vtable); + br_x509_minimal_set_rsa(&g_minimal, br_rsa_pkcs1_vrfy_get_default()); + br_x509_minimal_set_ecdsa(&g_minimal, br_ec_get_default(), + br_ecdsa_vrfy_asn1_get_default()); + br_x509_minimal_set_time( + &g_minimal, + (uint32_t)(epoch_seconds / XAPT_TLS_SECONDS_PER_DAY) + XAPT_TLS_EPOCH_DAYS, + (uint32_t)(epoch_seconds % XAPT_TLS_SECONDS_PER_DAY)); + br_ssl_engine_set_x509(&g_client.eng, &g_minimal.vtable); + return 0; +} + +/* Accept exactly one leaf public key and ignore the rest of the certificate. + Used for private deployments whose key is long-lived and whose certificate + is reachable only by address, where no public chain exists to validate. */ +static int use_pinned_key(const char *rsa_modulus_hex) { + br_rsa_public_key key; + if (parse_modulus(rsa_modulus_hex) != 0) { + g_error = 80; + return -1; + } + key.n = g_modulus; + key.nlen = sizeof(g_modulus); + key.e = g_exponent; + key.elen = sizeof(g_exponent); + br_x509_knownkey_init_rsa(&g_knownkey, &key, BR_KEYTYPE_SIGN); + br_ssl_engine_set_x509(&g_client.eng, &g_knownkey.vtable); + return 0; +} + int xapt_tls_open(u64 socket, const char *server_name, const char *rsa_modulus_hex) { - static const uint16_t suites[] = { + /* The pinned mode accepts exactly one RSA key, so it must offer only the + RSA suite: a server holding both certificate types honours the client's + order, and offering ECDSA first would steer such a server into a suite + the pin can never satisfy. Chain validation handles either type. */ + static const uint16_t chain_suites[] = { + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}; - br_rsa_public_key key; + static const uint16_t pinned_suites[] = { + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}; + int pinned = rsa_modulus_hex != 0 && rsa_modulus_hex[0] != '\0'; unsigned char entropy[32]; g_error = 0; - if (server_name == 0 || parse_modulus(rsa_modulus_hex) != 0) { + if (server_name == 0) { g_error = 80; return -1; } @@ -78,19 +141,26 @@ int xapt_tls_open(u64 socket, const char *server_name, g_deadline = xaios_clock_nanos() + UINT64_C(600000000000); br_ssl_client_zero(&g_client); br_ssl_engine_set_versions(&g_client.eng, BR_TLS12, BR_TLS12); - br_ssl_engine_set_suites(&g_client.eng, suites, 1U); + if (pinned) { + br_ssl_engine_set_suites(&g_client.eng, pinned_suites, + sizeof(pinned_suites) / sizeof(pinned_suites[0])); + } else { + br_ssl_engine_set_suites(&g_client.eng, chain_suites, + sizeof(chain_suites) / sizeof(chain_suites[0])); + } br_ssl_client_set_default_rsapub(&g_client); br_ssl_engine_set_default_rsavrfy(&g_client.eng); + br_ssl_engine_set_default_ecdsa(&g_client.eng); br_ssl_engine_set_default_ec(&g_client.eng); br_ssl_engine_set_hash(&g_client.eng, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_hash(&g_client.eng, br_sha384_ID, &br_sha384_vtable); + br_ssl_engine_set_hash(&g_client.eng, br_sha512_ID, &br_sha512_vtable); br_ssl_engine_set_prf_sha256(&g_client.eng, &br_tls12_sha256_prf); + br_ssl_engine_set_prf_sha384(&g_client.eng, &br_tls12_sha384_prf); br_ssl_engine_set_default_aes_gcm(&g_client.eng); - key.n = g_modulus; - key.nlen = sizeof(g_modulus); - key.e = g_exponent; - key.elen = sizeof(g_exponent); - br_x509_knownkey_init_rsa(&g_validator, &key, BR_KEYTYPE_SIGN); - br_ssl_engine_set_x509(&g_client.eng, &g_validator.vtable); + if (pinned ? use_pinned_key(rsa_modulus_hex) : use_chain_validation()) { + return -1; + } br_ssl_engine_set_buffer(&g_client.eng, g_io_buffer, sizeof(g_io_buffer), 1); br_ssl_engine_inject_entropy(&g_client.eng, entropy, sizeof(entropy)); if (!br_ssl_client_reset(&g_client, server_name, 0)) { diff --git a/userspace/apps/xapt_tls.h b/userspace/apps/xapt_tls.h index 6cdb3c86..6074abc3 100644 --- a/userspace/apps/xapt_tls.h +++ b/userspace/apps/xapt_tls.h @@ -3,6 +3,10 @@ #include +/* Open a TLS 1.2 session. With rsa_modulus_hex null or empty the presented + chain is validated against the compiled-in roots, including server name and + validity window; otherwise that exact leaf RSA key is required and the rest + of the certificate is ignored. */ int xapt_tls_open(u64 socket, const char *server_name, const char *rsa_modulus_hex); int xapt_tls_write(const void *data, u64 size); diff --git a/userspace/apps/xapt_trust_anchors.c b/userspace/apps/xapt_trust_anchors.c new file mode 100644 index 00000000..d62a20c0 --- /dev/null +++ b/userspace/apps/xapt_trust_anchors.c @@ -0,0 +1,99 @@ +/* Generated by tools/gen_xapt_trust_anchors.py. Do not edit. */ +#include "xapt_trust_anchors.h" + +/* CN=ISRG Root X1,O=Internet Security Research Group,C=US (ISRG_Root_X1.pem) */ +static const unsigned char TA0_DN[] = { + 0x30, 0x4F, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, 0x49, 0x53, 0x52, + 0x47, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x58, 0x31, +}; +static const unsigned char TA0_N[] = { + 0xAD, 0xE8, 0x24, 0x73, 0xF4, 0x14, 0x37, 0xF3, 0x9B, 0x9E, 0x2B, 0x57, + 0x28, 0x1C, 0x87, 0xBE, 0xDC, 0xB7, 0xDF, 0x38, 0x90, 0x8C, 0x6E, 0x3C, + 0xE6, 0x57, 0xA0, 0x78, 0xF7, 0x75, 0xC2, 0xA2, 0xFE, 0xF5, 0x6A, 0x6E, + 0xF6, 0x00, 0x4F, 0x28, 0xDB, 0xDE, 0x68, 0x86, 0x6C, 0x44, 0x93, 0xB6, + 0xB1, 0x63, 0xFD, 0x14, 0x12, 0x6B, 0xBF, 0x1F, 0xD2, 0xEA, 0x31, 0x9B, + 0x21, 0x7E, 0xD1, 0x33, 0x3C, 0xBA, 0x48, 0xF5, 0xDD, 0x79, 0xDF, 0xB3, + 0xB8, 0xFF, 0x12, 0xF1, 0x21, 0x9A, 0x4B, 0xC1, 0x8A, 0x86, 0x71, 0x69, + 0x4A, 0x66, 0x66, 0x6C, 0x8F, 0x7E, 0x3C, 0x70, 0xBF, 0xAD, 0x29, 0x22, + 0x06, 0xF3, 0xE4, 0xC0, 0xE6, 0x80, 0xAE, 0xE2, 0x4B, 0x8F, 0xB7, 0x99, + 0x7E, 0x94, 0x03, 0x9F, 0xD3, 0x47, 0x97, 0x7C, 0x99, 0x48, 0x23, 0x53, + 0xE8, 0x38, 0xAE, 0x4F, 0x0A, 0x6F, 0x83, 0x2E, 0xD1, 0x49, 0x57, 0x8C, + 0x80, 0x74, 0xB6, 0xDA, 0x2F, 0xD0, 0x38, 0x8D, 0x7B, 0x03, 0x70, 0x21, + 0x1B, 0x75, 0xF2, 0x30, 0x3C, 0xFA, 0x8F, 0xAE, 0xDD, 0xDA, 0x63, 0xAB, + 0xEB, 0x16, 0x4F, 0xC2, 0x8E, 0x11, 0x4B, 0x7E, 0xCF, 0x0B, 0xE8, 0xFF, + 0xB5, 0x77, 0x2E, 0xF4, 0xB2, 0x7B, 0x4A, 0xE0, 0x4C, 0x12, 0x25, 0x0C, + 0x70, 0x8D, 0x03, 0x29, 0xA0, 0xE1, 0x53, 0x24, 0xEC, 0x13, 0xD9, 0xEE, + 0x19, 0xBF, 0x10, 0xB3, 0x4A, 0x8C, 0x3F, 0x89, 0xA3, 0x61, 0x51, 0xDE, + 0xAC, 0x87, 0x07, 0x94, 0xF4, 0x63, 0x71, 0xEC, 0x2E, 0xE2, 0x6F, 0x5B, + 0x98, 0x81, 0xE1, 0x89, 0x5C, 0x34, 0x79, 0x6C, 0x76, 0xEF, 0x3B, 0x90, + 0x62, 0x79, 0xE6, 0xDB, 0xA4, 0x9A, 0x2F, 0x26, 0xC5, 0xD0, 0x10, 0xE1, + 0x0E, 0xDE, 0xD9, 0x10, 0x8E, 0x16, 0xFB, 0xB7, 0xF7, 0xA8, 0xF7, 0xC7, + 0xE5, 0x02, 0x07, 0x98, 0x8F, 0x36, 0x08, 0x95, 0xE7, 0xE2, 0x37, 0x96, + 0x0D, 0x36, 0x75, 0x9E, 0xFB, 0x0E, 0x72, 0xB1, 0x1D, 0x9B, 0xBC, 0x03, + 0xF9, 0x49, 0x05, 0xD8, 0x81, 0xDD, 0x05, 0xB4, 0x2A, 0xD6, 0x41, 0xE9, + 0xAC, 0x01, 0x76, 0x95, 0x0A, 0x0F, 0xD8, 0xDF, 0xD5, 0xBD, 0x12, 0x1F, + 0x35, 0x2F, 0x28, 0x17, 0x6C, 0xD2, 0x98, 0xC1, 0xA8, 0x09, 0x64, 0x77, + 0x6E, 0x47, 0x37, 0xBA, 0xCE, 0xAC, 0x59, 0x5E, 0x68, 0x9D, 0x7F, 0x72, + 0xD6, 0x89, 0xC5, 0x06, 0x41, 0x29, 0x3E, 0x59, 0x3E, 0xDD, 0x26, 0xF5, + 0x24, 0xC9, 0x11, 0xA7, 0x5A, 0xA3, 0x4C, 0x40, 0x1F, 0x46, 0xA1, 0x99, + 0xB5, 0xA7, 0x3A, 0x51, 0x6E, 0x86, 0x3B, 0x9E, 0x7D, 0x72, 0xA7, 0x12, + 0x05, 0x78, 0x59, 0xED, 0x3E, 0x51, 0x78, 0x15, 0x0B, 0x03, 0x8F, 0x8D, + 0xD0, 0x2F, 0x05, 0xB2, 0x3E, 0x7B, 0x4A, 0x1C, 0x4B, 0x73, 0x05, 0x12, + 0xFC, 0xC6, 0xEA, 0xE0, 0x50, 0x13, 0x7C, 0x43, 0x93, 0x74, 0xB3, 0xCA, + 0x74, 0xE7, 0x8E, 0x1F, 0x01, 0x08, 0xD0, 0x30, 0xD4, 0x5B, 0x71, 0x36, + 0xB4, 0x07, 0xBA, 0xC1, 0x30, 0x30, 0x5C, 0x48, 0xB7, 0x82, 0x3B, 0x98, + 0xA6, 0x7D, 0x60, 0x8A, 0xA2, 0xA3, 0x29, 0x82, 0xCC, 0xBA, 0xBD, 0x83, + 0x04, 0x1B, 0xA2, 0x83, 0x03, 0x41, 0xA1, 0xD6, 0x05, 0xF1, 0x1B, 0xC2, + 0xB6, 0xF0, 0xA8, 0x7C, 0x86, 0x3B, 0x46, 0xA8, 0x48, 0x2A, 0x88, 0xDC, + 0x76, 0x9A, 0x76, 0xBF, 0x1F, 0x6A, 0xA5, 0x3D, 0x19, 0x8F, 0xEB, 0x38, + 0xF3, 0x64, 0xDE, 0xC8, 0x2B, 0x0D, 0x0A, 0x28, 0xFF, 0xF7, 0xDB, 0xE2, + 0x15, 0x42, 0xD4, 0x22, 0xD0, 0x27, 0x5D, 0xE1, 0x79, 0xFE, 0x18, 0xE7, + 0x70, 0x88, 0xAD, 0x4E, 0xE6, 0xD9, 0x8B, 0x3A, 0xC6, 0xDD, 0x27, 0x51, + 0x6E, 0xFF, 0xBC, 0x64, 0xF5, 0x33, 0x43, 0x4F, +}; +static const unsigned char TA0_E[] = { + 0x01, 0x00, 0x01, +}; + +/* CN=ISRG Root X2,O=Internet Security Research Group,C=US (ISRG_Root_X2.pem) */ +static const unsigned char TA1_DN[] = { + 0x30, 0x4F, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, 0x49, 0x53, 0x52, + 0x47, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x58, 0x32, +}; +static const unsigned char TA1_Q[] = { + 0x04, 0xCD, 0x9B, 0xD5, 0x9F, 0x80, 0x83, 0x0A, 0xEC, 0x09, 0x4A, 0xF3, + 0x16, 0x4A, 0x3E, 0x5C, 0xCF, 0x77, 0xAC, 0xDE, 0x67, 0x05, 0x0D, 0x1D, + 0x07, 0xB6, 0xDC, 0x16, 0xFB, 0x5A, 0x8B, 0x14, 0xDB, 0xE2, 0x71, 0x60, + 0xC4, 0xBA, 0x45, 0x95, 0x11, 0x89, 0x8E, 0xEA, 0x06, 0xDF, 0xF7, 0x2A, + 0x16, 0x1C, 0xA4, 0xB9, 0xC5, 0xC5, 0x32, 0xE0, 0x03, 0xE0, 0x1E, 0x82, + 0x18, 0x38, 0x8B, 0xD7, 0x45, 0xD8, 0x0A, 0x6A, 0x6E, 0xE6, 0x00, 0x77, + 0xFB, 0x02, 0x51, 0x7D, 0x22, 0xD8, 0x0A, 0x6E, 0x9A, 0x5B, 0x77, 0xDF, + 0xF0, 0xFA, 0x41, 0xEC, 0x39, 0xDC, 0x75, 0xCA, 0x68, 0x07, 0x0C, 0x1F, + 0xEA, +}; + +const br_x509_trust_anchor XAPT_TRUST_ANCHORS[] = { + { { (unsigned char *)TA0_DN, sizeof TA0_DN }, + BR_X509_TA_CA, + { BR_KEYTYPE_RSA, + { .rsa = { (unsigned char *)TA0_N, sizeof TA0_N, + (unsigned char *)TA0_E, sizeof TA0_E } } } }, + { { (unsigned char *)TA1_DN, sizeof TA1_DN }, + BR_X509_TA_CA, + { BR_KEYTYPE_EC, + { .ec = { 24, (unsigned char *)TA1_Q, + sizeof TA1_Q } } } }, +}; + +const size_t XAPT_TRUST_ANCHORS_COUNT = + sizeof XAPT_TRUST_ANCHORS / sizeof XAPT_TRUST_ANCHORS[0]; diff --git a/userspace/apps/xapt_trust_anchors.h b/userspace/apps/xapt_trust_anchors.h new file mode 100644 index 00000000..9c0aa7e2 --- /dev/null +++ b/userspace/apps/xapt_trust_anchors.h @@ -0,0 +1,11 @@ +#ifndef XAIOS_XAPT_TRUST_ANCHORS_H +#define XAIOS_XAPT_TRUST_ANCHORS_H + +#include + +/* Root certificates the updater's chain is validated against, generated from + userspace/apps/trust by tools/gen_xapt_trust_anchors.py. */ +extern const br_x509_trust_anchor XAPT_TRUST_ANCHORS[]; +extern const size_t XAPT_TRUST_ANCHORS_COUNT; + +#endif diff --git a/userspace/include/xaios_user.h b/userspace/include/xaios_user.h index 7b9b479a..d65306de 100644 --- a/userspace/include/xaios_user.h +++ b/userspace/include/xaios_user.h @@ -4,6 +4,7 @@ typedef unsigned long long u64; typedef unsigned int u32; typedef unsigned short u16; +typedef unsigned char u8; typedef int s32; typedef long long s64; @@ -77,6 +78,7 @@ void *xaios_memcpy(void *dst, const void *src, u64 size); #define XAIOS_SYSCALL_CONSOLE_WRITE 48ULL #define XAIOS_SYSCALL_NET_LOCAL_IPV4 49ULL #define XAIOS_SYSCALL_NET_CONNECT 50ULL +#define XAIOS_SYSCALL_NET_LOCAL_IPV6 51ULL #define XAIOS_THREAD_CPU_ANY (~0ULL) #define XAIOS_CLOCK_MONOTONIC 0ULL @@ -360,6 +362,9 @@ int xaios_remote_login_child_release(u64 child_channel_id); int xaios_console_read(char *value); int xaios_console_write(const char *buffer, u64 size); u32 xaios_net_local_ipv4(void); +/* Copies the public IPv6 address out and returns 1, or returns 0 when the + guest has no public IPv6 address configured. */ +int xaios_net_local_ipv6(u8 address[16]); int xaios_net_external_session(u64 protocol, u64 port, const void *payload, u64 payload_size, char *output, u64 output_size, u64 *out_size); diff --git a/userspace/init/xapt.conf b/userspace/init/xapt.conf index a61ea8ff..bc45958b 100644 --- a/userspace/init/xapt.conf +++ b/userspace/init/xapt.conf @@ -1,5 +1,5 @@ -host=91.99.176.243 -port=8443 +host=xaios.91.99.176.243.nip.io +address=91.99.176.243 +port=80 base=/ -tls=required -tls_rsa_modulus=b4cef411efa36fc7f79c728c9a792dd206a2c72a5eeeedca708ac7afa743c1cac9bf6fea56782ee92bc359861381f40b0db41968e5490ca2f214b5f29ab4c6144d8e24f453c2ed415d95b8789b71fd1fd33b8c491212d5865d31f135f2736f38cdef3aa13ad64ec7af59f2795f0ff944d3ea70018c8ec874e684dbc1c640123ea060a52e8101f9d87713e91ba77635f1e83321010dd56e01b652623b9dd9cd56ac516541640f3b9ef0e6ab84c98e4f667d75c5e7c547a584155eddba0d0e7ffe712a23b44f14166f4fe859473c2fc8f3c7ab25151e86ae53169f2aa8f8ef784d9c4a0251c3a5e54b2a3083f722c26df97f31c9b364bd840eef503029d91e00df +tls=off diff --git a/userspace/lib/xaios_user.c b/userspace/lib/xaios_user.c index c119c199..d3c72bea 100644 --- a/userspace/lib/xaios_user.c +++ b/userspace/lib/xaios_user.c @@ -105,6 +105,11 @@ u32 xaios_net_local_ipv4(void) { return (u32)xaios_syscall3(XAIOS_SYSCALL_NET_LOCAL_IPV4, 0U, 0U, 0U); } +int xaios_net_local_ipv6(u8 address[16]) { + return (int)(s64)xaios_syscall3(XAIOS_SYSCALL_NET_LOCAL_IPV6, + (u64)(void *)address, 16U, 0U); +} + void xaios_exit(int code) { (void)xaios_syscall3(XAIOS_SYSCALL_EXIT, (u64)(u32)code, 0, 0); for (;;) { diff --git a/userspace/sshd/ssh_channel.c b/userspace/sshd/ssh_channel.c index 2bc19c65..d2689493 100644 --- a/userspace/sshd/ssh_channel.c +++ b/userspace/sshd/ssh_channel.c @@ -431,6 +431,8 @@ static int command_option_text(const char *command, const char *option, return 0; } +static xaios_htop_sink_t channel_htop_sink(ssh_channel_t *ch); + static const char *htop_sort_name(uint32_t key) { switch (key) { case SSH_HTOP_SORT_MEMORY: return "mem"; @@ -444,100 +446,107 @@ static const char *htop_sort_name(uint32_t key) { } } -static void htop_initialize(ssh_channel_t *ch, const char *command) { +void xaios_htop_start(xaios_htop_session_t *session, const char *command, + uint32_t columns, uint32_t rows) { char sort[24]; uint32_t value; - ch->htop_active = 1U; - ch->htop_show_all = command_has_option(command, "--active") == 0; - ch->htop_show_cpus = command_has_option(command, "--no-cpus") == 0; - ch->htop_sort_key = SSH_HTOP_SORT_CPU; - ch->htop_reverse = command_has_option(command, "--reverse") != 0; - ch->htop_cpu_start = 0U; - ch->htop_cpu_count = UINT32_MAX; - ch->htop_process_start = 0U; - ch->htop_selected = 0U; - ch->htop_refresh_ms = SSH_HTOP_DEFAULT_REFRESH_MS; - ch->htop_filter_mode = 0U; - ch->htop_help = 0U; - ch->htop_filter_length = 0U; - ch->htop_last_frame_ns = 0U; - ch->htop_filter[0] = '\0'; + session->active = 1U; + session->show_all = command_has_option(command, "--active") == 0; + session->show_cpus = command_has_option(command, "--no-cpus") == 0; + session->sort_key = SSH_HTOP_SORT_CPU; + session->reverse = command_has_option(command, "--reverse") != 0; + session->cpu_start = 0U; + session->cpu_count = UINT32_MAX; + session->process_start = 0U; + session->selected = 0U; + session->refresh_ms = SSH_HTOP_DEFAULT_REFRESH_MS; + session->filter_mode = 0U; + session->help = 0U; + session->filter_length = 0U; + session->last_frame_ns = 0U; + session->filter[0] = '\0'; if (command_has_option(command, "--tree") != 0) { - ch->htop_sort_key = SSH_HTOP_SORT_PARENT; + session->sort_key = SSH_HTOP_SORT_PARENT; } else if (command_option_text(command, "--sort", sort, sizeof(sort)) > 0) { - if (ssh_str_eq(sort, "mem")) ch->htop_sort_key = SSH_HTOP_SORT_MEMORY; - else if (ssh_str_eq(sort, "time")) ch->htop_sort_key = SSH_HTOP_SORT_TIME; - else if (ssh_str_eq(sort, "pid")) ch->htop_sort_key = SSH_HTOP_SORT_PID; - else if (ssh_str_eq(sort, "state")) ch->htop_sort_key = SSH_HTOP_SORT_STATE; - else if (ssh_str_eq(sort, "syscalls")) ch->htop_sort_key = SSH_HTOP_SORT_SYSCALLS; - else if (ssh_str_eq(sort, "command")) ch->htop_sort_key = SSH_HTOP_SORT_COMMAND; - else if (ssh_str_eq(sort, "parent")) ch->htop_sort_key = SSH_HTOP_SORT_PARENT; + if (ssh_str_eq(sort, "mem")) session->sort_key = SSH_HTOP_SORT_MEMORY; + else if (ssh_str_eq(sort, "time")) session->sort_key = SSH_HTOP_SORT_TIME; + else if (ssh_str_eq(sort, "pid")) session->sort_key = SSH_HTOP_SORT_PID; + else if (ssh_str_eq(sort, "state")) session->sort_key = SSH_HTOP_SORT_STATE; + else if (ssh_str_eq(sort, "syscalls")) session->sort_key = SSH_HTOP_SORT_SYSCALLS; + else if (ssh_str_eq(sort, "command")) session->sort_key = SSH_HTOP_SORT_COMMAND; + else if (ssh_str_eq(sort, "parent")) session->sort_key = SSH_HTOP_SORT_PARENT; } if (command_option_u32(command, "--cpu-start", &value) > 0) { - ch->htop_cpu_start = value; + session->cpu_start = value; } if (command_option_u32(command, "--cpu-count", &value) > 0 && value != 0U) { - ch->htop_cpu_count = value; + session->cpu_count = value; } if (command_option_u32(command, "--process-start", &value) > 0) { - ch->htop_process_start = value; + session->process_start = value; } if (command_option_u32(command, "--selected", &value) > 0) { - ch->htop_selected = value; + session->selected = value; } if (command_option_u32(command, "--sample-ms", &value) > 0) { - ch->htop_refresh_ms = value < SSH_HTOP_MIN_REFRESH_MS + session->refresh_ms = value < SSH_HTOP_MIN_REFRESH_MS ? SSH_HTOP_MIN_REFRESH_MS : value; - if (ch->htop_refresh_ms > SSH_HTOP_MAX_REFRESH_MS) { - ch->htop_refresh_ms = SSH_HTOP_MAX_REFRESH_MS; + if (session->refresh_ms > SSH_HTOP_MAX_REFRESH_MS) { + session->refresh_ms = SSH_HTOP_MAX_REFRESH_MS; } } - if (command_option_text(command, "--filter", ch->htop_filter, - sizeof(ch->htop_filter)) > 0) { - ch->htop_filter_length = ssh_str_len(ch->htop_filter); + if (command_option_text(command, "--filter", session->filter, + sizeof(session->filter)) > 0) { + session->filter_length = ssh_str_len(session->filter); } + session->columns = columns; + session->rows = rows; } -static int htop_build_command(const ssh_channel_t *ch, char *command, +int xaios_htop_build_command(const xaios_htop_session_t *session, + char *command, uint32_t capacity) { command[0] = '\0'; if (append_command_text(command, capacity, "htop --color --interactive --sample-ms ") != 0 || append_command_u32(command, capacity, SSH_HTOP_SAMPLE_MS) != 0 || append_command_text(command, capacity, " --columns ") != 0 || - append_command_u32(command, capacity, ch->terminal_columns) != 0 || + append_command_u32(command, capacity, session->columns) != 0 || append_command_text(command, capacity, " --rows ") != 0 || - append_command_u32(command, capacity, ch->terminal_rows) != 0 || + append_command_u32(command, capacity, session->rows) != 0 || append_command_text(command, capacity, " --sort ") != 0 || - append_command_text(command, capacity, htop_sort_name(ch->htop_sort_key)) != 0 || + append_command_text(command, capacity, htop_sort_name(session->sort_key)) != 0 || append_command_text(command, capacity, " --cpu-start ") != 0 || - append_command_u32(command, capacity, ch->htop_cpu_start) != 0 || + append_command_u32(command, capacity, session->cpu_start) != 0 || append_command_text(command, capacity, " --cpu-count ") != 0 || - append_command_u32(command, capacity, ch->htop_cpu_count) != 0 || + append_command_u32(command, capacity, session->cpu_count) != 0 || append_command_text(command, capacity, " --process-start ") != 0 || - append_command_u32(command, capacity, ch->htop_process_start) != 0 || + append_command_u32(command, capacity, session->process_start) != 0 || append_command_text(command, capacity, " --selected ") != 0 || - append_command_u32(command, capacity, ch->htop_selected) != 0) { + append_command_u32(command, capacity, session->selected) != 0) { return -1; } if (append_command_text(command, capacity, - ch->htop_show_all != 0U ? " --all" : " --active") != + session->show_all != 0U ? " --all" : " --active") != 0) return -1; - if (ch->htop_show_cpus == 0U && + if (session->show_cpus == 0U && append_command_text(command, capacity, " --no-cpus") != 0) return -1; - if (ch->htop_reverse != 0U && + if (session->reverse != 0U && append_command_text(command, capacity, " --reverse") != 0) return -1; - if (ch->htop_filter_length != 0U && + if (session->filter_length != 0U && (append_command_text(command, capacity, " --filter ") != 0 || - append_command_text(command, capacity, ch->htop_filter) != 0)) return -1; + append_command_text(command, capacity, session->filter) != 0)) return -1; return 0; } -static int prepare_terminal_command(const ssh_channel_t *ch, char *command, - uint32_t capacity) { - if (ch == 0 || command == 0 || ch->pty_requested == 0U || - command_token_equal(command, "htop") == 0 || +/* Shared by the SSH channel and the local console so an application is + launched with the same options on both, and therefore renders the same. + The two surfaces still differ in what happens after the first frame: the + channel keeps an interactive session alive, the console does not yet. */ +int ssh_terminal_promote_command(char *command, uint32_t capacity, + uint32_t columns, uint32_t rows) { + if (command == 0 || command_token_equal(command, "htop") == 0 || command_has_option(command, "--plain") != 0) { return 0; } @@ -551,17 +560,25 @@ static int prepare_terminal_command(const ssh_channel_t *ch, char *command, } if (command_has_option(command, "--columns") == 0 && (append_command_text(command, capacity, " --columns ") != 0 || - append_command_u32(command, capacity, ch->terminal_columns) != 0)) { + append_command_u32(command, capacity, columns) != 0)) { return -1; } if (command_has_option(command, "--rows") == 0 && (append_command_text(command, capacity, " --rows ") != 0 || - append_command_u32(command, capacity, ch->terminal_rows) != 0)) { + append_command_u32(command, capacity, rows) != 0)) { return -1; } return 0; } +static int prepare_terminal_command(const ssh_channel_t *ch, char *command, + uint32_t capacity) { + if (ch == 0 || ch->pty_requested == 0U) return 0; + return ssh_terminal_promote_command(command, capacity, ch->terminal_columns, + ch->terminal_rows); +} + + static int command_rate_allowed(ssh_connection_t *connection) { static const u64 window_ns = 60000000000ULL; u64 now; @@ -747,51 +764,111 @@ int ssh_channel_agent_send(const ssh_channel_t *session, const uint8_t *data, data, len); } -static int htop_frame_ready(ssh_channel_t *ch, uint64_t now_ns) { +int xaios_htop_frame_ready(xaios_htop_session_t *session, uint64_t now_ns) { uint64_t earliest_ns; - if (ch->htop_last_frame_ns == 0U) return 1; - earliest_ns = ch->htop_last_frame_ns > UINT64_MAX - SSH_HTOP_MIN_FRAME_NS + if (session->last_frame_ns == 0U) return 1; + earliest_ns = session->last_frame_ns > UINT64_MAX - SSH_HTOP_MIN_FRAME_NS ? UINT64_MAX - : ch->htop_last_frame_ns + SSH_HTOP_MIN_FRAME_NS; + : session->last_frame_ns + SSH_HTOP_MIN_FRAME_NS; if (now_ns >= earliest_ns) return 1; - ch->htop_next_refresh_ns = earliest_ns; + session->next_refresh_ns = earliest_ns; return 0; } -static void htop_frame_sent(ssh_channel_t *ch, uint64_t now_ns) { - ch->htop_last_frame_ns = now_ns; +void xaios_htop_frame_sent(xaios_htop_session_t *session, uint64_t now_ns) { + session->last_frame_ns = now_ns; } -static int htop_render_frame(ssh_channel_t *ch, uint64_t now_ns) { - ssh_connection_t *connection; +int xaios_htop_render(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, uint64_t now_ns) { char command[256]; char output[8192]; u64 out_size = 0U; - int result; - if (ch == 0 || ch->htop_active == 0U || ch->pending_used != 0U) return 0; - if (htop_frame_ready(ch, now_ns) == 0) return 0; - connection = ssh_conn_find(ch->owner_sockfd); - if (connection == 0 || - connection->principal_role != XAIOS_CONTROL_ROLE_ADMIN || - htop_build_command(ch, command, sizeof(command)) != 0) { + if (session == 0 || sink == 0 || session->active == 0U) return 0; + if (sink->busy != 0 && sink->busy(sink->context) != 0) return 0; + if (xaios_htop_frame_ready(session, now_ns) == 0) return 0; + if (xaios_htop_build_command(session, command, sizeof(command)) != 0) { return -1; } - result = xaios_remote_login_session( - connection->sockfd, "admin", command, output, sizeof(output), &out_size); - if (result < 0 || out_size == 0U || out_size > sizeof(output)) return -1; - connection->remote_login_session_active = 1U; - connection->last_activity = now_ns; - if (ssh_channel_send_data((int)ch->owner_sockfd, ch->remote_id, - (const uint8_t *)output, - (uint32_t)out_size) != 0) return -1; - htop_frame_sent(ch, now_ns); - ch->htop_next_refresh_ns = - now_ns + (uint64_t)ch->htop_refresh_ms * UINT64_C(1000000); - if (ch->htop_next_refresh_ns < now_ns) ch->htop_next_refresh_ns = UINT64_MAX; + if (sink->run(sink->context, command, output, sizeof(output), &out_size) < 0 || + out_size == 0U || out_size > sizeof(output)) { + return -1; + } + if (sink->write(sink->context, (const uint8_t *)output, + (uint32_t)out_size) != 0) { + return -1; + } + xaios_htop_frame_sent(session, now_ns); + session->next_refresh_ns = + now_ns + (uint64_t)session->refresh_ms * UINT64_C(1000000); + if (session->next_refresh_ns < now_ns) session->next_refresh_ns = UINT64_MAX; return 0; } -static int htop_send_help(ssh_channel_t *ch, uint64_t now_ns) { +static int htop_handle_input(ssh_channel_t *ch, const uint8_t *data, + uint32_t length) { + xaios_htop_sink_t sink = channel_htop_sink(ch); + uint32_t was_active = ch->htop.active; + int result = xaios_htop_input(&ch->htop, &sink, xaios_clock_nanos(), data, + length); + if (result < 0) return -1; + if (was_active != 0U && ch->htop.active == 0U) { + if (ch->interactive_returns_to_shell != 0U && ch->shell_active != 0U) { + ch->interactive_returns_to_shell = 0U; + return shell_send_prompt(ch); + } + ch->close_after_flush = 1U; + return flush_channel(ch); + } + return 0; +} + +static int htop_render_frame(ssh_channel_t *ch, uint64_t now_ns) { + xaios_htop_sink_t sink = channel_htop_sink(ch); + return xaios_htop_render(&ch->htop, &sink, now_ns); +} + +/* The channel's own sink: writes through the SSH transport and runs the + sampling command against this connection's remote-login session. */ +static int channel_sink_write(void *context, const uint8_t *data, + uint32_t length) { + ssh_channel_t *ch = (ssh_channel_t *)context; + return ssh_channel_send_data((int)ch->owner_sockfd, ch->remote_id, + (const uint8_t *)data, (uint32_t)length); +} + +static int channel_sink_busy(void *context) { + ssh_channel_t *ch = (ssh_channel_t *)context; + return ch->pending_used != 0U ? 1 : 0; +} + +static int channel_sink_run(void *context, const char *command, char *output, + unsigned long long capacity, + unsigned long long *output_length) { + ssh_channel_t *ch = (ssh_channel_t *)context; + ssh_connection_t *connection = ssh_conn_find(ch->owner_sockfd); + if (connection == 0 || + connection->principal_role != XAIOS_CONTROL_ROLE_ADMIN) { + return -1; + } + int result = xaios_remote_login_session(connection->sockfd, "admin", command, + output, capacity, output_length); + if (result >= 0) connection->remote_login_session_active = 1U; + return result; +} + +static xaios_htop_sink_t channel_htop_sink(ssh_channel_t *ch) { + xaios_htop_sink_t sink; + sink.write = channel_sink_write; + sink.run = channel_sink_run; + sink.busy = channel_sink_busy; + sink.context = ch; + return sink; +} + +int xaios_htop_send_help(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, + uint64_t now_ns) { static const char help[] = "\033[2J\033[H\033[?25l\033[44;97m XAIOS htop help \033[0m\r\n\r\n" " Up/Down, j/k select process\r\n" @@ -808,18 +885,19 @@ static int htop_send_help(ssh_channel_t *ch, uint64_t now_ns) { "XAIOS exposes read-only process telemetry here. Generic kill and nice " "operations are unavailable because no safe process-control ABI exists.\r\n\r\n" "Press F1, h, Escape, or q to return.\033[0m"; - if (htop_frame_ready(ch, now_ns) == 0) return 0; - int result = ssh_channel_send_data((int)ch->owner_sockfd, ch->remote_id, - (const uint8_t *)help, - (uint32_t)(sizeof(help) - 1U)); + if (xaios_htop_frame_ready(session, now_ns) == 0) return 0; + int result = sink->write(sink->context, (const uint8_t *)help, + (uint32_t)(sizeof(help) - 1U)); if (result == 0) { - htop_frame_sent(ch, now_ns); - ch->htop_next_refresh_ns = UINT64_MAX; + xaios_htop_frame_sent(session, now_ns); + session->next_refresh_ns = UINT64_MAX; } return result; } -static int htop_send_filter_prompt(ssh_channel_t *ch, uint64_t now_ns) { +int xaios_htop_send_filter_prompt(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, + uint64_t now_ns) { char prompt[256]; uint32_t used = 0U; static const char prefix[] = @@ -827,33 +905,40 @@ static int htop_send_filter_prompt(ssh_channel_t *ch, uint64_t now_ns) { "Filter: "; static const char suffix[] = "\r\n\r\nType a single token. Enter applies, Backspace edits, Escape cancels.\r\n"; - if (htop_frame_ready(ch, now_ns) == 0) return 0; - if (sizeof(prefix) - 1U + ch->htop_filter_length + sizeof(suffix) - 1U > + if (xaios_htop_frame_ready(session, now_ns) == 0) return 0; + if (sizeof(prefix) - 1U + session->filter_length + sizeof(suffix) - 1U > sizeof(prompt)) return -1; ssh_mem_copy(prompt + used, prefix, sizeof(prefix) - 1U); used += sizeof(prefix) - 1U; - ssh_mem_copy(prompt + used, ch->htop_filter, ch->htop_filter_length); - used += ch->htop_filter_length; + ssh_mem_copy(prompt + used, session->filter, session->filter_length); + used += session->filter_length; ssh_mem_copy(prompt + used, suffix, sizeof(suffix) - 1U); used += sizeof(suffix) - 1U; - int result = ssh_channel_send_data((int)ch->owner_sockfd, ch->remote_id, - (const uint8_t *)prompt, used); + int result = sink->write(sink->context, (const uint8_t *)prompt, used); if (result == 0) { - htop_frame_sent(ch, now_ns); - ch->htop_next_refresh_ns = UINT64_MAX; + xaios_htop_frame_sent(session, now_ns); + session->next_refresh_ns = UINT64_MAX; } return result; } -static int htop_finish(ssh_channel_t *ch) { +int xaios_htop_stop(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink) { static const char restore[] = "\033[0m\033[?25h\033[?1049l\033[0m\033[?25h\r"; - ch->htop_active = 0U; - ch->htop_help = 0U; - ch->htop_filter_mode = 0U; - if (ssh_channel_send_data((int)ch->owner_sockfd, ch->remote_id, - (const uint8_t *)restore, - (uint32_t)(sizeof(restore) - 1U)) != 0) return -1; + if (session == 0 || sink == 0) return -1; + session->active = 0U; + session->help = 0U; + session->filter_mode = 0U; + return sink->write(sink->context, (const uint8_t *)restore, + (uint32_t)(sizeof(restore) - 1U)); +} + +/* Channel lifecycle around the shared teardown: return to the shell that + launched htop, or close the channel when it was the only command. */ +static int htop_finish(ssh_channel_t *ch) { + xaios_htop_sink_t sink = channel_htop_sink(ch); + if (xaios_htop_stop(&ch->htop, &sink) != 0) return -1; if (ch->interactive_returns_to_shell != 0U && ch->shell_active != 0U) { ch->interactive_returns_to_shell = 0U; return shell_send_prompt(ch); @@ -882,19 +967,19 @@ static uint32_t htop_cpu_grid_columns(uint32_t cpu_count, return requested < maximum ? requested : maximum; } -static uint32_t htop_cpu_page(const ssh_channel_t *ch) { - uint32_t lines = ch->terminal_rows > 10U ? ch->terminal_rows - 10U : 1U; +static uint32_t htop_cpu_page(const xaios_htop_session_t *session) { + uint32_t lines = session->rows > 10U ? session->rows - 10U : 1U; if (lines > 8U) lines = 8U; - uint32_t columns = htop_cpu_max_columns(ch->terminal_columns); + uint32_t columns = htop_cpu_max_columns(session->columns); uint32_t visible = lines > UINT32_MAX / columns ? UINT32_MAX : lines * columns; - return ch->htop_cpu_count < visible ? ch->htop_cpu_count : visible; + return session->cpu_count < visible ? session->cpu_count : visible; } -static uint32_t htop_process_page(const ssh_channel_t *ch) { - uint32_t cpu_shown = ch->htop_show_cpus != 0U ? htop_cpu_page(ch) : 0U; +static uint32_t htop_process_page(const xaios_htop_session_t *session) { + uint32_t cpu_shown = session->show_cpus != 0U ? htop_cpu_page(session) : 0U; uint32_t grid_columns = - htop_cpu_grid_columns(cpu_shown, ch->terminal_columns); + htop_cpu_grid_columns(cpu_shown, session->columns); uint32_t cpu_lines = cpu_shown == 0U ? 0U : (cpu_shown + grid_columns - 1U) / grid_columns; @@ -902,45 +987,46 @@ static uint32_t htop_process_page(const ssh_channel_t *ch) { ? cpu_lines + 2U : cpu_lines + 3U; if (header_lines < 3U) header_lines = 3U; - return ch->terminal_rows > header_lines + 6U - ? ch->terminal_rows - header_lines - 6U : 1U; + return session->rows > header_lines + 6U + ? session->rows - header_lines - 6U : 1U; } -static int htop_handle_input(ssh_channel_t *ch, const uint8_t *data, - uint32_t length) { +int xaios_htop_input(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, uint64_t now_ns, + const uint8_t *data, uint32_t length) { int render = 0; for (uint32_t i = 0U; i < length; ++i) { uint8_t key = data[i]; - if (ch->htop_filter_mode != 0U) { + if (session->filter_mode != 0U) { if (key == 27U) { - ch->htop_filter_mode = 0U; - ch->htop_next_refresh_ns = 0U; + session->filter_mode = 0U; + session->next_refresh_ns = 0U; render = 1; } else if (key == '\r' || key == '\n') { - ch->htop_filter_mode = 0U; - ch->htop_process_start = 0U; - ch->htop_selected = 0U; - ch->htop_next_refresh_ns = 0U; - if (ch->pending_used == 0U && - htop_render_frame(ch, xaios_clock_nanos()) != 0) return -1; + session->filter_mode = 0U; + session->process_start = 0U; + session->selected = 0U; + session->next_refresh_ns = 0U; + if ((sink->busy == 0 || sink->busy(sink->context) == 0) && + xaios_htop_render(session, sink, now_ns) != 0) return -1; render = 0; } else if (key == 8U || key == 127U) { - if (ch->htop_filter_length != 0U) { - ch->htop_filter[--ch->htop_filter_length] = '\0'; + if (session->filter_length != 0U) { + session->filter[--session->filter_length] = '\0'; } - ch->htop_next_refresh_ns = 0U; - if (ch->pending_used == 0U && - htop_send_filter_prompt(ch, xaios_clock_nanos()) != 0) return -1; - } else if (ch->htop_filter_length + 1U < sizeof(ch->htop_filter) && + session->next_refresh_ns = 0U; + if ((sink->busy == 0 || sink->busy(sink->context) == 0) && + xaios_htop_send_filter_prompt(session, sink, now_ns) != 0) return -1; + } else if (session->filter_length + 1U < sizeof(session->filter) && ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9') || key == '_' || key == '-' || key == '.' || key == '/')) { - ch->htop_filter[ch->htop_filter_length++] = (char)key; - ch->htop_filter[ch->htop_filter_length] = '\0'; - ch->htop_next_refresh_ns = 0U; - if (ch->pending_used == 0U && - htop_send_filter_prompt(ch, xaios_clock_nanos()) != 0) return -1; + session->filter[session->filter_length++] = (char)key; + session->filter[session->filter_length] = '\0'; + session->next_refresh_ns = 0U; + if ((sink->busy == 0 || sink->busy(sink->context) == 0) && + xaios_htop_send_filter_prompt(session, sink, now_ns) != 0) return -1; } continue; } @@ -965,7 +1051,7 @@ static int htop_handle_input(ssh_channel_t *ch, const uint8_t *data, if (i + 4U < length && data[i + 4U] == '~') i += 2U; } else if (code == '2' && i + 4U < length && data[i + 3U] == '1' && data[i + 4U] == '~') { - return htop_finish(ch); + return xaios_htop_stop(session, sink); } i += 2U; } else if (key == 27U && i + 2U < length && data[i + 1U] == 'O' && @@ -974,115 +1060,115 @@ static int htop_handle_input(ssh_channel_t *ch, const uint8_t *data, i += 2U; } - if (ch->htop_help != 0U) { + if (session->help != 0U) { if (key == 'h' || key == 'q' || key == 27U) { - ch->htop_help = 0U; - ch->htop_next_refresh_ns = 0U; + session->help = 0U; + session->next_refresh_ns = 0U; render = 1; } continue; } - if (key == 'q' || key == 3U) return htop_finish(ch); + if (key == 'q' || key == 3U) return xaios_htop_stop(session, sink); if (key == 'h') { - ch->htop_help = 1U; - ch->htop_next_refresh_ns = 0U; - if (ch->pending_used == 0U && - htop_send_help(ch, xaios_clock_nanos()) != 0) return -1; + session->help = 1U; + session->next_refresh_ns = 0U; + if ((sink->busy == 0 || sink->busy(sink->context) == 0) && + xaios_htop_send_help(session, sink, now_ns) != 0) return -1; } else if (key == '/' ) { - ch->htop_filter_mode = 1U; - ch->htop_filter_length = 0U; - ch->htop_filter[0] = '\0'; - ch->htop_next_refresh_ns = 0U; - if (ch->pending_used == 0U && - htop_send_filter_prompt(ch, xaios_clock_nanos()) != 0) return -1; + session->filter_mode = 1U; + session->filter_length = 0U; + session->filter[0] = '\0'; + session->next_refresh_ns = 0U; + if ((sink->busy == 0 || sink->busy(sink->context) == 0) && + xaios_htop_send_filter_prompt(session, sink, now_ns) != 0) return -1; } else if (key == 'x') { - ch->htop_filter_length = 0U; - ch->htop_filter[0] = '\0'; - ch->htop_process_start = 0U; - ch->htop_selected = 0U; + session->filter_length = 0U; + session->filter[0] = '\0'; + session->process_start = 0U; + session->selected = 0U; render = 1; } else if (key == 'j') { - ++ch->htop_selected; - if (ch->htop_selected >= - ch->htop_process_start + htop_process_page(ch)) { - ++ch->htop_process_start; + ++session->selected; + if (session->selected >= + session->process_start + htop_process_page(session)) { + ++session->process_start; } render = 1; } else if (key == 'k') { - if (ch->htop_selected != 0U) --ch->htop_selected; - if (ch->htop_selected < ch->htop_process_start) { - ch->htop_process_start = ch->htop_selected; + if (session->selected != 0U) --session->selected; + if (session->selected < session->process_start) { + session->process_start = session->selected; } render = 1; } else if (key == 'D') { - uint32_t page = htop_process_page(ch); - ch->htop_selected += page; - ch->htop_process_start += page; + uint32_t page = htop_process_page(session); + session->selected += page; + session->process_start += page; render = 1; } else if (key == 'U') { - uint32_t page = htop_process_page(ch); - ch->htop_selected = ch->htop_selected > page ? ch->htop_selected - page : 0U; - ch->htop_process_start = ch->htop_process_start > page - ? ch->htop_process_start - page : 0U; + uint32_t page = htop_process_page(session); + session->selected = session->selected > page ? session->selected - page : 0U; + session->process_start = session->process_start > page + ? session->process_start - page : 0U; render = 1; } else if (key == 'P') { - ch->htop_sort_key = SSH_HTOP_SORT_CPU; + session->sort_key = SSH_HTOP_SORT_CPU; render = 1; } else if (key == 'M') { - ch->htop_sort_key = SSH_HTOP_SORT_MEMORY; + session->sort_key = SSH_HTOP_SORT_MEMORY; render = 1; } else if (key == 'T') { - ch->htop_sort_key = SSH_HTOP_SORT_TIME; + session->sort_key = SSH_HTOP_SORT_TIME; render = 1; } else if (key == 'N') { - ch->htop_sort_key = SSH_HTOP_SORT_PID; + session->sort_key = SSH_HTOP_SORT_PID; render = 1; } else if (key == 'S') { - ch->htop_sort_key = SSH_HTOP_SORT_SYSCALLS; + session->sort_key = SSH_HTOP_SORT_SYSCALLS; render = 1; } else if (key == 'C') { - ch->htop_sort_key = SSH_HTOP_SORT_COMMAND; + session->sort_key = SSH_HTOP_SORT_COMMAND; render = 1; } else if (key == 'F') { - ch->htop_sort_key = (ch->htop_sort_key + 1U) % 7U; + session->sort_key = (session->sort_key + 1U) % 7U; render = 1; } else if (key == 'I') { - ch->htop_reverse ^= 1U; + session->reverse ^= 1U; render = 1; } else if (key == 't') { - ch->htop_sort_key = ch->htop_sort_key == SSH_HTOP_SORT_PARENT + session->sort_key = session->sort_key == SSH_HTOP_SORT_PARENT ? SSH_HTOP_SORT_CPU : SSH_HTOP_SORT_PARENT; render = 1; } else if (key == 'a') { - ch->htop_show_all ^= 1U; + session->show_all ^= 1U; render = 1; } else if (key == '1') { - ch->htop_show_cpus ^= 1U; + session->show_cpus ^= 1U; render = 1; } else if (key == '[') { - uint32_t page = htop_cpu_page(ch); - ch->htop_cpu_start = ch->htop_cpu_start > page - ? ch->htop_cpu_start - page : 0U; + uint32_t page = htop_cpu_page(session); + session->cpu_start = session->cpu_start > page + ? session->cpu_start - page : 0U; render = 1; } else if (key == ']') { - uint32_t page = htop_cpu_page(ch); - if (UINT32_MAX - ch->htop_cpu_start >= page) { - ch->htop_cpu_start += page; + uint32_t page = htop_cpu_page(session); + if (UINT32_MAX - session->cpu_start >= page) { + session->cpu_start += page; } render = 1; } else if (key == '+') { - if (ch->htop_refresh_ms > SSH_HTOP_MIN_REFRESH_MS) { - ch->htop_refresh_ms /= 2U; - if (ch->htop_refresh_ms < SSH_HTOP_MIN_REFRESH_MS) { - ch->htop_refresh_ms = SSH_HTOP_MIN_REFRESH_MS; + if (session->refresh_ms > SSH_HTOP_MIN_REFRESH_MS) { + session->refresh_ms /= 2U; + if (session->refresh_ms < SSH_HTOP_MIN_REFRESH_MS) { + session->refresh_ms = SSH_HTOP_MIN_REFRESH_MS; } } render = 1; } else if (key == '-') { - if (ch->htop_refresh_ms < SSH_HTOP_MAX_REFRESH_MS) { - ch->htop_refresh_ms *= 2U; - if (ch->htop_refresh_ms > SSH_HTOP_MAX_REFRESH_MS) { - ch->htop_refresh_ms = SSH_HTOP_MAX_REFRESH_MS; + if (session->refresh_ms < SSH_HTOP_MAX_REFRESH_MS) { + session->refresh_ms *= 2U; + if (session->refresh_ms > SSH_HTOP_MAX_REFRESH_MS) { + session->refresh_ms = SSH_HTOP_MAX_REFRESH_MS; } } render = 1; @@ -1090,8 +1176,8 @@ static int htop_handle_input(ssh_channel_t *ch, const uint8_t *data, render = 1; } } - if (render != 0 && ch->htop_active != 0U && ch->pending_used == 0U) { - return htop_render_frame(ch, xaios_clock_nanos()); + if (render != 0 && session->active != 0U && (sink->busy == 0 || sink->busy(sink->context) == 0)) { + return xaios_htop_render(session, sink, now_ns); } return 0; } @@ -1146,7 +1232,8 @@ static int shell_start_htop(ssh_channel_t *ch, char *command) { } return shell_send_prompt(ch); } - htop_initialize(ch, command); + xaios_htop_start(&ch->htop, command, ch->terminal_columns, + ch->terminal_rows); ch->interactive_returns_to_shell = 1U; if (ssh_channel_send_data((int)ch->owner_sockfd, ch->remote_id, (const uint8_t *)enter_screen, @@ -1157,9 +1244,9 @@ static int shell_start_htop(ssh_channel_t *ch, char *command) { return -1; } uint64_t now_ns = xaios_clock_nanos(); - htop_frame_sent(ch, now_ns); - ch->htop_next_refresh_ns = - now_ns + (uint64_t)ch->htop_refresh_ms * UINT64_C(1000000); + xaios_htop_frame_sent(&ch->htop, now_ns); + ch->htop.next_refresh_ns = + now_ns + (uint64_t)ch->htop.refresh_ms * UINT64_C(1000000); return 0; } @@ -1461,7 +1548,7 @@ static int shell_handle_input(ssh_channel_t *ch, const uint8_t *data, ch->shell_ignore_lf = value == '\r' ? 1U : 0U; if (shell_execute_line(ch) != 0) return -1; if (ch->shell_active == 0U) return 0; - if (ch->htop_active != 0U) { + if (ch->htop.active != 0U) { return i + 1U < length ? htop_handle_input(ch, data + i + 1U, length - i - 1U) : 0; @@ -1546,17 +1633,19 @@ int ssh_channel_tick(uint64_t now_ns) { ch->exit_status = 1U; if (pong_finish(ch, 1U) != 0) return -1; } - if (ch->active == 0U || ch->htop_active == 0U || + if (ch->active == 0U || ch->htop.active == 0U || ch->pending_used != 0U || - now_ns < ch->htop_next_refresh_ns) { + now_ns < ch->htop.next_refresh_ns) { continue; } - if (ch->htop_help != 0U) { - if (htop_send_help(ch, now_ns) != 0) return -1; + if (ch->htop.help != 0U) { + xaios_htop_sink_t sink = channel_htop_sink(ch); + if (xaios_htop_send_help(&ch->htop, &sink, now_ns) != 0) return -1; continue; } - if (ch->htop_filter_mode != 0U) { - if (htop_send_filter_prompt(ch, now_ns) != 0) return -1; + if (ch->htop.filter_mode != 0U) { + xaios_htop_sink_t sink = channel_htop_sink(ch); + if (xaios_htop_send_filter_prompt(&ch->htop, &sink, now_ns) != 0) return -1; continue; } if (htop_render_frame(ch, now_ns) != 0) { @@ -1600,7 +1689,7 @@ static int handle_channel_request(int sockfd, const ssh_packet_t *pkt) { if (ssh_str_eq(request_type, "window-change")) { int valid = parse_window_change(ch, pkt, data_start) == 0; - if (valid && ch->htop_active != 0U) ch->htop_next_refresh_ns = 0U; + if (valid && ch->htop.active != 0U) ch->htop.next_refresh_ns = 0U; if (valid && ch->nano.active != 0U) { nano_editor_resize(&ch->nano, ch->terminal_columns, ch->terminal_rows); if (nano_render_frame(ch) != 0) return -1; @@ -1721,7 +1810,8 @@ static int handle_channel_request(int sockfd, const ssh_packet_t *pkt) { ch->close_after_flush = 1U; return flush_channel(ch); } - htop_initialize(ch, command); + xaios_htop_start(&ch->htop, command, ch->terminal_columns, + ch->terminal_rows); if (ssh_channel_send_data(sockfd, ch->remote_id, (const uint8_t *)enter_screen, (uint32_t)(sizeof(enter_screen) - 1U)) != 0) { @@ -1733,10 +1823,10 @@ static int handle_channel_request(int sockfd, const ssh_packet_t *pkt) { return -1; } uint64_t now_ns = xaios_clock_nanos(); - htop_frame_sent(ch, now_ns); - ch->htop_next_refresh_ns = + xaios_htop_frame_sent(&ch->htop, now_ns); + ch->htop.next_refresh_ns = now_ns + - (uint64_t)ch->htop_refresh_ms * UINT64_C(1000000); + (uint64_t)ch->htop.refresh_ms * UINT64_C(1000000); return 0; } @@ -2017,7 +2107,7 @@ int ssh_channel_handle_packet(int sockfd, const ssh_packet_t *pkt) { return 0; } - if (ch->htop_active != 0U) { + if (ch->htop.active != 0U) { return htop_handle_input(ch, pkt->data + 9U, data_len); } @@ -2098,7 +2188,7 @@ int ssh_channel_handle_packet(int sockfd, const ssh_packet_t *pkt) { ch->close_after_flush = 1U; return flush_channel(ch); } - if (ch->htop_active != 0U) return htop_finish(ch); + if (ch->htop.active != 0U) return htop_finish(ch); if (ch->nano.active != 0U) return nano_finish(ch, 0U); if (ch->less.active != 0U) return less_finish(ch, 0U); if (ch->pong.active != 0U) return pong_finish(ch, 0U); diff --git a/userspace/sshd/ssh_channel.h b/userspace/sshd/ssh_channel.h index 54fe35c9..84300848 100644 --- a/userspace/sshd/ssh_channel.h +++ b/userspace/sshd/ssh_channel.h @@ -5,6 +5,70 @@ #include "less_pager.h" #include "nano_editor.h" #include "pong_game.h" + +/* Interactive process-monitor session state. + Held apart from the transport so the SSH channel and the local console can + drive the same state machine instead of each keeping their own copy, which + is what let the two surfaces disagree about how htop behaves. */ +typedef struct xaios_htop_session { + uint32_t active; + uint32_t show_all; + uint32_t show_cpus; + uint32_t sort_key; + uint32_t reverse; + uint32_t cpu_start; + uint32_t cpu_count; + uint32_t process_start; + uint32_t selected; + uint32_t refresh_ms; + uint32_t filter_mode; + uint32_t help; + uint32_t filter_length; + uint32_t columns; + uint32_t rows; + uint64_t last_frame_ns; + uint64_t next_refresh_ns; + char filter[32]; +} xaios_htop_session_t; + +/* Where an htop session sends output and how it runs a sampling command. + The SSH channel fills this in with its own transport, the local console with + direct console writes, so both drive the identical session logic. */ +typedef struct xaios_htop_sink { + int (*write)(void *context, const uint8_t *data, uint32_t length); + int (*run)(void *context, const char *command, char *output, + unsigned long long capacity, + unsigned long long *output_length); + int (*busy)(void *context); + void *context; +} xaios_htop_sink_t; + +void xaios_htop_start(xaios_htop_session_t *session, const char *command, + uint32_t columns, uint32_t rows); +int xaios_htop_build_command(const xaios_htop_session_t *session, + char *command, uint32_t capacity); +int xaios_htop_frame_ready(xaios_htop_session_t *session, + uint64_t now_ns); +void xaios_htop_frame_sent(xaios_htop_session_t *session, + uint64_t now_ns); +int xaios_htop_render(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, uint64_t now_ns); +int xaios_htop_send_help(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, + uint64_t now_ns); +int xaios_htop_send_filter_prompt(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, + uint64_t now_ns); +/* Writes the terminal restore sequence and clears the session. Surface + lifecycle (returning to a shell, closing a channel) stays with the caller. */ +int xaios_htop_stop(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink); +/* Returns negative on error. The session ends by clearing session->active, + which the caller checks so it can run its own teardown -- returning to a + shell, closing a channel, or reprinting the console prompt. */ +int xaios_htop_input(xaios_htop_session_t *session, + const xaios_htop_sink_t *sink, uint64_t now_ns, + const unsigned char *data, uint32_t length); #include "ssh_protocol.h" #define SSH_CHANNELS_PER_CONNECTION 2U @@ -40,27 +104,12 @@ typedef struct ssh_channel { uint32_t pty_requested; uint32_t terminal_columns; uint32_t terminal_rows; - uint32_t htop_active; - uint32_t htop_show_all; - uint32_t htop_show_cpus; - uint32_t htop_sort_key; - uint32_t htop_reverse; - uint32_t htop_cpu_start; - uint32_t htop_cpu_count; - uint32_t htop_process_start; - uint32_t htop_selected; - uint32_t htop_refresh_ms; - uint32_t htop_filter_mode; - uint32_t htop_help; - uint32_t htop_filter_length; + xaios_htop_session_t htop; uint32_t shell_active; uint32_t shell_line_length; uint32_t shell_ignore_lf; uint32_t ssh_client_slot; uint32_t interactive_returns_to_shell; - uint64_t htop_last_frame_ns; - uint64_t htop_next_refresh_ns; - char htop_filter[32]; char shell_line[SSH_CHANNEL_SHELL_LINE_SIZE]; nano_editor_t nano; less_pager_t less; @@ -79,4 +128,11 @@ int ssh_channel_send_data(int sockfd, uint32_t remote_id, int ssh_channel_agent_send(const ssh_channel_t *session, const uint8_t *data, uint32_t len); + +/* Apply the terminal-application option promotion used for a PTY session. + Exposed so the local console launches applications exactly as the SSH + channel does, instead of maintaining a second copy of the rules. */ +int ssh_terminal_promote_command(char *command, uint32_t capacity, + uint32_t columns, uint32_t rows); + #endif diff --git a/userspace/sshd/ssh_host_key.c b/userspace/sshd/ssh_host_key.c index c4c51006..c2477e02 100644 --- a/userspace/sshd/ssh_host_key.c +++ b/userspace/sshd/ssh_host_key.c @@ -1,4 +1,5 @@ #include "ssh_host_key.h" +#include "sshd.h" #include "ssh_crypto.h" #include "ssh_utils.h" #include "tweetnacl_subset.h" @@ -9,6 +10,9 @@ static uint8_t g_host_private_key[32]; static uint8_t g_host_public_key[32]; static uint32_t g_key_initialized = 0; +/* Set when the key in use could not be written to persistent storage, so the + condition can be reported rather than silently tolerated. */ +static uint32_t g_host_key_ephemeral = 0; /* Convert hex char to nibble */ static int hex_to_nibble(char c) { @@ -62,15 +66,27 @@ static int ensure_key(void) { } xaios_ed25519_public_key(g_host_public_key, g_host_private_key); - /* Save private key to persistent storage */ + /* Save private key to persistent storage. + + A failure here used to abort SSH startup, which made an unwritable + persistent filesystem mean no remote access at all. The key pair in + hand is perfectly good; only its durability is in question, so the + service continues with it rather than leaving the machine unreachable + at exactly the moment an operator needs to get in and repair storage. + + The cost is a host key that does not survive this boot, which clients + see as a changed key and warn about. That warning is the point: it is + visible, whereas an unreachable machine offers nothing to act on. */ char hex_buf[65]; bin_to_hex(g_host_private_key, 32, hex_buf); int save_ret = xaios_write_file(HOST_KEY_PATH, hex_buf); ssh_mem_zero(hex_buf, sizeof(hex_buf)); if (save_ret != 64) { - ssh_mem_zero(g_host_private_key, sizeof(g_host_private_key)); - ssh_mem_zero(g_host_public_key, sizeof(g_host_public_key)); - return -1; + g_host_key_ephemeral = 1U; + ssh_log(SSH_LOG_ERROR, + "Host key could not be persisted; continuing with an ephemeral " + "key. Clients will report a changed host key until persistent " + "storage is repaired.\n"); } g_key_initialized = 1; @@ -82,6 +98,10 @@ int ssh_host_key_init(void) { return ensure_key(); } +int ssh_host_key_is_ephemeral(void) { + return g_host_key_ephemeral != 0U; +} + int ssh_host_key_reload(void) { ssh_mem_zero(g_host_private_key, sizeof(g_host_private_key)); ssh_mem_zero(g_host_public_key, sizeof(g_host_public_key)); diff --git a/userspace/sshd/ssh_host_key.h b/userspace/sshd/ssh_host_key.h index 386da299..d28aa650 100644 --- a/userspace/sshd/ssh_host_key.h +++ b/userspace/sshd/ssh_host_key.h @@ -5,6 +5,9 @@ /* Persistent Ed25519 host identity seeded from the kernel entropy service. */ int ssh_host_key_init(void); +/* True when the active host key could not be persisted, so it will not + survive this boot and clients will report it as changed. */ +int ssh_host_key_is_ephemeral(void); int ssh_host_key_reload(void); int ssh_host_key_get_private(uint8_t priv[32]); int ssh_host_key_get_public(uint8_t pub[32]); diff --git a/userspace/sshd/sshd.c b/userspace/sshd/sshd.c index 891bea1f..b836cecb 100644 --- a/userspace/sshd/sshd.c +++ b/userspace/sshd/sshd.c @@ -7,6 +7,7 @@ #include "ssh_mlkem.h" #include "ssh_utils.h" #include "tweetnacl_subset.h" +#include "less_pager.h" #include "nano_editor.h" #include "pong_game.h" #include @@ -40,6 +41,7 @@ static uint32_t g_console_ssh_ready; static int32_t g_console_boot_error; static nano_editor_t g_console_nano; static pong_game_t g_console_pong; +static less_pager_t g_console_less; static uint32_t g_console_auth_state; static uint32_t g_console_auth_failures; static uint64_t g_console_ui_next_refresh; @@ -170,6 +172,12 @@ static void sha256_update_kex_secret(sha256_ctx_t *context, sha256_update_mpint(context, value); } +/* The local console has no window-size protocol, so applications are given a + conservative terminal that renders correctly on a serial line and inside the + framebuffer terminal alike. */ +#define SSHD_CONSOLE_COLUMNS 80U +#define SSHD_CONSOLE_ROWS 24U + static int g_log_fd = -1; static uint32_t g_log_bytes = 0; @@ -435,6 +443,73 @@ static void console_write_ipv4(uint32_t address) { console_write(line); } +/* Render the public IPv6 address in RFC 5952 form, with the longest run of + zero groups collapsed to "::". Prints nothing when the guest has no public + IPv6 address, so an IPv4-only network shows an IPv4-only boot screen. */ +static void console_write_ipv6(void) { + uint8_t address[16]; + if (xaios_net_local_ipv6(address) != 1) return; + + uint16_t groups[8]; + for (uint32_t i = 0U; i < 8U; ++i) { + groups[i] = (uint16_t)(((uint16_t)address[i * 2U] << 8U) | + address[i * 2U + 1U]); + } + uint32_t best_start = 8U; + uint32_t best_length = 0U; + uint32_t run_start = 8U; + uint32_t run_length = 0U; + for (uint32_t i = 0U; i < 8U; ++i) { + if (groups[i] == 0U) { + if (run_length == 0U) run_start = i; + ++run_length; + if (run_length > best_length) { + best_length = run_length; + best_start = run_start; + } + } else { + run_length = 0U; + } + } + if (best_length < 2U) best_start = 8U; + + static const char hex[] = "0123456789abcdef"; + /* The marker carries both of its colons, and the group after it therefore + adds no separator of its own. Splitting the pair across the marker and + the next group breaks whenever there is no next group: a run reaching + the last group would render one colon short. */ + uint32_t marker_end = best_start < 8U ? best_start + best_length : 8U; + char line[48]; + u64 offset = 0U; + xaios_memzero(line, sizeof(line)); + for (uint32_t i = 0U; i < 8U;) { + if (i == best_start) { + xaios_append_cstr(line, sizeof(line), &offset, "::"); + i += best_length; + continue; + } + if (i != 0U && i != marker_end) { + xaios_append_cstr(line, sizeof(line), &offset, ":"); + } + uint16_t value = groups[i]; + char digits[4]; + uint32_t count = 0U; + do { + digits[count++] = hex[value & 0xfU]; + value = (uint16_t)(value >> 4U); + } while (value != 0U); + while (count != 0U) { + char single[2]; + single[0] = digits[--count]; + single[1] = '\0'; + xaios_append_cstr(line, sizeof(line), &offset, single); + } + ++i; + } + console_write("\nIPv6: "); + console_write(line); +} + static void console_write_error(int32_t status) { char line[32]; u64 offset = 0U; @@ -504,6 +579,43 @@ static int console_nano_argument(const char *command, char *argument, return 0; } +/* Local console pager, driving the same less_pager_t the SSH channel uses so + paging, searching and quitting behave identically on both surfaces. */ +static int console_start_less(const char *command) { + char cwd[LESS_PAGER_PATH_MAX]; + u64 cwd_size = 0U; + uint32_t frame_size = 0U; + if (xaios_remote_login_session(SSHD_CONSOLE_SESSION_ID, "admin", "pwd", cwd, + sizeof(cwd), &cwd_size) < 0 || + cwd_size == 0U || cwd_size >= sizeof(cwd)) { + return -1; + } + while (cwd_size != 0U && + (cwd[cwd_size - 1U] == '\n' || cwd[cwd_size - 1U] == '\r')) { + cwd[--cwd_size] = '\0'; + } + if (less_pager_open(&g_console_less, command, cwd, SSHD_CONSOLE_COLUMNS, + SSHD_CONSOLE_ROWS) != 0) { + console_write( + "less: usage: less [-N] FILE (regular files up to 128 KiB)\n"); + return -1; + } + if (less_pager_render(&g_console_less, g_console_output, + sizeof(g_console_output), &frame_size) != 0) { + less_pager_close(&g_console_less); + return -1; + } + console_write("\033[?1049h\033[?25l"); + (void)console_write_bytes(g_console_output, frame_size); + return 0; +} + +static void console_finish_less(void) { + less_pager_close(&g_console_less); + console_write("\033[0m\033[?25h\033[?1049l\033[0m\033[?25h\r"); + console_prompt(); +} + static int console_start_nano(const char *command) { char argument[NANO_EDITOR_PATH_MAX]; char cwd[NANO_EDITOR_PATH_MAX]; @@ -584,6 +696,99 @@ static int console_start_pong(void) { return 0; } +/* Local console htop session. + Drives the same xaios_htop_session_t state machine the SSH channel uses, so + refresh cadence, sort keys, filtering, help and quit behave identically on + both surfaces. Only the sink differs: bytes go straight to the console and + sampling runs against the console's own remote-login session. */ +/* "htop" with or without options, but not "htop --plain": that form asks for + the snapshot output on purpose, on either surface. */ +static int console_command_is_htop(const char *command) { + static const char name[] = "htop"; + uint32_t i = 0U; + if (command == 0) return 0; + for (; i < sizeof(name) - 1U; ++i) { + if (command[i] != name[i]) return 0; + } + if (command[i] != '\0' && command[i] != ' ') return 0; + for (uint32_t j = i; command[j] != '\0'; ++j) { + if (command[j] == '-' && command[j + 1U] == '-' && + command[j + 2U] == 'p' && command[j + 3U] == 'l' && + command[j + 4U] == 'a' && command[j + 5U] == 'i' && + command[j + 6U] == 'n') { + return 0; + } + } + return 1; +} + +static xaios_htop_session_t g_console_htop; + +static int console_htop_write(void *context, const uint8_t *data, + uint32_t length) { + (void)context; + /* console_write_bytes reports success as 0, not a byte count. */ + return console_write_bytes((const char *)data, length); +} + +static int console_htop_run(void *context, const char *command, char *output, + unsigned long long capacity, + unsigned long long *output_length) { + (void)context; + return xaios_remote_login_session(SSHD_CONSOLE_SESSION_ID, "admin", command, + output, capacity, output_length); +} + +static xaios_htop_sink_t console_htop_sink(void) { + xaios_htop_sink_t sink; + sink.write = console_htop_write; + sink.run = console_htop_run; + sink.busy = 0; + sink.context = 0; + return sink; +} + +static void console_finish_htop(void) { + xaios_htop_sink_t sink = console_htop_sink(); + (void)xaios_htop_stop(&g_console_htop, &sink); + console_prompt(); +} + +static int console_start_htop(const char *command) { + xaios_htop_sink_t sink = console_htop_sink(); + xaios_htop_start(&g_console_htop, command, SSHD_CONSOLE_COLUMNS, + SSHD_CONSOLE_ROWS); + console_write("\033[?1049h\033[?25l"); + if (xaios_htop_render(&g_console_htop, &sink, xaios_clock_nanos()) != 0) { + console_finish_htop(); + return -1; + } + return 0; +} + +static void console_service_htop(uint64_t now_ns) { + xaios_htop_sink_t sink = console_htop_sink(); + if (g_console_htop.active == 0U) return; + if (g_console_htop.help != 0U) { + if (now_ns >= g_console_htop.next_refresh_ns && + xaios_htop_send_help(&g_console_htop, &sink, now_ns) != 0) { + console_finish_htop(); + } + return; + } + if (g_console_htop.filter_mode != 0U) { + if (now_ns >= g_console_htop.next_refresh_ns && + xaios_htop_send_filter_prompt(&g_console_htop, &sink, now_ns) != 0) { + console_finish_htop(); + } + return; + } + if (now_ns >= g_console_htop.next_refresh_ns && + xaios_htop_render(&g_console_htop, &sink, now_ns) != 0) { + console_finish_htop(); + } +} + static void console_finish_pong(void) { g_console_pong.active = 0U; console_write("\033[0m\033[?25h\033[?1049l\033[0m\033[?25h\r"); @@ -604,6 +809,7 @@ static void console_render_boot_status(void) { console_write("Loaded: system services\nLoading: complete\n"); console_write("Remaining: 0 components\n\nIPv4: "); console_write_ipv4(g_console_ipv4); + console_write_ipv6(); console_write("\nSSH server: "); if (g_console_ssh_ready != 0U) { console_write("up and running (tcp/22)\n\n"); @@ -651,6 +857,12 @@ static void console_execute_command(void) { (void)console_start_nano(g_console_command); } else if (ssh_str_eq(g_console_command, "pong")) { (void)console_start_pong(); + } else if (g_console_command[0] == 'l' && g_console_command[1] == 'e' && + g_console_command[2] == 's' && g_console_command[3] == 's' && + (g_console_command[4] == '\0' || g_console_command[4] == ' ')) { + (void)console_start_less(g_console_command); + } else if (console_command_is_htop(g_console_command)) { + (void)console_start_htop(g_console_command); } else if (ssh_str_eq(g_console_command, "clear")) { console_write("\x1b[2J\x1b[H"); } else if (ssh_str_eq(g_console_command, "exit") || @@ -664,6 +876,13 @@ static void console_execute_command(void) { return; } else { u64 output_bytes = 0U; + /* Launch terminal applications with the same options the SSH channel + gives them, so htop and friends render identically on both surfaces + rather than falling back to their plain snapshot form here. */ + (void)ssh_terminal_promote_command(g_console_command, + sizeof(g_console_command), + SSHD_CONSOLE_COLUMNS, + SSHD_CONSOLE_ROWS); xaios_memzero(g_console_output, sizeof(g_console_output)); int status = xaios_remote_login_session( SSHD_CONSOLE_SESSION_ID, "admin", g_console_command, g_console_output, @@ -679,7 +898,8 @@ static void console_execute_command(void) { } } g_console_command_length = 0U; - if (g_console_nano.active == 0U && g_console_pong.active == 0U) + if (g_console_nano.active == 0U && g_console_pong.active == 0U && + g_console_htop.active == 0U && g_console_less.active == 0U) console_prompt(); } @@ -789,6 +1009,31 @@ static void console_tick(void) { } continue; } + if (g_console_less.active != 0U) { + uint32_t frame_size = 0U; + uint32_t should_exit = 0U; + if (less_pager_input(&g_console_less, (const uint8_t *)&value, 1U, + &should_exit) != 0) { + should_exit = 1U; + } + if (should_exit != 0U) { + console_finish_less(); + } else if (less_pager_render(&g_console_less, g_console_output, + sizeof(g_console_output), + &frame_size) == 0) { + (void)console_write_bytes(g_console_output, frame_size); + } + continue; + } + if (g_console_htop.active != 0U) { + xaios_htop_sink_t sink = console_htop_sink(); + (void)xaios_htop_input(&g_console_htop, &sink, xaios_clock_nanos(), + (const uint8_t *)&value, 1U); + /* The session signals it is finished by clearing active; the surface + then does its own teardown, here reprinting the shell prompt. */ + if (g_console_htop.active == 0U) console_prompt(); + continue; + } if (g_console_pong.active != 0U) { uint32_t should_exit = 0U; uint64_t now_ns = xaios_clock_nanos(); @@ -2398,6 +2643,7 @@ int sshd_run(void) { uint64_t now = timer_now(); console_refresh_boot_ui(now); console_service_pong(now); + console_service_htop(now); console_tick(); for (uint32_t i = 0; g_console_ssh_ready != 0U && i < 4U; ++i) { uint8_t udp_buffer[1478]; diff --git a/wiki/Applications.md b/wiki/Applications.md index ea1e2666..6226e5ba 100644 --- a/wiki/Applications.md +++ b/wiki/Applications.md @@ -10,6 +10,11 @@ are documented separately in [[Commands|Commands]]. ## Boot and service applications +`/bin` is the boot image mounted read-only into the VFS, so `ls /bin` +lists the shipped executables and `ls -l` reports their sizes. Writes, +renames, and deletions under it are rejected; mutable data belongs under +`/state`, `/apps`, and `/tmp`. + | Path | Purpose | Normal startup | |---|---|---| | `/init` | First userspace process. Establishes the initial service lifecycle and returns status to the kernel. | Started once during boot. | diff --git a/wiki/Boot-and-Console.md b/wiki/Boot-and-Console.md index fa37ecd0..0fcb0510 100644 --- a/wiki/Boot-and-Console.md +++ b/wiki/Boot-and-Console.md @@ -20,6 +20,14 @@ It then reports one of these outcomes: credentials, crypto, or listener initialization fails. +Once networking is active and before any service starts, the kernel sets the +wall clock from NTP. The default server is a bare address, so this needs no +DNS. It is bounded and non-fatal: a network that filters UDP/123 costs a pause +of at most six seconds and boot continues on the RTC reading. Without this the +clock is whatever the RTC reports, and QEMU's PL031 commonly reports epoch +zero, which leaves anything checking a certificate validity window seeing every +certificate as not yet valid. Confirm the result with `date` and `ntp status`. + SSH is not opened until networking is active and a bounded IPv4 TCP connection to `1.1.1.1:443` succeeds. This checks configured external reachability without making SSH startup depend on public DNS. Failure leaves the listener closed. @@ -62,6 +70,42 @@ not create a shell session. The same byte-oriented console interface accepts USB HID boot-keyboard input from the default xHCI device on both QEMU ARM64 and QEMU x86_64; PL011 serial remains available when no USB keyboard is attached. +## Diagnosing a boot failure + +A panic prints registers, a backtrace, and the tail of the kernel log. That +last section matters because a normal boot redraws the progress display over +the serial console, so the message explaining a failure is cleared from the +screen moments before the panic replaces it. Capture starts at the very +beginning of boot and does not depend on storage, so it is available even for +failures that happen before any filesystem is mounted. + +Boot-time reads of the boot image retry a bounded number of times before +failing. A read that only succeeded on a retry is logged, so a marginal device +stays visible rather than being silently absorbed. + +## Rebuilding over an existing persistent disk + +The active administration configuration lives in persistent storage, and a +stored record takes precedence over the compiled default. `build/xaios-persistent.img` +is not recreated by a rebuild, so an image rebuilt over a disk written by an +earlier key-only build inherits `password=disabled` from that disk and comes up +with the console locked, whatever the new build was configured for. The boot +log names it: + +```text +admin-control: initialized schema=1 generation=1 password=disabled +``` + +Discard the stale state to get the development credentials the build intended: + +```sh +make clean-persistent +make image +``` + +This deletes persistent state, so use it on development images rather than on a +disk holding anything worth keeping. + ## Verbose diagnostics Normal boot avoids scrolling logs. For failure analysis: diff --git a/wiki/Current-Limitations.md b/wiki/Current-Limitations.md index df4a48a0..95b47165 100644 --- a/wiki/Current-Limitations.md +++ b/wiki/Current-Limitations.md @@ -57,12 +57,22 @@ Progress status and ownership live only in [[Project Tracker|Project-Tracker]]. - DNS performs asynchronous A/AAAA resolution with timeout, retry, bounded TTL cache, and DNS-over-TCP fallback. It locally validates DNSKEY, DS, and RRSIG chains from compiled root DS anchors and accepts signed exact-owner NSEC NODATA proofs. - NXDOMAIN, NSEC3, CNAME/DNAME and wildcard synthesis, plus production root-anchor - rollover/update policy, remain unsupported and fail closed. + It also resolves names under an unsigned delegation, proving the absent DS + from a signed NSEC3 the parent serves, including the opt-out form, and then + accepting the unsigned answer as insecure rather than refusing it as bogus. + Insecure answers are counted separately from authenticated ones, because + they carry a weaker guarantee. NSEC3 iteration counts above 150 are refused + rather than computed. NXDOMAIN, CNAME/DNAME and wildcard synthesis, plus + production root-anchor rollover/update policy, remain unsupported and fail + closed. - The SNTP client validates request binding, server mode/version, stratum, and bounded retry/timeout behavior, then applies corrections through a monotonic - 500-ppm slew after initial calibration. QEMU's PL031 RTC may report epoch - zero, and public UDP/123 may be filtered; both conditions remain explicit. + 500-ppm slew after initial calibration. Boot performs one bounded, non-fatal + synchronization against a fixed server address before services start, and an + offset from an unset clock is stepped rather than slewed. QEMU's PL031 RTC may + report epoch zero, and public UDP/123 may be filtered; a filtered port leaves + boot on the RTC reading after a bounded pause, so both conditions remain + explicit. Production NTP authentication, source policy, oscillator characterization, and physical RTC qualification remain open. - TCP implements retained segments, cumulative and partial ACK handling, @@ -98,6 +108,14 @@ Progress status and ownership live only in [[Project Tracker|Project-Tracker]]. - ModelFS activation and MutableFS audit persistence are separate durability domains. A post-publication audit failure cannot roll back an already published active generation. +- MutableFS v5 keeps two metadata copies and alternates writes between them, + so a write interrupted by power loss damages only the copy that is not + currently authoritative and mount falls back to the survivor. The mirror + sits past the data region, so volumes written before it keep mounting, and + a volume with no room for it operates single-copy. When both copies are + damaged the mount still refuses rather than formatting, because falling + back is a recovery and not a licence to discard data. Host tests damage + each copy in turn and require the volume to mount with contents intact. - MutableFS v5 is intentionally bounded to 256 nodes, 256 open handles, 256 KiB files and 4 MiB of data space. Interactive `nano` is further bounded to a 32 KiB editing buffer. This is suitable for OS state and small user files, @@ -115,7 +133,13 @@ Progress status and ownership live only in [[Project Tracker|Project-Tracker]]. - Role, capability, replay, rollback, host-key rotation, sensitive-path denial, and secret-redaction behavior pass QEMU/OpenSSH gates but have not received an independent production security review. -- `xapt` requires TLS 1.2 with an exact RSA public-key pin and supports signed +- `xapt` supports TLS 1.2, validating the certificate chain against compiled-in + ISRG roots with server-name and validity checks, or an exact RSA public-key + pin for a private origin. Chain validation depends on the realtime clock set + during boot and refuses an unset one. The shipped configuration currently + sets `tls=off` and fetches over plain HTTP; signed catalogs and per-artifact + hashes remain the authenticity layer, and transport confidentiality is + forfeited until TLS is restored. It supports signed release-root rotation, revocation, offline recovery, and rollback of an interrupted trust/catalog activation. The checked-in TLS and signing private fixtures are public; production key custody and release authorization remain diff --git a/wiki/Xapt-Package-Updates.md b/wiki/Xapt-Package-Updates.md index 39223d0a..14c78bab 100644 --- a/wiki/Xapt-Package-Updates.md +++ b/wiki/Xapt-Package-Updates.md @@ -68,19 +68,41 @@ not determine OS-slot health. /apps/NAME/previous.* one rollback version ``` -The default development origin is `91.99.176.243:8443`. Configuration is plain -text, but transport security is mandatory: +The default development origin is `xaios.91.99.176.243.nip.io`. The shipped +configuration reaches it over plain HTTP: ```text -host=91.99.176.243 -port=8443 +host=xaios.91.99.176.243.nip.io +address=91.99.176.243 +port=80 base=/ -tls=required -tls_rsa_modulus=OPERATOR_RSA_MODULUS_HEX +tls=off ``` -`xapt` uses TLS 1.2 with an exact operator-managed RSA public-key pin and -fail-closed entropy. Signed catalogs/manifests and payload hashes remain the +`address` is the literal to dial. The name still identifies the origin: it is +what goes in the `Host` header, and what a certificate would be checked +against. The two are separate here because the resolver cannot yet return an +address for this name, an unsigned delegation it treats as bogus; once it can, +`address` can be dropped. Without `address` the client resolves `host`. + +`tls=off` is a deliberate interim setting, not a default to keep. Update +authenticity does not depend on it: catalogs and manifests are signed and every +artifact carries a `sha256`, so a tampered payload is rejected whatever the +transport. What plain HTTP gives up is confidentiality and transport integrity +of the exchange itself, and an observer can see which artifacts a host fetches. +Restore `tls=required` with `port=443` once the resolver reaches the name. + +With `tls=required`, `xapt` uses TLS 1.2 with fail-closed entropy and validates +the presented certificate chain against the compiled-in ISRG roots, checking +the server name and the validity window. Because expiry is checked, the +realtime clock must be set; boot synchronizes it, and an unset clock is refused +rather than silently accepted. + +Add `tls_rsa_modulus=OPERATOR_RSA_MODULUS_HEX` to require one exact leaf RSA +key instead, ignoring the rest of the certificate. That suits a private origin +reached by address, where no publicly issued chain exists. It is the wrong +choice for a publicly issued certificate, which is reissued with a fresh key +every renewal and would strand every deployed image. Signed catalogs/manifests and payload hashes remain the content authenticity layer. The client requires HTTP/1.1 with `Content-Length`; transfer encoding, compression, mirrors, proxies, deltas, dependencies, and unattended upgrades are not supported. @@ -101,15 +123,18 @@ make xapt-repository The first command creates and verifies both architecture trees under `build/xapt/repository`. The publisher re-verifies locally, synchronizes to -`/var/xaios_updater`, validates the repository Caddy configuration, and reloads -Caddy on TLS port 8443. Override the destination with `XAIOS_UPDATE_HOST` and -`XAIOS_UPDATE_ROOT`. - -Publishing requires `XAIOS_XAPT_TLS_CERT` and `XAIOS_XAPT_TLS_KEY`; the script -refuses to publish without an operator-managed identity. The public fixture may -be selected only for disposable tests with `XAIOS_ALLOW_TEST_TLS_FIXTURE=1`. -The configured `tls_rsa_modulus` must match the deployed certificate key. -Caddy exposes only the TLS listener on port 8443. +`/var/xaios_updater`, and reloads the updater's own server. Override the +destination with `XAIOS_UPDATE_HOST` and `XAIOS_UPDATE_ROOT`, and the reloaded +unit with `XAIOS_UPDATE_SERVICE`. + +The publisher handles no TLS material. A master edge in `/var/caddy` owns +`:443` for `xaios.91.99.176.243.nip.io` and forwards to the updater's own +instance, `xaios-caddy`, which serves plain HTTP on `:8090` and is the only +service publishing reloads. That edge also fronts unrelated projects, so +nothing here writes `/etc/caddy` or reloads the shared `caddy` service. + +A configured `tls_rsa_modulus` must match the deployed certificate key; with +chain validation no pin has to be re-cut when the certificate renews. The repository shape is: