]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tipc: fix duplicate publication key in tipc_service_insert_publ()
authorTung Nguyen <tung.quang.nguyen@est.tech>
Fri, 20 Feb 2026 05:05:41 +0000 (05:05 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 24 Feb 2026 01:40:52 +0000 (17:40 -0800)
TIPC uses named table to store TIPC services represented by type and
instance. Each time an application calls TIPC API bind() to bind a
type/instance to a socket, an entry is created and inserted into the
named table. It looks like this:

named table:
key1, entry1 (type, instance ...)
key2, entry2 (type, instance ...)

In the above table, each entry represents a route for sending data
from one socket to the other. For all publications originated from
the same node, the key is UNIQUE to identify each entry.
It is calculated by this formula:
key = socket portid + number of bindings + 1 (1)

where:
 - socket portid: unique and calculated by using linux kernel function
                  get_random_u32_below(). So, the value is randomized.
 - number of bindings: the number of times a type/instance pair is bound
                       to a socket. This number is linearly increased,
                       starting from 0.

While the socket portid is unique and randomized by linux kernel, the
linear increment of "number of bindings" in formula (1) makes "key" not
unique anymore. For example:
- Socket 1 is created with its associated port number 20062001. Type 1000,
instance 1 is bound to socket 1:
key1: 20062001 + 0 + 1 = 20062002

Then, bind() is called a second time on Socket 1 to by the same type 1000,
instance 1:
key2: 20062001 + 1 + 1 = 20062003

Named table:
key1 (20062002), entry1 (1000, 1 ...)
key2 (20062003), entry2 (1000, 1 ...)

- Socket 2 is created with its associated port number 20062002. Type 1000,
instance 1 is bound to socket 2:
key3: 20062002 + 0 + 1 = 20062003

TIPC looks up the named table and finds out that key2 with the same value
already exists and rejects the insertion into the named table.
This leads to failure of bind() call from application on Socket 2 with error
message EINVAL "Invalid argument".

This commit fixes this issue by adding more port id checking to make sure
that the key is unique to publications originated from the same port id
and node.

Fixes: 218527fe27ad ("tipc: replace name table service range array with rb tree")
Signed-off-by: Tung Nguyen <tung.quang.nguyen@est.tech>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260220050541.237962-1-tung.quang.nguyen@est.tech
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/tipc/name_table.c

index e74940eab3a47901d49b552767b16793c4459aa2..7f42fb6a8481fe3db7b53f2637c85ed33b6d8d14 100644 (file)
@@ -348,7 +348,8 @@ static bool tipc_service_insert_publ(struct net *net,
 
        /* Return if the publication already exists */
        list_for_each_entry(_p, &sr->all_publ, all_publ) {
-               if (_p->key == key && (!_p->sk.node || _p->sk.node == node)) {
+               if (_p->key == key && _p->sk.ref == p->sk.ref &&
+                   (!_p->sk.node || _p->sk.node == node)) {
                        pr_debug("Failed to bind duplicate %u,%u,%u/%u:%u/%u\n",
                                 p->sr.type, p->sr.lower, p->sr.upper,
                                 node, p->sk.ref, key);
@@ -388,7 +389,8 @@ static struct publication *tipc_service_remove_publ(struct service_range *r,
        u32 node = sk->node;
 
        list_for_each_entry(p, &r->all_publ, all_publ) {
-               if (p->key != key || (node && node != p->sk.node))
+               if (p->key != key || p->sk.ref != sk->ref ||
+                   (node && node != p->sk.node))
                        continue;
                list_del(&p->all_publ);
                list_del(&p->local_publ);