* [PATCH net-next v2 3/5] net/tcp-ao: Use stack-allocated MAC and traffic_key buffers
From: Eric Biggers @ 2026-04-27 17:27 UTC (permalink / raw)
To: netdev
Cc: linux-crypto, linux-kernel, Eric Dumazet, Neal Cardwell,
Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski,
Paolo Abeni, Simon Horman, Ard Biesheuvel, Jason A . Donenfeld,
Herbert Xu, Dmitry Safonov, Eric Biggers
In-Reply-To: <20260427172727.9310-1-ebiggers@kernel.org>
Now that the maximum MAC and traffic key lengths are statically-known
small values, allocate MACs and traffic keys on the stack instead of
with kmalloc. This eliminates multiple failure-prone GFP_ATOMIC
allocations.
Note that some cases such as tcp_ao_prepare_reset() are left unchanged
for now since they would require slightly wider changes.
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
net/ipv4/tcp_ao.c | 44 +++++++++++---------------------------------
net/ipv6/tcp_ao.c | 17 +++++------------
2 files changed, 16 insertions(+), 45 deletions(-)
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 0d24cbd66c9a..69f1d6d26562 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -737,26 +737,19 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne)
{
- void *hash_buf = NULL;
+ u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
int err;
- hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC);
- if (!hash_buf)
- return -ENOMEM;
-
- err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req);
+ err = tcp_v4_ao_calc_key_rsk(ao_key, tkey_buf, req);
if (err)
- goto out;
+ return err;
- err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb,
- hash_buf, hash_offset, sne);
-out:
- kfree(hash_buf);
- return err;
+ return tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb,
+ tkey_buf, hash_offset, sne);
}
struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid)
@@ -867,13 +860,13 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key, struct tcphdr *th,
__u8 *hash_location)
{
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
+ u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_ao_info *ao;
- void *tkey_buf = NULL;
u8 *traffic_key;
u32 sne;
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
lockdep_sock_is_held(sk));
@@ -881,13 +874,10 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) {
__be32 disn;
if (!(tcb->tcp_flags & TCPHDR_ACK)) {
disn = 0;
- tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
- if (!tkey_buf)
- return -ENOMEM;
traffic_key = tkey_buf;
} else {
disn = ao->risn;
}
tp->af_specific->ao_calc_key_sk(key, traffic_key,
@@ -895,11 +885,10 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
}
sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una),
ntohl(th->seq));
tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key,
hash_location - (u8 *)th, sne);
- kfree(tkey_buf);
return 0;
}
static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family,
const struct sock *sk, const struct sk_buff *skb,
@@ -961,54 +950,48 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key,
u8 *traffic_key, u8 *phash, u32 sne, int l3index)
{
const struct tcphdr *th = tcp_hdr(skb);
u8 maclen = tcp_ao_hdr_maclen(aoh);
- void *hash_buf = NULL;
+ u8 hash_buf[TCP_AO_MAX_MAC_LEN];
if (maclen != tcp_ao_maclen(key)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
atomic64_inc(&info->counters.pkt_bad);
atomic64_inc(&key->pkt_bad);
trace_tcp_ao_wrong_maclen(sk, skb, aoh->keyid,
aoh->rnext_keyid, maclen);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
- hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
- if (!hash_buf)
- return SKB_DROP_REASON_NOT_SPECIFIED;
-
/* XXX: make it per-AF callback? */
tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key,
(phash - (u8 *)th), sne);
if (crypto_memneq(phash, hash_buf, maclen)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
atomic64_inc(&info->counters.pkt_bad);
atomic64_inc(&key->pkt_bad);
trace_tcp_ao_mismatch(sk, skb, aoh->keyid,
aoh->rnext_keyid, maclen);
- kfree(hash_buf);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD);
atomic64_inc(&info->counters.pkt_good);
atomic64_inc(&key->pkt_good);
- kfree(hash_buf);
return SKB_NOT_DROPPED_YET;
}
enum skb_drop_reason
tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
unsigned short int family, const struct request_sock *req,
int l3index, const struct tcp_ao_hdr *aoh)
{
+ u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
const struct tcphdr *th = tcp_hdr(skb);
u8 maclen = tcp_ao_hdr_maclen(aoh);
u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */
struct tcp_ao_info *info;
- enum skb_drop_reason ret;
struct tcp_ao_key *key;
__be32 sisn, disn;
u8 *traffic_key;
int state;
u32 sne = 0;
@@ -1112,18 +1095,13 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
} else {
WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", state);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
verify_hash:
- traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
- if (!traffic_key)
- return SKB_DROP_REASON_NOT_SPECIFIED;
- tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family);
- ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
- traffic_key, phash, sne, l3index);
- kfree(traffic_key);
- return ret;
+ tcp_ao_calc_key_skb(key, tkey_buf, skb, sisn, disn, family);
+ return tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
+ tkey_buf, phash, sne, l3index);
key_not_found:
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
atomic64_inc(&info->counters.key_not_found);
trace_tcp_ao_key_not_found(sk, skb, aoh->keyid,
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 2dcfe9dda7f4..bf30b970181d 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -136,22 +136,15 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd,
int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne)
{
- void *hash_buf = NULL;
+ u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
int err;
- hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC);
- if (!hash_buf)
- return -ENOMEM;
-
- err = tcp_v6_ao_calc_key_rsk(ao_key, hash_buf, req);
+ err = tcp_v6_ao_calc_key_rsk(ao_key, tkey_buf, req);
if (err)
- goto out;
+ return err;
- err = tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
- hash_buf, hash_offset, sne);
-out:
- kfree(hash_buf);
- return err;
+ return tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
+ tkey_buf, hash_offset, sne);
}
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 4/5] net/tcp-ao: Return void from functions that can no longer fail
From: Eric Biggers @ 2026-04-27 17:27 UTC (permalink / raw)
To: netdev
Cc: linux-crypto, linux-kernel, Eric Dumazet, Neal Cardwell,
Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski,
Paolo Abeni, Simon Horman, Ard Biesheuvel, Jason A . Donenfeld,
Herbert Xu, Dmitry Safonov, Eric Biggers
In-Reply-To: <20260427172727.9310-1-ebiggers@kernel.org>
Since tcp-ao now uses the crypto library API instead of crypto_ahash,
and MACs and keys now have a statically-known maximum size, many tcp-ao
functions can no longer fail. Propagate this change up into the return
types of various functions.
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
include/net/tcp.h | 8 +--
include/net/tcp_ao.h | 44 +++++++--------
net/ipv4/tcp_ao.c | 128 ++++++++++++++++++++++--------------------
net/ipv4/tcp_output.c | 10 +---
net/ipv6/tcp_ao.c | 65 ++++++++++-----------
5 files changed, 123 insertions(+), 132 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index ecbadcb3a744..8817e7b8cc67 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2508,13 +2508,13 @@ struct tcp_sock_af_ops {
#ifdef CONFIG_TCP_AO
int (*ao_parse)(struct sock *sk, int optname, sockptr_t optval, int optlen);
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
struct sock *addr_sk,
int sndid, int rcvid);
- int (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key,
- const struct sock *sk,
- __be32 sisn, __be32 disn, bool send);
+ void (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key,
+ const struct sock *sk,
+ __be32 sisn, __be32 disn, bool send);
int (*calc_ao_hash)(char *location, struct tcp_ao_key *ao,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne);
#endif
};
@@ -2531,11 +2531,11 @@ struct tcp_request_sock_ops {
#endif
#ifdef CONFIG_TCP_AO
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
- int (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *sk);
+ void (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *sk);
int (*ao_synack_hash)(char *ao_hash, struct tcp_ao_key *mkt,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne);
#endif
#ifdef CONFIG_SYN_COOKIES
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 20997aef3b0d..29fd7b735afa 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -185,13 +185,13 @@ struct tcp6_ao_context {
/* Established states are fast-path and there always is current_key/rnext_key */
#define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \
TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING)
-int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
- struct tcp_ao_key *key, struct tcphdr *th,
- __u8 *hash_location);
+void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
+ struct tcp_ao_key *key, struct tcphdr *th,
+ __u8 *hash_location);
void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data,
size_t data_len);
int tcp_ao_hash_skb(unsigned short int family,
char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
@@ -236,32 +236,33 @@ int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
int sndid, int rcvid);
int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *mkt,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne);
-int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
- const struct sock *sk,
- __be32 sisn, __be32 disn, bool send);
-int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
- struct request_sock *req);
+void tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+ const struct sock *sk,
+ __be32 sisn, __be32 disn, bool send);
+void tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+ struct request_sock *req);
struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne);
/* ipv6 specific functions */
-int tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr, int nbytes);
-int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
- const struct sk_buff *skb, __be32 sisn, __be32 disn);
-int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
- const struct sock *sk, __be32 sisn,
- __be32 disn, bool send);
-int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
- struct request_sock *req);
+void tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr, int nbytes);
+void tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+ const struct sk_buff *skb, __be32 sisn,
+ __be32 disn);
+void tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+ const struct sock *sk, __be32 sisn,
+ __be32 disn, bool send);
+void tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+ struct request_sock *req);
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
struct sock *addr_sk, int sndid, int rcvid);
struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
@@ -277,15 +278,14 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
void tcp_ao_connect_init(struct sock *sk);
void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
struct request_sock *req, unsigned short int family);
#else /* CONFIG_TCP_AO */
-static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
- struct tcp_ao_key *key, struct tcphdr *th,
- __u8 *hash_location)
+static inline void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
+ struct tcp_ao_key *key,
+ struct tcphdr *th, __u8 *hash_location)
{
- return 0;
}
static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
struct request_sock *req, unsigned short int family)
{
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 69f1d6d26562..36a64c1cd8c9 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -433,14 +433,14 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp)
tcptw->ao_info = NULL;
}
}
/* 4 tuple and ISNs are expected in NBO */
-static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
- __be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport,
- __be32 sisn, __be32 disn)
+static void tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
+ __be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport,
+ __be32 sisn, __be32 disn)
{
/* See RFC5926 3.1.1 */
struct kdf_input_block {
u8 counter;
u8 label[6];
@@ -459,91 +459,92 @@ static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
},
.outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */
};
tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
- return 0;
}
-int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
- const struct sock *sk,
- __be32 sisn, __be32 disn, bool send)
+void tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+ const struct sock *sk,
+ __be32 sisn, __be32 disn, bool send)
{
if (send)
- return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr,
- sk->sk_daddr, htons(sk->sk_num),
- sk->sk_dport, sisn, disn);
+ tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, sk->sk_daddr,
+ htons(sk->sk_num), sk->sk_dport, sisn, disn);
else
- return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr,
- sk->sk_rcv_saddr, sk->sk_dport,
- htons(sk->sk_num), disn, sisn);
+ tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, sk->sk_rcv_saddr,
+ sk->sk_dport, htons(sk->sk_num), disn, sisn);
}
static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send)
{
- if (mkt->family == AF_INET)
- return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
+ if (mkt->family == AF_INET) {
+ tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
+ return 0;
+ }
#if IS_ENABLED(CONFIG_IPV6)
- else if (mkt->family == AF_INET6)
- return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
+ if (mkt->family == AF_INET6) {
+ tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
+ return 0;
+ }
#endif
- else
- return -EOPNOTSUPP;
+ return -EOPNOTSUPP;
}
-int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
- struct request_sock *req)
+void tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+ struct request_sock *req)
{
struct inet_request_sock *ireq = inet_rsk(req);
- return tcp_v4_ao_calc_key(mkt, key,
- ireq->ir_loc_addr, ireq->ir_rmt_addr,
- htons(ireq->ir_num), ireq->ir_rmt_port,
- htonl(tcp_rsk(req)->snt_isn),
- htonl(tcp_rsk(req)->rcv_isn));
+ tcp_v4_ao_calc_key(mkt, key, ireq->ir_loc_addr, ireq->ir_rmt_addr,
+ htons(ireq->ir_num), ireq->ir_rmt_port,
+ htonl(tcp_rsk(req)->snt_isn),
+ htonl(tcp_rsk(req)->rcv_isn));
}
-static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
- const struct sk_buff *skb,
- __be32 sisn, __be32 disn)
+static void tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+ const struct sk_buff *skb,
+ __be32 sisn, __be32 disn)
{
const struct iphdr *iph = ip_hdr(skb);
const struct tcphdr *th = tcp_hdr(skb);
- return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr,
- th->source, th->dest, sisn, disn);
+ tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, th->source,
+ th->dest, sisn, disn);
}
static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb,
__be32 sisn, __be32 disn, int family)
{
- if (family == AF_INET)
- return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn);
+ if (family == AF_INET) {
+ tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn);
+ return 0;
+ }
#if IS_ENABLED(CONFIG_IPV6)
- else if (family == AF_INET6)
- return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn);
+ if (family == AF_INET6) {
+ tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn);
+ return 0;
+ }
#endif
return -EAFNOSUPPORT;
}
-static int tcp_v4_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
- __be32 daddr, __be32 saddr,
- int nbytes)
+static void tcp_v4_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
+ __be32 daddr, __be32 saddr, int nbytes)
{
struct tcp4_pseudohdr phdr = {
.saddr = saddr,
.daddr = daddr,
.pad = 0,
.protocol = IPPROTO_TCP,
.len = cpu_to_be16(nbytes),
};
tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
- return 0;
}
static int tcp_ao_hash_pseudoheader(unsigned short int family,
const struct sock *sk,
const struct sk_buff *skb,
@@ -551,35 +552,42 @@ static int tcp_ao_hash_pseudoheader(unsigned short int family,
{
const struct tcphdr *th = tcp_hdr(skb);
/* TODO: Can we rely on checksum being zero to mean outbound pkt? */
if (!th->check) {
- if (family == AF_INET)
- return tcp_v4_ao_hash_pseudoheader(mac_ctx, sk->sk_daddr,
- sk->sk_rcv_saddr, skb->len);
+ if (family == AF_INET) {
+ tcp_v4_ao_hash_pseudoheader(mac_ctx, sk->sk_daddr,
+ sk->sk_rcv_saddr, skb->len);
+ return 0;
+ }
#if IS_ENABLED(CONFIG_IPV6)
- else if (family == AF_INET6)
- return tcp_v6_ao_hash_pseudoheader(mac_ctx, &sk->sk_v6_daddr,
- &sk->sk_v6_rcv_saddr, skb->len);
+ if (family == AF_INET6) {
+ tcp_v6_ao_hash_pseudoheader(mac_ctx, &sk->sk_v6_daddr,
+ &sk->sk_v6_rcv_saddr,
+ skb->len);
+ return 0;
+ }
#endif
- else
- return -EAFNOSUPPORT;
+ return -EAFNOSUPPORT;
}
if (family == AF_INET) {
const struct iphdr *iph = ip_hdr(skb);
- return tcp_v4_ao_hash_pseudoheader(mac_ctx, iph->daddr,
- iph->saddr, skb->len);
+ tcp_v4_ao_hash_pseudoheader(mac_ctx, iph->daddr, iph->saddr,
+ skb->len);
+ return 0;
+ }
#if IS_ENABLED(CONFIG_IPV6)
- } else if (family == AF_INET6) {
+ if (family == AF_INET6) {
const struct ipv6hdr *iph = ipv6_hdr(skb);
- return tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr,
- &iph->saddr, skb->len);
-#endif
+ tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr, &iph->saddr,
+ skb->len);
+ return 0;
}
+#endif
return -EAFNOSUPPORT;
}
u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq)
{
@@ -738,15 +746,12 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne)
{
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
- int err;
- err = tcp_v4_ao_calc_key_rsk(ao_key, tkey_buf, req);
- if (err)
- return err;
+ tcp_v4_ao_calc_key_rsk(ao_key, tkey_buf, req);
return tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb,
tkey_buf, hash_offset, sne);
}
@@ -855,13 +860,13 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
snd_basis, seq);
}
return 0;
}
-int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
- struct tcp_ao_key *key, struct tcphdr *th,
- __u8 *hash_location)
+void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
+ struct tcp_ao_key *key, struct tcphdr *th,
+ __u8 *hash_location)
{
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_ao_info *ao;
@@ -885,11 +890,10 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
}
sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una),
ntohl(th->seq));
tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key,
hash_location - (u8 *)th, sne);
- return 0;
}
static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family,
const struct sock *sk, const struct sk_buff *skb,
int sndid, int rcvid, int l3index)
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index f9d8755705f7..2671355661e4 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1661,18 +1661,12 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
sk_gso_disable(sk);
tp->af_specific->calc_md5_hash(opts.hash_location,
key.md5_key, sk, skb);
#endif
} else if (tcp_key_is_ao(&key)) {
- int err;
-
- err = tcp_ao_transmit_skb(sk, skb, key.ao_key, th,
- opts.hash_location);
- if (err) {
- sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_NOT_SPECIFIED);
- return -ENOMEM;
- }
+ tcp_ao_transmit_skb(sk, skb, key.ao_key, th,
+ opts.hash_location);
}
/* BPF prog is the last one writing header option */
bpf_skops_write_hdr_opt(sk, skb, NULL, NULL, 0, &opts);
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index bf30b970181d..01a8472805d1 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -10,15 +10,15 @@
#include <linux/tcp.h>
#include <net/tcp.h>
#include <net/ipv6.h>
-static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
- const struct in6_addr *saddr,
- const struct in6_addr *daddr,
- __be16 sport, __be16 dport,
- __be32 sisn, __be32 disn)
+static void tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
+ const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ __be16 sport, __be16 dport,
+ __be32 sisn, __be32 disn)
{
struct kdf_input_block {
u8 counter;
u8 label[6];
struct tcp6_ao_context ctx;
@@ -36,49 +36,46 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
},
.outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */
};
tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
- return 0;
}
-int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
- const struct sk_buff *skb,
- __be32 sisn, __be32 disn)
+void tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+ const struct sk_buff *skb, __be32 sisn, __be32 disn)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
const struct tcphdr *th = tcp_hdr(skb);
- return tcp_v6_ao_calc_key(mkt, key, &iph->saddr,
- &iph->daddr, th->source,
- th->dest, sisn, disn);
+ tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr, th->source,
+ th->dest, sisn, disn);
}
-int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
- const struct sock *sk, __be32 sisn,
- __be32 disn, bool send)
+void tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+ const struct sock *sk, __be32 sisn,
+ __be32 disn, bool send)
{
if (send)
- return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
- &sk->sk_v6_daddr, htons(sk->sk_num),
- sk->sk_dport, sisn, disn);
+ tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
+ &sk->sk_v6_daddr, htons(sk->sk_num),
+ sk->sk_dport, sisn, disn);
else
- return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
- &sk->sk_v6_rcv_saddr, sk->sk_dport,
- htons(sk->sk_num), disn, sisn);
+ tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
+ &sk->sk_v6_rcv_saddr, sk->sk_dport,
+ htons(sk->sk_num), disn, sisn);
}
-int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
- struct request_sock *req)
+void tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+ struct request_sock *req)
{
struct inet_request_sock *ireq = inet_rsk(req);
- return tcp_v6_ao_calc_key(mkt, key,
- &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
- htons(ireq->ir_num), ireq->ir_rmt_port,
- htonl(tcp_rsk(req)->snt_isn),
- htonl(tcp_rsk(req)->rcv_isn));
+ tcp_v6_ao_calc_key(mkt, key,
+ &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
+ htons(ireq->ir_num), ireq->ir_rmt_port,
+ htonl(tcp_rsk(req)->snt_isn),
+ htonl(tcp_rsk(req)->rcv_isn));
}
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
struct sock *addr_sk,
int sndid, int rcvid)
@@ -102,24 +99,23 @@ struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr,
AF_INET6, sndid, rcvid);
}
-int tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr, int nbytes)
+void tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr, int nbytes)
{
/* 1. TCP pseudo-header (RFC2460) */
struct tcp6_pseudohdr phdr = {
.saddr = *saddr,
.daddr = *daddr,
.len = cpu_to_be32(nbytes),
.protocol = cpu_to_be32(IPPROTO_TCP),
};
tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
- return 0;
}
int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne)
@@ -137,14 +133,11 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd,
int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne)
{
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
- int err;
- err = tcp_v6_ao_calc_key_rsk(ao_key, tkey_buf, req);
- if (err)
- return err;
+ tcp_v6_ao_calc_key_rsk(ao_key, tkey_buf, req);
return tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
tkey_buf, hash_offset, sne);
}
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 5/5] net/tcp: Remove tcp_sigpool
From: Eric Biggers @ 2026-04-27 17:27 UTC (permalink / raw)
To: netdev
Cc: linux-crypto, linux-kernel, Eric Dumazet, Neal Cardwell,
Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski,
Paolo Abeni, Simon Horman, Ard Biesheuvel, Jason A . Donenfeld,
Herbert Xu, Dmitry Safonov, Eric Biggers
In-Reply-To: <20260427172727.9310-1-ebiggers@kernel.org>
tcp_sigpool is no longer used. It existed only as a workaround for
issues in the design of the crypto_ahash API, which have been avoided by
switching to the much easier-to-use library APIs instead. Remove it.
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
include/net/tcp.h | 34 ----
net/ipv4/Kconfig | 3 -
net/ipv4/Makefile | 1 -
net/ipv4/tcp_sigpool.c | 366 -----------------------------------------
4 files changed, 404 deletions(-)
delete mode 100644 net/ipv4/tcp_sigpool.c
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 8817e7b8cc67..660ad8c63395 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2015,44 +2015,10 @@ struct tcp6_pseudohdr {
struct in6_addr daddr;
__be32 len;
__be32 protocol; /* including padding */
};
-/*
- * struct tcp_sigpool - per-CPU pool of ahash_requests
- * @scratch: per-CPU temporary area, that can be used between
- * tcp_sigpool_start() and tcp_sigpool_end() to perform
- * crypto request
- * @req: pre-allocated ahash request
- */
-struct tcp_sigpool {
- void *scratch;
- struct ahash_request *req;
-};
-
-int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size);
-void tcp_sigpool_get(unsigned int id);
-void tcp_sigpool_release(unsigned int id);
-int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp,
- const struct sk_buff *skb,
- unsigned int header_len);
-
-/**
- * tcp_sigpool_start - disable bh and start using tcp_sigpool_ahash
- * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
- * @c: returned tcp_sigpool for usage (uninitialized on failure)
- *
- * Returns: 0 on success, error otherwise.
- */
-int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c);
-/**
- * tcp_sigpool_end - enable bh and stop using tcp_sigpool
- * @c: tcp_sigpool context that was returned by tcp_sigpool_start()
- */
-void tcp_sigpool_end(struct tcp_sigpool *c);
-size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len);
-/* - functions */
void tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
const struct sock *sk, const struct sk_buff *skb);
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
int family, u8 prefixlen, int l3index, u8 flags,
const u8 *newkey, u8 newkeylen);
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 77b053b445a0..301b47660305 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -739,13 +739,10 @@ config DEFAULT_TCP_CONG
default "dctcp" if DEFAULT_DCTCP
default "cdg" if DEFAULT_CDG
default "bbr" if DEFAULT_BBR
default "cubic"
-config TCP_SIGPOOL
- tristate
-
config TCP_AO
bool "TCP: Authentication Option (RFC5925)"
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_SHA1
select CRYPTO_LIB_SHA256
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 7f9f98813986..7964234f0d08 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -58,11 +58,10 @@ obj-$(CONFIG_TCP_CONG_NV) += tcp_nv.o
obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o
obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
-obj-$(CONFIG_TCP_SIGPOOL) += tcp_sigpool.o
obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o
obj-$(CONFIG_BPF_SYSCALL) += udp_bpf.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
diff --git a/net/ipv4/tcp_sigpool.c b/net/ipv4/tcp_sigpool.c
deleted file mode 100644
index 10b2e5970c40..000000000000
--- a/net/ipv4/tcp_sigpool.c
+++ /dev/null
@@ -1,366 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <crypto/hash.h>
-#include <linux/cpu.h>
-#include <linux/kref.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/percpu.h>
-#include <linux/workqueue.h>
-#include <net/tcp.h>
-
-static size_t __scratch_size;
-struct sigpool_scratch {
- local_lock_t bh_lock;
- void __rcu *pad;
-};
-
-static DEFINE_PER_CPU(struct sigpool_scratch, sigpool_scratch) = {
- .bh_lock = INIT_LOCAL_LOCK(bh_lock),
-};
-
-struct sigpool_entry {
- struct crypto_ahash *hash;
- const char *alg;
- struct kref kref;
- uint16_t needs_key:1,
- reserved:15;
-};
-
-#define CPOOL_SIZE (PAGE_SIZE / sizeof(struct sigpool_entry))
-static struct sigpool_entry cpool[CPOOL_SIZE];
-static unsigned int cpool_populated;
-static DEFINE_MUTEX(cpool_mutex);
-
-/* Slow-path */
-struct scratches_to_free {
- struct rcu_head rcu;
- unsigned int cnt;
- void *scratches[];
-};
-
-static void free_old_scratches(struct rcu_head *head)
-{
- struct scratches_to_free *stf;
-
- stf = container_of(head, struct scratches_to_free, rcu);
- while (stf->cnt--)
- kfree(stf->scratches[stf->cnt]);
- kfree(stf);
-}
-
-/**
- * sigpool_reserve_scratch - re-allocates scratch buffer, slow-path
- * @size: request size for the scratch/temp buffer
- */
-static int sigpool_reserve_scratch(size_t size)
-{
- struct scratches_to_free *stf;
- size_t stf_sz = struct_size(stf, scratches, num_possible_cpus());
- int cpu, err = 0;
-
- lockdep_assert_held(&cpool_mutex);
- if (__scratch_size >= size)
- return 0;
-
- stf = kmalloc(stf_sz, GFP_KERNEL);
- if (!stf)
- return -ENOMEM;
- stf->cnt = 0;
-
- size = max(size, __scratch_size);
- cpus_read_lock();
- for_each_possible_cpu(cpu) {
- void *scratch, *old_scratch;
-
- scratch = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu));
- if (!scratch) {
- err = -ENOMEM;
- break;
- }
-
- old_scratch = rcu_replace_pointer(per_cpu(sigpool_scratch.pad, cpu),
- scratch, lockdep_is_held(&cpool_mutex));
- if (!cpu_online(cpu) || !old_scratch) {
- kfree(old_scratch);
- continue;
- }
- stf->scratches[stf->cnt++] = old_scratch;
- }
- cpus_read_unlock();
- if (!err)
- __scratch_size = size;
-
- call_rcu(&stf->rcu, free_old_scratches);
- return err;
-}
-
-static void sigpool_scratch_free(void)
-{
- int cpu;
-
- for_each_possible_cpu(cpu)
- kfree(rcu_replace_pointer(per_cpu(sigpool_scratch.pad, cpu),
- NULL, lockdep_is_held(&cpool_mutex)));
- __scratch_size = 0;
-}
-
-static int __cpool_try_clone(struct crypto_ahash *hash)
-{
- struct crypto_ahash *tmp;
-
- tmp = crypto_clone_ahash(hash);
- if (IS_ERR(tmp))
- return PTR_ERR(tmp);
-
- crypto_free_ahash(tmp);
- return 0;
-}
-
-static int __cpool_alloc_ahash(struct sigpool_entry *e, const char *alg)
-{
- struct crypto_ahash *cpu0_hash;
- int ret;
-
- e->alg = kstrdup(alg, GFP_KERNEL);
- if (!e->alg)
- return -ENOMEM;
-
- cpu0_hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(cpu0_hash)) {
- ret = PTR_ERR(cpu0_hash);
- goto out_free_alg;
- }
-
- e->needs_key = crypto_ahash_get_flags(cpu0_hash) & CRYPTO_TFM_NEED_KEY;
-
- ret = __cpool_try_clone(cpu0_hash);
- if (ret)
- goto out_free_cpu0_hash;
- e->hash = cpu0_hash;
- kref_init(&e->kref);
- return 0;
-
-out_free_cpu0_hash:
- crypto_free_ahash(cpu0_hash);
-out_free_alg:
- kfree(e->alg);
- e->alg = NULL;
- return ret;
-}
-
-/**
- * tcp_sigpool_alloc_ahash - allocates pool for ahash requests
- * @alg: name of async hash algorithm
- * @scratch_size: reserve a tcp_sigpool::scratch buffer of this size
- */
-int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size)
-{
- int i, ret;
-
- /* slow-path */
- mutex_lock(&cpool_mutex);
- ret = sigpool_reserve_scratch(scratch_size);
- if (ret)
- goto out;
- for (i = 0; i < cpool_populated; i++) {
- if (!cpool[i].alg)
- continue;
- if (strcmp(cpool[i].alg, alg))
- continue;
-
- /* pairs with tcp_sigpool_release() */
- if (!kref_get_unless_zero(&cpool[i].kref))
- kref_init(&cpool[i].kref);
- ret = i;
- goto out;
- }
-
- for (i = 0; i < cpool_populated; i++) {
- if (!cpool[i].alg)
- break;
- }
- if (i >= CPOOL_SIZE) {
- ret = -ENOSPC;
- goto out;
- }
-
- ret = __cpool_alloc_ahash(&cpool[i], alg);
- if (!ret) {
- ret = i;
- if (i == cpool_populated)
- cpool_populated++;
- }
-out:
- mutex_unlock(&cpool_mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(tcp_sigpool_alloc_ahash);
-
-static void __cpool_free_entry(struct sigpool_entry *e)
-{
- crypto_free_ahash(e->hash);
- kfree(e->alg);
- memset(e, 0, sizeof(*e));
-}
-
-static void cpool_cleanup_work_cb(struct work_struct *work)
-{
- bool free_scratch = true;
- unsigned int i;
-
- mutex_lock(&cpool_mutex);
- for (i = 0; i < cpool_populated; i++) {
- if (kref_read(&cpool[i].kref) > 0) {
- free_scratch = false;
- continue;
- }
- if (!cpool[i].alg)
- continue;
- __cpool_free_entry(&cpool[i]);
- }
- if (free_scratch)
- sigpool_scratch_free();
- mutex_unlock(&cpool_mutex);
-}
-
-static DECLARE_WORK(cpool_cleanup_work, cpool_cleanup_work_cb);
-static void cpool_schedule_cleanup(struct kref *kref)
-{
- schedule_work(&cpool_cleanup_work);
-}
-
-/**
- * tcp_sigpool_release - decreases number of users for a pool. If it was
- * the last user of the pool, releases any memory that was consumed.
- * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
- */
-void tcp_sigpool_release(unsigned int id)
-{
- if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg))
- return;
-
- /* slow-path */
- kref_put(&cpool[id].kref, cpool_schedule_cleanup);
-}
-EXPORT_SYMBOL_GPL(tcp_sigpool_release);
-
-/**
- * tcp_sigpool_get - increases number of users (refcounter) for a pool
- * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
- */
-void tcp_sigpool_get(unsigned int id)
-{
- if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg))
- return;
- kref_get(&cpool[id].kref);
-}
-EXPORT_SYMBOL_GPL(tcp_sigpool_get);
-
-int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) __cond_acquires(0, RCU_BH)
-{
- struct crypto_ahash *hash;
-
- rcu_read_lock_bh();
- if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg)) {
- rcu_read_unlock_bh();
- return -EINVAL;
- }
-
- hash = crypto_clone_ahash(cpool[id].hash);
- if (IS_ERR(hash)) {
- rcu_read_unlock_bh();
- return PTR_ERR(hash);
- }
-
- c->req = ahash_request_alloc(hash, GFP_ATOMIC);
- if (!c->req) {
- crypto_free_ahash(hash);
- rcu_read_unlock_bh();
- return -ENOMEM;
- }
- ahash_request_set_callback(c->req, 0, NULL, NULL);
-
- /* Pairs with tcp_sigpool_reserve_scratch(), scratch area is
- * valid (allocated) until tcp_sigpool_end().
- */
- local_lock_nested_bh(&sigpool_scratch.bh_lock);
- c->scratch = rcu_dereference_bh(*this_cpu_ptr(&sigpool_scratch.pad));
- return 0;
-}
-EXPORT_SYMBOL_GPL(tcp_sigpool_start);
-
-void tcp_sigpool_end(struct tcp_sigpool *c) __releases(RCU_BH)
-{
- struct crypto_ahash *hash = crypto_ahash_reqtfm(c->req);
-
- local_unlock_nested_bh(&sigpool_scratch.bh_lock);
- rcu_read_unlock_bh();
- ahash_request_free(c->req);
- crypto_free_ahash(hash);
-}
-EXPORT_SYMBOL_GPL(tcp_sigpool_end);
-
-/**
- * tcp_sigpool_algo - return algorithm of tcp_sigpool
- * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
- * @buf: buffer to return name of algorithm
- * @buf_len: size of @buf
- */
-size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len)
-{
- if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg))
- return -EINVAL;
-
- return strscpy(buf, cpool[id].alg, buf_len);
-}
-EXPORT_SYMBOL_GPL(tcp_sigpool_algo);
-
-/**
- * tcp_sigpool_hash_skb_data - hash data in skb with initialized tcp_sigpool
- * @hp: tcp_sigpool pointer
- * @skb: buffer to add sign for
- * @header_len: TCP header length for this segment
- */
-int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp,
- const struct sk_buff *skb,
- unsigned int header_len)
-{
- const unsigned int head_data_len = skb_headlen(skb) > header_len ?
- skb_headlen(skb) - header_len : 0;
- const struct skb_shared_info *shi = skb_shinfo(skb);
- const struct tcphdr *tp = tcp_hdr(skb);
- struct ahash_request *req = hp->req;
- struct sk_buff *frag_iter;
- struct scatterlist sg;
- unsigned int i;
-
- sg_init_table(&sg, 1);
-
- sg_set_buf(&sg, ((u8 *)tp) + header_len, head_data_len);
- ahash_request_set_crypt(req, &sg, NULL, head_data_len);
- if (crypto_ahash_update(req))
- return 1;
-
- for (i = 0; i < shi->nr_frags; ++i) {
- const skb_frag_t *f = &shi->frags[i];
- unsigned int offset = skb_frag_off(f);
- struct page *page;
-
- page = skb_frag_page(f) + (offset >> PAGE_SHIFT);
- sg_set_page(&sg, page, skb_frag_size(f), offset_in_page(offset));
- ahash_request_set_crypt(req, &sg, NULL, skb_frag_size(f));
- if (crypto_ahash_update(req))
- return 1;
- }
-
- skb_walk_frags(skb, frag_iter)
- if (tcp_sigpool_hash_skb_data(hp, frag_iter, 0))
- return 1;
-
- return 0;
-}
-EXPORT_SYMBOL(tcp_sigpool_hash_skb_data);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Per-CPU pool of crypto requests");
--
2.54.0
^ permalink raw reply related
* [PATCH iproute2-next] man: remove libnetlink man page
From: Stephen Hemminger @ 2026-04-27 17:28 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger
The iproute2 libnetlink is not a public and stable API.
Having a man page encourages users to think it is available.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
man/Makefile | 2 +-
man/man3/Makefile | 18 ----
man/man3/libnetlink.3 | 200 ------------------------------------------
3 files changed, 1 insertion(+), 219 deletions(-)
delete mode 100644 man/man3/Makefile
delete mode 100644 man/man3/libnetlink.3
diff --git a/man/Makefile b/man/Makefile
index c0b0d416..91dd8f3d 100644
--- a/man/Makefile
+++ b/man/Makefile
@@ -8,7 +8,7 @@ MAN_CHECK=LC_ALL=en_US.UTF-8 MANROFFSEQ='' MANWIDTH=100 man --warnings \
# Hide man output, count and print errors.
MAN_REDIRECT=2>&1 >/dev/null | tee /dev/fd/2 | wc -l
-SUBDIRS = man3 man7 man8
+SUBDIRS = man7 man8
all clean install check:
@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@ || exit $$?; done
diff --git a/man/man3/Makefile b/man/man3/Makefile
deleted file mode 100644
index 1732be26..00000000
--- a/man/man3/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-MAN3PAGES = $(wildcard *.3)
-
-all:
-
-distclean: clean
-
-clean:
-
-install:
- $(INSTALLDIR) $(DESTDIR)$(MANDIR)/man3
- $(INSTALLMAN) $(MAN3PAGES) $(DESTDIR)$(MANDIR)/man3
-
-check:
- @for page in $(MAN3PAGES); do test 0 -eq $$($(MAN_CHECK) $$page \
- $(MAN_REDIRECT)) || { echo "Error in $$page"; exit 1; }; done
-
-.PHONY: install clean distclean check
diff --git a/man/man3/libnetlink.3 b/man/man3/libnetlink.3
deleted file mode 100644
index 9a2c801c..00000000
--- a/man/man3/libnetlink.3
+++ /dev/null
@@ -1,200 +0,0 @@
-.TH libnetlink 3
-.SH NAME
-libnetlink \- A library for accessing the netlink service
-.SH SYNOPSIS
-.nf
-#include <asm/types.h>
-.br
-#include <libnetlink.h>
-.br
-#include <linux/netlink.h>
-.br
-#include <linux/rtnetlink.h>
-.sp
-int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
-.sp
-int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
-.sp
-int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
-.sp
-int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
-.sp
-int rtnl_dump_filter(struct rtnl_handle *rth,
- int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
- void *arg1,
- int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *arg2)
-.sp
-int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
- unsigned groups, struct nlmsghdr *answer,
-.br
- int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
-.br
- void *jarg)
-.sp
-int rtnl_listen(struct rtnl_handle *rtnl,
- int (*handler)(struct sockaddr_nl *, struct rtnl_ctrl_data *,
- struct nlmsghdr *n, void *),
- void *jarg)
-.sp
-int rtnl_from_file(FILE *rtnl,
- int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
- void *jarg)
-.sp
-int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
-.sp
-int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
-.sp
-int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
-.sp
-int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
-.SH DESCRIPTION
-libnetlink provides a higher level interface to
-.BR rtnetlink (7).
-The read functions return 0 on success and a negative errno on failure.
-The send functions return the amount of data sent, or -1 on error.
-.TP
-rtnl_open
-Open a rtnetlink socket and save the state into the
-.B rth
-handle. This handle is passed to all subsequent calls.
-.B subscriptions
-is a bitmap of the rtnetlink multicast groups the socket will be
-a member of.
-
-.TP
-rtnl_wilddump_request
-Request a full dump of the
-.B type
-database for
-.B family
-addresses.
-.B type
-is a rtnetlink message type.
-.\" XXX
-
-.TP
-rtnl_dump_request
-Request a full dump of the
-.B type
-data buffer into
-.B buf
-with maximum length of
-.B len.
-.B type
-is a rtnetlink message type.
-
-.TP
-rtnl_dump_filter
-Receive netlink data after a request and filter it.
-The
-.B filter
-callback checks if the received message is wanted. It gets the source
-address of the message, the message itself and
-.B arg1
-as arguments. 0 as return means that the filter passed, a negative
-value is returned
-by
-.I rtnl_dump_filter
-in case of error. NULL for
-.I filter
-means to not use a filter.
-.B junk
-is used to filter messages not destined to the local socket.
-Only one message bundle is received. If there is a message
-pending, this function does not block.
-
-.TP
-rtnl_listen
-Receive netlink data after a request and pass it to
-.I handler.
-.B handler
-is a callback that gets the message source address, anscillary data, the message
-itself, and the
-.B jarg
-cookie as arguments. It will get called for all received messages.
-Only one message bundle is received. If there is a message
-pending this function does not block.
-
-.TP
-rtnl_from_file
-Works like
-.I rtnl_listen,
-but reads a netlink message bundle from the file
-.B file
-and passes the messages to
-.B handler
-for parsing. The file should contain raw data as received from a rtnetlink socket.
-.PP
-The following functions are useful to construct custom rtnetlink messages. For
-simple database dumping with filtering it is better to use the higher level
-functions above. See
-.BR rtnetlink (3)
-and
-.BR netlink (3)
-on how to generate a rtnetlink message. The following utility functions
-require a continuous buffer that already contains a netlink message header
-and a rtnetlink request.
-
-.TP
-rtnl_send
-Send the rtnetlink message in
-.B buf
-of length
-.B len
-to handle
-.B rth.
-
-.TP
-addattr32
-Add a __u32 attribute of type
-.B type
-and with value
-.B data
-to netlink message
-.B n,
-which is part of a buffer of length
-.B maxlen.
-
-.TP
-addattr_l
-Add a variable length attribute of type
-.B type
-and with value
-.B data
-and
-.B alen
-length to netlink message
-.B n,
-which is part of a buffer of length
-.B maxlen.
-.B data
-is copied.
-
-.TP
-rta_addattr32
-Initialize the rtnetlink attribute
-.B rta
-with a __u32 data value.
-
-.TP
-rta_addattr32
-Initialize the rtnetlink attribute
-.B rta
-with a variable length data value.
-
-.SH BUGS
-This library is meant for internal use, use libmnl for new programs.
-
-The functions sometimes use fprintf and exit when a fatal error occurs.
-This library should be named librtnetlink.
-
-.SH AUTHORS
-netlink/rtnetlink was designed and written by Alexey Kuznetsov.
-Andi Kleen wrote the man page.
-
-.SH SEE ALSO
-.BR netlink (7),
-.BR rtnetlink (7)
-.br
-/usr/include/linux/rtnetlink.h
--
2.53.0
^ permalink raw reply related
* [PATCH] net: nixge: fix skb leak and missing bail on DMA mapping error
From: kernelcoredev @ 2026-04-27 17:46 UTC (permalink / raw)
To: netdev; +Cc: andrew, andrew+netdev, davem, edumazet, kuba, pabeni,
kernelcoredev
When dma_mapping_error() fires during RX buffer refill in nixge_recv(),
the skb allocated by netdev_alloc_skb_ip_align() is never freed, and
execution continues writing a corrupt physical address into the hardware
descriptor ring.
Fix by freeing the skb with dev_kfree_skb() and returning early.
This was tested via code inspection, as I do not have access to the
hardware. If dma_mapping_error() occurs, the current code continues
execution, which can leak the skb and program an invalid DMA address
into the descriptor ring. This patch ensures proper cleanup and early
return on error.
Fixes: 492caffa8a1a ("net: ethernet: nixge: Add support for National Instruments XGE netdev")
Signed-off-by: Bentley Blacketer <sonionwhat@gmail.com>
---
drivers/net/ethernet/ni/nixge.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c
index 230d5ff99..b64e2f355 100644
--- a/drivers/net/ethernet/ni/nixge.c
+++ b/drivers/net/ethernet/ni/nixge.c
@@ -645,8 +645,9 @@ static int nixge_recv(struct net_device *ndev, int budget)
NIXGE_MAX_JUMBO_FRAME_SIZE,
DMA_FROM_DEVICE);
if (dma_mapping_error(ndev->dev.parent, cur_phys)) {
- /* FIXME: bail out and clean up */
- netdev_err(ndev, "Failed to map ...\n");
+ netdev_err(ndev, "Failed to map RX buffer\n");
+ dev_kfree_skb(new_skb);
+ return packets;
}
nixge_hw_dma_bd_set_phys(cur_p, cur_phys);
cur_p->cntrl = NIXGE_MAX_JUMBO_FRAME_SIZE;
--
2.54.0
^ permalink raw reply related
* Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
From: Yury Norov @ 2026-04-27 17:58 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Andrew Morton, Linus Torvalds, David Laight, Thomas Gleixner,
linux-alpha, linux-kernel, linux-snps-arc, linux-arm-kernel,
linux-mips, linuxppc-dev, kvm, linux-riscv, linux-s390,
sparclinux, linux-um, dmaengine, linux-efi, linux-fsi, amd-gfx,
dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch
In-Reply-To: <0ee46bb228d97163fbdc14f2a7c52b93d8bc34ce.1777306795.git.chleroy@kernel.org>
On Mon, Apr 27, 2026 at 07:13:48PM +0200, Christophe Leroy (CS GROUP) wrote:
> At the time being, x86 and arm64 are missing unsafe_copy_from_user().
No, they don't. They (should) rely on a generic implementation from
linux/uaccess.h, like every other arch, except for PPC and RISCV.
But they #define arch_unsafe_get_user, and the unsafe_copy_from_user()
becomes undefined conditionally on that.
So please, fix that bug instead of introducing another arch flavor.
We'd always choose generic version, unless there's strong evidence
that arch one is better.
Thanks,
Yury
> Add it.
>
> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++-----
> 1 file changed, 24 insertions(+), 5 deletions(-)
>
> diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
> index 3a0dd3c2b233..10c458ffa399 100644
> --- a/arch/x86/include/asm/uaccess.h
> +++ b/arch/x86/include/asm/uaccess.h
> @@ -598,7 +598,7 @@ _label: \
> * We want the unsafe accessors to always be inlined and use
> * the error labels - thus the macro games.
> */
> -#define unsafe_copy_loop(dst, src, len, type, label) \
> +#define unsafe_put_loop(dst, src, len, type, label) \
> while (len >= sizeof(type)) { \
> unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \
> dst += sizeof(type); \
> @@ -611,10 +611,29 @@ do { \
> char __user *__ucu_dst = (_dst); \
> const char *__ucu_src = (_src); \
> size_t __ucu_len = (_len); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
> +} while (0)
> +
> +#define unsafe_get_loop(dst, src, len, type, label) \
> + while (len >= sizeof(type)) { \
> + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \
> + dst += sizeof(type); \
> + src += sizeof(type); \
> + len -= sizeof(type); \
> + }
> +
> +#define unsafe_copy_from_user(_dst,_src,_len,label) \
> +do { \
> + char *__ucu_dst = (_dst); \
> + const char __user *__ucu_src = (_src); \
> + size_t __ucu_len = (_len); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
> } while (0)
>
> #ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
> --
> 2.49.0
>
^ permalink raw reply
* Re: [net-next PATCH 06/10] net: dsa: realtek: rtl8365mb: add VLAN support
From: Luiz Angelo Daros de Luca @ 2026-04-27 18:07 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Alvin Šipraga,
Yury Norov, Rasmus Villemoes, Russell King, netdev, linux-kernel,
Gabor Juhos
In-Reply-To: <CAJq09z6O81Sev07pQiQzCXzbKC2JxBCjep6=tX2Noqd=pPW1KQ@mail.gmail.com>
Hi Linus,
> > I haven't yet wrapped my head around if the RTL8367 (no extra letters)
> > is more RTL8366RB-ish or more RTL8365MB-ish... take a look at the
> > old code if you can figure it out from register maps etc:
> > https://github.com/openwrt/openwrt/tree/main/target/linux/generic/files/drivers/net/phy
> > (maybe Gabor knows, put him on cc)
Regarding the RTL8367 (no extra letters), the RTL8367R is indeed a
member of that original family, rather than the "B" family as I
previously suggested. It uses Port 9 for the CPU, which is absent in
RTL8367B. Architecturally, it is definitely more RTL8365MB-ish.
The main challenge with the base RTL8367 is the lack of a public API.
Most vendors support it via binary managers (ASUS) or proprietary
kernel modules (TP-Link). The only available references I’ve found are
the OpenWrt swconfig driver you mentioned and some U-Boot
initialization code. I do have the rtl8367{b,c,d} APIs. While the
rtl8367b seems close to the original RTL8367, it has fewer ports. Does
anyone happen to have access to the original RTL8367 API
documentation?
In any case, I’ve successfully reverse-engineered the table access
functions and format, which are similar to the rtl8367b, to the point
where I have the rtl8365mb driver running on an RTL8367R. I also
managed to enable CPU tagging, and it is currently functional using
the rtl8_4 tag. The code still needs some cleanup, support for
additional features (like LEDs) and a lot of tests, but it’s looking
promising. Adding support for the RTL8367 variant should make adding
RTL8367B or RTL8367D support much easier in the future.
Regards,
Luiz
^ permalink raw reply
* Re: [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
From: Alice Ryhl @ 2026-04-27 18:07 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
Thomas Gleixner, linux-alpha, linux-kernel, linux-snps-arc,
linux-arm-kernel, linux-mips, linuxppc-dev, kvm, linux-riscv,
linux-s390, sparclinux, linux-um, dmaengine, linux-efi, linux-fsi,
amd-gfx, dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch
In-Reply-To: <289b424e243ba2c4139ea04009cf8b9c448a87ff.1777306795.git.chleroy@kernel.org>
On Mon, Apr 27, 2026 at 07:13:46PM +0200, Christophe Leroy (CS GROUP) wrote:
> diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c
> index 01de4fbbcc84..710e07cd60ae 100644
> --- a/rust/helpers/uaccess.c
> +++ b/rust/helpers/uaccess.c
> @@ -5,13 +5,13 @@
> __rust_helper unsigned long
> rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
> {
> - return copy_from_user(to, from, n);
> + return copy_from_user_partial(to, from, n);
> }
>
> __rust_helper unsigned long
> rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)
> {
> - return copy_to_user(to, from, n);
> + return copy_to_user_partial(to, from, n);
> }
No Rust code uses the return value for anything other than comparing it
with zero, so you can keep these as copy_[from|to]_user() without
issues.
Thanks, Alice
^ permalink raw reply
* Re: [REGRESSION] aquantia: Sunshine/Moonlight UDP video streaming broken since 5b4015ad833c ("net: aquantia: Remove redundant UDP length adjustment with GSO_PARTIAL")
From: Gal Pressman @ 2026-04-27 18:09 UTC (permalink / raw)
To: Matthew Schwartz, Dragos Tatulea, Jakub Kicinski
Cc: regressions, netdev, linux-kernel@vger.kernel.org
In-Reply-To: <6c3fb15e-711d-4b8d-b152-e03d9b05293f@linux.dev>
Hello Matthew,
On 27/04/2026 2:20, Matthew Schwartz wrote:
> Hello,
>
> When using a previously working setup of remote streaming from my workstation to another device via Sunshine (the host server) and Moonlight (the client app) on my home network, I no longer receive any video output on the client app after upgrading my host workstation to kernel 7.0. Reverting back to kernel 6.19 on the host restored my setup to a working state.
>
> After bisecting, I landed on 5b4015ad833c ("net: aquantia: Remove redundant UDP length adjustment with GSO_PARTIAL") as the first bad commit. I confirmed this by moving the cable to my second on-board NIC (Intel) on the same workstation, which restored video output without any other kernel changes. My affected on-board NIC is Aquantia AQC113 [1d6a:04c0] (rev 03), atlantic driver, firmware 1.3.34, MTU 1500.
>
> Looking into it a bit further, ethtool -K enp97s0 tx-udp-segmentation off also serves as a workaround on my Aquantia port without changing to my other ethernet port. The working Intel NIC reports tx-udp-segmentation as "off [fixed]", so traffic falls back to software UDP segmentation on there.
>
> Please let me know if there's any additional info I can provide.
>
> Thanks,
> Matt
>
> #regzbot introduced: 5b4015ad833c
Thank you for the report and the bisect!
I will take a look and try to figure out what's wrong (though I don't
have real hardware to test on).
Is the userspace app open source? can I see its code and try to run it
myself?
I will be OOO for the rest of the week, hope to have some meaningful
reply by the end of next week.
^ permalink raw reply
* Re: [PATCH] net: nixge: fix skb leak and missing bail on DMA mapping error
From: Andrew Lunn @ 2026-04-27 18:14 UTC (permalink / raw)
To: kernelcoredev; +Cc: netdev, andrew+netdev, davem, edumazet, kuba, pabeni
In-Reply-To: <20260427174608.8201-1-sonionwhat@gmail.com>
On Mon, Apr 27, 2026 at 01:46:07PM -0400, kernelcoredev wrote:
> When dma_mapping_error() fires during RX buffer refill in nixge_recv(),
> the skb allocated by netdev_alloc_skb_ip_align() is never freed, and
> execution continues writing a corrupt physical address into the hardware
> descriptor ring.
>
> Fix by freeing the skb with dev_kfree_skb() and returning early.
>
> This was tested via code inspection, as I do not have access to the
> hardware. If dma_mapping_error() occurs, the current code continues
> execution, which can leak the skb and program an invalid DMA address
> into the descriptor ring. This patch ensures proper cleanup and early
> return on error.
Please continue your code inspection. What happens to the ring
descriptors if you return early?
If the fix was so simple, why do you think there is a FIXME? This is
the problem of not testing on hardware. Do you know if your change
makes the code better or worse? Please argue why it is better,
including an explanation of what happens to the ring descriptors with
your fix in place.
Andrew
^ permalink raw reply
* Re: [PATCH v2 iproute2-next 0/4] Introduce FRMR pools
From: Stephen Hemminger @ 2026-04-27 18:20 UTC (permalink / raw)
To: David Ahern; +Cc: Chiara Meiohas, leon, michaelgur, jgg, linux-rdma, netdev
In-Reply-To: <a441f862-1ebe-4fd9-9ef5-aac718fb008c@gmail.com>
On Sun, 5 Apr 2026 11:09:55 -0600
David Ahern <dsahern@gmail.com> wrote:
> On 3/30/26 11:31 AM, Chiara Meiohas wrote:
> > From Michael:
> >
> > This series adds support for managing Fast Registration Memory Region
> > (FRMR) pools in rdma tool, enabling users to monitor and configure FRMR
> > pool behavior.
> >
> > FRMR pools are used to cache and reuse Fast Registration Memory Region
> > handles to improve performance by avoiding the overhead of repeated
> > memory region creation and destruction. This series introduces commands
> > to view FRMR pool statistics and configure pool parameters such as
> > aging time and pinned handle count.
> >
> > The 'show' command allows users to display FRMR pools created on
> > devices, their properties, and usage statistics. Each pool is identified
> > by a unique key (hex-encoded properties) for easy reference in
> > subsequent operations.
> >
> > The aging 'set' command allows users to modify the aging time parameter,
> > which controls how long unused FRMR handles remain in the pool before
> > being released.
> >
> > The pinned 'set' command allows users to configure the number of pinned
> > handles in a pool. Pinned handles are exempt from aging and remain
> > permanently available for reuse, which is useful for workloads with
> > predictable memory region usage patterns.
> >
> > Command usage and examples are included in the commits and man pages.
> >
> > These patches are complimentary to the kernel patches:
> > https://lore.kernel.org/linux-rdma/20260226-frmr_pools-v4-0-95360b54f15e@nvidia.com/
> >
>
> applied after fixing up a few nits.
>
> Please clone the ai review prompts from:
> https://github.com/masoncl/review-prompts.git
>
> Run the setup scripts and have ai review patches before sending. This
> should really be part of both kernel and iproute2 development workflow now.
I rebased UAPI headers based on 7.1-rc1 and iproute2/rdma will not build.
Looks like RDMA did not get merged in 7.1.
Will have to back it out if not going in 7.1
^ permalink raw reply
* Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
From: Christophe Leroy (CS GROUP) @ 2026-04-27 18:20 UTC (permalink / raw)
To: Yury Norov
Cc: Andrew Morton, Linus Torvalds, David Laight, Thomas Gleixner,
linux-alpha, linux-kernel, linux-snps-arc, linux-arm-kernel,
linux-mips, linuxppc-dev, kvm, linux-riscv, linux-s390,
sparclinux, linux-um, dmaengine, linux-efi, linux-fsi, amd-gfx,
dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch
In-Reply-To: <ae-j2_QirCySZD02@yury>
Le 27/04/2026 à 19:58, Yury Norov a écrit :
> On Mon, Apr 27, 2026 at 07:13:48PM +0200, Christophe Leroy (CS GROUP) wrote:
>> At the time being, x86 and arm64 are missing unsafe_copy_from_user().
>
> No, they don't. They (should) rely on a generic implementation from
> linux/uaccess.h, like every other arch, except for PPC and RISCV.
>
> But they #define arch_unsafe_get_user, and the unsafe_copy_from_user()
> becomes undefined conditionally on that.
>
> So please, fix that bug instead of introducing another arch flavor.
> We'd always choose generic version, unless there's strong evidence
> that arch one is better.
But they both implement the exact same unsafe_copy_to_user(). What is
the difference here ?
Should that function become generic too ?
Christophe
>
>
> Thanks,
> Yury
>
>> Add it.
>>
>> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
>> ---
>> arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++-----
>> 1 file changed, 24 insertions(+), 5 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
>> index 3a0dd3c2b233..10c458ffa399 100644
>> --- a/arch/x86/include/asm/uaccess.h
>> +++ b/arch/x86/include/asm/uaccess.h
>> @@ -598,7 +598,7 @@ _label: \
>> * We want the unsafe accessors to always be inlined and use
>> * the error labels - thus the macro games.
>> */
>> -#define unsafe_copy_loop(dst, src, len, type, label) \
>> +#define unsafe_put_loop(dst, src, len, type, label) \
>> while (len >= sizeof(type)) { \
>> unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \
>> dst += sizeof(type); \
>> @@ -611,10 +611,29 @@ do { \
>> char __user *__ucu_dst = (_dst); \
>> const char *__ucu_src = (_src); \
>> size_t __ucu_len = (_len); \
>> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
>> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
>> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
>> - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
>> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
>> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
>> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
>> + unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
>> +} while (0)
>> +
>> +#define unsafe_get_loop(dst, src, len, type, label) \
>> + while (len >= sizeof(type)) { \
>> + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \
>> + dst += sizeof(type); \
>> + src += sizeof(type); \
>> + len -= sizeof(type); \
>> + }
>> +
>> +#define unsafe_copy_from_user(_dst,_src,_len,label) \
>> +do { \
>> + char *__ucu_dst = (_dst); \
>> + const char __user *__ucu_src = (_src); \
>> + size_t __ucu_len = (_len); \
>> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
>> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
>> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
>> + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
>> } while (0)
>>
>> #ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
>> --
>> 2.49.0
>>
^ permalink raw reply
* Re: [PATCH v2 iproute2-next 0/4] Introduce FRMR pools
From: Jason Gunthorpe @ 2026-04-27 18:22 UTC (permalink / raw)
To: Stephen Hemminger
Cc: David Ahern, Chiara Meiohas, leon, michaelgur, linux-rdma, netdev
In-Reply-To: <20260427112025.49ebbd73@phoenix.local>
On Mon, Apr 27, 2026 at 11:20:25AM -0700, Stephen Hemminger wrote:
> On Sun, 5 Apr 2026 11:09:55 -0600
> David Ahern <dsahern@gmail.com> wrote:
>
> > On 3/30/26 11:31 AM, Chiara Meiohas wrote:
> > > From Michael:
> > >
> > > This series adds support for managing Fast Registration Memory Region
> > > (FRMR) pools in rdma tool, enabling users to monitor and configure FRMR
> > > pool behavior.
> > >
> > > FRMR pools are used to cache and reuse Fast Registration Memory Region
> > > handles to improve performance by avoiding the overhead of repeated
> > > memory region creation and destruction. This series introduces commands
> > > to view FRMR pool statistics and configure pool parameters such as
> > > aging time and pinned handle count.
> > >
> > > The 'show' command allows users to display FRMR pools created on
> > > devices, their properties, and usage statistics. Each pool is identified
> > > by a unique key (hex-encoded properties) for easy reference in
> > > subsequent operations.
> > >
> > > The aging 'set' command allows users to modify the aging time parameter,
> > > which controls how long unused FRMR handles remain in the pool before
> > > being released.
> > >
> > > The pinned 'set' command allows users to configure the number of pinned
> > > handles in a pool. Pinned handles are exempt from aging and remain
> > > permanently available for reuse, which is useful for workloads with
> > > predictable memory region usage patterns.
> > >
> > > Command usage and examples are included in the commits and man pages.
> > >
> > > These patches are complimentary to the kernel patches:
> > > https://lore.kernel.org/linux-rdma/20260226-frmr_pools-v4-0-95360b54f15e@nvidia.com/
> > >
> >
> > applied after fixing up a few nits.
> >
> > Please clone the ai review prompts from:
> > https://github.com/masoncl/review-prompts.git
> >
> > Run the setup scripts and have ai review patches before sending. This
> > should really be part of both kernel and iproute2 development workflow now.
>
> I rebased UAPI headers based on 7.1-rc1 and iproute2/rdma will not build.
> Looks like RDMA did not get merged in 7.1.
>
> Will have to back it out if not going in 7.1
I see it here:
commit dbd0472fd7a5bdd0b86c21c36f8afa713baa7653
Author: Michael Guralnik <michaelgur@nvidia.com>
Date: Thu Feb 26 15:52:16 2026 +0200
RDMA/nldev: Expose kernel-internal FRMR pools in netlink
Allow netlink users, through the usage of driver-details netlink
attribute, to get information about internal FRMR pools that use the
kernel_vendor_key FRMR key member.
Signed-off-by: Michael Guralnik <michaelgur@nvidia.com>
Reviewed-by: Patrisious Haddad <phaddad@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
Link: https://patch.msgid.link/20260226-frmr_pools-v4-11-95360b54f15e@nvidia.com
Signed-off-by: Leon Romanovsky <leon@kernel.org>
Did some part get missed?
Jason
^ permalink raw reply
* Re: [PATCH v2 iproute2-next 1/4] rdma: Update headers
From: Stephen Hemminger @ 2026-04-27 18:25 UTC (permalink / raw)
To: Chiara Meiohas
Cc: leon, dsahern, michaelgur, jgg, linux-rdma, netdev,
Patrisious Haddad
In-Reply-To: <20260330173118.766885-2-cmeiohas@nvidia.com>
On Mon, 30 Mar 2026 20:31:15 +0300
Chiara Meiohas <cmeiohas@nvidia.com> wrote:
> From: Michael Guralnik <michaelgur@nvidia.com>
>
> Update rdma_netlink.h file up to kernel commit dbd0472fd7a5
> ("RDMA/nldev: Expose kernel-internal FRMR pools in netlink")
>
> Signed-off-by: Michael Guralnik <michaelgur@nvidia.com>
> Reviewed-by: Patrisious Haddad <phaddad@nvidia.com>
> Reviewed-by: Chiara Meiohas <cmeiohas@nvidia.com>
The upstream macro names changed, the iproute2 build is broken after
current headers sync.
In file included from res.c:7:
res.h: In function ‘_res_frmr_pools’:
res.h:203:26: error: ‘RDMA_NLDEV_CMD_RES_FRMR_POOLS_GET’ undeclared (first use in this function); did you mean ‘RDMA_NLDEV_CMD_FRMR_POOLS_GET’?
203 | RES_FUNC(res_frmr_pools, RDMA_NLDEV_CMD_RES_FRMR_POOLS_GET,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
res.h:56:44: note: in definition of macro ‘RES_FUNC’
56 | _command = res_get_command(command, rd); \
| ^~~~~~~
res.h:203:26: note: each undeclared identifier is reported only once for each function it appears in
203 | RES_FUNC(res_frmr_pools, RDMA_NLDEV_CMD_RES_FRMR_POOLS_GET,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
res.h:56:44: note: in definition of macro ‘RES_FUNC’
56 | _command = res_get_command(command, rd); \
| ^~~~~~~
^ permalink raw reply
* Re: [REGRESSION] aquantia: Sunshine/Moonlight UDP video streaming broken since 5b4015ad833c ("net: aquantia: Remove redundant UDP length adjustment with GSO_PARTIAL")
From: Matthew Schwartz @ 2026-04-27 18:26 UTC (permalink / raw)
To: Gal Pressman, Dragos Tatulea, Jakub Kicinski
Cc: regressions, netdev, linux-kernel@vger.kernel.org
In-Reply-To: <5293b717-6a8e-40b6-bd6b-61043a062451@nvidia.com>
On 4/27/26 11:09 AM, Gal Pressman wrote:
> Hello Matthew,
>
> On 27/04/2026 2:20, Matthew Schwartz wrote:
>> Hello,
>>
>> When using a previously working setup of remote streaming from my workstation to another device via Sunshine (the host server) and Moonlight (the client app) on my home network, I no longer receive any video output on the client app after upgrading my host workstation to kernel 7.0. Reverting back to kernel 6.19 on the host restored my setup to a working state.
>>
>> After bisecting, I landed on 5b4015ad833c ("net: aquantia: Remove redundant UDP length adjustment with GSO_PARTIAL") as the first bad commit. I confirmed this by moving the cable to my second on-board NIC (Intel) on the same workstation, which restored video output without any other kernel changes. My affected on-board NIC is Aquantia AQC113 [1d6a:04c0] (rev 03), atlantic driver, firmware 1.3.34, MTU 1500.
>>
>> Looking into it a bit further, ethtool -K enp97s0 tx-udp-segmentation off also serves as a workaround on my Aquantia port without changing to my other ethernet port. The working Intel NIC reports tx-udp-segmentation as "off [fixed]", so traffic falls back to software UDP segmentation on there.
>>
>> Please let me know if there's any additional info I can provide.
>>
>> Thanks,
>> Matt
>>
>> #regzbot introduced: 5b4015ad833c
>
> Thank you for the report and the bisect!
>
> I will take a look and try to figure out what's wrong (though I don't
> have real hardware to test on).
> Is the userspace app open source? can I see its code and try to run it
> myself?
Thanks for the reply. The code for Sunshine is available here: https://github.com/LizardByte/Sunshine and the code for Moonlight is here: https://github.com/moonlight-stream/moonlight-qt.
I have been using the Arch Linux Sunshine package which I installed by following the Linux instructions here: https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2getting__started.html, but there are also binaries for other distros or it's buildable from source. For Moonlight, I have been using the Flatpak distributed on Flathub because the client device runs an atomic rootfs, but you can also use any other device that Moonlight supports.
>
> I will be OOO for the rest of the week, hope to have some meaningful
> reply by the end of next week.
^ permalink raw reply
* Re: [PATCH v2 iproute2-next 1/4] rdma: Update headers
From: David Ahern @ 2026-04-27 18:27 UTC (permalink / raw)
To: Stephen Hemminger, Chiara Meiohas
Cc: leon, michaelgur, jgg, linux-rdma, netdev, Patrisious Haddad
In-Reply-To: <20260427112505.684c21f3@phoenix.local>
On 4/27/26 12:25 PM, Stephen Hemminger wrote:
> On Mon, 30 Mar 2026 20:31:15 +0300
> Chiara Meiohas <cmeiohas@nvidia.com> wrote:
>
>> From: Michael Guralnik <michaelgur@nvidia.com>
>>
>> Update rdma_netlink.h file up to kernel commit dbd0472fd7a5
>> ("RDMA/nldev: Expose kernel-internal FRMR pools in netlink")
>>
>> Signed-off-by: Michael Guralnik <michaelgur@nvidia.com>
>> Reviewed-by: Patrisious Haddad <phaddad@nvidia.com>
>> Reviewed-by: Chiara Meiohas <cmeiohas@nvidia.com>
>
> The upstream macro names changed, the iproute2 build is broken after
> current headers sync.
>
> In file included from res.c:7:
> res.h: In function ‘_res_frmr_pools’:
> res.h:203:26: error: ‘RDMA_NLDEV_CMD_RES_FRMR_POOLS_GET’ undeclared (first use in this function); did you mean ‘RDMA_NLDEV_CMD_FRMR_POOLS_GET’?
> 203 | RES_FUNC(res_frmr_pools, RDMA_NLDEV_CMD_RES_FRMR_POOLS_GET,
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> res.h:56:44: note: in definition of macro ‘RES_FUNC’
> 56 | _command = res_get_command(command, rd); \
> | ^~~~~~~
> res.h:203:26: note: each undeclared identifier is reported only once for each function it appears in
> 203 | RES_FUNC(res_frmr_pools, RDMA_NLDEV_CMD_RES_FRMR_POOLS_GET,
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> res.h:56:44: note: in definition of macro ‘RES_FUNC’
> 56 | _command = res_get_command(command, rd); \
> | ^~~~~~~
Looks like the merged API does not have the _RES part of the uapi:
kernel vs iproute2:
@@ -590,19 +590,19 @@
/*
* FRMR Pools attributes
*/
- RDMA_NLDEV_ATTR_FRMR_POOLS, /* nested table */
- RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY, /* nested table */
- RDMA_NLDEV_ATTR_FRMR_POOL_KEY, /* nested table */
- RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS, /* u8 */
- RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS, /* u32 */
- RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY, /* u64 */
- RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS, /* u64 */
- RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES, /* u32 */
- RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, /* u64 */
- RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, /* u64 */
- RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD, /* u32 */
- RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */
- RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOLS, /* nested table */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_ENTRY, /* nested table */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_KEY, /* nested table */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_KEY_ATS, /* u8 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_KEY_ACCESS_FLAGS, /* u32 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_KEY_VENDOR_KEY, /* u64 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_KEY_NUM_DMA_BLOCKS, /* u64 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_QUEUE_HANDLES, /* u32 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_MAX_IN_USE, /* u64 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_IN_USE, /* u64 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_AGING_PERIOD, /* u32 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_PINNED, /* u32 */
+ RDMA_NLDEV_ATTR_RES_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */
/*
^ permalink raw reply
* Re: [PATCH v1 1/2] vfio: add callback to get tph info for dma-buf
From: Leon Romanovsky @ 2026-04-27 18:35 UTC (permalink / raw)
To: Zhiping Zhang
Cc: Alex Williamson, Stanislav Fomichev, Keith Busch, Jason Gunthorpe,
Bjorn Helgaas, linux-rdma, linux-pci, netdev, dri-devel,
Yochai Cohen, Yishai Hadas
In-Reply-To: <CAH3zFs2Sy0mv=QkK4VSV+MVR=ef_CdoxMhTFgzaqoZ+uSOpxoQ@mail.gmail.com>
On Mon, Apr 27, 2026 at 07:28:57AM -0700, Zhiping Zhang wrote:
> On Mon, Apr 27, 2026 at 6:37 AM Leon Romanovsky <leon@kernel.org> wrote:
> >
> > >
> > On Wed, Apr 22, 2026 at 09:23:27AM -0600, Alex Williamson wrote:
> > > On Mon, 20 Apr 2026 11:39:15 -0700
> > > Zhiping Zhang <zhipingz@meta.com> wrote:
> > >
> > > > Add a dma-buf callback that returns raw TPH metadata from the exporter
> > > > so peer devices can reuse the steering tag and processing hint
> > > > associated with a VFIO-exported buffer.
> > > >
> > > > Keep the existing VFIO_DEVICE_FEATURE_DMA_BUF uAPI layout intact by
> > > > using a flag plus one extra trailing entries[] object for the optional
> > > > TPH metadata. Rename the uAPI field dma_ranges to entries. The
> > > > nr_ranges field remains the DMA range count; when VFIO_DMABUF_FLAG_TPH
> > > > is set the kernel reads one extra entry beyond nr_ranges for the TPH
> > > > metadata.
> > > >
> > > > Add an st_width parameter to get_tph() so the exporter can reject
> > > > steering tags that exceed the consumer's supported width (8 vs 16 bit).
> > > > When no TPH metadata was supplied, make get_tph() return -EOPNOTSUPP.
> > > >
> > > > Signed-off-by: Zhiping Zhang <zhipingz@meta.com>
> > > > ---
> > > > drivers/vfio/pci/vfio_pci_dmabuf.c | 62 +++++++++++++++++++++++-------
> > > > include/linux/dma-buf.h | 17 ++++++++
> > > > include/uapi/linux/vfio.h | 28 ++++++++++++--
> > > > 3 files changed, 89 insertions(+), 18 deletions(-)
> >
> > <...>
> >
> > > > diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
> > > > index bb7b89330d35..a0bd24623c52 100644
> > > > --- a/include/uapi/linux/vfio.h
> > > > +++ b/include/uapi/linux/vfio.h
> > > > @@ -1490,16 +1490,36 @@ struct vfio_device_feature_bus_master {
> > > > * open_flags are the typical flags passed to open(2), eg O_RDWR, O_CLOEXEC,
> > > > * etc. offset/length specify a slice of the region to create the dmabuf from.
> > > > * nr_ranges is the total number of (P2P DMA) ranges that comprise the dmabuf.
> > > > + * When VFIO_DMABUF_FLAG_TPH is set, entries[] contains one extra trailing
> > > > + * object after the nr_ranges DMA ranges carrying the TPH steering tag and
> > > > + * processing hint.
> > >
> > > I really don't think we want to design an API where entries is
> > > implicitly one-off from what's actually there. This feeds back into
> > > the below removal of the __counted by attribute, which is a red flag
> > > that this is the wrong approach.
> >
> > I believe removing `__counted` is a mistake. In my proposal, the intent
> > was to adjust the meaning of the storage object based on the flag bit.
> > The size of the array should still be represented correctly.
> >
> > Thanks
>
> Thanks Leon — you're right that __counted_by should be preserved. In
> your approach, when the flag is set, the last entry in the array
> carries the TPH data, so the effective DMA range count is nr_ranges -
> 1.
It is correct only if you keep the original name *nr_range*. However, the
variable name is worth changing to something clearer, such as
*nr_array_elements*.
>
> That said, after discussing internally, we're leaning toward
> introducing a new VFIO device feature with dedicated TPH fields (as
> Alex suggested too), to avoid overloading vfio_region_dma_range with a
> union that changes semantics based on position.
>
> Would you have concerns with that direction? I'll post a v3 with the
> new approach.
I don’t have any concerns. My only worry was about “stealing” too many
bits from the flags variable, and you’ve avoided that here.
Thanks
^ permalink raw reply
* Re: [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
From: Yury Norov @ 2026-04-27 18:39 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Andrew Morton, Linus Torvalds, David Laight, Thomas Gleixner,
linux-alpha, Yury Norov, linux-kernel, linux-snps-arc,
linux-arm-kernel, linux-mips, linuxppc-dev, kvm, linux-riscv,
linux-s390, sparclinux, linux-um, dmaengine, linux-efi, linux-fsi,
amd-gfx, dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch
In-Reply-To: <9fe875d2f55af59c12708336c571a46038528678.1777306795.git.chleroy@kernel.org>
On Mon, Apr 27, 2026 at 07:13:43PM +0200, Christophe Leroy (CS GROUP) wrote:
> Among the 21 architectures supported by the kernel, 16 define both
> INLINE_COPY_TO_USER and INLINE_COPY_FROM_USER while the 5 other ones
> don't define any of the two.
>
> To simplify and reduce risk of mistakes, convert them to a single
> kconfig item named CONFIG_ARCH_WANTS_NOINLINE_COPY which will be
We've got a special word for it: outline. Can you name it
CONFIG_OUTLINE_USERCOPY, or similar?
> selected by the 5 architectures that don't want inlined copy.
>
> To minimise complication in a later patch, also remove
> ifdefery and replace it with IS_ENABLED().
>
> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
Andrew has taken my consolidation patch for INLINE_COPY_USER:
https://lore.kernel.org/all/20260427085814.7ca0b134603b8d5813e23396@linux-foundation.org/
Please base your series on top of it.
I'm not sure this patch is relevant to the goal of your series. Maybe
send it separately?
Thanks,
Yury
^ permalink raw reply
* Re: [PATCH net] net: mana: hardening: Validate SHM offset from BAR0 register to prevent crash due to alignment fault
From: Dipayaan Roy @ 2026-04-27 18:40 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
kuba, pabeni, leon, longli, kotaranov, horms, shradhagupta,
ssengar, ernis, shirazsaleem, linux-hyperv, netdev, linux-kernel,
linux-rdma, stephen, jacob.e.keller, dipayanroy, leitao, kees,
john.fastabend, hawk, bpf, daniel, ast, sdf, yury.norov
In-Reply-To: <20260426131555.GA3501894@ziepe.ca>
On Sun, Apr 26, 2026 at 10:15:55AM -0300, Jason Gunthorpe wrote:
> On Thu, Apr 23, 2026 at 09:16:28AM -0700, Dipayaan Roy wrote:
> > During Function Level Reset recovery, the MANA driver reads
> > hardware BAR0 registers that may temporarily contain garbage values.
> > The SHM (Shared Memory) offset read from GDMA_REG_SHM_OFFSET is used
> > to compute gc->shm_base, which is later dereferenced via readl() in
> > mana_smc_poll_register(). If the hardware returns an unaligned or
> > out-of-range value, the driver must not blindly use it, as this would
> > propagate the hardware error into a kernel crash.
>
> It is not what we are calling "hardening" if you are hitting actual
> crashes in actual real systems.
>
> "hardening" is the driver defending against actively malicious
> hardware, operating in ways that will never be seen in real systems,
> attempting to compromise the kernel.
>
> Drivers working around real world broken/buggy/malfunctioning HW is
> just entirely normal stuff.
>
Hi Jason,
sure, I will drop the hardening label, in v2.
> > @@ -73,10 +74,25 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev)
> > gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off;
> >
> > sriov_base_off = mana_gd_r64(gc, GDMA_SRIOV_REG_CFG_BASE_OFF);
> > + if (sriov_base_off >= gc->bar0_size ||
> > + !IS_ALIGNED(sriov_base_off, sizeof(u32))) {
> > + dev_err(gc->dev,
> > + "SRIOV base offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n",
> > + sriov_base_off, (u64)gc->bar0_size);
> > + return -EPROTO;
> > + }
>
> .. and if it is entirely normal and something that happens is EPROTO
> really the right way to deal with this race, or should the driver be
> looping somehow until the device stabilizes??
This is the current flow of the driver:
mana_serv_reset()
mana_gd_suspend()
msleep(MANA_SERVICE_PERIOD * 1000); -> 10s
mana_gd_resume()
mana_gd_init_registers(); -> read the garbage followed by fault
mana_serv_rescan(); -> On mana_gd_resume err(EPROTO) full PCI remove + rescan
The 10-second sleep in mana_serv_reset() already happens before
mana_gd_resume() is called, so by the time we read the registers
hardware should have stabilized. If we still see garbage after 10
seconds, it suggests deeper hardware issue where PCI rescan is
recomended from HW. This patch returns -EPROTO on detection
of unaligned / out of range offset and that err code triggers the
mana_serv_rescan().
>
> Jason
Thanks Jason for the review comments, will post a v2 to drop the
hardening label.
Regards
Dipayaan Roy
^ permalink raw reply
* RE: [PATCH 2/2] drm/hyperv: use VMBUS_RING_SIZE()
From: Dexuan Cui @ 2026-04-27 18:42 UTC (permalink / raw)
To: Hamza Mahfooz, Saurabh Singh Sengar
Cc: linux-kernel@vger.kernel.org, KY Srinivasan, Haiyang Zhang,
Wei Liu, Long Li, Stefano Garzarella, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Himadri Pandya, Michael Kelley, linux-hyperv@vger.kernel.org,
virtualization@lists.linux.dev, netdev@vger.kernel.org,
Saurabh Sengar, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Deepak Rawat,
dri-devel@lists.freedesktop.org, stable@kernel.vger.org
In-Reply-To: <ae9NxmDBTkzPP3H6@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
> From: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
> Sent: Monday, April 27, 2026 4:52 AM
> To: Saurabh Singh Sengar <ssengar@microsoft.com>
> ...
> On Sun, Apr 26, 2026 at 05:00:24AM +0000, Saurabh Singh Sengar wrote:
> > > Subject: [PATCH 2/2] drm/hyperv: use VMBUS_RING_SIZE()
> > >
> > > VMBUS ring buffers must be page aligned. So, use VMBUS_RING_SIZE() to
> > > ensure they are always aligned and large enough to hold all of the relevant
> > > data.
> > >
> > > Cc: stable@kernel.vger.org
> > > Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic
> > > video device")
IMO the Fixes tag is unnecessary because the existing VMBUS_RING_BUFSIZE
is 256KB, which is already aligned to 4KB, 16KB and 64KB.
VMBUS_RING_SIZE(256 * 1024) is still 256KB.
> > > Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
> > > ---
> > > drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> > > b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> > > index 051ecc526832..753d97bff76f 100644
> > > --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> > > +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> > > @@ -10,7 +10,7 @@
> > >
> > > #include "hyperv_drm.h"
> > >
> > > -#define VMBUS_RING_BUFSIZE (256 * 1024)
> > > +#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024)
> > > #define VMBUS_VSP_TIMEOUT (10 * HZ)
> > >
> > > #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
> > > --
> > > 2.54.0
> >
> > Although this lgtm, but this may change the behaviour on ARM64 systems
> with page size > 4K ?
Actually the behavior won't change, because
VMBUS_RING_SIZE(256 * 1024) is still 256KB.
> > Have we tested it ?
>
> Yup, I tested it on an ARM64 windows machine with a 64K page size guest
> kernel.
>
> >
> > Reviewed-by: Saurabh Sengar <ssengar@linux.microsoft.com>
>
> Pushed to drm-misc.
^ permalink raw reply
* RE: [PATCH 1/2] hv_sock: fix ARM64 support
From: Dexuan Cui @ 2026-04-27 18:53 UTC (permalink / raw)
To: Hamza Mahfooz, linux-kernel@vger.kernel.org
Cc: KY Srinivasan, Haiyang Zhang, Wei Liu, Long Li,
Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Himadri Pandya, Michael Kelley,
linux-hyperv@vger.kernel.org, virtualization@lists.linux.dev,
netdev@vger.kernel.org, Saurabh Sengar, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Deepak Rawat, dri-devel@lists.freedesktop.org,
stable@kernel.vger.org
In-Reply-To: <20260425181719.1538483-1-hamzamahfooz@linux.microsoft.com>
> From: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
> Sent: Saturday, April 25, 2026 11:17 AM
> ...
> Cc: stable@kernel.vger.org
> Fixes: 77ffe33363c0 ("hv_sock: use HV_HYP_PAGE_SIZE for Hyper-V
> communication")
It looks like 77ffe33363c0 was not tested with
CONFIG_ARM64_64K_PAGES=y...
Thanks for the fix!
> Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
Tested-by: Dexuan Cui <decui@microsoft.com>
Reviewed-by: Dexuan Cui <decui@microsoft.com>
^ permalink raw reply
* Re: [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
From: Linus Torvalds @ 2026-04-27 19:01 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Yury Norov, Andrew Morton, David Laight, Thomas Gleixner,
linux-alpha, linux-kernel, linux-snps-arc, linux-arm-kernel,
linux-mips, linuxppc-dev, kvm, linux-riscv, linux-s390,
sparclinux, linux-um, dmaengine, linux-efi, linux-fsi, amd-gfx,
dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch
In-Reply-To: <289b424e243ba2c4139ea04009cf8b9c448a87ff.1777306795.git.chleroy@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 6419 bytes --]
On Mon, 27 Apr 2026 at 10:18, Christophe Leroy (CS GROUP)
<chleroy@kernel.org> wrote:
>
> In a subsequent patch, copy_{to/from}_user() will be modified to
> return -EFAULT when copy fails.
Please don't do this.
This is a maintenance nightmare, and changes pretty much three decades
of semantics, and will cause *very* subtle backporting issues if
somebody happens to rely on the old / new behavior.
I understand the reasoning for the change, but I really don't think
the pain of creating yet another user copy interface is worth it.
We already have a lot of different versions of user copies for
different reasons, and while they all tend to have a good reason (and
some not-so-good, but historical reasons) for existing, this one
doesn't seem worth it.
The main - perhaps only - reason for this "partial" version is that
you want to do that "automatically inlined and optimized fixed-sized
case".
But here's the thing: I think you can already do that. Yes, it
requires some improvements to unsafe_copy_from_user(), but *that*
interface doesn't have three decades of history associated with it,
_and_ you're extending on that one anyway in this series.
"unsafe_copy_from_user()" is very odd, is meant only for small simple
copies that can be inlined and it's special-cased for 'objtool' anyway
(because objtool would have complained about an out-of-line call,
although it could have been special-cased other ways).
In other words: unsafe_copy_from_user() is *very* close to what you
want for that "Oh, I noticed that it's a small fixed-size copy, so I
want to special-case copy-from-user for that".
The _only_ issue with unsafe_copy_from_user() is that you can't see
that there were partial successes. But if *that* was fixed, then this
whole "create a new copy_from_user interface" issue would just go
away.
So please - let's just change unsafe_copy_from_user() to be usable for
the partial case.
And the thing is, all the existing unsafe_copy_from_user()
implementations already effectively *have* the "how much did I not
copy" internally, and they actually do extra work to hide it, ie they
have things like that
int _i;
that is "how many bytes have I copied" in the powerpc implementation,
or the x86 code does
size_t __ucu_len = (_len);
where that "ucu_len" is updated as you go along and is literally the
"how many bytes are left to copy" return value that is missing from
this interface.
So what I would suggest is
- introduce a new user accessor helper that is used for *both*
unsafe_copy_to/from_user() *and* the "inline small constant-sized
normal copy_to/from_user()" calls
- it's the same thing as the existing unsafe_copy_to/from_user()
implementation, except it exposes how many bytes are left to be copied
to the exception label.
IOW, it would look something like
#define unsafe_copy_to_user_outlen(_dst,_src,_len,label)...
which is exactly the same as the current unsafe_copy_to_user(),
*except* it changes "_len" as it does along.
And then you use that for both the "real" unsafe_copy_user and for the
"small constant values" case.
Just as an example, attached is a completely stupid rough draft of a
patch that does this for x86 and only for unsafe_copy_to_user().
And I made a very very hacky change to kernel/sys.c to see what the
code generation looks like.
This is what it results in on x86 with clang (with all the magic
.section data edited out):
... edited out the code to generate the times
... this is the actual user copy:
# HERE!
movabsq $81985529216486895, %rcx # imm = 0x123456789ABCDEF
cmpq %rcx, %rbx
cmovaq %rcx, %rbx
stac
movq %r13, (%rbx) # exception to .LBB45_8
movq %r14, 8(%rbx) # exception to .LBB45_8
movq %r15, 16(%rbx) # exception to .LBB45_8
movq %rax, 24(%rbx) # exception to .LBB45_8
clac
.LBB45_6:
movq jiffies(%rip), %rdi
callq jiffies_64_to_clock_t
.LBB45_7:
addq $16, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
retq
.LBB45_8:
clac
movq $-14, %rax
jmp .LBB45_7
and notice how the compiler noticed that the 'outlen' isn't actually
used, and turned the exception label into just a "return -EFAULT" and
never actually generated any code for updating remaining lengths?
That actually looks pretty much optimal for a 32-byte user copy.
And it didn't involve changing the semantics at all.
Just to check, I changed that "times()" system call to return the
number of bytes uncopied instead (to emulate the "I actually want to
know what's left" case), and it generated this:
# HERE!
movabsq $81985529216486895, %rcx # imm = 0x123456789ABCDEF
cmpq %rcx, %rbx
cmovaq %rcx, %rbx
stac
movl $32, %ecx
movq %r13, (%rbx) # exception to .LBB45_7
movl $24, %ecx
movq %r15, 8(%rbx) # exception to .LBB45_7
movl $16, %ecx
movq %r14, 16(%rbx) # exception to .LBB45_7
movl $8, %ecx
movq %rax, 24(%rbx) # exception to .LBB45_7
clac
xorl %ecx, %ecx
.LBB45_8:
movq %rcx, %rax
addq $16, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
retq
.LBB45_6:
movq jiffies(%rip), %rdi
jmp jiffies_64_to_clock_t # TAILCALL
.LBB45_7:
clac
jmp .LBB45_8
so it all seems to work - although obviously the above is *not* the normal case.
NOTE NOTE NOTE! The attached patch is entirely untested. I obviously
did some "test code generation" with it, but I only *looked* at the
result, and maybe it has some fundamental problem that I just didn't
notice. So treat this as a "how about this approach" patch, not as
anything more serious than that.
And the kerrnel/sys.c hack is very obviously just that: a complate
hack for testing.
A real patch would do that "for small constant-sized copies, turn
copy_to_user() automatically into "_small_copy_to_user()".
The attached is *not* a real patch. Treat it with the contempt it deserves.
Linus
[-- Attachment #2: patch.diff --]
[-- Type: text/x-patch, Size: 2637 bytes --]
arch/x86/include/asm/uaccess.h | 17 +++++++++++------
include/linux/uaccess.h | 16 ++++++++++++++++
kernel/sys.c | 3 ++-
3 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 3a0dd3c2b233..3b2c57c91418 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -606,15 +606,20 @@ _label: \
len -= sizeof(type); \
}
-#define unsafe_copy_to_user(_dst,_src,_len,label) \
+#define unsafe_copy_to_user_outlen(_dst,_src,_len,label) \
do { \
char __user *__ucu_dst = (_dst); \
const char *__ucu_src = (_src); \
- size_t __ucu_len = (_len); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
+ unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u64, label); \
+ unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u32, label); \
+ unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u16, label); \
+ unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u8, label); \
+} while (0)
+
+#define unsafe_copy_to_user(_dst,_src,_len,label) \
+do { \
+ size_t __ucu_len = _len; \
+ unsafe_copy_to_user_outlen(_dst,_src,__ucu_len,label); \
} while (0)
#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 56328601218c..1a70ef70784c 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -874,4 +874,20 @@ void __noreturn usercopy_abort(const char *name, const char *detail,
unsigned long len);
#endif
+static __always_inline __must_check unsigned long
+_small_copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ size_t uncopied = n;
+
+ might_fault();
+ if (should_fail_usercopy())
+ return n;
+ instrument_copy_to_user(to, from, n);
+ scoped_user_write_access_size(to, n, failed)
+ unsafe_copy_to_user_outlen(to, from, uncopied, failed);
+ return 0;
+failed:
+ return uncopied;
+}
+
#endif /* __LINUX_UACCESS_H__ */
diff --git a/kernel/sys.c b/kernel/sys.c
index 62e842055cc9..65b2d0103a73 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1067,7 +1067,8 @@ SYSCALL_DEFINE1(times, struct tms __user *, tbuf)
struct tms tmp;
do_sys_times(&tmp);
- if (copy_to_user(tbuf, &tmp, sizeof(struct tms)))
+ asm volatile("# HERE!");
+ if (_small_copy_to_user(tbuf, &tmp, sizeof(struct tms)))
return -EFAULT;
}
force_successful_syscall_return();
^ permalink raw reply related
* Re: [RFC PATCH v1 0/9] uaccess: Convert small fixed size copy_{to/from}_user() to scoped user access
From: Helge Deller @ 2026-04-27 19:01 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP), Yury Norov, Andrew Morton,
Linus Torvalds, David Laight, Thomas Gleixner
Cc: linux-alpha, linux-kernel, linux-snps-arc, linux-arm-kernel,
linux-mips, linuxppc-dev, kvm, linux-riscv, linux-s390,
sparclinux, linux-um, dmaengine, linux-efi, linux-fsi, amd-gfx,
dri-devel, intel-gfx, linux-wpan, netdev, linux-wireless,
linux-spi, linux-media, linux-staging, linux-serial, linux-usb,
xen-devel, linux-fsdevel, ocfs2-devel, bpf, kasan-dev, linux-mm,
linux-x25, rust-for-linux, linux-sound, sound-open-firmware,
linux-csky, linux-hexagon, loongarch, linux-m68k, linux-openrisc,
linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>
Hello Christophe,
On 4/27/26 19:13, Christophe Leroy (CS GROUP) wrote:
> A lot of copy_from_user() and copy_to_user() perform copies of small
> fixed size pieces of data between kernel and userspace, and don't
> care about partial copies.
>
> copy_from_user() and copy_to_user() are big functions optimised for
> copying large amount of data, with cache management, etc ...
They take care of much more: alignments, exception handling (e.g. if userpage
is read-only and kernel writes to it), various rules when to return faults
(e.g. sometime reading from page0 is allowed for other arches not), and
much more. I've seen so many strange things during the last few years,
and you would need to get it right if you want to "make small" versions
of those functions.
> This is often overkill for small copies that could just be inlined
> instead.
Isn't put_user() and get_user() for that ?
And if you inline you need to take care of faults as well, so indirectly
you will add more fault handlers (or fault pointers) to the generated code,
effectively making the kernel bigger.
> What makes things a bit more tricky is that those copy functions
> are designed to handle partial copies in case of page fault. But among
> the 6000 callers of those functions, only 2% really care about the
> quantity of no-copied data that those functions return. All other ones
> fails as soon as the returned value is not 0, returning -EACCESS.
>
> So first step in this series is to introduce variants called
> copy_from_user_partial() and copy_to_user_partial() which will be
> called by the 2% users that care about the partial copy, then the
> original copy_from_user() and copy_to_user() are changed to return
> -EFAULT when the copy fails.
>
> Then the second step is to implement copy of small fixed-size data
> with scoped user access instead of calling the arch specific heavy
> user copy functions.
I'm not against your idea or your patch, but I wonder if you
really gain much from it.
Have you done some size or speed comparisons ?
Helge
> Patch 5, can be split in different patches for each archicture or
> subsystem, but let's get a first feedback and agree on the principle.
>
> Christophe Leroy (CS GROUP) (9):
> uaccess: Split check_zeroed_user() out of usercopy.c
> uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce
> ifdefery
> x86/umip: Be stricter in fixup_umip_exception()
> uaccess: Introduce copy_{to/from}_user_partial()
> uaccess: Switch to copy_{to/from}_user_partial() when relevant
> uaccess: Change copy_{to/from}_user to return -EFAULT
> x86: Add unsafe_copy_from_user()
> arm64: Add unsafe_copy_from_user()
> uaccess: Convert small fixed size copy_{to/from}_user() to scoped user
> access
>
> arch/alpha/Kconfig | 1 +
> arch/alpha/kernel/osf_sys.c | 4 +-
> arch/alpha/kernel/termios.c | 2 +-
> arch/arc/include/asm/uaccess.h | 3 -
> arch/arc/kernel/disasm.c | 2 +-
> arch/arm/include/asm/uaccess.h | 2 -
> arch/arm64/include/asm/gcs.h | 2 +-
> arch/arm64/include/asm/uaccess.h | 30 +++--
> arch/arm64/kernel/signal32.c | 2 +-
> arch/csky/Kconfig | 1 +
> arch/hexagon/include/asm/uaccess.h | 3 -
> arch/loongarch/include/asm/uaccess.h | 3 -
> arch/m68k/include/asm/uaccess.h | 3 -
> arch/microblaze/include/asm/uaccess.h | 2 -
> arch/mips/include/asm/uaccess.h | 3 -
> arch/mips/kernel/rtlx.c | 8 +-
> arch/mips/kernel/vpe.c | 2 +-
> arch/nios2/include/asm/uaccess.h | 2 -
> arch/openrisc/include/asm/uaccess.h | 2 -
> arch/parisc/include/asm/uaccess.h | 3 -
> arch/powerpc/Kconfig | 1 +
> arch/powerpc/kvm/book3s_64_mmu_hv.c | 4 +-
> arch/powerpc/kvm/book3s_64_mmu_radix.c | 4 +-
> arch/powerpc/kvm/book3s_hv.c | 2 +-
> arch/riscv/Kconfig | 1 +
> arch/riscv/kernel/signal.c | 2 +-
> arch/s390/include/asm/idals.h | 8 +-
> arch/s390/include/asm/uaccess.h | 3 -
> arch/sh/include/asm/uaccess.h | 2 -
> arch/sparc/include/asm/uaccess_32.h | 3 -
> arch/sparc/include/asm/uaccess_64.h | 2 -
> arch/sparc/kernel/termios.c | 2 +-
> arch/um/include/asm/uaccess.h | 3 -
> arch/um/kernel/process.c | 2 +-
> arch/x86/Kconfig | 1 +
> arch/x86/include/asm/uaccess.h | 29 ++++-
> arch/x86/kernel/umip.c | 2 +-
> arch/x86/lib/insn-eval.c | 2 +-
> arch/x86/um/signal.c | 2 +-
> arch/xtensa/include/asm/uaccess.h | 2 -
> drivers/android/binder_alloc.c | 2 +-
> drivers/comedi/comedi_fops.c | 4 +-
> drivers/dma/idxd/cdev.c | 2 +-
> drivers/firmware/efi/test/efi_test.c | 2 +-
> drivers/fsi/fsi-scom.c | 2 +-
> .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 2 +-
> drivers/gpu/drm/i915/gt/intel_sseu.c | 4 +-
> drivers/gpu/drm/i915/i915_gem.c | 4 +-
> drivers/hwtracing/intel_th/msu.c | 2 +-
> drivers/misc/ibmvmc.c | 2 +-
> drivers/misc/vmw_vmci/vmci_host.c | 2 +-
> drivers/most/most_cdev.c | 2 +-
> drivers/net/ieee802154/ca8210.c | 4 +-
> drivers/net/wireless/ath/wil6210/debugfs.c | 2 +-
> .../intel/iwlwifi/pcie/gen1_2/trans.c | 2 +-
> drivers/net/wireless/ti/wlcore/debugfs.c | 2 +-
> drivers/ps3/ps3-lpm.c | 2 +-
> drivers/s390/crypto/zcrypt_api.h | 4 +-
> drivers/spi/spidev.c | 2 +-
> .../staging/media/atomisp/pci/atomisp_cmd.c | 8 +-
> drivers/tty/tty_ioctl.c | 14 +--
> drivers/tty/vt/vc_screen.c | 4 +-
> drivers/usb/gadget/function/f_hid.c | 4 +-
> drivers/usb/gadget/function/f_printer.c | 2 +-
> drivers/vfio/vfio_iommu_type1.c | 4 +-
> drivers/xen/xenbus/xenbus_dev_frontend.c | 2 +-
> fs/namespace.c | 2 +-
> fs/ocfs2/dlmfs/dlmfs.c | 2 +-
> fs/proc/base.c | 4 +-
> include/asm-generic/uaccess.h | 2 -
> include/linux/bpfptr.h | 2 +-
> include/linux/sockptr.h | 4 +-
> include/linux/uaccess.h | 107 ++++++++++++++----
> ipc/msg.c | 8 +-
> ipc/sem.c | 8 +-
> ipc/shm.c | 18 +--
> kernel/regset.c | 2 +-
> kernel/sys.c | 4 +-
> lib/Kconfig | 3 +
> lib/Makefile | 4 +-
> lib/kfifo.c | 8 +-
> lib/{usercopy.c => usercheck.c} | 22 ----
> lib/usercopy.c | 66 -----------
> mm/kasan/kasan_test_c.c | 4 +-
> mm/memory.c | 2 +-
> net/x25/af_x25.c | 2 +-
> rust/helpers/uaccess.c | 6 +-
> sound/pci/emu10k1/emufx.c | 4 +-
> sound/pci/rme9652/hdsp.c | 6 +-
> sound/soc/intel/avs/probes.c | 6 +-
> sound/soc/sof/compress.c | 12 +-
> sound/soc/sof/sof-client-probes.c | 6 +-
> 92 files changed, 269 insertions(+), 288 deletions(-)
> copy lib/{usercopy.c => usercheck.c} (73%)
>
^ permalink raw reply
* Re: [PATCH v5] net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
From: Simon Horman @ 2026-04-27 19:03 UTC (permalink / raw)
To: jhapavitra98
Cc: 'Simon Horman', w, pabeni, chandrashekar.devegowda,
linux-wwan, netdev, stable
In-Reply-To: <20260423150733.2025838-1-jhapavitra98@gmail.com>
From: 'Simon Horman' <horms@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
The patch attempts to fix a slab-out-of-bounds read in
t7xx_port_enum_msg_handler() by checking msg_len against struct_size().
It passes msg_len from t7xx_parse_host_rt_data() and control_msg_handler().
Cc: stable@vger.kernel.org
> diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
> index 7968e208dd37c..dc90691ef3f54 100644
> --- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c
> +++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
> @@ -453,12 +453,14 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
> {
> enum mtk_feature_support_type ft_spt_st, ft_spt_cfg;
> struct mtk_runtime_feature *rt_feature;
> + size_t feat_data_len;
> int i, offset;
>
> offset = sizeof(struct feature_query);
> for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) {
> rt_feature = data + offset;
The loop condition "offset < data_length" only ensures there is at least 1 byte
remaining in the buffer. Could accessing rt_feature->data_len read out-of-bounds
if data_length - offset is less than sizeof(*rt_feature)?
> - offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len);
> + feat_data_len = le32_to_cpu(rt_feature->data_len);
> + offset += sizeof(*rt_feature) + feat_data_len;
Could adding the 32-bit untrusted feat_data_len to offset cause an integer
overflow since offset is a signed int? If a malicious modem provides a large
value (e.g., 0xFFFFFFF0), offset could become negative, which would keep the
loop condition true and lead to arbitrary backward out-of-bounds reads on the
next iteration.
> ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]);
> if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED)
> @@ -468,8 +470,10 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
> if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED)
> return -EINVAL;
>
> - if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM)
> - t7xx_port_enum_msg_handler(ctl->md, rt_feature->data);
> + if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) {
> + t7xx_port_enum_msg_handler(ctl->md, rt_feature->data,
> + feat_data_len);
> + }
Here, feat_data_len is passed as msg_len, which is extracted directly from the
untrusted modem payload (rt_feature->data_len). Is this length validated against
the actual remaining buffer size (data_length - offset - sizeof(*rt_feature))?
If a malicious modem provides a small physical buffer but a very large data_len
field, the new msg_len bounds check in t7xx_port_enum_msg_handler() could still
pass, defeating the purpose of this patch and allowing the out-of-bounds read to
occur.
> }
>
> return 0;
> diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
> index ae632ef966983..fa24284448e55 100644
> --- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
> +++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
[ ... ]
> @@ -141,6 +142,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
> }
>
> port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info));
This function reads port_msg->info, port_msg->head_pattern, and
port_msg->tail_pattern before validating msg_len. If the provided msg_len is
smaller than sizeof(struct port_msg) (12 bytes), could these accesses read
out-of-bounds memory?
> +
> + if (msg_len < struct_size(port_msg, data, port_count)) {
> + dev_err(dev, "Port enum msg too short: need %zu, have %zu\n",
> + struct_size(port_msg, data, port_count), msg_len);
> + return -EINVAL;
> + }
> +
[ ... ]
> @@ -191,7 +199,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
>
> case CTL_ID_PORT_ENUM:
> skb_pull(skb, sizeof(*ctrl_msg_h));
Is the SKB length validated before calling skb_pull()? If skb->len is less than
sizeof(*ctrl_msg_h), skb_pull() fails and leaves the SKB unmodified. Passing the
unpulled skb->data could cause the handler to misinterpret the control header as
the port message payload.
> - ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data);
> + ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len);
> if (!ret)
> ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0);
> else
Does this function leak the skb buffer in the CTL_ID_PORT_ENUM and default
switch cases?
In control_msg_handler(), the skb is explicitly freed using dev_kfree_skb_any()
for CTL_ID_HS2_MSG and CTL_ID_MD_EX*:
drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c:control_msg_handler() {
...
switch (le32_to_cpu(ctrl_msg_h->ctrl_msg_id)) {
case CTL_ID_HS2_MSG:
...
dev_kfree_skb_any(skb);
break;
case CTL_ID_MD_EX:
case CTL_ID_MD_EX_ACK:
case CTL_ID_MD_EX_PASS:
case CTL_ID_DRV_VER_ERROR:
ret = fsm_ee_message_handler(port, ctl, skb);
dev_kfree_skb_any(skb);
break;
...
}
However, for CTL_ID_PORT_ENUM and default, the function returns without freeing
the skb, and the caller does not free it either. Could a malicious modem trigger
memory exhaustion by repeatedly sending CTL_ID_PORT_ENUM or invalid control
messages?
^ permalink raw reply
* Re: [PATCH v5] net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
From: Simon Horman @ 2026-04-27 19:04 UTC (permalink / raw)
To: jhapavitra98
Cc: Simon Horman, w, netdev, chandrashekar.devegowda, linux-wwan,
pabeni, stable
In-Reply-To: <20260423150733.2025838-1-jhapavitra98@gmail.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
t7xx_port_enum_msg_handler() uses the modem-supplied port_count field as
a loop bound over port_msg->data[] without checking that the message buffer
contains sufficient data. A modem sending port_count=65535 in a 12-byte
buffer triggers a slab-out-of-bounds read of up to 262140 bytes.
Add a struct_size() check after extracting port_count and before the loop.
Pass msg_len to t7xx_port_enum_msg_handler() and use it to validate
the message size before accessing port_msg->data[].
Pass msg_len from both call sites: skb->len at the DPMAIF path after
skb_pull(), and the captured rt_feature->data_len at the handshake path.
> Fixes: 39d439047f1d ("net: wwan: t7xx: Add control DMA interface")
The Fixes: tag appears to reference the wrong commit. The vulnerable
function t7xx_port_enum_msg_handler() was added in commit da45d2566a1d
("net: wwan: t7xx: Add control port"), not in 39d439047f1d.
The bug being fixed is the missing validation of port_count against
message length before accessing port_msg->data[i] in the loop. This
validation was absent from the initial implementation in da45d2566a1d.
Should the Fixes: tag be:
Fixes: da45d2566a1d ("net: wwan: t7xx: Add control port")
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox