mmap() causing segmentation fault in C - c

I'm pretty sure my mistake is very evident, but I just can't seem to find where the problem is.
I'm learning how to use mmap() in C, everything looks correct to me, but I get a segmentation fault.
Here is my code:
int n=50;
char * tab = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, -1, 0);
for(int i=0; i<n; i++)
{
tab[i] = 1;
}
Using valgrind, I get an error saying "Invalid write of size 1" at the line where I do tab[i]=1, (I have tried replacing 1 by '1' thinking that maybe a char has a smaller size than an int, but still get the same error), followed by "Address 0xfffff..ff is not stack'd, malloc'd, or (recently) free'd".
I have no idea where my mistake is. Can somebody help me find it?

From man 2 mmap:
The contents of a file mapping (as opposed to an anonymous mapping;
see MAP_ANONYMOUS below), are initialized using length bytes starting
at offset offset in the file (or other object) referred to by the
file descriptor fd.
I suppose that you are trying to create an anonymous mapping (i.e. not backed by a file). In such case, you need to add MAP_ANONYMOUS to the flags, otherwise the system will try to read from the specified fd, which is invalid (-1) and will fail.
The correct code is:
char *tab = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (tab == MAP_FAILED) {
perror("mmap");
exit(1);
}
For the future, note that you can easily detect the error like I did above with a simple call to perror() in case the returned value indicates failure. In your case it should have printed the following:
mmap: Bad file descriptor
Checking the manual again you can see in the "ERRORS" section:
EBADF: fd is not a valid file descriptor (and MAP_ANONYMOUS was not set).

Related

Why am I getting bad file descriptor?

