シェルと/proc/sys/fs/nr_open
シェルはファイルIDを0/1/2/255を有しており、外部コマンドはforkで実行されるプロセスで、ファイルその物はクーロンでないため継承されませんが、コマンドプロセスのファイルIDバッファは、親プロセスの最大ファイルIDを基準に取得されます。ただし、それがsysctl_nr_openより大きいとsysctl_nr_openを基準にしたバッファが取得されます。
catは外部コマンドでechoはシェル下の実装です。longが32ビットのシステムで、取得数がmax ID < sysctl_nr_openならシェルのmax ID値で取得されますが(max ID値を基準にした1024バイト単位)、そうでないならsysctl_nr_openの32の倍数値で取得されます。
sysctl_nr_open=224なら32×7=224の224で255に満たない故エラーです。sysctl_nr_open=255である必要はありません。sysctl_nr_open=225なら32×8=256で、マックスIDが255のバッファが取得でき、外部コマンドの実行が可能となります。
sysctl_nr_open - 1の-1は、sysctl_nr_openが32の倍数値の時、次の32の倍数値とさせないためで、sysctl_nr_open=32なら64に成ってしまいますが、-1で演算することで32は32です。
ファイルID取得制限は、sysctl_nr_open値そのものです。
catは外部コマンドでechoはシェル下の実装です。longが32ビットのシステムで、取得数がmax ID < sysctl_nr_openならシェルのmax ID値で取得されますが(max ID値を基準にした1024バイト単位)、そうでないならsysctl_nr_openの32の倍数値で取得されます。
sysctl_nr_open=224なら32×7=224の224で255に満たない故エラーです。sysctl_nr_open=255である必要はありません。sysctl_nr_open=225なら32×8=256で、マックスIDが255のバッファが取得でき、外部コマンドの実行が可能となります。
[root@localhost ~]# ps PID TTY TIME CMD 1243 pts/0 00:00:00 bash 1300 pts/0 00:00:00 ps [root@localhost c]# ls -l /proc/1243/fd 合計 0 lrwx------ 1 root root 64 8月 16 00:17 0 -> /dev/pts/0 lrwx------ 1 root root 64 8月 16 00:17 1 -> /dev/pts/0 lrwx------ 1 root root 64 8月 16 00:17 2 -> /dev/pts/0 lrwx------ 1 root root 64 8月 16 00:17 255 -> /dev/pts/0 [root@localhost ~]# cat /proc/sys/fs/nr_open 1048576 [root@localhost ~]# echo 224 > /proc/sys/fs/nr_open [root@localhost ~]# cat /proc/sys/fs/nr_open -bash: fork: ファイルを開きすぎです [root@localhost ~]# echo 225 > /proc/sys/fs/nr_open [root@localhost ~]# cat /proc/sys/fs/nr_open 225copy_process()からcopy_files()、そしてdup_fd()でタスクのtsk->filesの構造体を取得します。
static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
{
struct files_struct *oldf, *newf;
int error = 0;
oldf = current->files;
if (!oldf)
goto out;
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
goto out;
}
newf = dup_fd(oldf, &error);
if (!newf)
goto out;
tsk->files = newf;
error = 0;
out:
return error;
}
count_open_files()で親タスクのカレントの取得している最大ファイルIDで、それがデフォルトサイズNR_OPEN_DEFAULTより大きいと、alloc_fdtable()でファイルIDバッファを取得します。取得サイズがsysctl_nr_open以上ならsysctl_nr_opeによる取得でnew_fdt->max_fds < open_filesとなりエラーです。
#define NR_OPEN_DEFAULT BITS_PER_LONG
struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
{
struct files_struct *newf;
struct file **old_fds, **new_fds;
int open_files, size, i;
struct fdtable *old_fdt, *new_fdt;
*errorp = -ENOMEM;
newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
if (!newf)
goto out;
atomic_set(&newf->count, 1);
spin_lock_init(&newf->file_lock);
newf->next_fd = 0;
new_fdt = &newf->fdtab;
new_fdt->max_fds = NR_OPEN_DEFAULT;
new_fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init;
new_fdt->open_fds = (fd_set *)&newf->open_fds_init;
new_fdt->fd = &newf->fd_array[0];
new_fdt->next = NULL;
spin_lock(&oldf->file_lock);
old_fdt = files_fdtable(oldf);
open_files = count_open_files(old_fdt);
while (unlikely(open_files > new_fdt->max_fds)) {
spin_unlock(&oldf->file_lock);
if (new_fdt != &newf->fdtab)
__free_fdtable(new_fdt);
new_fdt = alloc_fdtable(open_files - 1);
if (!new_fdt) {
*errorp = -ENOMEM;
goto out_release;
}
if (unlikely(new_fdt->max_fds < open_files)) {
__free_fdtable(new_fdt);
*errorp = -EMFILE;
goto out_release;
}
spin_lock(&oldf->file_lock);
old_fdt = files_fdtable(oldf);
open_files = count_open_files(old_fdt);
}
old_fds = old_fdt->fd;
new_fds = new_fdt->fd;
memcpy(new_fdt->open_fds->fds_bits,
old_fdt->open_fds->fds_bits, open_files/8);
memcpy(new_fdt->close_on_exec->fds_bits,
old_fdt->close_on_exec->fds_bits, open_files/8);
for (i = open_files; i != 0; i--) {
struct file *f = *old_fds++;
if (f) {
get_file(f);
} else {
FD_CLR(open_files - i, new_fdt->open_fds);
}
rcu_assign_pointer(*new_fds++, f);
}
spin_unlock(&oldf->file_lock);
size = (new_fdt->max_fds - open_files) * sizeof(struct file *);
memset(new_fds, 0, size);
if (new_fdt->max_fds > open_files) {
int left = (new_fdt->max_fds-open_files)/8;
int start = open_files / (8 * sizeof(unsigned long));
memset(&new_fdt->open_fds->fds_bits[start], 0, left);
memset(&new_fdt->close_on_exec->fds_bits[start], 0, left);
}
rcu_assign_pointer(newf->fdt, new_fdt);
return newf;
out_release:
kmem_cache_free(files_cachep, newf);
out:
return NULL;
}
nr(取得サイズ) > sysctl_nr_openなら nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1で、sysctl_nr_openの32倍数サイズとなります。、
static struct fdtable * alloc_fdtable(unsigned int nr)
{
struct fdtable *fdt;
char *data;
:
if (unlikely(nr > sysctl_nr_open))
nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
if (!fdt)
goto out;
fdt->max_fds = nr;
data = alloc_fdmem(nr * sizeof(struct file *));
if (!data)
goto out_fdt;
fdt->fd = (struct file **)data;
data = alloc_fdmem(max_t(unsigned int,
2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES));
if (!data)
goto out_arr;
fdt->open_fds = (fd_set *)data;
data += nr / BITS_PER_BYTE;
fdt->close_on_exec = (fd_set *)data;
fdt->next = NULL;
return fdt;
out_arr:
free_fdmem(fdt->fd);
out_fdt:
kfree(fdt);
out:
return NULL;
}
補足
nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1で、(BITS_PER_LONG - 1)) + 1は、sysctl_nr_open -1 の下記5ビット(BITS_PER_LONG - 1)をセットし、+1とする事で下記5ビットは繰り上げリセットされ、結果32の倍数値となります。sysctl_nr_open - 1の-1は、sysctl_nr_openが32の倍数値の時、次の32の倍数値とさせないためで、sysctl_nr_open=32なら64に成ってしまいますが、-1で演算することで32は32です。
ファイルID取得制限は、sysctl_nr_open値そのものです。






