Seg Fault Using a Struct in Shared Memory - c

I apologize in advance for my ignorance, this is giving me a lot more trouble than it should, but I've been banging my head into my desk for hours trying to come up with what I'm doing wrong. I want to write an application that has shared memory storing a struct. For some reason, I can't get off the ground to start, I keep getting a seg fault from accessing the members of my struct.
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#define MAX_SEQUENCE 10
struct shared_data
{
long sequence[10];
int sequence_size;
};
typedef struct shared_data shared_data;
int main(int argc, char * argv[])
{
int segment_id;
shared_data * shared_memory;
segment_id = shmget(IPC_PRIVATE, sizeof(shared_data), S_IRUSR | S_IWUSR);
shared_memory = (shared_data *) shmat(segment_id, NULL, 0);
shared_memory->sequence_size = atoi(argv[1]);
printf("\n\nSequence Size: %d\n\n",shared_memory->sequence_size);
shmdt(shared_memory);
}
UPDATE: Thanks everyone, my system administrator was running diagnostics and somehow disabled shared memory.

Your code doesn't look to bad to me. The only obvious thing missing is some kind of check for the number of arguments passed like:
if (argc != 2)
return 1;
Is it possible you just missed to call your program with an argument. In this case it would be
atoi (argv[1])
that leads to your segfault.
BTW: additionally checking return values of shmget and shmat might be a good idea too.

Related

Mapping existing memory (data segment) to another memory segment

As the title suggests, I would like to ask if there is any way for me to map the data segment of my executable to another memory so that any changes to the second are updated instantly on the first. One initial thought I had was to use mmap, but unfortunately mmap requires a file descriptor and I do not know of a way to somehow open a file descriptor on my running processes memory. I tried to use shmget/shmat in order to create a shared memory object on the process data segment (&__data_start) but again I failed ( even though that might have been a mistake on my end as I am unfamiliar with the shm API). A similar question I found is this: Linux mapping virtual memory range to existing virtual memory range? , but the replies are not helpful.. Any thoughts are welcome.
Thank you in advance.
Some pseudocode would look like this:
extern char __data_start, _end;
char test = 'A';
int main(int argc, char *argv[]){
size_t size = &_end - &__data_start;
char *mirror = malloc(size);
magic_map(&__data_start, mirror, size); //this is the part I need.
printf("%c\n", test) // prints A
int offset = &test - &__data_start;
*(mirror + offset) = 'B';
printf("%c\n", test) // prints B
free(mirror);
return 0;
}
it appears I managed to solve this. To be honest I don't know if it will cause problems in the future and what side effects this might have, but this is it (If any issues arise I will try to log them here for future references).
Solution:
Basically what I did was use the mmap flags MAP_ANONYMOUS and MAP_FIXED.
MAP_ANONYMOUS: With this flag a file descriptor is no longer required (hence the -1 in the call)
MAP_FIXED: With this flag the addr argument is no longer a hint, but it will put the mapping on the address you specify.
MAP_SHARED: With this you have the shared mapping so that any changes are visible to the original mapping.
I have left in a comment the munmap function. This is because if unmap executes we free the data_segment (pointed to by &__data_start) and as a result the global and static variables are corrupted. When at_exit function is called after main returns the program will crash with a segmentation fault. (Because it tries to double free the data segment)
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define _GNU_SOURCE 1
#include <unistd.h>
#include <sys/mman.h>
extern char __data_start;
extern char _end;
int test = 10;
int main(int argc, char *argv[])
{
size_t size = 4096;
char *shared = mmap(&__data_start, 4096, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if(shared == (void *)-1){
printf("Cant mmap\n");
exit(-1);
}
printf("original: %p, shared: %p\n",&__data_start, shared);
size_t offset = (void *)&test - (void *)&__data_start;
*(shared+offset) = 50;
msync(shared, 4096, MS_SYNC);
printf("test: %d :: %d\n", test, *(shared+offset));
test = 25;
printf("test: %d :: %d\n", test, *(shared+offset));
//munmap(shared, 4096);
}
Output:
original: 0x55c4066eb000, shared: 0x55c4066eb000
test: 50 :: 50
test: 25 :: 25

Why does can't shmat() access my shared memory segment? (ERRNO 13)

For a UNIX/C project, I'm supposed to allocate two shared memory segments (which child processes will eventually access with read-only and write permissions, respectively) of two integers each. But any time I try to call shmat(3), it ends up returning -1, setting errno to EACCES, apparently indicating insufficient permissions. I've produced what seems to be the minimum required code fro the error below, with possibly a couple extra includes:
#define _SVID_SOURCE
#include <errno.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int i, j, shmid, tshmid;
int * clock;
int * shmMsg;
tshmid = shmget(IPC_PRIVATE,sizeof(int)*2, IPC_CREAT | IPC_EXCL | 0777); //0777 permissions are more liberal than I need, but I've tried other various literals and numbers as well as just CREAT/EXCL.
if (tshmid < 1){
printf("Error: In parent process (%d), shmid came up %d \n",getpid(),shmid);
exit(-1);
}
clock = (int *) shmat(shmid,NULL,0); //I've also tried this with the second argument as (void *) 0, and with the third argument as "(SHM_R | SHM_W)" and "0777"
if (clock == (void *) -1){
printf("Error: First shmat couldn't shmattach to shmid #%d. ERRNO %d\n",shmid,errno);
shmdt(clock);
exit(-1);
} //it never even gets this far
shmdt(clock);
}
Each time, this produces an error message like:
Error: First shmat couldn't shmattach to shmid #1033469981. ERRNO 13
The longer version of my program initially returned an identical error, but errno was set to 43 (EIDRM: Segment identified by shm_id was removed). I've recursively chmodded the whole directory to full access, so that's not the issue, and every time my program crashes I have to manually deallocate the shared memory using ipcrm, so the shmids apply to actual segments. Why won't it attach?

