Skip to content

Commit 3f1f755

Browse files
manizadaPaolo Abeni
authored andcommitted
net: openvswitch: reject oversized nested action attrs
Open vSwitch stores generated flow actions as nlattrs, whose nla_len field is u16. Commit a1e64ad ("net: openvswitch: remove misbehaving actions length check") allowed the total sw_flow_actions stream to grow beyond 64 KiB, which is valid, but also removed the last guard preventing a generated nested action attribute from exceeding U16_MAX. An oversized generated container can thus be closed with a truncated nla_len. A later dump or teardown then walks a structurally different stream than the one that was validated. In particular, an oversized nested CLONE/CT action may cause subsequent bytes in the generated stream to be interpreted as independent actions. Keep the larger total-action-stream behavior, but make nested action close reject generated containers that do not fit in nla_len, and return the error through all callers. For recursive SAMPLE, CLONE, DEC_TTL, and CHECK_PKT_LEN builders, trim resource-owning action-list tails in reverse construction order before discarding failed wrappers, so resources copied into the rejected tails are released before the wrappers are removed. Most failed outer wrappers are discarded by truncating actions_len after child resources have been released. CHECK_PKT_LEN also trims its parent after branch resources are gone. SET/TUNNEL close failures unwind their known tun_dst ownership directly, and SET_TO_MASKED has no external ownership and truncates on close failure. Fixes: a1e64ad ("net: openvswitch: remove misbehaving actions length check") Cc: stable@vger.kernel.org Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Signed-off-by: Asim Viladi Oglu Manizada <manizada@pm.me> Reviewed-by: Eelco Chaudron <echaudro@redhat.com> Reviewed-by: Aaron Conole <aconole@redhat.com> Reviewed-by: Ilya Maximets <i.maximets@ovn.org> Link: https://patch.msgid.link/20260706094336.38639-1-manizada@pm.me Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 7410d11 commit 3f1f755

1 file changed

Lines changed: 157 additions & 44 deletions

File tree

net/openvswitch/flow_netlink.c

Lines changed: 157 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,13 +2496,56 @@ static inline int add_nested_action_start(struct sw_flow_actions **sfa,
24962496
return used;
24972497
}
24982498

2499-
static inline void add_nested_action_end(struct sw_flow_actions *sfa,
2500-
int st_offset)
2499+
static inline int add_nested_action_end(struct sw_flow_actions *sfa,
2500+
int st_offset)
25012501
{
2502-
struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions +
2503-
st_offset);
2502+
struct nlattr *a;
2503+
u32 attr_len;
2504+
2505+
if (WARN_ON_ONCE(st_offset < 0 ||
2506+
(u32)st_offset > sfa->actions_len))
2507+
return -EINVAL;
2508+
2509+
attr_len = sfa->actions_len - (u32)st_offset;
2510+
if (WARN_ON_ONCE(attr_len < NLA_HDRLEN))
2511+
return -EINVAL;
25042512

2505-
a->nla_len = sfa->actions_len - st_offset;
2513+
if (attr_len > U16_MAX)
2514+
return -EMSGSIZE;
2515+
2516+
a = (struct nlattr *)((u8 *)sfa->actions + st_offset);
2517+
a->nla_len = attr_len;
2518+
return 0;
2519+
}
2520+
2521+
/* Free the generated action-list tail at @start and truncate it.
2522+
* If @nested, @start points to its containing nlattr header.
2523+
*/
2524+
static void ovs_nla_trim(struct sw_flow_actions *sfa, int start, bool nested)
2525+
{
2526+
const struct nlattr *actions;
2527+
u32 len;
2528+
2529+
if (start < 0)
2530+
return;
2531+
2532+
if (WARN_ON_ONCE((u32)start > sfa->actions_len))
2533+
return;
2534+
2535+
actions = (const struct nlattr *)((u8 *)sfa->actions + start);
2536+
len = sfa->actions_len - (u32)start;
2537+
2538+
if (nested) {
2539+
if (len < NLA_HDRLEN)
2540+
goto out;
2541+
2542+
actions = (const struct nlattr *)((u8 *)actions + NLA_HDRLEN);
2543+
len -= NLA_HDRLEN;
2544+
}
2545+
2546+
ovs_nla_free_nested_actions(actions, len);
2547+
out:
2548+
sfa->actions_len = start;
25062549
}
25072550

25082551
static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
@@ -2522,6 +2565,7 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
25222565
const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
25232566
const struct nlattr *probability, *actions;
25242567
const struct nlattr *a;
2568+
int actions_start;
25252569
int rem, start, err;
25262570
struct sample_arg arg;
25272571

@@ -2565,18 +2609,27 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
25652609
err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg),
25662610
log);
25672611
if (err)
2568-
return err;
2612+
goto err;
25692613

