Dealing with errno

I have recently begun experimenting with kernel modules and have several questions about dealing with errno. Since this post is quite lengthy I have bolded the most relevant parts. I'm aware that errno is not part of the FreeBSD kernel and is actually part of the C language. From what I understand, the return value that a function stores in EAX prior to exiting is what decides whether errno is needed or not.

The problem I am having is that I have yet to find a way to 'catch' the value of errno after a system call executes while within a hooked syscall function. In short, I want to find out when a system call causes errno to be set, and what with.

For example, in my function 'open_hook':

Code:
static int
open_hook(struct thread *td, void *args)
{
	int rval;
	
	rval = open(td, args);
	if(rval == EACESS)
		uprintf("Error was 'Permission denied'\n");
	...
}

Getting the value through rval is the only way I have been able to do this, and I'm positive there are far better ways. One that comes to mind is modifying the syscall 'dispatcher' to include something that works on all system calls, rather than relying on hooks.

Some ideas I have (but have no idea how to implement) to remedy the problem are:

- Using cpu_set_syscall_retval(), found @ http://lists.freebsd.org/pipermail/freebsd-hackers/2010-March/031094.html
- Checking td_errno
- Hooking some sort of function related to traps

Basically what I'm asking is:

- Are those ideas I mentioned dead-ends?
- Are syscall hooks the wrong way to go about detecting EACCES et al?
- Is there anything global (ie. a function) that handles errno?

I suspect that I am in far over my head but I figured I would ask incase there is a simple solution that I am overlooking. This has been bugging me for quite a while, so if I am indeed in over my head I would really appreciate some recommended reading material.

Thank you.
 
When you make a call that generates an error, usually the return from the call is a -1. That only tells you an error occured but you must read errno to find the error number so you can look up the error description located in errno.h.

Or am I not understanding you?
 
From what I understand, the return value that a function stores in EAX prior to exiting is what decides whether errno is needed or not.
Actually, it's the carry bit that decides whether errno is needed or not.
Code:
cpu_set_syscall_retval(struct thread *td, int error)
{

         switch (error) {
         case 0:
                 td->td_frame->tf_eax = td->td_retval[0];
                 td->td_frame->tf_edx = td->td_retval[1];
                 td->td_frame->tf_eflags &= ~PSL_C;
                 break;
        
         [...]

         default:
                 if (td->td_proc->p_sysent->sv_errsize) {
                         if (error >= td->td_proc->p_sysent->sv_errsize)
                                 error = -1;     /* XXX */
                         else
                                 error = td->td_proc->p_sysent->sv_errtbl[error];
                 }
                 td->td_frame->tf_eax = error;
                 td->td_frame->tf_eflags |= PSL_C;
                 break;
         }
}

The routines that implement the system call in the kernel return the respective errno values in case of a failure or 0 in case of a successful call. The kernel system call handler (syscall()) checks the return value and if it is a positive integer, the carry bit (bit 0 of the EFLAGS register, aka CF) is set and the return value is stored in EAX. If the return value is 0 then the handler stores td_retval to EAX. In case of a successful system call invocation td_retval contains the value returned from the kernel (i.e., the file descriptor, bytes of data read, etc). Then, the low-level libc syscall dispatcher checks the carry bit and if it's set, it calls __error() which assigns errno EAX's current value and then changes EAX to -1. If the carry bit is cleared it just returns EAX. If upon a system call completion the carry bit is cleared, then the __error() function is not called at all.

Petros Barbagiannis
 
Last edited:
jake_t said:
In short, I want to find out when a system call causes errno to be set, and what with.
As the doctor mentioned, most calls will return -1 to indicate an error. If you don't feel like relying on that (not all syscalls behave that way, after all), you can always "just" check the value of errno directly. It's zero until set otherwise. One thing to keep in mind, though, is that once errno has been set (to something other than 0), it retains that value until either another error overwrites it or you manually reset it to 0. The latter generally isn't very good programming practice, but if you're careful enough it should be ok.

Hope this helps,

Fonz
 
jake_t said:
I'm aware that errno is not part of the FreeBSD kernel and is actually part of the C language.
% nitpick -s on
Actually, it's part of the standard library, not of the C language itself :stud

Fonz
 
Back
Top