简体   繁体   中英

fscanf causes segmentation fault

I'm writing a code that will count the number of processes who parent process is init . Called fork and had the child use an exec function and pass its output through a pipe back to the parent. All seemed fine on that end, but when I used fdopen on the parent's read-end of the pipe, followed by fscanf the program crashed, even though the FILE stream is not NULL . I placed checks on every function call.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

static void     fatalError(char *message);

int main(int argc, char *argv[])
{

    int     total, initCount = 0;
    int     pipeToParent[2];
    pid_t   pid;
    FILE    *file;



    if (pipe(pipeToParent) < 0)
        fatalError("pipe() error");

    if ((pid = fork()) < 0)
        fatalError("fork() error");
    else if (pid == 0) {

        if (close(pipeToParent[0]) < 0)
            fatalError("close() error");

        if (dup2(pipeToParent[1], STDOUT_FILENO) < 0)
            fatalError("dup2() error");

        execlp("ps", "ps", "ahxo", "ppid", NULL);
        fatalError("exec() error");

    }


    if (close(pipeToParent[1]) < 0)
        fatalError("close() error");

    wait(NULL);

    if ((file = fdopen(pipeToParent[0], "r")) == NULL)
        fatalError("fdopen() error");


    for (total = 0; fscanf(file, "%d", &pid) != EOF; total++)
        if (pid == 1)
            initCount++;

    if (fclose(file) < 0)
        fatalError("fclose() error");

    printf("%.2f%%\n", (float) initCount * 100 / total);
    exit(EXIT_SUCCESS);


}


static void     fatalError(char *message) {
    perror(message);
    exit(EXIT_FAILURE);
}

Running GDB gave this:

Program received signal SIGSEGV, Segmentation fault.
__isoc99_fscanf (stream=0x55757260, format=0x555555554c44 "%d") at isoc99_fscanf.c:30
30  isoc99_fscanf.c: No such file or directory.

And the Makefile warnings:

gcc -std=c11 -g -Wall -pedantic    init.c   -o init
init.c: In function ‘main’:
init.c:55:14: warning: implicit declaration of function ‘fdopen’; did you mean ‘fopen’? [-Wimplicit-function-declaration]
  if ((file = fdopen(pipeToParent[0], "r")) == NULL)
              ^~~~~~
              fopen
init.c:55:12: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
  if ((file = fdopen(pipeToParent[0], "r")) == NULL)
            ^

In C if no previous declaration of function exist it is assumed the function returns an int . If the compiler assumes the function returns an int while it returns FILE * and if sizeof(FILE*) < sizeof(int) the return value is truncated and is invalid. Thus you get internal glibc errors as the pointer passed to fscanf is truncated and invalid.

From man fdopen you can read:

fdopen(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

These are the macros that you need to define to have fdopen() declaration in your program. You need to define them before any inclusion. I usually just define _GNU_SOURCE which defines _POSIX_C_SOURCE etc. in features.h .

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

.. rest of the program ...

For more information see future test macros .

When using gcc you can also do gcc -std=gnu11 or other -std=gnu* . -std=c11 is the same as -std=gnu11 except the macro _GNU_SOURCE is predefined when compiling.

Per the Linux man page for fdopen() , Linux/glibc requires definition of the _POSIX_C_SOURCE macro:

Feature Test Macro Requirements for glibc (see feature_test_macros(7) ):

  fdopen(): _POSIX_C_SOURCE 

Otherwise, you get an implicit declaration, which as already noted means the function is assumed to return int . And the pointer that fdopen() actually returns likely doesn't fit into that int , so it's truncated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM