Gem Taylor
Gem Taylor

Reputation: 5613

Can a fork child determine whether it is a fork or a vfork?

Within the child process, is there any way that it determine whether it was launched as a fork with overlay memory, or a vfork with shared memory?

Basically, our logging engine needs to be much more careful (and not log some classes of activity) in vfork. In fork it needs to cooperate with the parent process in ways that it doesn't in vfork. We know how to do those two things, but not how to decide.

I know I could probably intercept the fork/vfork/clone calls, and store the fork/vfork/mapping status as a flag, but it would make life somewhat simpler if there was an API call the child could make to determine its own state.

Extra marks: Ideally I also need to pick up any places in libraries that have done a fork or vfork and then called back into our code. And how that can happen? At least one of the libraries we have offers a popen-like API where a client call-back is called from the fork child before the exec. Clearly the utility of that call-back is significantly restricted in vfork.

Upvotes: 4

Views: 426

Answers (4)

Gem Taylor
Gem Taylor

Reputation: 5613

I came across kcmp today, which looks like it can answer the basic question - i.e. do two tids or pids share the same VM. If you know they represent forked parent/child pids, this can perhaps tell you if they are vfork()ed.

Of course if they are tids in the same process group then they will by definition share VM.

https://man7.org/linux/man-pages/man2/kcmp.2.html

int syscall(SYS_kcmp, pid_t pid1, pid_t pid2, int type,
               unsigned long idx1, unsigned long idx2);

KCMP_VM Check whether the processes share the same address space. The arguments idx1 and idx2 are ignored. See the discussion of the CLONE_VM flag in clone(2).

Upvotes: 0

Rachid K.
Rachid K.

Reputation: 5211

A simple solution could use pthread_atfork(). The callbacks registered with this service are triggered only upon fork(). So, the 3rd parameter of the function, which is called in the child process right after the fork, could update a global variable. The child can check the variable and if it is modified, then it has been forked:

/*
  Simple program which demonstrates a solution to
  make the child process know if it has been forked or vforked
*/
#include <pthread.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

pid_t forked;

void child_hdl(void)
{
  forked = getpid();
}


int main(void)
{
pid_t pid;

  pthread_atfork(0, 0, child_hdl);

  pid = fork();
  if (pid == 0) {
    if (forked != 0) {
      printf("1. It is a fork()\n");
    }
    exit(0);
  }

  // Father continues here
  wait(NULL);

  pid = vfork();
  if (pid == 0) {
    if (forked != 0) {
      printf("2. It is a fork()\n");
    }
    _exit(0);
  }

  // Father continues here
  wait(NULL);

  return 0;
}

Build/execution:

$ gcc fork_or_vfork.c
$ ./a.out
1. It is a fork()

Upvotes: 1

David Schwartz
David Schwartz

Reputation: 182779

If you were created by vfork, your parent will be waiting for you to terminate. Otherwise, it's still running. Here's some very ugly code:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

void doCheck()
{
    char buf[512];
    sprintf(buf, "/proc/%d/wchan", (int) getppid());
    int j = open(buf, O_RDONLY);
    if (j < 0) printf("No open!\n");
    int k = read(j, buf, 500);
    if (k <= 0) printf("k=%d\n", k);
    close(j);
    buf[k] = 0;
    char *ptr = strstr(buf, "vfork");
    if (ptr != NULL)
       printf("I am the vfork child!\n");
    else
       printf("I am the fork child!\n");   
}

int main()
{
    if (fork() == 0)
    {
       doCheck();
       _exit(0);
    }
    sleep(1);
    if (vfork() == 0)
    {
        doCheck();
        _exit(0);
    }
    sleep(1);
}

This is not perfect, the parent might be waiting for a subsequent vfork call to complete.

Upvotes: -3

Joshua
Joshua

Reputation: 43280

All code not specifically designed to work under vfork() doesn't work under vfork().

Technically, you can check if you're in a vfork() child by calling mmap() and checking if the memory mapping was inherited by the parent process under /proc. Do not write this code. It's a really bad idea and nobody should be using it. Really, the best way to tell if you're in a vfork() child or not is to be passed that information. But here comes the punchline. What are you going to do with it?

The things you can't do as a vfork() child include calling fprintf(), puts(), fopen(), or any other standard I/O function, nor malloc() for that matter. Unless the code is very carefully designed, you're best off not calling into your logging framework at all, and if it is carefully designed you don't need to know. A better design would most likely be log your intent before calling vfork() in the first place.

You ask in comments about a library calling fork() and then back into your code. That's already kind of bad. But no library should ever ever call vfork() and back into your code without being explicitly documented as doing so. vfork() is a constrained environment and calling things not expected to be in that environment really should not happen.

Upvotes: 6

Related Questions