Linux Kernel System Call Returns -1 Instead of {-1, -256}

Why System Call returns -1 instead of actual error code

Summary: C in itself doesn't know what exactly has gone wrong, it just knows something is wrong. So to maintain generic standard it just returns -1 as a signal to notify the caller that error has occurred, now it is up to the caller to check the errno for what exactly.

Brief Explanation:
The reason is that all the errno returned from the kernel are defined in the kernel itself.

C is just a generic programing language that receives the errno from the kernel and sets the variable according to it and returns -1 as a signal to indicate an error or failure. So C language in itself doesn't know what exactly has gone wrong, it just receives a signal from the registered error handler of the kernel that some error has occurred, and this error handler then supplies the actual error code.

So for maintaining generic nature C just returns -1. This is useful in other scenarios, lets say you were using C for some other purpose like embedded systems development. Now here several error codes are different from the Linux kernel error codes. So how does C know what error code to return if something goes wrong in the embedded system?

The answer is C doesn't actually know what is wrong. The registered error handler of the system will signal your C program that something has gone wrong and give you the appropriate error code for it and C will return -1 and set the errno.

Now, why can't C return the Errno: Answer is to maintain generic design. This allowed C developers to not care much about the return type of the error signal. If you interpret it correctly -1 returned by the C program is just a generic signal to userland API or shell script invoking the C program that something has gone wrong and if the invoker of C program needs to know what exactly has gone wrong they can check up the errno variable.

What are the return values of system calls in Assembly?

See also this excellent LWN article about system calls which assumes C knowledge.

Also: The Definitive Guide to Linux System Calls (on x86), and related: What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?


C is the language of Unix systems programming, so all the documentation is in terms of C. And then there's documentation for the minor differences between the C interface and the asm on any given platform, usually in the Notes section of man pages.

sys_read means the raw system call (as opposed to the libc wrapper function). The kernel implementation of the read system call is a kernel function called sys_read(). You can't call it with a call instruction, because it's in the kernel, not a library. But people still talk about "calling sys_read" to distinguish it from the libc function call. However, it's ok to say read even when you mean the raw system call (especially when the libc wrapper doesn't do anything special), like I do in this answer.

Also note that syscall.h defines constants like SYS_read with the actual system call number, or asm/unistd.h for the Linux __NR_read names for the same constants. (The value you put in EAX before an int 0x80 or syscall instruction).


Linux system call return values (in EAX/RAX on x86) are either "normal" success, or a -errno code for error. e.g. -EFAULT if you pass an invalid pointer. This behaviour is documented in the syscalls(2) man page.

-1 to -4095 means error, anything else means success. See AOSP non-obvious syscall() implementation for more details on this -4095UL .. -1UL range, which is portable across architectures on Linux, and applies to every system call. (In the future, a different architecture could use a different value for MAX_ERRNO, but the value for existing arches like x86-64 is guaranteed to stay the same as part of Linus's don't-break-userspace policy of keeping kernel ABIs stable.)

For example, glibc's generic syscall(2) wrapper function uses this sequence: cmp rax, -4095 / jae SYSCALL_ERROR_LABEL, which is guaranteed to be future-proof for all Linux system calls.