2614+
actions_start = (*sfa)->actions_len;
25702615
err = __ovs_nla_copy_actions(net, actions, key, sfa,
25712616
eth_type, vlan_tci, mpls_label_count, log,
25722617
depth + 1);
25732618

25742619
if (err)
2575-
return err;
2620+
goto err_free;
25762621

2577-
add_nested_action_end(*sfa, start);
2622+
err = add_nested_action_end(*sfa, start);
2623+
if (err)
2624+
goto err_free;
25782625

25792626
return 0;
2627+
2628+
err_free:
2629+
ovs_nla_trim(*sfa, actions_start, false);
2630+
err:
2631+
(*sfa)->actions_len = start;
2632+
return err;
25802633
}
25812634

25822635
static int validate_and_copy_dec_ttl(struct net *net,
@@ -2624,18 +2677,31 @@ static int validate_and_copy_dec_ttl(struct net *net,
26242677
return start;
26252678

26262679
action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log);
2627-
if (action_start < 0)
2628-
return action_start;
2680+
if (action_start < 0) {
2681+
err = action_start;
2682+
goto err;
2683+
}
26292684

26302685
err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type,
26312686
vlan_tci, mpls_label_count, log,
26322687
depth + 1);
26332688
if (err)
2634-
return err;
2689+
goto err_free;
2690+
2691+
err = add_nested_action_end(*sfa, action_start);
2692+
if (err)
2693+
goto err_free;
26352694

2636-
add_nested_action_end(*sfa, action_start);
2637-
add_nested_action_end(*sfa, start);
2695+
err = add_nested_action_end(*sfa, start);
2696+
if (err)
2697+
goto err_free;
26382698
return 0;
2699+
2700+
err_free:
2701+
ovs_nla_trim(*sfa, action_start, true);
2702+
err:
2703+
(*sfa)->actions_len = start;
2704+
return err;
26392705
}
26402706

26412707
static int validate_and_copy_clone(struct net *net,
@@ -2646,6 +2712,7 @@ static int validate_and_copy_clone(struct net *net,
26462712
u32 mpls_label_count, bool log, bool last,
26472713
u32 depth)
26482714
{
2715+
int actions_start;
26492716
int start, err;
26502717
u32 exec;
26512718

@@ -2661,17 +2728,26 @@ static int validate_and_copy_clone(struct net *net,
26612728
err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec,
26622729
sizeof(exec), log);
26632730
if (err)
2664-
return err;
2731+
goto err;
26652732

2733+
actions_start = (*sfa)->actions_len;
26662734
err = __ovs_nla_copy_actions(net, attr, key, sfa,
26672735
eth_type, vlan_tci, mpls_label_count, log,
26682736
depth + 1);
26692737
if (err)
2670-
return err;
2738+
goto err_free;
26712739

2672-
add_nested_action_end(*sfa, start);
2740+
err = add_nested_action_end(*sfa, start);
2741+
if (err)
2742+
goto err_free;
26732743

26742744
return 0;
2745+
2746+
err_free:
2747+
ovs_nla_trim(*sfa, actions_start, false);
2748+
err:
2749+
(*sfa)->actions_len = start;
2750+
return err;
26752751
}
26762752

26772753
void ovs_match_init(struct sw_flow_match *match,
@@ -2763,20 +2839,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
27632839
tun_dst = metadata_dst_alloc(key.tun_opts_len, METADATA_IP_TUNNEL,
27642840
GFP_KERNEL);
27652841

2766-
if (!tun_dst)
2767-
return -ENOMEM;
2842+
if (!tun_dst) {
2843+
err = -ENOMEM;
2844+
goto err;
2845+
}
27682846

27692847
err = dst_cache_init(&tun_dst->u.tun_info.dst_cache, GFP_KERNEL);
2770-
if (err) {
2771-
dst_release((struct dst_entry *)tun_dst);
2772-
return err;
2773-
}
2848+
if (err)
2849+
goto err_free_tun_dst;
27742850

27752851
a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
27762852
sizeof(*ovs_tun), log);
27772853
if (IS_ERR(a)) {
2778-
dst_release((struct dst_entry *)tun_dst);
2779-
return PTR_ERR(a);
2854+
err = PTR_ERR(a);
2855+
goto err_free_tun_dst;
27802856
}
27812857

27822858
ovs_tun = nla_data(a);
@@ -2797,8 +2873,16 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
27972873
ip_tunnel_info_opts_set(tun_info,
27982874
TUN_METADATA_OPTS(&key, key.tun_opts_len),
27992875
key.tun_opts_len, dst_opt_type);
2800-
add_nested_action_end(*sfa, start);
2876+
err = add_nested_action_end(*sfa, start);
2877+
if (WARN_ON_ONCE(err))
2878+
goto err_free_tun_dst;
2879+
2880+
return 0;
28012881

2882+
err_free_tun_dst:
2883+
dst_release((struct dst_entry *)tun_dst);
2884+
err:
2885+
(*sfa)->actions_len = start;
28022886
return err;
28032887
}
28042888

