Package: rpcbind
Version: 1.2.7-1
Severity: important
Tags: patch upstream

Dear Maintainer,
        please consider the behaviour described later as a serious defect in
rpcbind code: it renders standards-compliant NFS v3 client with locking
support enabled unusable.
        Linux NLM code is unaffected: it drops IP address information
received from rpcbind completely and uses only remote port number, but
FreeBSD uses both and stucks with 'remote lockd is not responding'.

Steps to reproduce:
-------------------

1) Install Debian host system with several interfaces configured.
2) Install, e.g, FreeBSD virtual machine attached to the network on
Debian host configured on interface which is NOT THE FIRST ONE in, e.g,
'ifconfig -a' output.
3) Share a filesystem on the host and mount it inside the VM.
4) Try to edit any file using system '/usr/bin/vi'.

Steps to analyse:
-----------------

Try to run the following script inside VM:

===
#!/usr/bin/env python3
import os, socket, struct, sys

def xs(s: str) -> bytes:
    b = s.encode()
    return struct.pack('>I', len(b)) + b + b'\0' * (-len(b) % 4)

def getaddr(srv: str, hint: str) -> str:
    xid = int.from_bytes(os.urandom(4), 'big')
    req = (struct.pack('>10I', xid, 0, 2, 100000, 4, 3, 0, 0, 0, 0)
           + struct.pack('>2I', 100021, 4) + xs('udp') + xs(hint) + xs(''))
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
        s.settimeout(3)
        s.sendto(req, (srv, 111))
        r = s.recv(65535)
    rxid, mtype, rstat, _, vlen = struct.unpack_from('>5I', r)
    if (rxid, mtype, rstat) != (xid, 1, 0):
        raise RuntimeError('bad reply header')
    off = 20 + vlen + (-vlen % 4)
    astat, slen = struct.unpack_from('>2I', r, off)
    if astat != 0:
        raise RuntimeError(f'accept_stat {astat}')
    return r[off + 8:off + 8 + slen].decode()

srv, me = sys.argv[1], sys.argv[2]
for h in ('', f'{me}.0.0', f'{srv}.0.111'):
    print(f'r_addr={h!r:24} -> {getaddr(srv, h)}')
===

... with the following arguments:

root@host:~ # python3 getaddr.py [IP address of the host] [IP address of the VM]

Suppose the host/VM have the following IP configuration:

hostif0=172.19.1.0/16
hostif1=172.29.1.0/16
vmif0=172.29.3.2/16

Script will output something similar to:

r_addr=''                       -> 172.19.1.0.209.217
r_addr='172.29.3.2.0.0'         -> 172.19.1.0.209.217
r_addr='172.29.1.0.0.111'       -> 172.29.1.0.209.217

(209 and 217 will be substituted by your values)

This is obviously wrong. Correct output should be:

r_addr=''                       -> 172.29.1.0.209.217
r_addr='172.29.3.2.0.0'         -> 172.29.1.0.209.217
r_addr='172.29.1.0.0.111'       -> 172.29.1.0.209.217

How to fix:
-----------

Apply the following patch:

===
--- a/src/util.c
+++ b/src/util.c
@@ -103,7 +103,7 @@
 addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
          char *netid)
 {
-       struct ifaddrs *ifap, *ifp = NULL, *bestif, *exactif;
+       struct ifaddrs *ifap, *ifp = NULL, *bestif, *netif, *exactif;
        struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf;
        struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa;
        struct sockaddr_storage ss;
@@ -157,8 +157,9 @@
         * network portion of its address is equal to that of the client.
         * If so, we have found the interface that we want to use.
         */
-       bestif = NULL;  /* first interface UP with same network & family */
+       bestif = NULL;  /* first usable interface UP with same family */
+       netif = NULL;   /* first interface UP with same network & family */
        exactif = NULL; /* the interface requested by the client */
        u_int8_t maskAllAddrBits[16] = {0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 
0xff, 0xff,
                                        0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 
0xff, 0xff}; /* 16 bytes for IPv6 */
        for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
@@ -178,8 +179,8 @@
                        if (!bitmaskcmp(&SA2SINADDR(ifsa),
                            &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa),
                            sizeof(struct in_addr))) {
-                               if(!bestif) /* for compatibility with previous 
code */
-                                   bestif = ifap;
+                               if (netif == NULL) /* first network match wins 
*/
+                                       netif = ifap;
                                /* Is this an exact match? */
                                if (!bitmaskcmp(&SA2SINADDR(ifsa),
                                    &SA2SINADDR(hint_sa), maskAllAddrBits,
@@ -208,8 +209,8 @@
                        } else if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
                            &SA2SIN6ADDR(hint_sa), &SA2SIN6ADDR(ifmasksa),
                            sizeof(struct in6_addr))) {
-                               if(!bestif) /* for compatibility with previous 
code */
-                                   bestif = ifap;
+                               if (netif == NULL) /* first network match wins 
*/
+                                       netif = ifap;
                                /* Is this an exact match? */
                                if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
                                    &SA2SIN6ADDR(hint_sa), maskAllAddrBits,
@@ -235,6 +236,8 @@
                        bestif = ifap;
        }
 
+       if (netif != NULL)
+               bestif = netif;
        if (bestif == NULL)
                goto freeit;
===

History information:
--------------------
The defect was from 2018 code change (matching changes).

-- System Information:
Debian Release: 13.7
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: arm64 (aarch64)

Kernel: Linux 6.18.39+rpt-rpi-v8 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_CRAP, TAINT_OOT_MODULE
Locale: LANG=C.UTF-8, LC_CTYPE=C.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages rpcbind depends on:
ii  adduser              3.152
ii  init-system-helpers  1.69~deb13u1
ii  libc6                2.41-12+rpt1+deb13u4
ii  libsystemd0          257.13-1~deb13u1
ii  libtirpc3t64         1.3.6+ds-1
ii  libwrap0             7.6.q-36

rpcbind recommends no packages.

rpcbind suggests no packages.

-- no debconf information

-- 
    Yours
            Andrei Kolchugin.

Reply via email to