Why a byte buffer from CGO can reads correctly but write fault?

Viewed 151

I am using this library https://github.com/billziss-gh/cgofuse, and some interfaces need to be implemented,one of them looks like this:

func (self *Memfs) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
    defer trace(path, buff, ofst, fh)(&n)
    defer self.synchronize()()
    node := self.getNode(path, fh)
    if nil == node {
        return -fuse.ENOENT
    }
    endofst := ofst + int64(len(buff))
    if endofst > node.stat.Size {
        node.data = resize(node.data, endofst, true)
        node.stat.Size = endofst
    }

    fmt.Println("len(buff) = ", len(buff))  // (1)
    fmt.Println("cap(buff) = ", cap(buff))  // (2)
    fmt.Println("buff[0] = ", buff[0])      // (3)
    buff[0] = 1                             // (4)

    n = copy(node.data[ofst:endofst], buff)
    tmsp := fuse.Now()
    node.stat.Ctim = tmsp
    node.stat.Mtim = tmsp
    return
}

This is a file system, Write is called when writing to a file. I added (1), (2), (3), (4) in the above code, but it was wrong at (4). The error stack is as follows:

unexpected fault address 0x116e6c60390
fatal error: fault
[signal 0xc0000005 code=0x1 addr=0x116e6c60390 pc=0xca7dad]

goroutine 17 [running, locked to thread]:
runtime.throw(0xcf373f, 0x5)
    D:/Scoop/apps/go/current/src/runtime/panic.go:1117 +0x79 fp=0xc000033bc8 sp=0xc000033b98 pc=0xc18db9
runtime.sigpanic()
    D:/Scoop/apps/go/current/src/runtime/signal_windows.go:245 +0x2d6 fp=0xc000033c20 sp=0xc000033bc8 pc=0xc2b7d6
main.(*Memfs).Write(0xc00001e400, 0xc00000a2b0, 0x9, 0x116e6c60390, 0x40000, 0x40000000, 0x0, 0x2, 0x9)
    D:/code/go/LiangFs/tool/memfs.go:310 +0x4cd fp=0xc000033de0 sp=0xc000033c20 pc=0xca7dad
github.com/billziss-gh/cgofuse/fuse.hostWrite(0x116e00d1480, 0x116e6c60390, 0x40000, 0x0, 0x1518fff7c8, 0x0)
    D:/go/pkg/mod/github.com/billziss-gh/cgofuse@v1.5.0/fuse/host.go:255 +0x102 fp=0xc000033e60 sp=0xc000033de0 pc=0xc9c282
github.com/billziss-gh/cgofuse/fuse.go_hostWrite(...)
    D:/go/pkg/mod/github.com/billziss-gh/cgofuse@v1.5.0/fuse/host_cgo.go:911
_cgoexp_12ef5be0dd8c_go_hostWrite(0x1518fff710)
    _cgo_gotypes.go:738 +0x59 fp=0xc000033ea0 sp=0xc000033e60 pc=0xca2919
runtime.cgocallbackg1(0xca28c0, 0x1518fff710, 0x0)
    D:/Scoop/apps/go/current/src/runtime/cgocall.go:292 +0x19a fp=0xc000033f40 sp=0xc000033ea0 pc=0xbe4c5a
runtime.cgocallbackg(0xca28c0, 0x1518fff710, 0x0)
    D:/Scoop/apps/go/current/src/runtime/cgocall.go:228 +0xfc fp=0xc000033fb8 sp=0xc000033f40 pc=0xbe49bc
runtime.cgocallback(0x0, 0x0, 0x0)
    D:/Scoop/apps/go/current/src/runtime/asm_amd64.s:788 +0xc0 fp=0xc000033fe0 sp=0xc000033fb8 pc=0xc48bc0
runtime.goexit()
    D:/Scoop/apps/go/current/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc000033fe8 sp=0xc000033fe0 pc=0xc48ea1

goroutine 1 [syscall]:
github.com/billziss-gh/cgofuse/fuse._Cfunc_hostMount(0x3, 0xc00001e420, 0x116e0316540, 0x0)
    _cgo_gotypes.go:502 +0x4f
github.com/billziss-gh/cgofuse/fuse.c_hostMount.func1(0xc000000003, 0xc00001e420, 0x116e0316540, 0x116e0316540)
    D:/go/pkg/mod/github.com/billziss-gh/cgofuse@v1.5.0/fuse/host_cgo.go:820 +0x8c
github.com/billziss-gh/cgofuse/fuse.c_hostMount(0xc000000003, 0xc00001e420, 0x116e0316540, 0xb)
    D:/go/pkg/mod/github.com/billziss-gh/cgofuse@v1.5.0/fuse/host_cgo.go:820 +0x45
github.com/billziss-gh/cgofuse/fuse.(*FileSystemHost).Mount(0xc00003e040, 0xcf4632, 0xb, 0xc000034210, 0x0, 0x0, 0x1ffffff00)
    D:/go/pkg/mod/github.com/billziss-gh/cgofuse@v1.5.0/fuse/host.go:704 +0x413
main.main()
    D:/code/go/LiangFs/tool/memfs.go:594 +0xce
len(buff) =  262144
cap(buff) =  1073741824
buff[0] =  97

the content of buff is all 97, becasue I copy the following file to this file system: enter image description here

The code comes from the example in the library https://github.com/billziss-gh/cgofuse/blob/master/examples/memfs/memfs.go , I just added the (1), (2), (3), (4) mentioned above.

My os is windows 10, go version is go1.16.7 windows/amd64.

Why does assigning a slice element make a mistake? Is it because the library uses CGO?

2 Answers

I am the author of both WinFsp and cgofuse and can explain what is happening here.

The buffer that you receive in Write should always be treated as a read-only buffer. Attempting to write into this buffer may result in an access violation. This is by design.

When an application issues a WriteFile request, the WinFsp kernel mode driver has to somehow transfer the data from the application to the user mode file system. The driver has a few different strategies for doing so and under certain circumstances it will choose a zero-copy technique: it will map the application buffer directly into the address space of the user mode file system, thus avoiding an expensive memory copy.

In the case of Write this zero-copy mapping will always be read-only in order to avoid the case where a user mode accidentally or maliciously writes into an application's WriteFile buffer. (Also note that when the WinFsp driver sets up this mapping, it does so in a way that ensures that there is no accidental data leakage from the application into the user mode file system either.)

Since nothing is printed after buf[0] = 1 // (4), we don't know wether the panic comes from this line of code or a line afterwards ; you have to debug further to spot where the bug lies.

Actually : the stack trace you pasted mentions LiangFs/tool/memfs.go:310.
If this line number refers to the file you linked to, this line is the return instruction in your .Write() function.

The error must be triggered by one of the two defer calls (which get executed when you return from your function).

Related