1

I am looking for a way to get a unique file descriptor for a process that is not associated with anything in particular. It must be a unique integer number for which there will be no equivalent file descriptor created by the kernel.

So far I am just opening /dev/null for the sake of getting a unique file descriptor number. I was wondering if somebody knows of a better way to achieve this (for example, some sort of "create_fd()" system call).

  • 1
    You're looking for an invalid file descriptor? Why? – This isn't my real name May 23 '13 at 02:20
  • @ElchononEdelson: For the purpose of “mocking” certain I/O operations in which kernel is not involved at all. –  May 23 '13 at 02:21
  • Create a socket. Don't bind it or connect it. A pipe will also do the same see `man -a pipe`. – Eric des Courtis May 23 '13 at 02:22
  • @EricdesCourtis: Opening `/dev/null` is simpler and does the same. I was just hoping there is a way to simply reserve a unique legitimate file descriptor not associated with any character device or file so that all calls that I do not intercept simply fail. –  May 23 '13 at 02:25
  • @VladLazarenko This might sound stupid but what about `/dev/net/tun` ? It should give you a bad file descriptor that can be opened but not read to or written to. – Eric des Courtis May 23 '13 at 03:20
  • 1
    Use negative numbers or numbers larger than the current open file limit. Both are guaranteed never to be valid file descriptors. – R.. GitHub STOP HELPING ICE May 23 '13 at 04:01
  • @R..: Exactly, and I need a valid one... :-D –  May 25 '13 at 20:38
  • What does it need to be valid for? – R.. GitHub STOP HELPING ICE May 25 '13 at 20:38
  • @R..: Like I said, I am mocking some functionality that works through file descriptors. So the code that uses it should not know that it is not real file descriptor. And it won't, but it will surely check for it to be non-negative. Using descriptors above the limit is not an option because limits can change dynamically. –  May 25 '13 at 20:54

2 Answers2

1

On POSIX:

unsigned maxfd = sysconf(_SC_OPEN_MAX);
unsigned nulfd = maxfd + 1;

Note the use of unsigned so that we don't overflow when sysconf() returns INT_MAX.

  • Cool, but isn't it possible that max open files limit is changed during the program run-time? –  May 23 '13 at 02:23
  • @VladLazarenko It is. [And you can't do anything about it.](http://stackoverflow.com/questions/899038/getting-the-highest-allocated-file-descriptor) –  May 23 '13 at 02:25
  • Uh, oh. Looks like `/dev/null` is still my best friend so far. I was thinking about a "negative" file number, but then it is against POSIX standard and many apps can simply crap out and refuse to work with it. –  May 23 '13 at 02:27
1

You could create a module and add mock file descriptor creation via ioctl() or using a proc file. The file descriptor table exists in the supervisor mode (in the kernel) only. I don't see how you could reserve it without creating a new kernel module or reuse one that happens to do what you want.

See fdtable.h in the kernel source and files_struct inside task_struct (process descriptor) for details.

Here is a proc filesystem module that could do what you want.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>

#define procfs_name "always_fail"

struct proc_dir_entry *proc_file;

int procfile_read(char *buffer,
          char **buffer_location,
          off_t offset, int buffer_length, int *eof, void *data)
{   
    printk(KERN_INFO "procfile_read (/proc/%s) called\n", procfs_name);

    return -1;
}

int init_module()
{
    proc_file = create_proc_entry(procfs_name, 0644, NULL);

    if (proc_file == NULL) {
        remove_proc_entry(procfs_name, &proc_root);
        printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
               procfs_name);
        return -ENOMEM;
    }

    proc_file->read_proc     = procfile_read;
    proc_file->owner         = THIS_MODULE;
    proc_file->mode          = S_IFREG | S_IRUGO;
    proc_file->uid           = 0;
    proc_file->gid           = 0;
    proc_file->size          = 0;

    printk(KERN_INFO "/proc/%s created\n", procfs_name);    
    return 0;
}

void cleanup_module()
{
    remove_proc_entry(procfs_name, &proc_root);
    printk(KERN_INFO "/proc/%s removed\n", procfs_name);
}

Should print something similar to this when read:

cat /proc/always_fail
cat: /proc/always_fail: File descriptor in bad state

