All of lore.kernel.org
 help / color / mirror / Atom feed
* _nfs4_open_and_get_state() should check IS_ERR() from d_splice_alias()
@ 2022-01-03 19:02 rtm
  0 siblings, 0 replies; only message in thread
From: rtm @ 2022-01-03 19:02 UTC (permalink / raw)
  To: Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

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

_nfs4_open_and_get_state() says:

                if (!alias)
                        alias = d_splice_alias(igrab(state->inode), dentry);
                /* d_splice_alias() can't fail here - it's a non-directory */
                if (alias) {
                        dput(ctx->dentry);
                        ctx->dentry = dentry = alias;
                ...;
                nfs_set_verifier(dentry, dir_verifier);
  
d_splice_alias() can return an ERR_PTR(), causing nfs_set_verifier() to
page-fault on &dentry->d_lock; I've attached a demo.
  
# uname -a
Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #12 SMP Mon Jan 3 18:38:50 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
# cc nfs_18.c
# ./a.out
...
[   71.811841] VFS: Lookup of 'x' in nfs4 0:16 would have caused loop
[   71.825707] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000034
[   72.064187] status: 0000000200000121 badaddr: 0000000000000034 cause: 000000000000000d
[   72.079110] [<ffffffff8004dfbc>] do_raw_spin_lock+0xa/0xd8
[   72.090221] [<ffffffff8064b9f2>] _raw_spin_lock+0x1a/0x22
[   72.101284] [<ffffffff80201944>] nfs_set_verifier+0x20/0x6a
[   72.112387] [<ffffffff8022a8b2>] nfs4_do_open.constprop.0+0x30e/0x874
[   72.125597] [<ffffffff8022aeb0>] nfs4_atomic_open+0xc/0x1c
[   72.136676] [<ffffffff802036f8>] nfs_atomic_open+0x1a0/0x35e
[   72.150205] [<ffffffff80137a7c>] path_openat+0x59e/0xadc
[   72.161311] [<ffffffff801391c8>] do_filp_open+0x70/0xea
[   72.172464] [<ffffffff801286f4>] do_sys_openat2+0x86/0x11a
[   72.183523] [<ffffffff80128a6a>] sys_openat+0x40/0x80
[   72.194578] [<ffffffff80003030>] ret_from_syscall+0x0/0x2
[   72.213692] ---[ end trace 107b62634b79d2f7 ]---


[-- Attachment #2: nfs_18.c --]
[-- Type: application/octet-stream, Size: 31817 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 = 18;
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] = {
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x2ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0xc000000ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
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(1){
        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(0){
        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-03 19:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-03 19:02 _nfs4_open_and_get_state() should check IS_ERR() from d_splice_alias() 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.