printf() function causes segfault

This code when compiled allows me to connect to a server using a correct IP address as the program parameter. So far, the connection is fine and data is sent to the server, but when I attempt to use the recv() function to try to collect data in non-blocking mode, I receive a segmentation fault. It always appears after the "receiving data..." line.
As you can see, I reserved 5000 bytes of stack memory for the operation and I'm testing with trying to read only one byte without success. Does recv() not work with stack memory?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
extern errno;
struct sockaddr_in a;
struct timeval tv;
fd_set wready;
long conntoserver(const char* ip,const char* data){
memset((char*)&a,0,sizeof(a));
if (inet_aton(ip,a.sin_addr.s_addr) == 0){printf("Can't set address\n");return -1;}
long s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if (s < 0){printf("Can't make socket\n");return -1;}
tv.tv_sec=1;tv.tv_usec=0;FD_ZERO(&wready);FD_SET(s,&wready);
//start non-blocking mode
if (fcntl(s,F_SETFL,O_NONBLOCK) < 0){printf("Can't make socket non-blocking\n");close(s);return -1;}
a.sin_family=AF_INET;a.sin_port=htons(80);long ret=connect(s,(struct sockaddr*)&a,sizeof(a));
if (ret < 0 && errno != EINPROGRESS){printf("Can't connect to IP. %s\n",strerror(errno));close(s);return -1;}
if (errno == EINPROGRESS){
printf("connection in progress...\n");
ret=select(s+1,NULL,&wready,NULL,&tv);
if (ret < 0){printf("select() error. %s\n",strerror(errno));close(s);return -1;}
if (ret==0){printf("select() timeout. %s\n",strerror(errno));close(s);return -1;}
}
unsigned long sz=strlen(data);
ssize_t ns=send(s,data,sz,0);
if (ns==sz){return s;}else{return -1;}
}
int main(int argc,char* argv[]){
if (argc < 2){printf("Format: %s < IP to connect to >\n",argv[0]);return -1;}
long s=conntoserver(argv[1],"GET / HTTP/1.1\nHost: example.com\n\n");
if (s < 0){printf("Error making server request. %s\n",strerror(errno));close(s);return -1;}
printf("receiving data...\n");
char b[5001];
ssize_t br=recv(s,b,1,0);
printf("br=%s\n",br);
close(s);
return 0;
}
printf("br=%s\n",br);
Should be:
printf("br=%ld\n",br);
BTW -- I had to fix a number of problems to make this code compile. You should always pay attention to compiler warnings.
In addition to the error pointed out by #keithmo, you have an error here:
if (inet_aton(ip,a.sin_addr.s_addr) == 0){printf("Can't set address\n");return -1;}
The second argument to inet_aton() is expected to be a pointer to a struct in_addr but you are passing a value of type uint32_t. In fact, that value is initialized to zero, so the result is likely (but not certain) to be equivalent to passing a NULL pointer. Either way, this produces undefined behavior, and I'd surprised if that didn't manifest as a segfault. Even if the program runs past that, the call as you wrote it cannot produce the (apparently) desired effect of initializing the address in variable a.
What you want instead is
if (inet_aton(ip, &a.sin_addr) == 0) {
printf("Can't set address\n");
return -1;
}
And for goodness's sake, do make free with the spaces and newlines. They greatly improve code legibility. If you're short, I can lend you some.

Why does pthread_create() return 12?

