Heap buffer overflow in read_worker() via malicious chunkserver
The MooseFS FUSE client (mfsmount) validates incoming chunkserver read data with (recleng - 20) + currpos > endpos, where all operands are uint32_t. A malicious chunkserver sends recleng = 0xFFFFFFFF, causing recleng - 20 to underflow to 0xFFFFFFEB. When added to a small currpos, the 32-bit sum wraps below endpos, and the bounds check passes. The client then calls readv() to write ~33 KB of attacker-controlled data past the end of a 4096-byte read buffer.
Vulnerable code
In 83142d5 mfsclient/readdata.c, read_worker at line 1649:
// readdata.c:1649-1657
if (datasrc[part].recleng<20) {
// ... "got too short data packet" — handles underflow for recleng < 20
status = EIO;
break;
} else if ((datasrc[part].recleng-20) + datasrc[part].currpos > datasrc[part].endpos) {
// BUG: recleng=0xFFFFFFFF passes the <20 check above,
// then recleng-20 = 0xFFFFFFEB, and 0xFFFFFFEB + currpos(256) wraps to 0xEB
// 0xEB < endpos(4096) → check passes
status = EIO;
break;
}
// ... later, at line 1718:
siov[1].iov_len = datasrc[part].recleng - 20; // 0xFFFFFFEB bytes requested via readv()With recleng = 0xFFFFFFFF:
recleng - 20 = 0xFFFFFFEB (underflow: 4,294,967,275)
0xFFFFFFEB + currpos(256) = 0x1000000EB → truncates to 0xD7 (uint32_t)
0xEB < endpos(4096) → check passesThe readv() receives attacker-controlled data from the fake chunkserver, writing ~33 KB past the end of a 4096-byte heap allocation.
Attack chain
This attack chain was demonstrated to the maintainers and requires registering as a fake chunkserver with the MooseFS master, which needs the AUTH_CODE from the master's configuration:
- Leak
AUTH_CODEviaANTOAN_GET_CONFIGon port 9419 - Register as a fake chunkserver with the master using
CSTOMA_REGISTER - Wait for chunk assignment
- When a client reads a file, serve a legitimate small
CSTOCL_READ_DATAto advancecurrpos, then a malicious response withrecleng = 0xFFFFFFFF - Client's
readv()writes ~33 KB of attacker-controlled data past the 4096-byte buffer
Reproduction output
=== Exploit Server Log ===
Waiting for chunk assignments and client connections
chunk 0x0000000000000001 v1 created
WRITE chunk=0x0000000000000001
READ #1: chunk=0x0000000000000001 off=0 sz=4096
sent 256B legit + overflow (36864B) + FIN
33024B past 4096B allocation
=== Readv Interceptor ===
readv iov[1]: base=0xffff98032380 len=0xffffffeb (underflow from recleng=0xFFFFFFFF)The readv interceptor confirms iov_len = 0xffffffeb (the underflowed recleng - 20), targeting a 4096-byte buffer at 0xffff98032380. The fake chunkserver sends 36864 bytes, of which 33024 overflow past the allocation boundary. Since readv() is a kernel syscall that writes directly to userspace memory, the overflow was captured via LD_PRELOAD interceptor rather than ASan instrumentation.
Patch
Fixed in: 1274d1f
if (datasrc[part].recleng<20) {
mfs_log(MFSLOG_SYSLOG,MFSLOG_WARNING,
"readworker: got too short data packet from chunkserver (leng:%"PRIu32")",
datasrc[part].recleng);
status = EIO;
resetpos = 1;
break;
+} else if (datasrc[part].recleng>20+MFSCHUNKSIZE) {
+ mfs_log(MFSLOG_SYSLOG,MFSLOG_WARNING,
+ "readworker: got too long data packet from chunkserver (leng:%"PRIu32")",
+ datasrc[part].recleng);
+ status = EIO;
+ resetpos = 1;
+ break;
} else if ((datasrc[part].recleng-20) + datasrc[part].currpos > datasrc[part].endpos) {MFSCHUNKSIZE is 64 MB. No single read response should exceed 20 + MFSCHUNKSIZE bytes. This rejects both the underflow case (recleng < 20, already handled) and the overflow case (recleng near UINT32_MAX) before the underflow-prone subtraction.