From dc418e098977a97bcbb74839b42dba68f95cc110 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Thu, 31 Oct 2013 05:22:56 -0400 Subject: [PATCH] dtrace: provide a corrected implementation of the 'errno' D variable This commit provides a corrected implementation for the 'errno' D variable. It is defined as holding the error code (if non-zero) during the current system call execution. If the system call is successful, or if no system call is being executed, its value is to be 0. On (Open)Solaris, this was retrieved from a task variable that is assigned an error code as soon as an error is encountered during the processing of a system call, i.e. system calls use a task variable to store any error code encountered during execution, and this is used upon return from the system call to alert userspace of the error code status of the system call. In Linux, system calls are implemented in the more regular fashion (for Linux at least) of returning error codes as return values of functions, and therefore there is no task level variable to consult. So, instead we recognize that at this point) 'errno' only has meaning during the processing of syscall return probes, which are handled from the system call wrapper, after the system call implementation has been executed. It would therefore be sufficient and correct to assign the value of 'errno' at that point, but that would require a task variable to be added to the task struct in order for this value to be recorded. In order to avoid adding a member to the task struct, we (ab)use the fact that we can recognize whether we are executing a D action for a syscall return probe, and if we are *and* if 'errno' is being retrieved, we look at the arg0 value for the probe (which is defined as the return value of the syscall), and if the value is between 0 and -2048, we return the error code it represents as errno. Orabug: 17704568 Signed-off-by: Kris Van Hees Reviewed-by: Chuck Anderson --- dtrace/dtrace_dif.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/dtrace/dtrace_dif.c b/dtrace/dtrace_dif.c index d6339a8dc558..d39a0a61f63b 100644 --- a/dtrace/dtrace_dif.c +++ b/dtrace/dtrace_dif.c @@ -2301,15 +2301,43 @@ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, */ return (uint64_t)current->real_cred->gid; - case DIF_VAR_ERRNO: + case DIF_VAR_ERRNO: { + int64_t arg0; + + ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); + if (!dtrace_priv_proc(state)) return 0; /* - * It is always safe to dereference current, it always points - * to a valid task_struct. + * We need to do some magic here to get the correct semantics + * for the 'errno' variable. It can only have a non-zero value + * when executing a system call, and for Linux, only after the + * actual system call implementation has completed, indicating + * in its return value either an error code (-2048 < errno < 0) + * or a valid result. So, the only time we can expect a valid + * value in errno is during the processing of any return probe + * in the syscall provider. In all other cases, it should have + * the value 0. + * + * So, we only look at probes that match: syscall:::return + */ + if (strncmp(mstate->dtms_probe->dtpr_provider->dtpv_name, + "syscall", 7) != 0) + return 0; + if (strncmp(mstate->dtms_probe->dtpr_name, "return", 6) != 0) + return 0; + + /* + * Error number is present if arg0 lies between 0 and -2048, + * exclusive. */ - return (uint64_t)current->thread.error_code; + arg0 = (int64_t)mstate->dtms_arg[ndx]; + if (arg0 < 0 && arg0 > -2048) + return (uint64_t)-arg0; + + return 0; + } case DIF_VAR_CURCPU: return (uint64_t)(uintptr_t)this_cpu_info; -- 2.50.1