emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.76k stars 3.3k forks source link

Protection flag of mmap doesn't work as expected #21707

Open hly2019 opened 6 months ago

hly2019 commented 6 months ago

Please include the following in your bug report:

Version of emscripten/emsdk: emcc version: 3.1.54

Failing command line in full: emcc test_mmap_prot.cpp -o main.js; node main.js

Full link command and output with -v appended:

Hi, I tried to use mmap in the program and tried to set the prot parameter as PROT_READ, expecting to set the memory as read-only. However, I found even though I set the parameter as PROT_READ only, the corresponding memory is still writable. Here is the code example:

// test_mmap_prot.cpp
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int len = 10 * sizeof(int);
    int offset = 0;
    int* ptr = (int*) mmap(nullptr, len, PROT_READ, MAP_ANONYMOUS, -1, offset);
    ptr[0] = 10;
    std::cout << "ptr[0] is: " << ptr[0] << std::endl;
    return 0;
}

If I compile it natively with gcc using g++ test_mmap_prot.cpp -o main; ./main, when runing the code I got the Segmentation fault (core dumped). It meets my expect because the program tries to write a read-only memory.

However, if I compile and run with emcc test_mmap_prot.cpp -o main.js; node main.js, I get the printed message ptr[0] is: 10, meaning that the read-only memory is written successfully. And this is not as expected.

The code above is just a simple example with anonymous mapping. In other specific usage cases, I tried to use mmap to map with a file descriptor and set the prot as PROT_READ, and the memory is also writable. It's like the following:

// emcc test_mmap_prot_2.cpp -o main.js; node main.js
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char* name = "/shared_file";
    int len = 10 * sizeof(int);
    int offset = 0;
    int fd = shm_open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 
    if (fd == -1) {
        exit(0);
    }
    ftruncate(fd, off_t(len));
    int* ptr = (int*) mmap(nullptr, len, PROT_READ, MAP_SHARED, fd, offset);

    ptr[0] = 10;

    std::cout << "ptr[0] is: " << ptr[0] << std::endl;
    shm_unlink(name);
    return 0;
}

And by compiling and running it with emcc test_mmap_prot_2.cpp -o main.js; node main.js, I found the read-only memory can still be written successfully (of course if I compiled using gcc and run natively, it still got segmentation fault as expected). So I guess it is a general issue of mmap.

Thank you!

sbc100 commented 6 months ago

Sadly Wasm doesn't have any concept of real memory maps, or any kind of memory protection yet.

The mmap support in emscripten is a simple/fake wrapper around malloc.

You can follow this proposal if you would like to know if/when this might be available: https://github.com/WebAssembly/memory-control/blob/main/proposals/memory-control/Overview.md

hly2019 commented 5 months ago

Sadly Wasm doesn't have any concept of real memory maps, or any kind of memory protection yet.

The mmap support in emscripten is a simple/fake wrapper around malloc.

You can follow this proposal if you would like to know if/when this might be available: https://github.com/WebAssembly/memory-control/blob/main/proposals/memory-control/Overview.md

Thank you very much for sharing the proposal! It's of great help to us as a guidance.

By the way, I also have a related question on #21706. Could you please check that one as well? It's about MAP_SHARED flag of mmap and seems also not to work as expected. I'm not sure whether it's something that could be solved or if it's still a kind of limitation of Wasm memory map design. We'd greatly appreciate it if you could take a look. Thank you! @sbc100