Another hackish way would be to simply use /dev/net/tun assuming you have it. It will fail on reads and writes but not on open.

Proof:

$ sudo strace cat /dev/net/tun 
execve("/bin/cat", ["cat", "/dev/net/tun"], [/* 18 vars */]) = 0
brk(0)                                  = 0x24c5000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fed22000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=128879, ...}) = 0
mmap(NULL, 128879, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83fed02000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1848024, ...}) = 0
mmap(NULL, 3961912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83fe73a000
mprotect(0x7f83fe8f8000, 2093056, PROT_NONE) = 0
mmap(0x7f83feaf7000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f83feaf7000
mmap(0x7f83feafd000, 17464, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83feafd000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fed01000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fecff000
arch_prctl(ARCH_SET_FS, 0x7f83fecff740) = 0
mprotect(0x7f83feaf7000, 16384, PROT_READ) = 0
mprotect(0x60a000, 4096, PROT_READ)     = 0
mprotect(0x7f83fed24000, 4096, PROT_READ) = 0
munmap(0x7f83fed02000, 128879)          = 0
brk(0)                                  = 0x24c5000
brk(0x24e6000)                          = 0x24e6000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7220496, ...}) = 0
mmap(NULL, 7220496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83fe057000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
**open("/dev/net/tun", O_RDONLY)          = 3**
fstat(3, {st_mode=S_IFCHR|S_ISVTX|0666, st_rdev=makedev(10, 200), ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
**read(3, 0x24c7000, 65536)               = -1 EBADFD (File descriptor in bad state)**
write(2, "cat: ", 5cat: )                    = 5
write(2, "/dev/net/tun", 12/dev/net/tun)            = 12
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83fed21000
read(4, "# Locale name alias data base.\n#"..., 4096) = 2570
read(4, "", 4096)                       = 0
close(4)                                = 0
munmap(0x7f83fed21000, 4096)            = 0
open("/usr/share/locale/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": File descriptor in bad state", 30: File descriptor in bad state) = 30
write(2, "\n", 1
)                       = 1
**close(3)                                = 0**
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?
eric@ideapad:~$ sudo strace cat /dev/net/tun 
execve("/bin/cat", ["cat", "/dev/net/tun"], [/* 18 vars */]) = 0
brk(0)                                  = 0x109f000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3db000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=128879, ...}) = 0
mmap(NULL, 128879, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdc6d3bb000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1848024, ...}) = 0
mmap(NULL, 3961912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc6cdf3000
mprotect(0x7fdc6cfb1000, 2093056, PROT_NONE) = 0
mmap(0x7fdc6d1b0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7fdc6d1b0000
mmap(0x7fdc6d1b6000, 17464, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d1b6000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3ba000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3b8000
arch_prctl(ARCH_SET_FS, 0x7fdc6d3b8740) = 0
mprotect(0x7fdc6d1b0000, 16384, PROT_READ) = 0
mprotect(0x60a000, 4096, PROT_READ)     = 0
mprotect(0x7fdc6d3dd000, 4096, PROT_READ) = 0
munmap(0x7fdc6d3bb000, 128879)          = 0
brk(0)                                  = 0x109f000
brk(0x10c0000)                          = 0x10c0000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7220496, ...}) = 0
mmap(NULL, 7220496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdc6c710000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
open("/dev/net/tun", O_RDONLY)          = 3
fstat(3, {st_mode=S_IFCHR|S_ISVTX|0666, st_rdev=makedev(10, 200), ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, 0x10a1000, 65536)               = -1 EBADFD (File descriptor in bad state)
write(2, "cat: ", 5cat: )                    = 5
write(2, "/dev/net/tun", 12/dev/net/tun)            = 12
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc6d3da000
read(4, "# Locale name alias data base.\n#"..., 4096) = 2570
read(4, "", 4096)                       = 0
close(4)                                = 0
munmap(0x7fdc6d3da000, 4096)            = 0
open("/usr/share/locale/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": File descriptor in bad state", 30: File descriptor in bad state) = 30
write(2, "\n", 1
)                       = 1
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?
Eric des Courtis
  • 5,135
  • 6
  • 24
  • 37