mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
openvswitch: fix GSO userspace truncation underflow
[ Upstream commit4032f8ed10] OVS_ACTION_ATTR_TRUNC currently stores a delta from the original skb length in OVS_CB(skb)->cutlen. When a later userspace action segments a GSO skb, queue_gso_packets() reuses that delta for each smaller segment. A segment can then reach queue_userspace_packet() with cutlen greater than skb->len, underflowing the length passed to skb_zerocopy(). Store the maximum preserved length instead and bound each consumer against the current skb length. Use U32_MAX as the no-truncation sentinel so the value remains valid if skb geometry changes before a consumer handles it. Fixes:f2a4d086ed("openvswitch: Add packet truncation support.") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng <kylebot@openai.com> Reviewed-by: Ilya Maximets <i.maximets@ovn.org> Reviewed-by: Aaron Conole <aconole@redhat.com> Link: https://patch.msgid.link/20260707221635.27489-1-kylebot@openai.com Signed-off-by: Paolo Abeni <pabeni@redhat.com> [6.6.y and older don't have OVS_ACTION_ATTR_PSAMPLE] Signed-off-by: Ilya Maximets <i.maximets@ovn.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
b7cb5bf085
commit
a16eaaf7c0
@@ -861,12 +861,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
|
||||
u16 mru = OVS_CB(skb)->mru;
|
||||
u32 cutlen = OVS_CB(skb)->cutlen;
|
||||
|
||||
if (unlikely(cutlen > 0)) {
|
||||
if (skb->len - cutlen > ovs_mac_header_len(key))
|
||||
pskb_trim(skb, skb->len - cutlen);
|
||||
else
|
||||
pskb_trim(skb, ovs_mac_header_len(key));
|
||||
}
|
||||
if (unlikely(cutlen < skb->len))
|
||||
pskb_trim(skb, max(cutlen, ovs_mac_header_len(key)));
|
||||
|
||||
if (likely(!mru ||
|
||||
(skb->len <= mru + vport->dev->hard_header_len))) {
|
||||
@@ -1258,22 +1254,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
||||
clone = skb_clone(skb, GFP_ATOMIC);
|
||||
if (clone)
|
||||
do_output(dp, clone, port, key);
|
||||
OVS_CB(skb)->cutlen = 0;
|
||||
OVS_CB(skb)->cutlen = U32_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
case OVS_ACTION_ATTR_TRUNC: {
|
||||
struct ovs_action_trunc *trunc = nla_data(a);
|
||||
|
||||
if (skb->len > trunc->max_len)
|
||||
OVS_CB(skb)->cutlen = skb->len - trunc->max_len;
|
||||
OVS_CB(skb)->cutlen = trunc->max_len;
|
||||
break;
|
||||
}
|
||||
|
||||
case OVS_ACTION_ATTR_USERSPACE:
|
||||
output_userspace(dp, skb, key, a, attr,
|
||||
len, OVS_CB(skb)->cutlen);
|
||||
OVS_CB(skb)->cutlen = 0;
|
||||
OVS_CB(skb)->cutlen = U32_MAX;
|
||||
if (nla_is_last(a, rem)) {
|
||||
consume_skb(skb);
|
||||
return 0;
|
||||
|
||||
+14
-11
@@ -273,7 +273,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
|
||||
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
|
||||
|
||||
upcall.mru = OVS_CB(skb)->mru;
|
||||
error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
|
||||
error = ovs_dp_upcall(dp, skb, key, &upcall, U32_MAX);
|
||||
switch (error) {
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
@@ -438,7 +438,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
||||
struct sk_buff *nskb = NULL;
|
||||
struct sk_buff *user_skb = NULL; /* to be queued to userspace */
|
||||
struct nlattr *nla;
|
||||
size_t len;
|
||||
size_t msg_size;
|
||||
size_t skb_len;
|
||||
unsigned int hlen;
|
||||
int err, dp_ifindex;
|
||||
u64 hash;
|
||||
@@ -459,7 +460,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
||||
skb = nskb;
|
||||
}
|
||||
|
||||
if (nla_attr_size(skb->len) > USHRT_MAX) {
|
||||
skb_len = min(skb->len, cutlen);
|
||||
if (nla_attr_size(skb_len) > USHRT_MAX) {
|
||||
err = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
@@ -474,13 +476,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
||||
* padding logic. Only perform zerocopy if padding is not required.
|
||||
*/
|
||||
if (dp->user_features & OVS_DP_F_UNALIGNED)
|
||||
hlen = skb_zerocopy_headlen(skb);
|
||||
hlen = min(skb_zerocopy_headlen(skb), cutlen);
|
||||
else
|
||||
hlen = skb->len;
|
||||
hlen = skb_len;
|
||||
|
||||
len = upcall_msg_size(upcall_info, hlen - cutlen,
|
||||
OVS_CB(skb)->acts_origlen);
|
||||
user_skb = genlmsg_new(len, GFP_ATOMIC);
|
||||
msg_size = upcall_msg_size(upcall_info, hlen,
|
||||
OVS_CB(skb)->acts_origlen);
|
||||
user_skb = genlmsg_new(msg_size, GFP_ATOMIC);
|
||||
if (!user_skb) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
@@ -541,7 +543,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
/* Add OVS_PACKET_ATTR_LEN when packet is truncated */
|
||||
if (cutlen > 0 &&
|
||||
if (skb_len < skb->len &&
|
||||
nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) {
|
||||
err = -ENOBUFS;
|
||||
goto out;
|
||||
@@ -566,9 +568,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
||||
err = -ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
nla->nla_len = nla_attr_size(skb->len - cutlen);
|
||||
nla->nla_len = nla_attr_size(skb_len);
|
||||
|
||||
err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen);
|
||||
err = skb_zerocopy(user_skb, skb, skb_len, hlen);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -625,6 +627,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
||||
packet->ignore_df = 1;
|
||||
}
|
||||
OVS_CB(packet)->mru = mru;
|
||||
OVS_CB(packet)->cutlen = U32_MAX;
|
||||
|
||||
if (a[OVS_PACKET_ATTR_HASH]) {
|
||||
hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]);
|
||||
|
||||
@@ -114,7 +114,7 @@ struct datapath {
|
||||
* @mru: The maximum received fragement size; 0 if the packet is not
|
||||
* fragmented.
|
||||
* @acts_origlen: The netlink size of the flow actions applied to this skb.
|
||||
* @cutlen: The number of bytes from the packet end to be removed.
|
||||
* @cutlen: The number of bytes in the packet to preserve on output.
|
||||
*/
|
||||
struct ovs_skb_cb {
|
||||
struct vport *input_vport;
|
||||
|
||||
@@ -503,7 +503,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
|
||||
|
||||
OVS_CB(skb)->input_vport = vport;
|
||||
OVS_CB(skb)->mru = 0;
|
||||
OVS_CB(skb)->cutlen = 0;
|
||||
OVS_CB(skb)->cutlen = U32_MAX;
|
||||
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
|
||||
u32 mark;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user