Passing -1 as File Descriptor to Mmap

Using mmap over a file

A few issues:

  1. Avoid mixing high-level I/O (fopen(), fseek()) and some low-level operation like mmap(). Although you can get the low-level file descriptor using fileno(), that is like taking the longest route to get to the same place. Also, just using mmap() breaks compatibility beyond BSD and POSIX, so you get nothing by using standard C I/O functions. Just use open() and lseek() directly.
  2. It doesn't make sense to use stream formatted I/O (fprintf()) on the same file you are memory-mapping. When you memory-map a file, you are implicitly telling the system you are going to use it as random-access (direct indexing) data. fprintf() is for stream output, you usually use it for sequential access. In fact, although possible, it is unusual to see fprintf() and fseek() in the same descriptor (this is not even portable, but due to the previous item, I'm not considering portability).
  3. The protection must match the open file protection. Since you are passing "w" to fopen(), and PROT_READ | PROT_WRITE | PROT_EXEC to mmap(), you are violating this restriction. This also highlights why you shouldn't mix high-level I/O with memory-mapping: how do you guarantee that fopen(...,"w") will open the file with the correct flags? This is supposed to be "implementation-detail" for the C library. If you want to memory-map a file with read and write permissions, you should use the low-level open(theSharedFileName, O_RDWR) to open the file.
  4. Do not use PROT_WRITE and PROT_EXEC together. It is not portable and it is a security risk. Read about W^X and executable space protection.

Access mmap memory from another process

This answer considers you are trying to do this stuff on linux/unix.

how can process 2 access the memory mapped by process 1, without knowing anything about the opened file?

Process 1 passes to mmap[1] the flag MAP_SHARED.

You can:

  • A) Share the file descriptor using unix domain sockets[2].
  • B) Send
    the name of the file using the queues you mentioned at the end of
    your message.

Process 2 opens mmap with the flag MAP_SHARED. Modifications to the mmaped memory in Process 1 will be visible for Process 2. If you need fine control of when the changes from process 1 are shown to process 2 you should control it with msync[3]

how can I put the mmap content in a new file? I suppose I have to
ftruncate a new file, mmap this file and memcpy the content of process
1 memory map to process 2 memory map (then msync)

Why just don't write the mmaped memory as regular memory with write?

[1]http://man7.org/linux/man-pages/man2/mmap.2.html

[2]Portable way to pass file descriptor between different processes

[3]http://man7.org/linux/man-pages/man2/msync.2.html

How does 'passing file descriptors between processes' work?

Expanding on the whole answer:

1) When you pass a file descriptor over a UNIX domain socket, the message structure - which includes the SCM_RIGHTS token, instructs the kernel to duplicate the file descriptor on the fly as it passes through the socket, magically emerging on the other end with the FD value being the next available slot.

As you suggested in your question, it couldn't possibly work if the FD were passed literally ("hey other end, here is FD#3 to do stuff with, good luck with that") and that FD was already in use. The dup-like behavior is what makes the FD usable on the other end.

The only time the FDs will match on both ends is by coincidence.

2) I don't know how the kernel actually handles ref counts, but I'm confident that it's treated as a dup operation, so passing from one end to another means that two processes now have the file open, and this isn't any kind of special case.

When one end closes the file, refcounts are handled as in any other kind of dup situation.

Process A creates a file through mmap and returns fd. How does process B read the mmap created by process A?

The Rust equivalent of using C to transmit an fd via sendmsg already exists in the passfd crate. Once the file descriptor is passed, you can make a memory map from it in the normal way.

How to pass mmap to execve function in C?

You cannot pass mmap to an execve-ed executable.

When execve is successful, the entire virtual address space is scraped and reinitialized for the executed ELF binary. That is one of the main roles of execve(2). At startup, a program should expect a well defined memory state.

Maybe you might adopt a convention, like using some named file, and pass the name of that file as some program argument. Or passing a file descriptor, etc.

(IIRC, someone proposed many years ago a kernel patch to add a flag to mmap(2) for memory mappings persistent accross execve; that patch was rejected, with good reasons; I completely forgot the details and could be very wrong)

Perhaps you want shm_overview(7). Or just adopt a convention with some file name and use mmap with MAP_SHARED.

Be aware that even if you share (virtual) memory, you need some means of synchronization. Read sem_overview(7), futex(7), ....

Read also ALP and Operating Systems: Three Easy Pieces (both freely downloadable). See also syscalls(2) (there are several ways to do inter-process communication).

Bad file descriptor after read syscall into mmap allocated buffer in nasm on x86_64

You never call the allocate_buffer instructions and you use bad level of indirection in read_chunk and print_chunk. With this patch applied, your code works:

diff --git a/main.asm b/main.asm
index c9c98e4..8c44223 100644
--- a/main.asm
+++ b/main.asm
@@ -51,7 +51,6 @@ open_file:
cmp rax, 0
jl err_open_failed
mov [fd], rax
- jmp read_chunk

allocate_buffer:
mov rax, SYS_MMAP
@@ -67,7 +66,7 @@ allocate_buffer:
read_chunk:
mov rax, SYS_READ
mov rdi, [fd]
- mov rsi, buf
+ mov rsi, [buf]
mov rdx, CHUNK_SIZE
syscall

@@ -85,7 +84,7 @@ print_chunk:

mov rax, SYS_WRITE
mov rdi, STD_OUT
- mov rsi, buf
+ mov rsi, [buf]
mov rdx, r10
syscall

The jmp removal lets allocate_buffer instructions be executed, the other two changes make the syscalls use the address of allocated memory instead of address, where this address is stored.

What happened?

You did not allocate the memory for the buffer as your strace shows (no mmap syscall is performed). The buf is address of 8 bytes of memory (dq) in the data section, which is initialized to 0 when the program starts.

Your code reads into buf directly and thus overwrites contents of the data section, including fp which is right after buf. Second iteration of read_chunk finds a strange value in fp and crashes.

When you skipped the allocation and changed your code to read into [buf], as we discussed in the comments, you read into address 0, which crashed your program even earlier from now obvious reasons.



Related Topics



Leave a reply



Submit