Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mpls: add seqcount to protect the platform_label{,s} pair

The RCU-protected codepaths (mpls_forward, mpls_dump_routes) can have
an inconsistent view of platform_labels vs platform_label in case of a
concurrent resize (resize_platform_label_table, under
platform_mutex). This can lead to OOB accesses.

This patch adds a seqcount, so that we get a consistent snapshot.

Note that mpls_label_ok is also susceptible to this, so the check
against RTA_DST in rtm_to_route_config, done outside platform_mutex,
is not sufficient. This value gets passed to mpls_label_ok once more
in both mpls_route_add and mpls_route_del, so there is no issue, but
that additional check must not be removed.

Reported-by: Yuan Tan <tanyuan98@outlook.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Fixes: 7720c01f3f590 ("mpls: Add a sysctl to control the size of the mpls label table")
Fixes: dde1b38e873c ("mpls: Convert mpls_dump_routes() to RCU.")
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/cd8fca15e3eb7e212b094064cd83652e20fd9d31.1774284088.git.sd@queasysnail.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Sabrina Dubroca and committed by
Jakub Kicinski
629ec78e 45dbf8fc

+26 -4
+1
include/net/netns/mpls.h
··· 17 17 size_t platform_labels; 18 18 struct mpls_route __rcu * __rcu *platform_label; 19 19 struct mutex platform_mutex; 20 + seqcount_mutex_t platform_label_seq; 20 21 21 22 struct ctl_table_header *ctl; 22 23 };
+25 -4
net/mpls/af_mpls.c
··· 83 83 return mpls_dereference(net, platform_label[index]); 84 84 } 85 85 86 + static struct mpls_route __rcu **mpls_platform_label_rcu(struct net *net, size_t *platform_labels) 87 + { 88 + struct mpls_route __rcu **platform_label; 89 + unsigned int sequence; 90 + 91 + do { 92 + sequence = read_seqcount_begin(&net->mpls.platform_label_seq); 93 + platform_label = rcu_dereference(net->mpls.platform_label); 94 + *platform_labels = net->mpls.platform_labels; 95 + } while (read_seqcount_retry(&net->mpls.platform_label_seq, sequence)); 96 + 97 + return platform_label; 98 + } 99 + 86 100 static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned int index) 87 101 { 88 102 struct mpls_route __rcu **platform_label; 103 + size_t platform_labels; 89 104 90 - if (index >= net->mpls.platform_labels) 105 + platform_label = mpls_platform_label_rcu(net, &platform_labels); 106 + 107 + if (index >= platform_labels) 91 108 return NULL; 92 109 93 - platform_label = rcu_dereference(net->mpls.platform_label); 94 110 return rcu_dereference(platform_label[index]); 95 111 } 96 112 ··· 2256 2240 if (index < MPLS_LABEL_FIRST_UNRESERVED) 2257 2241 index = MPLS_LABEL_FIRST_UNRESERVED; 2258 2242 2259 - platform_label = rcu_dereference(net->mpls.platform_label); 2260 - platform_labels = net->mpls.platform_labels; 2243 + platform_label = mpls_platform_label_rcu(net, &platform_labels); 2261 2244 2262 2245 if (filter.filter_set) 2263 2246 flags |= NLM_F_DUMP_FILTERED; ··· 2660 2645 } 2661 2646 2662 2647 /* Update the global pointers */ 2648 + local_bh_disable(); 2649 + write_seqcount_begin(&net->mpls.platform_label_seq); 2663 2650 net->mpls.platform_labels = limit; 2664 2651 rcu_assign_pointer(net->mpls.platform_label, labels); 2652 + write_seqcount_end(&net->mpls.platform_label_seq); 2653 + local_bh_enable(); 2665 2654 2666 2655 mutex_unlock(&net->mpls.platform_mutex); 2667 2656 ··· 2747 2728 int i; 2748 2729 2749 2730 mutex_init(&net->mpls.platform_mutex); 2731 + seqcount_mutex_init(&net->mpls.platform_label_seq, &net->mpls.platform_mutex); 2732 + 2750 2733 net->mpls.platform_labels = 0; 2751 2734 net->mpls.platform_label = NULL; 2752 2735 net->mpls.ip_ttl_propagate = 1;