Close() x86_64 системный вызов странное возвращаемое значение
Мой демон xinetd внезапно прекратил работу после обновления ядра (с 2.6.24 до 2.6.33).
Я запустил 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) = ?
В принципе, похоже, что закрытый системный вызов возвращает нечто отличное от 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
Хуже всего то, что я не могу воспроизвести ошибку на другой машине с тем же ядром.
Любые идеи?
EDIT: в соответствии с запросом: здесь код, используемый для 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
Ответы
Ответ 1
Как и ожидалось, откат к предыдущей версии ядра решил проблему. Я не специалист по ядрам, но, насколько я понимаю, ответ @R.. имеет смысл:
Это 64-разрядная машина, поэтому 1 < 32-9 никогда не должно появляться. Проблема в том, что ядро внутренне использует unsigned вместо int для возвращаемого значения некоторых из этих функций, а затем возвращает -EBADF, который уменьшается по модулю 2 ^ 32, а не по модулю 2 ^ 64
Проблема заключается в том, что общий код в оболочках libc syscall, который обрабатывает возвраты ошибки syscall, должен обрабатывать возвращаемое значение как длинное (поскольку он может быть указателем или длинным для некоторых системных вызовов) при сравнении, чтобы увидеть, небольшое отрицательное значение, которое указывает на ошибку. Но ядро вернулось (long) (unsigned) -9, которое сильно отличается от (long) -9. или (unsigned long) -9 (любой из них работал бы).