16

カーネルのアップグレード(2.6.24から2.6.33)後に、xinetdデーモンが突然動作を停止しました。私はstraceを実行し、これを見つけました:

[...]
close(3)                                = 0
munmap(0x7f1a93b43000, 4096)            = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=8*1024, rlim_max=16*1024}) = 0
setrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=1024}) = 0
close(3)                                = 4294967287
exit_group(1)                           = ?

したがって、基本的に、closeシステムコールは0または-1とは異なるものを返したように見えます

いくつかのテストを行いましたが、64ビットの実行可能ファイルでのみ発生するようです。

$ file closetest32
closetest32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
$ strace closetest32
execve("./closetest32", ["closetest32"], [/* 286 vars */]) = 0
[ Process PID=4731 runs in 32 bit mode. ]
open("/proc/mounts", O_RDONLY)          = 3
close(3)                                = 0
close(3)                                = -1 EBADF (Bad file descriptor)
_exit(0)                                = ?


$ file closetest64
closetest64: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), statically linked, not stripped
$ strace closetest64
execve("./closetest64", ["closetest64"], [/* 286 vars */]) = 0
open("/proc/mounts", O_RDONLY)          = 3
close(3)                                = 0
close(3)                                = 4294967287
_exit(0)                                = ?

私は次のカーネルを実行しています:

Linux foobar01 2.6.33.9-rt31.64.el5rt #1 SMP PREEMPT RT Wed May 4 10:34:12 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux

最悪の部分は、同じカーネルを持つ別のマシンでバグを再現できないことです。

何か案は ?

編集:要求に応じて:closetest32とclosetest64に使用されるコードは次のとおりです

closetest32.asm:

.section .data

filename:
    .ascii "/proc/mounts"

.section .text
.globl _start
_start:
    xorl %edi, %edi
    movl $5, %eax # open() i386 system call
    leal filename, %ebx # %ebx ---> filename
    movl $0, %esi # O_RDONLY flag into esi
    int $0x80

    xorl %edi, %edi
    movl $6, %eax # close() i386 system call
    movl $3, %ebx # fd 3
    int $0x80

    xorl %edi, %edi
    movl $6, %eax # close() i386 system call
    movl $3, %ebx # fd 3
    int $0x80

    ## terminate program via _exit () system call
    movl $1, %eax # %eax =  _exit() i386 system call
    xorl %ebx, %ebx # %ebx = 0 normal program return code
    int $0x80

次のようにコンパイルされます:

as test32.asm -o test32.o --32
ld -m elf_i386 test32.o -o closetest32

closetest64.asm:

.section .data

filename:
    .ascii "/proc/mounts"

.section .text
.globl _start
_start:
    xorq %rdi, %rdi
    movq $2, %rax # open() system call
    leaq filename, %rdi # %rdi ---> filename
    movq $0, %rsi # O_RDONLY flag into rsi
    syscall

    xorq %rdi, %rdi
    movq $3, %rax # close() system call
    movq $3, %rdi # fd 3
    syscall

    xorq %rdi, %rdi
    movq $3, %rax # close() system call
    movq $3, %rdi # fd 3
    syscall

    ## terminate program via _exit () system call
    movq $60, %rax # %rax = _exit() system call
    xorq %rdi, %rdi # %rdi = 0 normal program return code
    syscall

コンパイル:

as test64.asm -o test64.o
ld test64.o -o closetest64
4

1 に答える 1

1

予想どおり、以前のカーネルバージョンにロールバックすると問題が解決しました。私は実際にはカーネルの専門家ではありませんが、私が理解している限り、@R..によって与えられた答えは理にかなっています。

これは64ビットマシンであるため、1<<32-9は表示されません。問題は、カーネルがこれらの関数の一部の戻り値にintではなくunsignedを内部的に使用し、-EBADFを返すことです。これにより、モジュロ2^64ではなくモジュロ2^32が削減されます。

問題は、syscallエラーの戻り値を処理するlibc syscallラッパーの汎用コードは、比較を行って小さな負の値であるかどうかを確認するときに、戻り値をlongとして処理する必要があることです(一部のsyscallではポインターまたはlongになる可能性があるため)エラーを示す値。しかし、カーネルは(long)(unsigned)-9を返しました。これは、(long)-9とは大きく異なります。または(unsigned long)-9(どちらも機能します)。

于 2012-05-08T09:41:35.200 に答える