@@ -2971,7 +3055,7 @@ static int validate_set(const struct nlattr *a,
29713055

29723056
/* Convert non-masked non-tunnel set actions to masked set actions. */
29733057
if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) {
2974-
int start, len = key_len * 2;
3058+
int err, start, len = key_len * 2;
29753059
struct nlattr *at;
29763060

29773061
*skip_copy = true;
@@ -2983,8 +3067,11 @@ static int validate_set(const struct nlattr *a,
29833067
return start;
29843068

29853069
at = __add_action(sfa, key_type, NULL, len, log);
2986-
if (IS_ERR(at))
2987-
return PTR_ERR(at);
3070+
if (IS_ERR(at)) {
3071+
err = PTR_ERR(at);
3072+
(*sfa)->actions_len = start;
3073+
return err;
3074+
}
29883075

29893076
memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */
29903077
memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */
@@ -2994,7 +3081,11 @@ static int validate_set(const struct nlattr *a,
29943081

29953082
mask->ipv6_label &= htonl(0x000FFFFF);
29963083
}
2997-
add_nested_action_end(*sfa, start);
3084+
err = add_nested_action_end(*sfa, start);
3085+
if (WARN_ON_ONCE(err)) {
3086+
(*sfa)->actions_len = start;
3087+
return err;
3088+
}
29983089
}
29993090

30003091
return 0;
@@ -3040,7 +3131,8 @@ static int validate_and_copy_check_pkt_len(struct net *net,
30403131
const struct nlattr *acts_if_greater, *acts_if_lesser_eq;
30413132
struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1];
30423133
struct check_pkt_len_arg arg;
3043-
int nested_acts_start;
3134+
int greater_acts_start = -1;
3135+
int lesser_acts_start = -1;
30443136
int start, err;
30453137

30463138
err = nla_parse_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX,
@@ -3075,37 +3167,58 @@ static int validate_and_copy_check_pkt_len(struct net *net,
30753167
err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg,
30763168
sizeof(arg), log);
30773169
if (err)
3078-
return err;
3170+
goto err_free;
30793171

3080-
nested_acts_start = add_nested_action_start(sfa,
3081-
OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log);
3082-
if (nested_acts_start < 0)
3083-
return nested_acts_start;
3172+
lesser_acts_start =
3173+
add_nested_action_start(sfa,
3174+
OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL,
3175+
log);
3176+
if (lesser_acts_start < 0) {
3177+
err = lesser_acts_start;
3178+
goto err_free;
3179+
}
30843180

30853181
err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa,
30863182
eth_type, vlan_tci, mpls_label_count, log,
30873183
depth + 1);
30883184

30893185
if (err)
3090-
return err;
3186+
goto err_free;
30913187

3092-
add_nested_action_end(*sfa, nested_acts_start);
3188+
err = add_nested_action_end(*sfa, lesser_acts_start);
3189+
if (err)
3190+
goto err_free;
30933191

3094-
nested_acts_start = add_nested_action_start(sfa,
3095-
OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log);
3096-
if (nested_acts_start < 0)
3097-
return nested_acts_start;
3192+
greater_acts_start =
3193+
add_nested_action_start(sfa,
3194+
OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER,
3195+
log);
3196+
if (greater_acts_start < 0) {
3197+
err = greater_acts_start;
3198+
goto err_free;
3199+
}
30983200

30993201
err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa,
31003202
eth_type, vlan_tci, mpls_label_count, log,
31013203
depth + 1);
31023204

31033205
if (err)
3104-
return err;
3206+
goto err_free;
3207+
3208+
err = add_nested_action_end(*sfa, greater_acts_start);
3209+
if (err)
3210+
goto err_free;
31053211

3106-
add_nested_action_end(*sfa, nested_acts_start);
3107-
add_nested_action_end(*sfa, start);
3212+
err = add_nested_action_end(*sfa, start);
3213+
if (err)
3214+
goto err_free;
31083215
return 0;
3216+
3217+
err_free:
3218+
ovs_nla_trim(*sfa, greater_acts_start, true);
3219+
ovs_nla_trim(*sfa, lesser_acts_start, true);
3220+
ovs_nla_trim(*sfa, start, false);
3221+
return err;
31093222
}
31103223

31113224
static int validate_psample(const struct nlattr *attr)

0 commit comments

Comments
 (0)