You can use that wrapper function to make any system call, like syscall( __NR_mmap, ... ). (Or use an inline-asm wrapper header like https://github.com/linux-on-ibm-z/linux-syscall-support/blob/master/linux_syscall_support.h that has safe inline-asm for multiple ISAs, avoiding problems like missing "memory" clobbers that some other inline-asm wrappers have.)


Interesting cases include getpriority where the kernel ABI maps the -20..19 return-value range to 1..40, and libc decodes it. More details in a related answer about decoding syscall error return values.

For mmap, if you wanted you could also detect error just by checking that the return value isn't page-aligned (e.g. any non-zero bits in the low 11, for a 4k page size), if that would be more efficient than checking p > -4096ULL.


To find the actual numeric values of constants for a specific platform, you need to find the C header file where they're #defined. See my answer on a question about that for details. e.g. in asm-generic/errno-base.h / asm-generic/errno.h.


The meanings of return values for each sys call are documented in the section 2 man pages, like read(2). (sys_read is the raw system call that the glibc read() function is a very thin wrapper for.) Most man pages have a whole section for the return value. e.g.

RETURN VALUE

On success, the number of bytes read is returned (zero indicates
end of file), and the file position is advanced by this number. It
is not an error if this number is smaller than the number of bytes
requested; this may happen for example because fewer bytes are

actually available right now (maybe because we were close to end-of-

file, or because we are reading from a pipe, or from a terminal), or

because read() was interrupted by a signal. See also NOTES.

On error, -1 is returned, and errno is set appropriately. In this
case, it is left unspecified whether the file position (if any)

changes.

Note that the last paragraph describes how the glibc wrapper decodes the value and sets errno to -EAX if the raw system call's return value is negative, so errno=EFAULT and return -1 if the raw system call returned -EFAULT.

And there's a whole section listing all the possible error codes that read() is allowed to return, and what they mean specifically for read(). (POSIX standardizes most of this behaviour.)

printf in system call returns malformed output

printf/IOLog uses a buffer for sending messages to userspace, where they're then logged to the system log. If you output a lot of messages, this buffer can get full before the logging daemon has a chance to clear it, and your messages will be truncated and start running into each other. (printf/IOLog will return as soon as the message has been submitted to the buffer, and will not wait for the daemon to receive it.)

Additionally, I'm not sure if kernel printf/IOLog is in any way thread-safe, so it might also be the case that calling it from multiple threads simultaneously causes race conditions. I'd have to check the source to be sure.

kprintf is fully synchronous and will block until your message has been transmitted on the serial port or submitted via firewire. It's also thread-safe. In the firewire case, the kprintf output is also buffered, so if you output a lot of messages, this can fill up too and cause truncation. The fwkpfv command lets you specify the buffer size using the --buffer argument though, so you can counteract that. The downside of kprintf being synchronous is that it can slow the system down considerably with a high volume of messages.

Error handling for system calls in x86 assembly, under Linux

The legitimate return values from system calls are always either positive (signed) integers or addresses. When they are positive integers, the negative values can be used as error codes, so any negative value is an error.

So the only tricky case is when the return value is an address. It turns out that the addresses corresponding to integers in the range -4096..-1 are all in a kernel reserved page that will never be returned by the kernel -- so any bit pattern in that range will only ever be returned as an error code, and not as a valid address.

In addition, ALL addresses that correspond to negative integers in x86_64 are reserved for the kernel or invalid -- user addresses will always be in the range 0..247-1. So for x86_64 you need only check the sign bit (top bit) of %rax -- if it is set, there was an error.

test %rax, %rax
js error

Fo 32-bit x86 code, this is not the case -- some valid addresses are negative numbbers. So in that case, you need to explicitly check for the error range, which is actually easiest to do with an unsigned comparison

cmpl  %eax, 0xfffff000   # unsigned 2^32 - 4096, aka signed -4096
ja error # -4095 .. -1 is an error, anything else is non-error

Why is 256 Returned When Calling A Bash Script Using Python

This is what os.system() returns; on Unix-like systems, the high 8 bits are the result code and the lower 8 are the signal (0 if no signal).

The proper way to do this in Python would be

check = False
with open("/etc/apt/sources.list") as repo:
for line in repo:
if "deb https://http.kali.org/kali kali-rolling main contrib non-free" \
in line or "deb https://http.kali.org/kali kali-rolling main non-free contrib" \
in line:
check = True
break

The proper way to do this in a shell script would be

if grep -q -e "deb https://http\.kali\.org/kali kali-rolling main contrib non-free" -e "deb https://http\.kali\.org/kali kali-rolling main non-free contrib" /etc/apt/sources.list
then ...

(or refactor to use grep -E with a single regex to cover both expressions; see below for an attempt).

If you want to put this in a function, the exit status from grep will be 0 or 1 so there is no need to separately return anything other than that.

The proper way to call this from a Python script would be

import subprocess

check = subprocess.check_call(['grep', '-q', '-E',
r'deb https://http\.kali\.org/kali kali-rolling main( contrib| non-free){2}',
'/etc/apt/sources.list'])


Related Topics



Leave a reply



Submit