Bug 263263 - [FUSEFS] fuse daemon can cause kernel page fault with huge size in write reply
Summary: [FUSEFS] fuse daemon can cause kernel page fault with huge size in write reply
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: Unspecified
Hardware: Any Any
: --- Affects Some People
Assignee: Alan Somers
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-04-13 17:56 UTC by Robert Morris
Modified: 2022-08-20 01:01 UTC (History)
3 users (show)

See Also:
asomers: mfc-stable13+
asomers: mfc-stable12-


Attachments
fuse daemon that replies to write with a huge size, causing a kernel page fault (13.34 KB, text/plain)
2022-04-13 17:56 UTC, Robert Morris
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Robert Morris 2022-04-13 17:56:23 UTC
Created attachment 233202 [details]
fuse daemon that replies to write with a huge size, causing a kernel page fault

In fuse_write_directbackend(), there's a check for the case in which
the daemon claims more bytes were written than the kernel asked for:

                diff = fwi->size - fwo->size;
                ...;
                if (diff < 0) {
                        fuse_warn(data, FSESS_WARN_WROTE_LONG,
                                "wrote more data than we provided it.");

But fwi->size and fwo->size are 32-bit unsigned, and diff is 32-bit
signed, so if fwi->size is small and fwo->size is huge, diff can end
up positive. In that case, a little farther on, this can execute:

                                void *src = (char*)fwi_data + fwo->size;
                                memmove(fwi_data, src, diff);

which, for a huge fwo->size, can cause a read through a wild pointer.

I've included a demo:

# uname -a
FreeBSD  14.0-CURRENT FreeBSD 14.0-CURRENT #194 main-n250915-a8123f770b1e-dirty: Tue Apr 12 15:44:48 EDT 2022     rtm@xxx:/usr/obj/usr/rtm/symbsd/src/riscv.riscv64/sys/RTM  riscv
# pkg install fusefs-libs
# cc -I/usr/local/include/fuse -o futo17a futo17a.c -L/usr/local/lib -lfuse
# ./futo17a
...
WARNING: FUSE protocol violation for server mounted at /mnt: short writes are only allowed with direct_io.  This warning will not be repeated.
...
panic: Fatal page fault at 0xffffffc000552bc4: 0xffffffd102104a0a
KDB: stack backtrace:
db_trace_self() at db_trace_self
db_trace_self_wrapper() at db_trace_self_wrapper+0x38
kdb_backtrace() at kdb_backtrace+0x2c
vpanic() at vpanic+0x16e
panic() at panic+0x2a
page_fault_handler() at page_fault_handler+0x1aa
do_trap_supervisor() at do_trap_supervisor+0x76
cpu_exception_handler_supervisor() at cpu_exception_handler_supervisor+0x70
--- exception 13, tval = 0xffffffd102104a0a
memcpy() at memcpy+0x48
fuse_write_directbackend() at fuse_write_directbackend+0x1f6
fuse_io_strategy() at fuse_io_strategy+0x1c4
fuse_vnop_strategy() at fuse_vnop_strategy+0x46
VOP_STRATEGY_APV() at VOP_STRATEGY_APV+0x32
VOP_STRATEGY() at VOP_STRATEGY+0x26
bufstrategy() at bufstrategy+0x2c
bstrategy() at bstrategy+0x14
bufwrite() at bufwrite+0x184
bwrite() at bwrite+0x16
fuse_write_biobackend() at fuse_write_biobackend+0x606
fuse_vnop_write() at fuse_vnop_write+0x16a
VOP_WRITE_APV() at VOP_WRITE_APV+0x66
VOP_WRITE() at VOP_WRITE+0x2e
vn_write() at vn_write+0x120
vn_io_fault() at vn_io_fault+0xd6
fo_write() at fo_write+0xa
dofilewrite() at dofilewrite+0x66
kern_writev() at kern_writev+0x40
sys_write() at sys_write+0x54
syscallenter() at syscallenter+0xf4
ecall_handler() at ecall_handler+0x18
do_trap_user() at do_trap_user+0xea
cpu_exception_handler_user() at cpu_exception_handler_user+0x72
--- exception 8, tval = 0
KDB: enter: panic
[ thread pid 88 tid 100063 ]
Stopped at      breakpoint+0xa: c.ldsp  s0,0(sp)
db>
Comment 1 commit-hook freebsd_committer freebsd_triage 2022-04-19 01:02:23 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=3a1b3c6a1e68063330e897a5a5c94518edae4a3b

commit 3a1b3c6a1e68063330e897a5a5c94518edae4a3b
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2022-04-18 23:03:53 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-04-19 00:59:10 +0000

    fusefs: correctly handle servers that report too much data written

    During a FUSE_WRITE, the kernel requests the server to write a certain
    amount of data, and the server responds with the amount that it actually
    did write.  It is obviously an error for the server to write more than
    it was provided, and we always treated it as such, but there were two
    problems:

    * If the server responded with a huge amount, greater than INT_MAX, it
      would trigger an integer overflow which would cause a panic.

    * When extending the file, we wrongly set the file's size before
      validing the amount written.

    PR:             263263
    Reported by:    Robert Morris <rtm@lcs.mit.edu>
    MFC after:      2 weeks
    Sponsored by:   Axcient
    Reviewed by:    emaste
    Differential Revision: https://reviews.freebsd.org/D34955

 sys/fs/fuse/fuse_io.c        | 18 ++++++++-----
 tests/sys/fs/fusefs/write.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 6 deletions(-)
Comment 2 commit-hook freebsd_committer freebsd_triage 2022-05-12 20:41:03 UTC
A commit in branch stable/13 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=04f7286f44c484b4288b3071139d451ae4a623d7

commit 04f7286f44c484b4288b3071139d451ae4a623d7
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2022-04-18 23:03:53 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-05-12 20:39:47 +0000

    fusefs: correctly handle servers that report too much data written

    During a FUSE_WRITE, the kernel requests the server to write a certain
    amount of data, and the server responds with the amount that it actually
    did write.  It is obviously an error for the server to write more than
    it was provided, and we always treated it as such, but there were two
    problems:

    * If the server responded with a huge amount, greater than INT_MAX, it
      would trigger an integer overflow which would cause a panic.

    * When extending the file, we wrongly set the file's size before
      validing the amount written.

    PR:             263263
    Reported by:    Robert Morris <rtm@lcs.mit.edu>
    Sponsored by:   Axcient
    Reviewed by:    emaste
    Differential Revision: https://reviews.freebsd.org/D34955

    (cherry picked from commit 3a1b3c6a1e68063330e897a5a5c94518edae4a3b)

 sys/fs/fuse/fuse_io.c        | 18 ++++++++-----
 tests/sys/fs/fusefs/write.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 6 deletions(-)
Comment 3 commit-hook freebsd_committer freebsd_triage 2022-08-20 01:01:07 UTC
A commit in branch stable/12 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=3a5ccf21b2aa27f51451b40be7388f671595d36a

commit 3a5ccf21b2aa27f51451b40be7388f671595d36a
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2022-04-18 23:03:53 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-08-20 00:55:25 +0000

    fusefs: correctly handle servers that report too much data written

    During a FUSE_WRITE, the kernel requests the server to write a certain
    amount of data, and the server responds with the amount that it actually
    did write.  It is obviously an error for the server to write more than
    it was provided, and we always treated it as such, but there were two
    problems:

    * If the server responded with a huge amount, greater than INT_MAX, it
      would trigger an integer overflow which would cause a panic.

    * When extending the file, we wrongly set the file's size before
      validing the amount written.

    PR:             263263
    Reported by:    Robert Morris <rtm@lcs.mit.edu>
    Sponsored by:   Axcient
    Reviewed by:    emaste
    Differential Revision: https://reviews.freebsd.org/D34955

    (cherry picked from commit 3a1b3c6a1e68063330e897a5a5c94518edae4a3b)

 sys/fs/fuse/fuse_io.c        | 18 ++++++++-----
 tests/sys/fs/fusefs/write.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 6 deletions(-)