For some reason, pthread_create isn't allowing me to pass a struct as an argument. The issue is not system related, although I have not had a chance to test it on anyone else's box. It simply won't allow me to pass a struct for some reason; it returns error #12.
The issue is not with memory. I know 12 is ENOMEM, and "that should be that", but it's not.. it simply won't accept my struct as a pointer.
struct mystruct info;
info.website = website;
info.file = file;
info.type = type;
info.timez = timez;
for(threadid = 0; threadid < thread_c; threadid++)
{
// printf("Creating #%ld..\n", threadid);
retcode = pthread_create(&threads[threadid], NULL, getstuff, (void *) &info);
//void * getstuff(void *threadid);
When I ran this code in GDB, for some reason, it didn't return code 12.. but when I run it from the command line, it returns 12.
Any ideas?
Error code 12 on Linux:
#define ENOMEM 12 /* Out of memory */
You are likely running out of memory. Make sure you're not allocating too many threads, and be sure to pthread_join threads when they're done (or use pthread_detach). Make sure you're not exhausting your memory through other means as well.
Passing a stack object as a parameter to pthread_create is a pretty bad idea, I'd allocate it on the heap. Error 12 is ENOMEM.
Try adding some proper error handling.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static void fail(const char *what, int code)
{
fprintf(stderr, "%s: %s\n", what, strerror(code));
abort();
}
...
if (retcode)
fail("pthread_create", retcode);
On my system, 12 is ENOMEM (out of memory).

Load shared memory in C

So I'm making a Load.c file, that basically will load a bunch of "students" into shared memory.
The students are stored in a struct that looks like this:
struct StudentInfo{
char fName[20];
char lName[20];
char telNumber[15];
char whoModified[10];
};
Anyway, I need to load this in shared memory. We were given some sample code and we are reading the code from a data file that will look like this:
John Blakeman
111223333
560 Southbrook Dr. Lexington, KY 40522
8591112222
Paul S Blair
111223344
3197 Trinity Rd. Lexington, KY 40533
etc....
Here's my idea for the code: (header.h just has struct info/ and semaphore count....I'm unsure of what it needs to be, right now it's labeled as 5)
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "header.h"
main()
{
int i,id;
struct StudentInfo *infoptr;
int sema_set;
id = shmget(KEY, SEGSIZE,IPC_CREAT|0666); /* get shared memory to store data */
if (id < 0){
perror("create: shmget failed");
exit(1);
}
infoptr=(struct StudentInfo *)shmat(id,0,0); /* attach the shared memory segment to the process's address space */
if(infoptr <= (struct StudentInfo *) (0)) {
perror("create: semget failed");
exit(2);
}
/* store data in shared memory segment */
//here's where I need help
That's about as far as I got. now I know I can store data using strcpy(infoptr->fName,"Joe"); (for example)
but I need to read an X number of names? How would I go about storing them? Using some sort of push/pop vector of structs? how would it look like?
And do I adjust semaphores based on how many "entries" there are I assume? I'm a little bit confused how to adjust my number of semaphores.
Oh BTW here's my header file just in case (SSN's are fake obviously)
/* header.h */
#define KEY ((key_t)(10101)) /* Change to Last 5 digits of SSN */
#define SEGSIZE sizeof(struct StudentInfo)
#define NUM_SEMAPHS 5
#define SEMA_KEY ((key_t) (1010)) /* Last 4 digits of SSN */
struct StudentInfo{
char fName[20];
char lName[20];
char telNumber[15];
char whoModified[10];
};
void Wait(int semaph, int n);
void Signal(int semaph, int n);
int GetSemaphs(key_t k, int n);
Uhm... I'm not sure here, but this is what I understood:
You're loading struct StudentInfo blocks into a shared memory space, and you want to be able to access it from other processes?
First, consider that your structure is fixed-size. If you want to read 10 names, you need to get sizeof(struct StudentInfo) * 10 bytes, if you want 400, make that * 400 -- so you don't need to push and pop your students from any kind of queue, since you can just use math to calculate from where and up to where you need to read. Getting students 10 to 20 is just reading the shared memory space from sizeof(struct StudentInfo) * 10 to sizeof(struct StudentInfo) * 10
As for mutual exclusion (if you're going to have multiple readers or writers, which I assume is what you wanted the semaphores for), I do not recommend semaphores. They are adequate for simpler kinds of exclusion, like "don't use this function if I'm using it", but not for locking large sets of data.
I'd use file locking. In Unix, you can use file locking primitives to create advisory locks over specific bytes in a file, even if the file is 0-bytes-long. What does this mean?
Advisory means you don't enforce them, other processes must respect them willingly. 0-byte-long means you can open a file that doesn't exist, and lock portions of it corresponding to your student structure positions in shared memory. You don't need the file to actually have data, you can use it to represent your shared memory database without writing anything to it.
What's the advantage of this over semaphores? You have fine-grained control of your locks with a single file descriptor!
Wheh, that got long. Hope I helped.

Resources