uint8_t *out = mmap(NULL, OUT_SIZE, MAP_ANON, PROT_READ | PROT_WRITE, -1, 0);
I am running this line on an ARM64 Macbook and I keep getting Bad file descriptor. I am not trying to map a file, so how do I fix this?
You appear to have reversed the prot argument with the flags argument. It is plausible that as a result, mmap() thinks you are trying to map a real file, and therefore expects the given file descriptor to be valid. Of course, -1 is not a valid file descriptor.
It looks like the call you wanted would be:
uint8_t *out = mmap(NULL, OUT_SIZE, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
Note: it occurs to me that detecting this kind of error is a good reason for the convention (or requirement, in some implementations) that the file descriptor be specified as -1 for an anonymous mapping.

MMAP segmentation fault

int fp, page;
char *data;
if(argc > 1){
printf("Read the docs");
exit(1);
}
fp = open("log.txt", O_RDONLY); //Opening file to read
page = getpagesize();
data = mmap(0, page, PROT_READ, 0,fp, 0);
initscr(); // Creating the ncurse screen
clear();
move(0, 0);
printw("%s", data);
endwin(); //Ends window
fclose(fp); //Closing file
return 0;
Here is my code I keep getting a segmentation fault for some reason.
All my header files have been included so that's not the problem (clearly, because its something to do with memory). Thanks in advance.
Edit: Got it - it wasn't being formatted as a string. and also had to use stat() to get the file info rather than getpagesize()
You can't fclose() a file descriptor you got from open(). You must use close(fp) instead. What you do is passing a small int that gets treated as a pointer. This causes a segmentation fault.
Note that your choice of identifier naming is unfortunate. Usually fp would be a pointer-to-FILE (FILE*, as used by the standard IO library), while fd would be a file descriptor (a small integer), used by the kernel's IO system calls.
Your compiler should have told you that you pass an int where a pointer-to-FILE was expected, or that you use fclose() without a prototype in scope. Did you enable the maximum warning level of your compiler?
Another segfault is possible if the data pointer does not point to a NUL (0) terminated string. Does your log.txt contain NUL-terminated strings?
You should also check if mmap() fails returning MAP_FAILED.
Okay so here is the code that got it working
#include <sys/stat.h>
int status;
struct stat s;
status = stat(file, &s);
if(status < 0){
perror("Stat:");
exit(1);
data = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Before i was using 'getpagesize();' thanks beej !!!
mmap's man page gives you information on the parameters:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
As you can see, your second argument may be wrong (except you really want to exactly map a part of the file fitting into a single page).
Also: Probably 0 is not a valid flag value? Let's have a look again at the man page:
The flags argument determines whether updates to the mapping are
visible to other processes mapping the same region, and whether
updates are carried through to the underlying file. This behavior is
determined by including exactly one of the following values in flags: MAP_SHARED or MAP_PRIVATE
So you could try something like
data = mmap(0, size, PROT_READ, MAP_SHARED, fp, 0);
Always use the provided flags, as the underlying value may differ from machine to machine.
Also, the mapped area should not be larger than the underlying file. Check the size of log.txt beforehand.
The second argument to mmap should not be page size, it should be the size of your file. Here is a nice example.

copy_to_user fails to copy data to mmap user from kernel?

In the user space programm I am allocating some memory via mmap as the following function call:
void *memory;
int fd;
fd = open(filepath, O_RDWR);
if (fd < 0)
return errno;
memory = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, fd, 0);
if (memory == MAP_FAILED)
return -1;
//syscall() goes here
In the kernel space in my system call I am trying to copy data to the memory mapped region as follows:
copy_to_user(memory,src,4096);
EDIT: added error checking code to the post for clarification
The copy_to_user() call is repeatedly failing in this case, whereas if I would have done a memory = malloc() it was succeeding always.
Am I getting some permission flags wrong in this case for mmap ?
Does the open succeed? What about mmap? Is the target file big enough? Can you write to the file through the mapping in userspace?
Also, the repeated 4096 is a strong hit your code is wrong. Userspace should pass the expected size instead.

Using mmap() instead of malloc()

I am trying to complete an exercise that is done with system calls and need to allocate memory for a struct *. My code is:
myStruct * entry = (myStruct *)mmap(0, SIZEOF(myStruct), PROT_READ|PROT_WRITE,
MAP_ANONYMOUS, -1, 0);
To clarify, I cannot use malloc() but can use mmap(). I was having no issues with this on Windows in Netbeans, now however I'm compiling and running from command line on Ubuntu I am getting "Segmentation Fault" each time I try to access it.
Is there a reason why it will work on one and not the other, and is mmap() a valid way of allocating memory in this fashion? My worry was I was going to be allocating big chunks of memory for each mmap() call initially, now I just cannot get it to run.
Additionally, the error returned my mmap is 22 - Invalid Argument (I did some troubleshooting while writing the question so the error check isn't in the above code). Address is 0, the custom SIZEOF() function works in other mmap arguments, I am using MAP_ANONYMOUS so the fd and offsetparameters must -1 and 0 respectively.
Is there something wrong with the PROT_READ|PROT_WRITE sections?
You need to specify MAP_PRIVATE in your flags.
myStruct * entry = (myStruct *)mmap(0, SIZEOF(myStruct),
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
From the manual page:
The flags argument determines whether updates to the mapping are
visible to other processes mapping the same region, and whether
updates are carried through to the underlying file. This behavior is
determined by including exactly one of the following values in flags:
You need exactly one of the flags MAP_PRIVATE or MAP_SHARED - but you didn't give either of them.
A complete example:
#include <sys/mman.h>
#include <stdio.h>
typedef struct
{
int a;
int b;
} myStruct;
int main()
{
myStruct * entry = (myStruct *)mmap(0, sizeof(myStruct),
PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (entry == MAP_FAILED) {
printf("Map failed.\n");
}
else {
entry->a = 4;
printf("Success: entry=%p, entry->a = %d\n", entry, entry->a);
}
return 0;
}
(The above, without MAP_PRIVATE of course, is a good example of what you might have provided as a an MCVE. This makes it much easier for others to help you, since they can see exactly what you've done, and test their proposed solutions. You should always provide an MCVE).
The man page for mmap() says that you must specify exactly one of MAP_SHARED and MAP_PRIVATE in the flags argument. In your case, to act like malloc(), you'll want MAP_PRIVATE:
myStruct *entry = mmap(0, sizeof *entry,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
(I've also made this more idiomatic C by omitting the harmful cast and matching the sizeof to the actual variable rather than its type).

why does mmap fail?

I'm given a physical address, specifically 0x000000368d76c0. I'm trying to mmap it into my program. The code that I'm using is
void *mmap64;
off_t offset = 0x000000368d76c0;
int memFd = open("/dev/mem", O_RDWR);
if (-1 == memFd)
perror("Error ");
mmap64 = mmap(0, sizeof(uint64_t), PROT_WRITE | PROT_READ, MAP_SHARED, memFd, offset);
if (MAP_FAILED == mmap64) {
perror("Error ");
return -1;
}
For some reason when I run this code I get a failure on mmap. Specifically it says Error Invalid argument. I'm pretty sure it is because of the offset value, but I don't know what is wrong with it.
I would appreciate any help on it.
According to mmap(2) - Linux manual page,
offset must be a multiple of the page size as
returned by sysconf(_SC_PAGE_SIZE).
When the page size is 4096 (a page size used in x86 CPU), 0x000000368d76c0 is not a multiple of 4096 and will be considered as invalid.
For that reason, you will have to adjust the offset.

Resources