Search code examples
arrayscstringshared-memorystring-length

Writing a string into shared memory from producer process with different character array sizes but same length string


I am trying to write a string into shared memory using a producer process as shown below.

int main(){
    char str[30];
    scanf("%[^\n]", str);
    int shm_id = shmget((key_t)1234, sizeof(str), IPC_CREAT | 0666);
    char *shm_addr = shmat(shm_id, NULL, 0);
    memcpy(shm_addr, str, sizeof(str));
    shmdt(shm_addr);
    return 0;
}

The part where I am failing to understand is this is causing a segmentation fault while running the program,if I have given the string input through scanf as my name(i.e., "Bhavesh Pendyala" which is 16 character string). But if I try it with a character array of size 20 rather than 30 for the same 16 character string then there is no error showing up.

The code snippet that is showing no error is given below.

int main(){
    char str[20];
    scanf("%[^\n]", str);
    int shmid = shmget((key_t)1234, sizeof(str), 0666 | IPC_CREAT);
    char *ptr = shmat(shmid, NULL, 0);
    memcpy(ptr, str, sizeof(str));
    shmdt(ptr);
    return 0;
}

Can anyone explain why this is happening. I am very much stuck here because anyhow in both the cases the array length is greater than that of the string. What is the reason that the code is not showing error for character array of 20 length but showing error for the character array of 30 length for the same string input of 16 character through scanf?


Solution

  • By not removing shared memory (detaching is not sufficient for this test), rerunning program uses previously successful create. If size is modified to larger than original shmget returns error which your code fails to detect leading to segfault.

    If the shmem error is ignored, then shmat then returns -1 which then produces segfault when dereferenced as pointer.

    Slighly modified form of code to demonstrate:

    #include <stdio.h>
    #include <string.h>
    #include <sys/shm.h>
    #include <errno.h>
    
    int main(){
        char str[20];
        scanf("%[^\n]", str);
        key_t k = 55555;
    
         printf("str=%s sizeof(str)=%ld ; k=%d\n", str, sizeof(str), k);
    
        int shmid = shmget(k, sizeof(str), 0666 | IPC_CREAT);
        if (shmid == -1) {
           printf("Error on shmget %d %s\n", errno, strerror(errno));
           printf("Should return here but continuing...\n");
    //       return 1;
        }
        char *ptr = shmat(shmid, NULL, 0);
        printf("%8p\n", ptr);
        if (ptr == (char *)-1) {
           printf("shmat failed...about to segfault\n");
        } else {
           printf("Contents before memcpy: %s\n", ptr);
        }
    
        memcpy(ptr, str, sizeof(str));
        shmdt(ptr);
        return 0;
    }
    

    In the following, test is run with same key throughout and first size=20

    ~/test$ ./a.out
    STACK
    str=STACK sizeof(str)=20 ; k=55555
    0x7f6a50371000
    Contents before memcpy:
    

    Same parameters with different value for str

    ~/test$ ./a.out
    OVERFLOW
    str=OVERFLOW sizeof(str)=20 ; k=55555
    0x7f6c30cc0000
    Contents before memcpy: STACK
    

    Finally changing sizeof str to 30

    ~/test$ ./a.out
    RULES
    str=RULES sizeof(str)=30 ; k=55555
    Error on shmget 22 Invalid argument
    Should return here but continuing...
    0xffffffffffffffff
    shmat failed...about to segfault
    Segmentation fault (core dumped)
    

    See comments for improvement suggestions aside from always checking for errors.

    And from what I read, removing (not simply detaching), such as shmctl(id, IPC_RMID, ...) does not completely eliminate possibility of reattaching in subsequent runs.


    UPDATE 1

    Show how to remove memory using 2nd test program:

    To remove the shared memory (after detach) the following worked for me:

    #include <stdio.h>
    #include <string.h>
    #include <sys/shm.h>
    #include <errno.h>
    
    int main(){
        char str[20];
        key_t k = 55555;
    
        printf("Removing shared memory after displaying contents.\n");
    
        int shmid = shmget(k, sizeof(str), 0666 | IPC_CREAT);
        if (shmid == -1) {
           printf("Error on shmget %d %s\n", errno, strerror(errno));
           return 1;
        }
        char *ptr = shmat(shmid, NULL, 0);
        printf("%8p\n", ptr);
        if (ptr == (char *)-1) {
           printf("shmat failed\n");
           return 1;
        } else {
           printf("Contents before removing: %s\n", ptr);
        }
    
        shmdt(ptr);
    
        shmctl(shmid, IPC_RMID, NULL);
    
        return 0;
    }
    

    So the whole test sequence (test1 is the first code and test2 is the second code (to remove):

    • Show shared memory empty
    • Run test1 to add shared memory buffer 'STACK OVERFLOW'
    • Show shared memory not empty
    • Run test2 to get/show memory buffer and remove.
    • Show shared memory empty

    output

    ~/test$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status
    
    ~/test$ ./test1
    STACK OVERFLOW
    str=STACK OVERFLOW sizeof(str)=20 ; k=55555
    0x7f25e5842000
    Contents before memcpy:
    
    ~/test$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status
    0x0000d903 10         bliss      666        20         0
    
    ~/test$ ./test2
    Removing shared memory after displaying contents.
    0x7f5e78160000
    Contents before removing: STACK OVERFLOW
    ~/test$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status