I'd like to dup a file descriptor running in an unrelated process on Linux. I know about sendmsg(2) and SCM_RIGHTS (e.g. https://stackoverflow.com/questions/4489433/sending-file-descriptor-over-unix-domain-socket-and-select) , but that works only if the other process is cooperating. I need a solution which doesn't need active cooperation from the other process. I also know that I could create the file descriptor first, keep a copy, and then create the other process, but I need a solution in which the other process creates its own file descriptor.

I can see the file descriptor:

$ ls -l /proc/13115/fd/3
lrwx------ 1 pts pts 64 2013-05-04 13:15 /proc/13115/fd/3 -> socket:[19445454]

However, open("/proc/13115/fd/3", O_RDWR) executed in another process returns the error No such device or address. Is there something else that would work? Probably with ptrace?

  • Always try to include error text rather than "an error". – Runium May 4 '13 at 11:42
  • "I need a solution which doesn't need active cooperation from the other process" So the process is supposed to just suddenly have an open file without having done anything? – Bratchley May 4 '13 at 11:50
  • Do you have permissions to access this processes file descriptor? I guess I'm asking if both processes are owned by the same userid. or at least the 2nd one has access to first user's file perms? – slm May 4 '13 at 11:51
  • @JoelDavis: Not like that. The other process has been running for a while, and it has already created the file descriptor for itself. I know the PID and the fd number. I want to get the file descriptor. I don't care about race conditions. – pts May 4 '13 at 11:55
  • 2
    Yes, execute sendmsg in process 13115 by injecting code via ptrace. – Gilles 'SO- stop being evil' May 4 '13 at 14:12

This is by design: sharing file descriptors with other processes is explicit. By default, file descriptors are as private as the process's own memory.

As usual, if you have the right to ptrace the process, you can do anything you like, including making it call sendmsg. Traditionally, calling ptrace requires running as the same user ID; security restrictions such as SELinux, capabilities, jails and so on can make ptrace more restrictive. For example, under the default Ubuntu configuration, a non-root process can only call ptrace on its own descendants (through AppArmor).

Using ptrace robustly is a bit tricky: you have to inject the right data, make sure not to overwrite anything, and clean up after yourself. So my recommendation is to inject the code in a roundabout way, and trigger that code with an existing tool.

Write a small shared library containing the sendmsg code, and LD_PRELOAD it to the other process. Here's some untested skeleton code with error checking missing.

int pts_gift_fd (char *path, int fd) {
    int sock;
    struct sockaddr_un addr = {0};
    struct msghdr msg = {0};
    struct iovec iov = {0};
    addr.sun_family = AF_UNIX;
    strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
    /* Populate msg, iov as in the code you've already found */
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    connect(sock, (struct sockaddr*)&addr, sizeof(addr));
    sendmsg(sock, &msg, 0);

Then, to trigger the code, run gdb -n -pid 13115 -batch -x /dev/stdin with popen and feed it input like this (where %d is the fd you want to obtain, and %s is a path to a unix socket that you've previously created and are listening on):

call pts_gift_fd("%s", %d)

You can't do this for the same reason you can't access the memory of an unrelated process, because a file descriptor is part of the memory of another process. The only reason there's information about stuff like this in /proc is because the kernel provides it there, and it is read-only (thus there are means to examine a copy of process memory).

If it relates to a file, you can of course try and access the file. If it's a socket, you could snoop on it using libpcap or something derived from it.

The situation is basically this: a file descriptor is (again) part of a process's memory. The descriptor has an underlying buffer which exists in kernel space; when the process reads or writes to/from the descriptor, it is writing to and from this buffer. For data going out, the kernel flushes the buffer out (to hardware) appropriately; for data coming in, it refills the buffer (from hardware) when the process empties it. Those buffers are not, AFAIK, accessible to other processes, although there are some means (eg. libpcap) of reading the data in some form determined by the particular kernel interface, just as the proc interface can provide some data from the process's user space memory.

  • 1
    Thank you for composing your answer. What you are saying (you can't access the memory of an unrelated process) is incorrect, because ptrace(2) can do it. Are you sure that what I need can't be done? If so, what is your justification? stackoverflow.com/questions/11214066/… ? – pts May 4 '13 at 13:16
  • 1
    @pts I'm pretty sure but I do not have enough granular knowledge of the kernel to prove it definitively. I've added a paragraph outlining my reasoning. By "access memory" I meant directly -- ptrace uses a kernel interface to get a copy of the data. – goldilocks May 4 '13 at 13:46
  • Thank you for the clarification. FYI I can't give +1 to your answer because the beginning (you can't access the memory of an unrelated process) is still incorrect, because ptrace(2) can do it. You are also incorrect about writing the memory of another process: /proc/13115/mem is writable, and it affects the other process. I fail to see why your explanation about buffers makes the socket impossible to dup: in fact, sendmsg(2) can do it using SCM_RIGHTS, also it gets duplicated upon fork(2). – pts May 4 '13 at 13:52
  • 4
    @pts goldilocks is correct: a process cannot access the memory of another process. A process can make the kernel access the memory of another process on its behalf, with ptrace. Access to another process's /proc/$pid/mem requires setup with ptrace first. – Gilles 'SO- stop being evil' May 4 '13 at 14:11
  • 1
    @pts Using /proc/$pid/mem is tricky — see How do I read from /proc/$pid/mem under Linux?. It's not the easiest way to do what you want though. – Gilles 'SO- stop being evil' May 5 '13 at 18:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.