From fb7985db7279e18fbd8e224779d64c504a5e35cf Mon Sep 17 00:00:00 2001 From: Peng Yong Date: Mon, 29 Jun 2026 18:41:42 +0800 Subject: [PATCH 1/2] toa: resolve ipv4_specific/tcp_v4_syn_recv_sock via kallsyms Debian kernel 6.12.94 stopped exporting ipv4_specific and tcp_v4_syn_recv_sock, so the IPv4 hook path failed to link (modpost: "xxx" undefined; 6.12.90 still built). Resolve both at runtime with kallsyms_lookup_name() in toa_init(), mirroring the existing IPv6 path, and route hook/unhook and the original syn_recv_sock chain-call through the saved pointers. Also unregister the kprobe on the toa_init() error path: a failed module_init() never triggers module_exit(), so the kprobe registered to bootstrap kallsyms_lookup_name() would otherwise leak (also covers the pre-existing goto-err paths). Co-Authored-By: Claude Opus 4.8 (1M context) --- kmod/toa/toa.c | 65 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/kmod/toa/toa.c b/kmod/toa/toa.c index 08e1e3d77..e0617f4c9 100644 --- a/kmod/toa/toa.c +++ b/kmod/toa/toa.c @@ -82,10 +82,7 @@ struct toa_ip6_sk_lock { static struct toa_ip6_sk_lock toa_ip6_sk_lock; #endif -#ifdef TOA_IPV6_ENABLE -static struct proto_ops *inet6_stream_ops_p = NULL; -static struct inet_connection_sock_af_ops *ipv6_specific_p = NULL; - +/* syn_recv_sock 函数指针类型,IPv4/IPv6 共用,故移出 TOA_IPV6_ENABLE */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) typedef struct sock *(*syn_recv_sock_func_pt)( const struct sock *sk, struct sk_buff *skb, @@ -99,6 +96,16 @@ typedef struct sock *(*syn_recv_sock_func_pt)( struct request_sock *req, struct dst_entry *dst); #endif + +/* ipv4_specific / tcp_v4_syn_recv_sock 在较新内核(如 Debian 6.12.94)已不再 + * EXPORT_SYMBOL,直接引用会 modpost undefined。改为运行时 kallsyms 解析, + * 与下面 IPv6 路径一致。 */ +static struct inet_connection_sock_af_ops *ipv4_specific_p = NULL; +static syn_recv_sock_func_pt tcp_v4_syn_recv_sock_org_pt = NULL; + +#ifdef TOA_IPV6_ENABLE +static struct proto_ops *inet6_stream_ops_p = NULL; +static struct inet_connection_sock_af_ops *ipv6_specific_p = NULL; static syn_recv_sock_func_pt tcp_v6_syn_recv_sock_org_pt = NULL; #endif @@ -751,9 +758,9 @@ tcp_v4_syn_recv_sock_toa(struct sock *sk, struct sk_buff *skb, /* call orginal one */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) - newsock = tcp_v4_syn_recv_sock(sk, skb, req, dst, req_unhash, own_req); + newsock = tcp_v4_syn_recv_sock_org_pt(sk, skb, req, dst, req_unhash, own_req); #else - newsock = tcp_v4_syn_recv_sock(sk, skb, req, dst); + newsock = tcp_v4_syn_recv_sock_org_pt(sk, skb, req, dst); #endif /* set our value if need */ @@ -844,9 +851,8 @@ hook_toa_functions(void) { struct proto_ops *inet_stream_ops_p; - struct inet_connection_sock_af_ops *ipv4_specific_p; int rw_enable = 0; - + /* hook inet_getname for ipv4 */ inet_stream_ops_p = (struct proto_ops *)&inet_stream_ops; @@ -862,19 +868,17 @@ hook_toa_functions(void) TOA_INFO("CPU [%u] hooked inet_getname <%p> --> <%p>\n", smp_processor_id(), inet_getname, inet_stream_ops_p->getname); - ipv4_specific_p = (struct inet_connection_sock_af_ops *)&ipv4_specific; - - if (is_ro_addr((unsigned long)(&ipv4_specific.syn_recv_sock))) { - set_addr_rw((unsigned long)(&ipv4_specific.syn_recv_sock)); + if (is_ro_addr((unsigned long)(&ipv4_specific_p->syn_recv_sock))) { + set_addr_rw((unsigned long)(&ipv4_specific_p->syn_recv_sock)); rw_enable = 1; } ipv4_specific_p->syn_recv_sock = tcp_v4_syn_recv_sock_toa; if (rw_enable == 1) { - set_addr_ro((unsigned long)(&ipv4_specific.syn_recv_sock)); + set_addr_ro((unsigned long)(&ipv4_specific_p->syn_recv_sock)); rw_enable = 0; } TOA_INFO("CPU [%u] hooked tcp_v4_syn_recv_sock <%p> --> <%p>\n", - smp_processor_id(), tcp_v4_syn_recv_sock, + smp_processor_id(), (void *)tcp_v4_syn_recv_sock_org_pt, ipv4_specific_p->syn_recv_sock); #ifdef TOA_IPV6_ENABLE if (is_ro_addr((unsigned long)(&inet6_stream_ops_p->getname))) { @@ -912,9 +916,8 @@ unhook_toa_functions(void) { struct proto_ops *inet_stream_ops_p; - struct inet_connection_sock_af_ops *ipv4_specific_p; int rw_enable = 0; - + /* unhook inet_getname for ipv4 */ inet_stream_ops_p = (struct proto_ops *)&inet_stream_ops; @@ -930,15 +933,14 @@ unhook_toa_functions(void) TOA_INFO("CPU [%u] unhooked inet_getname\n", smp_processor_id()); /* unhook tcp_v4_syn_recv_sock for ipv4 */ - ipv4_specific_p = (struct inet_connection_sock_af_ops *)&ipv4_specific; - if (is_ro_addr((unsigned long)(&ipv4_specific.syn_recv_sock))) { - set_addr_rw((unsigned long)(&ipv4_specific.syn_recv_sock)); + if (is_ro_addr((unsigned long)(&ipv4_specific_p->syn_recv_sock))) { + set_addr_rw((unsigned long)(&ipv4_specific_p->syn_recv_sock)); rw_enable = 1; } - set_addr_rw((unsigned long)(&ipv4_specific.syn_recv_sock)); - ipv4_specific_p->syn_recv_sock = tcp_v4_syn_recv_sock; + set_addr_rw((unsigned long)(&ipv4_specific_p->syn_recv_sock)); + ipv4_specific_p->syn_recv_sock = tcp_v4_syn_recv_sock_org_pt; if (rw_enable == 1) { - set_addr_ro((unsigned long)(&ipv4_specific.syn_recv_sock)); + set_addr_ro((unsigned long)(&ipv4_specific_p->syn_recv_sock)); rw_enable = 0; } @@ -1098,6 +1100,20 @@ toa_init(void) goto err; } + /* ipv4_specific / tcp_v4_syn_recv_sock 不再导出,运行时解析 */ + ipv4_specific_p = (struct inet_connection_sock_af_ops *) + kallsyms_lookup_name("ipv4_specific"); + if (NULL == ipv4_specific_p) { + TOA_INFO("cannot find ipv4_specific.\n"); + goto err; + } + tcp_v4_syn_recv_sock_org_pt = (syn_recv_sock_func_pt) + kallsyms_lookup_name("tcp_v4_syn_recv_sock"); + if (NULL == tcp_v4_syn_recv_sock_org_pt) { + TOA_INFO("cannot find tcp_v4_syn_recv_sock.\n"); + goto err; + } + #if (defined(TOA_IPV6_ENABLE) || defined(TOA_NAT64_ENABLE)) if (0 != init_toa_ip6()) { TOA_INFO("init toa ip6 fail.\n"); @@ -1126,6 +1142,11 @@ toa_init(void) return 0; err: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) + /* init 失败时 module_exit 不会被调用,必须在此注销已注册的 kprobe, + * 否则会遗留悬挂探针、阻塞后续重新加载 */ + unregister_kprobe(&kp); +#endif proc_net_remove(&init_net, "toa_stats"); if (NULL != ext_stats) { free_percpu(ext_stats); From e7f834591a976865643bc0136da5b170ed772ef3 Mon Sep 17 00:00:00 2001 From: Peng Yong Date: Wed, 8 Jul 2026 22:46:15 +0800 Subject: [PATCH 2/2] toa: support syn_recv_sock opt_child_init 7th arg (v7.0 / CVE-2026-43198) Upstream commit 858d2a4f67ff ("tcp: fix potential race in tcp_v6_syn_recv_sock()", merged in v7.0) added a 7th argument void (*opt_child_init)(struct sock *, const struct sock *) to inet_connection_sock_af_ops.syn_recv_sock. It is backported per stable/distro: AlmaLinux/RHEL 10 carries it since kernel-6.12.0-211.28.1.el10_2 (10.0/10.1 and earlier 10.2 z-streams are still 6-arg); Debian/Ubuntu 6.12.y LTS will follow. Detect the argument by probing the kernel header for opt_child_init at build time (kmod/toa/Makefile) instead of any version macro: RHEL_RELEASE_CODE only has minor granularity and cannot express the z-stream boundary (211.7.4 and 211.30.1 are both 2562, yet 6- vs 7-arg). toa.c gates the 7-arg typedef, v4/v6 wrapper signatures and forwarding calls on TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT, forwarding opt_child_init untouched (the original syn_recv_sock owns invoking it). Verified on AlmaLinux 10.2 (6.12.0-211.30.1.el10_2): build + DKMS load + v4/v6 syn_recv_sock hook OK; Debian 6.12.x still takes the 6-arg path; older kernels 4-arg path unchanged. --- kmod/toa/Makefile | 12 ++++++++++++ kmod/toa/toa.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/kmod/toa/Makefile b/kmod/toa/Makefile index 8d42c507d..0a1482569 100644 --- a/kmod/toa/Makefile +++ b/kmod/toa/Makefile @@ -10,6 +10,18 @@ PWD := $(shell pwd) ccflags-y += -DTOA_IPV6_ENABLE ccflags-y += -DTOA_NAT64_ENABLE +# Feature probe: does the target kernel's inet_connection_sock_af_ops.syn_recv_sock +# carry the opt_child_init 7th arg (upstream CVE-2026-43198 / commit 858d2a4f67ff, +# merged in v7.0; EL10 backported since 6.12.0-211.28.1.el10_2)? Probe the kernel +# header rather than a version macro: RHEL_RELEASE_CODE has only minor granularity +# and cannot express the z-stream (211.28.1) boundary, and the same change will land +# in Debian/Ubuntu 6.12.y LTS and mainline v7.0+. +ifneq ($(wildcard $(KDIR)/include/net/inet_connection_sock.h),) +ifneq ($(shell grep -c 'opt_child_init' $(KDIR)/include/net/inet_connection_sock.h 2>/dev/null),0) +ccflags-y += -DTOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT +endif +endif + ifeq ($(DEBUG), 1) ccflags-y += -g -O0 endif diff --git a/kmod/toa/toa.c b/kmod/toa/toa.c index e0617f4c9..77ef6e2ac 100644 --- a/kmod/toa/toa.c +++ b/kmod/toa/toa.c @@ -83,7 +83,23 @@ static struct toa_ip6_sk_lock toa_ip6_sk_lock; #endif /* syn_recv_sock 函数指针类型,IPv4/IPv6 共用,故移出 TOA_IPV6_ENABLE */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) +/* The syn_recv_sock 7th arg opt_child_init comes from upstream CVE-2026-43198 + * (commit 858d2a4f67ff, merged in v7.0), backported per stable/distro: + * AlmaLinux/RHEL 10 has it since 6.12.0-211.28.1.el10_2 (10.0/10.1 and 10.2 + * before 211.28.1 do not); Debian/Ubuntu 6.12.y LTS will follow. Gate on the + * build-time feature-probe macro TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT (see the + * Makefile probing inet_connection_sock.h) rather than a version macro: + * RHEL_RELEASE_CODE has only minor granularity and cannot express the + * z-stream (211.28.1) boundary. */ +#ifdef TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT +typedef struct sock *(*syn_recv_sock_func_pt)( + const struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst, + struct request_sock *req_unhash, + bool *own_req, + void (*opt_child_init)(struct sock *newsk, const struct sock *sk)); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) typedef struct sock *(*syn_recv_sock_func_pt)( const struct sock *sk, struct sk_buff *skb, struct request_sock *req, @@ -738,7 +754,15 @@ get_kernel_ipv6_symbol(void) * @param dst [out] route cache entry * @return NULL if fail new socket if succeed. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) +#ifdef TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT +static struct sock * +tcp_v4_syn_recv_sock_toa(const struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst, + struct request_sock *req_unhash, + bool *own_req, + void (*opt_child_init)(struct sock *newsk, const struct sock *sk)) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) static struct sock * tcp_v4_syn_recv_sock_toa(const struct sock *sk, struct sk_buff *skb, struct request_sock *req, @@ -757,7 +781,9 @@ tcp_v4_syn_recv_sock_toa(struct sock *sk, struct sk_buff *skb, TOA_DBG("tcp_v4_syn_recv_sock_toa called\n"); /* call orginal one */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) +#ifdef TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT + newsock = tcp_v4_syn_recv_sock_org_pt(sk, skb, req, dst, req_unhash, own_req, opt_child_init); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) newsock = tcp_v4_syn_recv_sock_org_pt(sk, skb, req, dst, req_unhash, own_req); #else newsock = tcp_v4_syn_recv_sock_org_pt(sk, skb, req, dst); @@ -792,7 +818,15 @@ tcp_v4_syn_recv_sock_toa(struct sock *sk, struct sk_buff *skb, } #ifdef TOA_IPV6_ENABLE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) +#ifdef TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT +static struct sock * +tcp_v6_syn_recv_sock_toa(const struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst, + struct request_sock *req_unhash, + bool *own_req, + void (*opt_child_init)(struct sock *newsk, const struct sock *sk)) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) static struct sock * tcp_v6_syn_recv_sock_toa(const struct sock *sk, struct sk_buff *skb, struct request_sock *req, @@ -811,7 +845,10 @@ tcp_v6_syn_recv_sock_toa(struct sock *sk, struct sk_buff *skb, TOA_DBG("tcp_v6_syn_recv_sock_toa called\n"); /* call orginal one */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) +#ifdef TOA_SYN_RECV_SOCK_HAS_OPT_CHILD_INIT + newsock = tcp_v6_syn_recv_sock_org_pt(sk, skb, req, dst, req_unhash, + own_req, opt_child_init); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,1) newsock = tcp_v6_syn_recv_sock_org_pt(sk, skb, req, dst, req_unhash, own_req); #else