#include <linux/kcmp.h> int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2);
Note: There is no glibc wrapper for this system call; see NOTES.
Permission to employ kcmp() is governed by ptrace access mode PTRACE_MODE_READ_REALCREDS checks against both pid1 and pid2; see ptrace(2).
The type argument specifies which resource is to be compared in the two processes. It has one of the following values:
struct kcmp_epoll_slot {
__u32 efd;
__u32 tfd;
__u64 toff;
};
Within this structure, efd is an epoll file descriptor returned from epoll_create(2), tfd is a target file descriptor number, and toff is a target file offset counted from zero. Several different targets may be registered with the same file descriptor number and setting a specific offset helps to investigate each of them.
Note the kcmp() is not protected against false positives which may occur if the processes are currently running. One should stop the processes by sending SIGSTOP (see signal(7)) prior to inspection with this system call to obtain meaningful results.
The easiest way to explain is to consider an example. Suppose that v1 and v2 are the addresses of appropriate resources, then the return value is one of the following:
On error, -1 is returned, and errno is set to indicate the error.
kcmp() was designed to return values suitable for sorting. This is particularly handy if one needs to compare a large number of file descriptors.
Before Linux 5.12, this system call is available only if the kernel is configured with CONFIG_CHECKPOINT_RESTORE, since the original pupose the system call was for the checkpoint/restore in user space (CRIU) feature. (The alternative to this system call would have been to expose suitable process information via the proc(5) filesystem; this was deemed to be unsuitable for security reasons.) Since Linux 5.12, this system call is made available unconditionally.
See clone(2) for some background information on the shared resources referred to on this page.
$ ./a.out Parent PID is 1144 Parent opened file on FD 3
PID of child of fork() is 1145
Compare duplicate FDs from different processes:
kcmp(1145, 1144, KCMP_FILE, 3, 3) ==> same
Child opened file on FD 4
Compare FDs from distinct open()s in same process:
kcmp(1145, 1145, KCMP_FILE, 3, 4) ==> different
Child duplicated FD 3 to create FD 5
Compare duplicated FDs in same process:
kcmp(1145, 1145, KCMP_FILE, 3, 5) ==> same
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
static int
kcmp(pid_t pid1, pid_t pid2, int type,
unsigned long idx1, unsigned long idx2)
{
return syscall(SYS_kcmp, pid1, pid2, type, idx1, idx2);
}
static void
test_kcmp(char *msg, pid_t pid1, pid_t pid2, int fd_a, int fd_b)
{
printf("\t%s\n", msg);
printf("\t\tkcmp(%jd, %jd, KCMP_FILE, %d, %d) ==> %s\n",
(intmax_t) pid1, (intmax_t) pid2, fd_a, fd_b,
(kcmp(pid1, pid2, KCMP_FILE, fd_a, fd_b) == 0) ?
"same" : "different");
}
int
main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char pathname[] = "/tmp/kcmp.test";
fd1 = open(pathname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd1 == -1)
errExit("open");
printf("Parent PID is %jd\n", (intmax_t) getpid());
printf("Parent opened file on FD %d\n\n", fd1);
switch (fork()) {
case -1:
errExit("fork");
case 0:
printf("PID of child of fork() is %jd\n", (intmax_t) getpid());
test_kcmp("Compare duplicate FDs from different processes:",
getpid(), getppid(), fd1, fd1);
fd2 = open(pathname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd2 == -1)
errExit("open");
printf("Child opened file on FD %d\n", fd2);
test_kcmp("Compare FDs from distinct open()s in same process:",
getpid(), getpid(), fd1, fd2);
fd3 = dup(fd1);
if (fd3 == -1)
errExit("dup");
printf("Child duplicated FD %d to create FD %d\n", fd1, fd3);
test_kcmp("Compare duplicated FDs in same process:",
getpid(), getpid(), fd1, fd3);
break;
default:
wait(NULL);
}