nfsd: don't alloc under spinlock in rpc_parse_scope_id
authorJ. Bruce Fields <bfields@redhat.com>
Tue, 14 Sep 2021 16:30:32 +0000 (12:30 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 18 Nov 2021 18:16:58 +0000 (19:16 +0100)
[ Upstream commit 9b6e27d01adcec58e046c624874f8a124e8b07ec ]

Dan Carpenter says:

  The patch d20c11d86d8f: "nfsd: Protect session creation and client
  confirm using client_lock" from Jul 30, 2014, leads to the following
  Smatch static checker warning:

        net/sunrpc/addr.c:178 rpc_parse_scope_id()
        warn: sleeping in atomic context

Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Fixes: d20c11d86d8f ("nfsd: Protect session creation and client...")
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/sunrpc/addr.c

index 6e4dbd5..d435bff 100644 (file)
@@ -162,8 +162,10 @@ static int rpc_parse_scope_id(struct net *net, const char *buf,
                              const size_t buflen, const char *delim,
                              struct sockaddr_in6 *sin6)
 {
-       char *p;
+       char p[IPV6_SCOPE_ID_LEN + 1];
        size_t len;
+       u32 scope_id = 0;
+       struct net_device *dev;
 
        if ((buf + buflen) == delim)
                return 1;
@@ -175,29 +177,23 @@ static int rpc_parse_scope_id(struct net *net, const char *buf,
                return 0;
 
        len = (buf + buflen) - delim - 1;
-       p = kmemdup_nul(delim + 1, len, GFP_KERNEL);
-       if (p) {
-               u32 scope_id = 0;
-               struct net_device *dev;
-
-               dev = dev_get_by_name(net, p);
-               if (dev != NULL) {
-                       scope_id = dev->ifindex;
-                       dev_put(dev);
-               } else {
-                       if (kstrtou32(p, 10, &scope_id) != 0) {
-                               kfree(p);
-                               return 0;
-                       }
-               }
-
-               kfree(p);
-
-               sin6->sin6_scope_id = scope_id;
-               return 1;
+       if (len > IPV6_SCOPE_ID_LEN)
+               return 0;
+
+       memcpy(p, delim + 1, len);
+       p[len] = 0;
+
+       dev = dev_get_by_name(net, p);
+       if (dev != NULL) {
+               scope_id = dev->ifindex;
+               dev_put(dev);
+       } else {
+               if (kstrtou32(p, 10, &scope_id) != 0)
+                       return 0;
        }
 
-       return 0;
+       sin6->sin6_scope_id = scope_id;
+       return 1;
 }
 
 static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen,