Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Audit: push audit success and retcode into arch ptrace.h

The audit system previously expected arches calling to audit_syscall_exit to
supply as arguments if the syscall was a success and what the return code was.
Audit also provides a helper AUDITSC_RESULT which was supposed to simplify things
by converting from negative retcodes to an audit internal magic value stating
success or failure. This helper was wrong and could indicate that a valid
pointer returned to userspace was a failed syscall. The fix is to fix the
layering foolishness. We now pass audit_syscall_exit a struct pt_reg and it
in turns calls back into arch code to collect the return value and to
determine if the syscall was a success or failure. We also define a generic
is_syscall_success() macro which determines success/failure based on if the
value is < -MAX_ERRNO. This works for arches like x86 which do not use a
separate mechanism to indicate syscall failure.

We make both the is_syscall_success() and regs_return_value() static inlines
instead of macros. The reason is because the audit function must take a void*
for the regs. (uml calls theirs struct uml_pt_regs instead of just struct
pt_regs so audit_syscall_exit can't take a struct pt_regs). Since the audit
function takes a void* we need to use static inlines to cast it back to the
arch correct structure to dereference it.

The other major change is that on some arches, like ia64, MIPS and ppc, we
change regs_return_value() to give us the negative value on syscall failure.
THE only other user of this macro, kretprobe_example.c, won't notice and it
makes the value signed consistently for the audit functions across all archs.

In arch/sh/kernel/ptrace_64.c I see that we were using regs[9] in the old
audit code as the return value. But the ptrace_64.h code defined the macro
regs_return_value() as regs[3]. I have no idea which one is correct, but this
patch now uses the regs_return_value() function, so it now uses regs[3].

For powerpc we previously used regs->result but now use the
regs_return_value() function which uses regs->gprs[3]. regs->gprs[3] is
always positive so the regs_return_value(), much like ia64 makes it negative
before calling the audit code when appropriate.

Signed-off-by: Eric Paris <eparis@redhat.com>
Acked-by: H. Peter Anvin <hpa@zytor.com> [for x86 portion]
Acked-by: Tony Luck <tony.luck@intel.com> [for ia64]
Acked-by: Richard Weinberger <richard@nod.at> [for uml]
Acked-by: David S. Miller <davem@davemloft.net> [for sparc]
Acked-by: Ralf Baechle <ralf@linux-mips.org> [for mips]
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> [for ppc]

authored by

Eric Paris and committed by
Al Viro
d7e7528b 85e7bac3

+132 -74
+12 -1
arch/ia64/include/asm/ptrace.h
··· 246 246 return regs->ar_bspstore; 247 247 } 248 248 249 - #define regs_return_value(regs) ((regs)->r8) 249 + static inline int is_syscall_success(struct pt_regs *regs) 250 + { 251 + return regs->r10 != -1; 252 + } 253 + 254 + static inline long regs_return_value(struct pt_regs *regs) 255 + { 256 + if (is_syscall_success(regs)) 257 + return regs->r8; 258 + else 259 + return -regs->r8; 260 + } 250 261 251 262 /* Conserve space in histogram by encoding slot bits in address 252 263 * bits 2 and 3 rather than bits 0 and 1.
+1 -8
arch/ia64/kernel/ptrace.c
··· 1268 1268 { 1269 1269 int step; 1270 1270 1271 - if (unlikely(current->audit_context)) { 1272 - int success = AUDITSC_RESULT(regs.r10); 1273 - long result = regs.r8; 1274 - 1275 - if (success != AUDITSC_SUCCESS) 1276 - result = -result; 1277 - audit_syscall_exit(success, result); 1278 - } 1271 + audit_syscall_exit(&regs); 1279 1272 1280 1273 step = test_thread_flag(TIF_SINGLESTEP); 1281 1274 if (step || test_thread_flag(TIF_SYSCALL_TRACE))
+5
arch/microblaze/include/asm/ptrace.h
··· 61 61 #define instruction_pointer(regs) ((regs)->pc) 62 62 #define profile_pc(regs) instruction_pointer(regs) 63 63 64 + static inline long regs_return_value(struct pt_regs *regs) 65 + { 66 + return regs->r3; 67 + } 68 + 64 69 #else /* __KERNEL__ */ 65 70 66 71 /* pt_regs offsets used by gdbserver etc in ptrace syscalls */
+1 -2
arch/microblaze/kernel/ptrace.c
··· 159 159 { 160 160 int step; 161 161 162 - if (unlikely(current->audit_context)) 163 - audit_syscall_exit(AUDITSC_RESULT(regs->r3), regs->r3); 162 + audit_syscall_exit(regs); 164 163 165 164 step = test_thread_flag(TIF_SINGLESTEP); 166 165 if (step || test_thread_flag(TIF_SYSCALL_TRACE))
+13 -1
arch/mips/include/asm/ptrace.h
··· 137 137 */ 138 138 #define user_mode(regs) (((regs)->cp0_status & KU_MASK) == KU_USER) 139 139 140 - #define regs_return_value(_regs) ((_regs)->regs[2]) 140 + static inline int is_syscall_success(struct pt_regs *regs) 141 + { 142 + return !regs->regs[7]; 143 + } 144 + 145 + static inline long regs_return_value(struct pt_regs *regs) 146 + { 147 + if (is_syscall_success(regs)) 148 + return regs->regs[2]; 149 + else 150 + return -regs->regs[2]; 151 + } 152 + 141 153 #define instruction_pointer(regs) ((regs)->cp0_epc) 142 154 #define profile_pc(regs) instruction_pointer(regs) 143 155
+1 -3
arch/mips/kernel/ptrace.c
··· 572 572 */ 573 573 asmlinkage void syscall_trace_leave(struct pt_regs *regs) 574 574 { 575 - if (unlikely(current->audit_context)) 576 - audit_syscall_exit(AUDITSC_RESULT(regs->regs[7]), 577 - -regs->regs[2]); 575 + audit_syscall_exit(regs); 578 576 579 577 if (!(current->ptrace & PT_PTRACED)) 580 578 return;
+12 -1
arch/powerpc/include/asm/ptrace.h
··· 86 86 #define instruction_pointer(regs) ((regs)->nip) 87 87 #define user_stack_pointer(regs) ((regs)->gpr[1]) 88 88 #define kernel_stack_pointer(regs) ((regs)->gpr[1]) 89 - #define regs_return_value(regs) ((regs)->gpr[3]) 89 + static inline int is_syscall_success(struct pt_regs *regs) 90 + { 91 + return !(regs->ccr & 0x10000000); 92 + } 93 + 94 + static inline long regs_return_value(struct pt_regs *regs) 95 + { 96 + if (is_syscall_success(regs)) 97 + return regs->gpr[3]; 98 + else 99 + return -regs->gpr[3]; 100 + } 90 101 91 102 #ifdef CONFIG_SMP 92 103 extern unsigned long profile_pc(struct pt_regs *regs);
+1 -3
arch/powerpc/kernel/ptrace.c
··· 1748 1748 { 1749 1749 int step; 1750 1750 1751 - if (unlikely(current->audit_context)) 1752 - audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, 1753 - regs->result); 1751 + audit_syscall_exit(regs); 1754 1752 1755 1753 if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) 1756 1754 trace_sys_exit(regs, regs->result);
+5 -1
arch/s390/include/asm/ptrace.h
··· 541 541 #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) 542 542 #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN) 543 543 #define user_stack_pointer(regs)((regs)->gprs[15]) 544 - #define regs_return_value(regs)((regs)->gprs[2]) 545 544 #define profile_pc(regs) instruction_pointer(regs) 545 + 546 + static inline long regs_return_value(struct pt_regs *regs) 547 + { 548 + return regs->gprs[2]; 549 + } 546 550 547 551 int regs_query_register_offset(const char *name); 548 552 const char *regs_query_register_name(unsigned int offset);
+1 -3
arch/s390/kernel/ptrace.c
··· 751 751 752 752 asmlinkage void do_syscall_trace_exit(struct pt_regs *regs) 753 753 { 754 - if (unlikely(current->audit_context)) 755 - audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), 756 - regs->gprs[2]); 754 + audit_syscall_exit(regs); 757 755 758 756 if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) 759 757 trace_sys_exit(regs, regs->gprs[2]);
+4 -1
arch/sh/include/asm/ptrace_32.h
··· 76 76 #ifdef __KERNEL__ 77 77 78 78 #define MAX_REG_OFFSET offsetof(struct pt_regs, tra) 79 - #define regs_return_value(_regs) ((_regs)->regs[0]) 79 + static inline long regs_return_value(struct pt_regs *regs) 80 + { 81 + return regs->regs[0]; 82 + } 80 83 81 84 #endif /* __KERNEL__ */ 82 85
+4 -1
arch/sh/include/asm/ptrace_64.h
··· 13 13 #ifdef __KERNEL__ 14 14 15 15 #define MAX_REG_OFFSET offsetof(struct pt_regs, tregs[7]) 16 - #define regs_return_value(_regs) ((_regs)->regs[3]) 16 + static inline long regs_return_value(struct pt_regs *regs) 17 + { 18 + return regs->regs[3]; 19 + } 17 20 18 21 #endif /* __KERNEL__ */ 19 22
+1 -3
arch/sh/kernel/ptrace_32.c
··· 530 530 { 531 531 int step; 532 532 533 - if (unlikely(current->audit_context)) 534 - audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]), 535 - regs->regs[0]); 533 + audit_syscall_exit(regs); 536 534 537 535 if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) 538 536 trace_sys_exit(regs, regs->regs[0]);
+1 -3
arch/sh/kernel/ptrace_64.c
··· 548 548 { 549 549 int step; 550 550 551 - if (unlikely(current->audit_context)) 552 - audit_syscall_exit(AUDITSC_RESULT(regs->regs[9]), 553 - regs->regs[9]); 551 + audit_syscall_exit(regs); 554 552 555 553 if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) 556 554 trace_sys_exit(regs, regs->regs[9]);
+9 -1
arch/sparc/include/asm/ptrace.h
··· 207 207 #define instruction_pointer(regs) ((regs)->tpc) 208 208 #define instruction_pointer_set(regs, val) ((regs)->tpc = (val)) 209 209 #define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP]) 210 - #define regs_return_value(regs) ((regs)->u_regs[UREG_I0]) 210 + static inline int is_syscall_success(struct pt_regs *regs) 211 + { 212 + return !(regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY)); 213 + } 214 + 215 + static inline long regs_return_value(struct pt_regs *regs) 216 + { 217 + return regs->u_regs[UREG_I0]; 218 + } 211 219 #ifdef CONFIG_SMP 212 220 extern unsigned long profile_pc(struct pt_regs *); 213 221 #else
+1 -10
arch/sparc/kernel/ptrace_64.c
··· 1086 1086 1087 1087 asmlinkage void syscall_trace_leave(struct pt_regs *regs) 1088 1088 { 1089 - #ifdef CONFIG_AUDITSYSCALL 1090 - if (unlikely(current->audit_context)) { 1091 - unsigned long tstate = regs->tstate; 1092 - int result = AUDITSC_SUCCESS; 1089 + audit_syscall_exit(regs); 1093 1090 1094 - if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) 1095 - result = AUDITSC_FAILURE; 1096 - 1097 - audit_syscall_exit(result, regs->u_regs[UREG_I0]); 1098 - } 1099 - #endif 1100 1091 if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) 1101 1092 trace_sys_exit(regs, regs->u_regs[UREG_G1]); 1102 1093
+2 -2
arch/um/kernel/ptrace.c
··· 175 175 UPT_SYSCALL_ARG2(regs), 176 176 UPT_SYSCALL_ARG3(regs), 177 177 UPT_SYSCALL_ARG4(regs)); 178 - else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)), 179 - UPT_SYSCALL_RET(regs)); 178 + else 179 + audit_syscall_exit(regs); 180 180 } 181 181 182 182 /* Fake a debug trap */
+5 -5
arch/x86/ia32/ia32entry.S
··· 14 14 #include <asm/segment.h> 15 15 #include <asm/irqflags.h> 16 16 #include <linux/linkage.h> 17 + #include <linux/err.h> 17 18 18 19 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */ 19 20 #include <linux/elf-em.h> ··· 209 208 TRACE_IRQS_ON 210 209 sti 211 210 movl %eax,%esi /* second arg, syscall return value */ 212 - cmpl $0,%eax /* is it < 0? */ 213 - setl %al /* 1 if so, 0 if not */ 211 + cmpl $-MAX_ERRNO,%eax /* is it an error ? */ 212 + setbe %al /* 1 if so, 0 if not */ 214 213 movzbl %al,%edi /* zero-extend that into %edi */ 215 - inc %edi /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */ 216 - call audit_syscall_exit 217 - movl RAX-ARGOFFSET(%rsp),%eax /* reload syscall return value */ 214 + call __audit_syscall_exit 215 + movq RAX-ARGOFFSET(%rsp),%rax /* reload syscall return value */ 218 216 movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi 219 217 cli 220 218 TRACE_IRQS_OFF
+4 -4
arch/x86/kernel/entry_32.S
··· 42 42 */ 43 43 44 44 #include <linux/linkage.h> 45 + #include <linux/err.h> 45 46 #include <asm/thread_info.h> 46 47 #include <asm/irqflags.h> 47 48 #include <asm/errno.h> ··· 467 466 TRACE_IRQS_ON 468 467 ENABLE_INTERRUPTS(CLBR_ANY) 469 468 movl %eax,%edx /* second arg, syscall return value */ 470 - cmpl $0,%eax /* is it < 0? */ 471 - setl %al /* 1 if so, 0 if not */ 469 + cmpl $-MAX_ERRNO,%eax /* is it an error ? */ 470 + setbe %al /* 1 if so, 0 if not */ 472 471 movzbl %al,%eax /* zero-extend that */ 473 - inc %eax /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */ 474 - call audit_syscall_exit 472 + call __audit_syscall_exit 475 473 DISABLE_INTERRUPTS(CLBR_ANY) 476 474 TRACE_IRQS_OFF 477 475 movl TI_flags(%ebp), %ecx
+5 -5
arch/x86/kernel/entry_64.S
··· 55 55 #include <asm/paravirt.h> 56 56 #include <asm/ftrace.h> 57 57 #include <asm/percpu.h> 58 + #include <linux/err.h> 58 59 59 60 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */ 60 61 #include <linux/elf-em.h> ··· 564 563 jmp system_call_fastpath 565 564 566 565 /* 567 - * Return fast path for syscall audit. Call audit_syscall_exit() 566 + * Return fast path for syscall audit. Call __audit_syscall_exit() 568 567 * directly and then jump back to the fast path with TIF_SYSCALL_AUDIT 569 568 * masked off. 570 569 */ 571 570 sysret_audit: 572 571 movq RAX-ARGOFFSET(%rsp),%rsi /* second arg, syscall return value */ 573 - cmpq $0,%rsi /* is it < 0? */ 574 - setl %al /* 1 if so, 0 if not */ 572 + cmpq $-MAX_ERRNO,%rsi /* is it < -MAX_ERRNO? */ 573 + setbe %al /* 1 if so, 0 if not */ 575 574 movzbl %al,%edi /* zero-extend that into %edi */ 576 - inc %edi /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */ 577 - call audit_syscall_exit 575 + call __audit_syscall_exit 578 576 movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi 579 577 jmp sysret_check 580 578 #endif /* CONFIG_AUDITSYSCALL */
+1 -2
arch/x86/kernel/ptrace.c
··· 1414 1414 { 1415 1415 bool step; 1416 1416 1417 - if (unlikely(current->audit_context)) 1418 - audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax); 1417 + audit_syscall_exit(regs); 1419 1418 1420 1419 if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) 1421 1420 trace_sys_exit(regs, regs->ax);
+2 -2
arch/x86/kernel/vm86_32.c
··· 335 335 if (info->flags & VM86_SCREEN_BITMAP) 336 336 mark_screen_rdonly(tsk->mm); 337 337 338 - /*call audit_syscall_exit since we do not exit via the normal paths */ 338 + /*call __audit_syscall_exit since we do not exit via the normal paths */ 339 339 if (unlikely(current->audit_context)) 340 - audit_syscall_exit(AUDITSC_RESULT(0), 0); 340 + __audit_syscall_exit(1, 0); 341 341 342 342 __asm__ __volatile__( 343 343 "movl %0,%%esp\n\t"
+5
arch/x86/um/shared/sysdep/ptrace.h
··· 3 3 #else 4 4 #include "ptrace_64.h" 5 5 #endif 6 + 7 + static inline long regs_return_value(struct uml_pt_regs *regs) 8 + { 9 + return UPT_SYSCALL_RET(regs); 10 + }
+14 -8
include/linux/audit.h
··· 26 26 27 27 #include <linux/types.h> 28 28 #include <linux/elf-em.h> 29 + #include <linux/ptrace.h> 29 30 30 31 /* The netlink messages for the audit system is divided into blocks: 31 32 * 1000 - 1099 are for commanding the audit system ··· 409 408 void *lsm_rule; 410 409 }; 411 410 412 - #define AUDITSC_INVALID 0 413 - #define AUDITSC_SUCCESS 1 414 - #define AUDITSC_FAILURE 2 415 - #define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS ) 416 411 extern int __init audit_register_class(int class, unsigned *list); 417 412 extern int audit_classify_syscall(int abi, unsigned syscall); 418 413 extern int audit_classify_arch(int arch); ··· 421 424 extern void audit_syscall_entry(int arch, 422 425 int major, unsigned long a0, unsigned long a1, 423 426 unsigned long a2, unsigned long a3); 424 - extern void audit_syscall_exit(int failed, long return_code); 427 + extern void __audit_syscall_exit(int ret_success, long ret_value); 425 428 extern void __audit_getname(const char *name); 426 429 extern void audit_putname(const char *name); 427 430 extern void __audit_inode(const char *name, const struct dentry *dentry); ··· 434 437 { 435 438 void *p = current->audit_context; 436 439 return !p || *(int *)p; 440 + } 441 + static inline void audit_syscall_exit(void *pt_regs) 442 + { 443 + if (unlikely(current->audit_context)) { 444 + int success = is_syscall_success(pt_regs); 445 + int return_code = regs_return_value(pt_regs); 446 + 447 + __audit_syscall_exit(success, return_code); 448 + } 437 449 } 438 450 static inline void audit_getname(const char *name) 439 451 { ··· 557 551 558 552 extern int audit_n_rules; 559 553 extern int audit_signals; 560 - #else 554 + #else /* CONFIG_AUDITSYSCALL */ 561 555 #define audit_finish_fork(t) 562 556 #define audit_alloc(t) ({ 0; }) 563 557 #define audit_free(t) do { ; } while (0) 564 558 #define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0) 565 - #define audit_syscall_exit(f,r) do { ; } while (0) 559 + #define audit_syscall_exit(r) do { ; } while (0) 566 560 #define audit_dummy_context() 1 567 561 #define audit_getname(n) do { ; } while (0) 568 562 #define audit_putname(n) do { ; } while (0) ··· 593 587 #define audit_ptrace(t) ((void)0) 594 588 #define audit_n_rules 0 595 589 #define audit_signals 0 596 - #endif 590 + #endif /* CONFIG_AUDITSYSCALL */ 597 591 598 592 #ifdef CONFIG_AUDIT 599 593 /* These are defined in audit.c */
+10
include/linux/ptrace.h
··· 112 112 113 113 #include <linux/compiler.h> /* For unlikely. */ 114 114 #include <linux/sched.h> /* For struct task_struct. */ 115 + #include <linux/err.h> /* for IS_ERR_VALUE */ 115 116 116 117 117 118 extern long arch_ptrace(struct task_struct *child, long request, ··· 264 263 * syscall handler, or something along those lines). 265 264 */ 266 265 #define force_successful_syscall_return() do { } while (0) 266 + #endif 267 + 268 + #ifndef is_syscall_success 269 + /* 270 + * On most systems we can tell if a syscall is a success based on if the retval 271 + * is an error value. On some systems like ia64 and powerpc they have different 272 + * indicators of success/failure and must define their own. 273 + */ 274 + #define is_syscall_success(regs) (!IS_ERR_VALUE((unsigned long)(regs_return_value(regs)))) 267 275 #endif 268 276 269 277 /*
+12 -4
kernel/auditsc.c
··· 70 70 71 71 #include "audit.h" 72 72 73 + /* flags stating the success for a syscall */ 74 + #define AUDITSC_INVALID 0 75 + #define AUDITSC_SUCCESS 1 76 + #define AUDITSC_FAILURE 2 77 + 73 78 /* AUDIT_NAMES is the number of slots we reserve in the audit_context 74 79 * for saving names from getname(). If we get more names we will allocate 75 80 * a name dynamically and also add those to the list anchored by names_list. */ ··· 1729 1724 1730 1725 /** 1731 1726 * audit_syscall_exit - deallocate audit context after a system call 1732 - * @valid: success/failure flag 1733 - * @return_code: syscall return value 1727 + * @pt_regs: syscall registers 1734 1728 * 1735 1729 * Tear down after system call. If the audit context has been marked as 1736 1730 * auditable (either because of the AUDIT_RECORD_CONTEXT state from ··· 1737 1733 * message), then write out the syscall information. In call cases, 1738 1734 * free the names stored from getname(). 1739 1735 */ 1740 - void audit_syscall_exit(int valid, long return_code) 1736 + void __audit_syscall_exit(int success, long return_code) 1741 1737 { 1742 1738 struct task_struct *tsk = current; 1743 1739 struct audit_context *context; 1744 1740 1745 - context = audit_get_context(tsk, valid, return_code); 1741 + if (success) 1742 + success = AUDITSC_SUCCESS; 1743 + else 1744 + success = AUDITSC_FAILURE; 1746 1745 1746 + context = audit_get_context(tsk, success, return_code); 1747 1747 if (likely(!context)) 1748 1748 return; 1749 1749