strcpy to mmap address retuns bus error - c

I created a process which calls mmap with MAP_SHARED flag set,when i attempt to copy a string to that address i receive Bus error core dumped,could some one please explain the reason behind it and how to fix it. Following is my code
int main()
{
int fd=0;
char* ret = NULL;
void *map_addr = NULL;
fd = open("./shared_file.txt", O_RDWR, S_IRUSR | S_IWUSR);
if(fd == -1) {
printf("errno = %d\n",errno);
printf("Aborting process1###########\n");
abort();
}
map_addr = mmap(NULL, 5*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map_addr == MAP_FAILED) {
printf("mmap failed error no =%d\n",errno);
close(fd);
return -1;
}
printf("map_addr = %p#################\n",(int*)map_addr);
printf("processid = %d#################\n",(int)getpid());
ret = strcpy((char*)map_addr,"Stack Overflow");
if(ret == (char*)map_addr)
printf("strcpy success\n");
/*if(msync(map_addr, sizeof(int), MS_SYNC))
printf("msync failed errno = %d\n",errno);*/
close(fd);
sleep(120);
return (0);
}

The cause of a bus error is usually an attempt to dereference a pointer that has not been initialized properly and contains junk data that are not accessible in a multiple of 4 or 1 or as related to datatype sizes.
First you should check if the shared_file.txt file size is >= 20 bytes(assuming sizeof int is 4 bytes) as specified in the mmap() length argument(where you put 5*(sizeof(int))) in the line below:
map_addr = mmap(NULL, 5*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
If file size is less than 20 bytes, you could use fallocate call to pre allocate the memory.
If shared_file.txt file size is <= 20 bytes and then you ask mmap to map 20 bytes, it could cause a bus error when you write beyond the actual no. of bytes available in file, because that would be access to an undefined portion of a memory. MMAP_FAILED will not be returned in this case, during memory initialization.
A quick check is to see if you can write a single character in the mmap char* pointer. If you can't( you will get a SIGBUS), that means file size is zero.

Related

bus error when memcopy to uio mmap device

I currently try to access UIO shared memory via memcopy.
My approach is:
open the corresponding device
map the memory via mmap with the offset speciality N*getpagesize()
memcopy / memset to the pointer that is returned from mmap
I tried also with ftruncate after Step 2 resulting in an error.
The memcopy / memset causes a bus error which is normaly a sign of writing out of the files boundaries.
Via cat /proc/'pid'/maps I'm able to see that there is a mapping for /dev/uioX
Also /sys/class/uio/uioX/maps/ has two map directories, of which I try to access the second one (map1 therefore N = 1)
Am I missing out something?
Would I have to mmap the full size of the memory specified in /sys/class/uio/uioX/maps/map1/size ?
I could not find any example for accessing the memory via memcopy, is there something that prevents memcopy on UIO mmaped memory?
Sources
unsigned char* GetMemPtr(const char *name, unsigned long Size)
{
long fd;
long truncret;
void* MemPtr;
unsigned long offst;
printf("Opening: %s with size %u\n" , name, Size);
fd = open(name, O_RDWR);
if (fd < 0) {
printf("Error: open : %u : %s\n", fd, strerror(errno));
}
offst = 1 * getpagesize();
/*truncret = ftruncate(fd, offst + Size);
if (truncret < 0)
{
printf("Error: ftruncate : %s : %d\n", strerror(errno), truncret);
}*/
MemPtr = mmap(0, Size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offst);
if (MemPtr == MAP_FAILED)
{
printf("Error: mmap : %p : %s\n", MemPtr, strerror(errno));
}
//MemPtr = MemPtr + offst;
printf("Mem pointer is %p\n", MemPtr);
memset(MemPtr, 0, Size);
printf("Pointer is : %p\n" , MemPtr);
return (unsigned char*) MemPtr;
}
Results in the output:
Opening: /dev/uio0 with size 4096
Mem pointer is 0xffff89232000
Bus error (core dumped)

How to use the file I/O write() instead of memcpy() to write the message to the SHM object

int main() {
int mfd; //file descriptor for the shared object
int seg_size = (sizeof(message)); //shared object sized to store message
char *shared_msg;
// create the shared memory object with shm_open()
mfd = shm_open(SHARED_OBJ_PATH, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG);
if (mfd < 0) {
perror("error in shm_open()");
exit(1);
}
printf("I have created a lucky shared memory object: %s\n", SHARED_OBJ_PATH);
ftruncate(mfd, seg_size) ; // define size of shared memory object
// map the shared memory object to this process
shared_msg = mmap(NULL, seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
if (shared_msg == NULL) {
perror("error in mmap()");
exit(1);
}
printf("Shared memory is now allocated %d bytes\n", seg_size);
memcpy(shared_msg, "Hello Man", 30); //put something onto the memory
printf("Message content is: %s\n\n Hit any key to finish!\n", shared_msg) ;
getchar() ; // wait for user to hit any key
shm_unlink(SHARED_OBJ_PATH) ; // unlink - better to add error check
return 0 ;
}
I don't know how to use write function, I just delete memcpy() and change it to write (fd, buf, 30), I do not know how to display "Hello Man".

Issues mmaping the same file twice

I'm using a Raspberry Pi B+, and I'm trying to mmap two different sections of /dev/mem - the first to be able to set two pins' functions from location 0x2020 0004 (0x04 bytes long), the other to manipulate the BSC Slave functions on the BCM2835 chip on the Pi from location 0x2021 4000 (0x1C bytes long).
static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
{
return (uint32_t *) mmap((void*)0x0, len,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_SHARED|MAP_LOCKED,
fd, addr);
}
int initialise(void) {
int fd;
fd = open("/dev/mem", O_RDWR | O_SYNC) ;
if (fd < 0)
{
fprintf(stderr, "This program needs root privileges. Try using sudo.\n");
return 1;
}
pinReg = initMapMem(fd, 0x20200004, 0x4);
bscReg = initMapMem(fd, 0x20214000, 0x1C);
close(fd);
if (bscReg == MAP_FAILED)
{
fprintf(stderr, "Bad, mmap failed.\n");
return 1;
}
if (pinReg == MAP_FAILED)
{
fprintf(stderr, "Bad, mmap failed.\n");
return 1;
}
return 0;
}
initialise() is called out of main(). Stepping through the program with gdb I find that bscReg gets positioned right, but pinReg returns as MAP_FAILED (aka 0xFFFFFFFF) with errno set to EINVAL. Doesn't matter which way it's done, either - pinReg always finds itself as MAP_FAILED when mmaped first or second.
How do I get pinReg to a valid value?
The first mmap() is failing because the offset you're trying to map (0x20200004) isn't page-aligned. Create a mapping at 0x20200000 with a size of at least 8, then write to it at an offset of 0x4.

mremap function failed to allocate new memory

I have write the following code , but the code is still fiven me EEERROR message , which tells that the mremap failed to extend the memory.
int main()
{
int size_of_mem = 1024
int fd = shm_open("/myregion", O_CREAT | O_RDWR, S_IRWXO | S_IRUSR | S_IWUSR);
if (fd == -1)
printf("ERROR in shm_open \n") ;
if (ftruncate(fd, size_of_mem) == -1)
printf("ERROR in ftruncate \n") ;
int shm_address = mmap(0 , size_of_mem , PROT_READ | PROT_WRITE | PROT_EXEC ,MAP_SHARED , fd , 0) ;
if (shm_address == MAP_FAILED)
{
printf("Error mmapping the file \n");
exit(EXIT_FAILURE);
}
int temp = mremap(shm_address , size_of_mem ,4000 , MREMAP_MAYMOVE) ;
if( temp < 0)
{
printf("EEEEEEEERROR\n") ;
}
return 0 ;
}
There are a couple of things wrong here.
First, mmap() and mremap() return a void* pointer, which you must not just cast to an int.
Second, the mremap() man page states:
RETURN VALUE
On success mremap() returns a pointer to the new virtual memory area. On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set appropriately.
So your check temp < 0 is wrong. It should be temp == (void*)-1. It is entirely possible that mremap() returns a valid pointer on success that is smaller than zero when cast to an int.
Third, both mmap() and mremap() set the errno (man page) variable when an error occurs. You can read that to get more information about what exactly went wrong. To just output a text error message use the perror() function (man page). Note that you have to #include <errno.h> for that.
Fourth, if you detect an error condition, you always print a message, but you mostly allow execution to continue. That doesn't make sense. If shm_open() failed, you want to return immediately (or call exit(EXIT_FAILURE)). None of the following functions will work if you couldn't even open the SHM file.
Thus, my cleaned up version looks like this:
#include <error.h>
int main()
{
int size_of_mem = 1024;
int fd = shm_open("/myregion", O_CREAT | O_RDWR,
S_IRWXO | S_IRUSR | S_IWUSR);
if (fd == -1)
{
perror("Error in shm_open");
return EXIT_FAILURE;
}
if (ftruncate(fd, size_of_mem) == -1)
{
perror("Error in ftruncate");
return EXIT_FAILURE;
}
void *shm_address = mmap(0, size_of_mem,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, fd, 0);
if (shm_address == MAP_FAILED)
{
perror("Error mmapping the file");
return EXIT_FAILURE;
}
void *temp = mremap(shm_address, size_of_mem, 4000, MREMAP_MAYMOVE);
if(temp == (void*)-1)
{
perror("Error on mremap()");
return EXIT_FAILURE;
}
return 0;
}
Note:
Correct data types (void*), correct error checking for mremap(), usage of perror() to print more informative error messages, error paths ending execution of the function.
Correct/consistent indentation.
No spaces before , in function calls.

Is it possible to map just part of a file using mmap?

I have a input file which has a header like this:
P6\n
width\n
height\n
depth\n
and then a struct is writen, pixel*, into this file, which is going to be mapped.
So, I want to skip the header and make my mmap function return the ptr to that structure. How can I do this? with lseek perhaps? Could you please exemplify?
I will leave part of my code here:
printf("Saving header to output file\n");
if (writeImageHeader(h, fpout) == -1) {
printf("Could not write to output file\n");
return -1;
}
last_index = (int)ftell(fpout);
//printf("offset after header= %d\n",last_index);
//alloc mem space for one row (width * size of one pixel struct)
row = malloc(h->width * sizeof (pixel));
/*Create a copy of the original image to the output file, which will be inverted*/
printf("Starting work\n");
for (i = 0; i < h->height; i++) {
printf("Reading row... ");
if (getImageRow(h->width, row, fpin) == -1) {
printf("Error while reading row\n");
}
printf("Got row %d || ", (i + 1));
printf("Saving row... ");
if (writeRow(h->width, row, fpout) == -1) {
printf("Error while reading row\n");
}
printf("Done\n");
}
/*Open file descriptor of the ouput file.
* O_RDWR - Read and Write operations both permitted
* O_CREAT - Create file if it doesn't already exist
* O_TRUNC - Delete existing contents of file*/
if ((fdout = open(argv[2], O_RDWR, FILE_MODE)) < 0) {
fprintf(stderr, "Can't create %s for writing\n", argv[2]);
exit(1);
}
/*Get size of the output file*/
if (fstat(fdout, &sbuf) == -1) {
perror("Stat error ---------->\n");
exit(1);
}
//printf("Size of output file: %d\n",(int)sbuf.st_size);
/*Maps output file to memory*/
if ((data = mmap((caddr_t) 0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) (-1)) {
perror("Error mmaping");
exit(EXIT_FAILURE);
}
As you see, right now my ppm image is mapped to char* data, but I want to skip the header and map just to the pixel* part.
Here's my code with the suggestion of using 2 pointers, a char* from mmap and another one equals that + offset.
main
c functions
header
makefile
If you read the man page for mmap, you wil find that its final parameter is off_t offset. The description:
... continuing or at most 'len' bytes to be mapped from the object described by 'fd', starting at byte offset 'offset'.
I suspect if you pass your offset in as that parameter, it will do what you want.
You can't if the amount you need to skip is less than the system page size, since offset must be a multiple of the page size on some systems.
You just need to keep 2 pointers - the pointer to the start of the mmap'd block, and the pointer to the start of the data you want inside there. As in:
unsigned char *block = mmap(0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0);
unsigned char *data = block + offset;
where offset is the offset in the file to the data you want.
So, from what I understand, can I do something like this?
off_t offset_after_header = lseek(fdout, last_index, SEEK_SET);
printf("Pointer is on %d\n",(int)offset_after_header);
/*Maps output file to memory*/
if ((data = mmap((caddr_t) 0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, offset_after_header)) == (caddr_t) (-1)) {
perror("Error mmaping");
exit(EXIT_FAILURE);
}
and, from that, I could map my file to whatever type I want, in this case the pixel*
If this is ok, what cautions should I take? For example, like those Ignacio Vazquez-Abrams said
Um, you did notice the 'offset' parameter that you are supplying with a zero? Assuming you know the absolute offset of what you want, you pass it.

Resources