Bug 706941

Summary: __vsyscall(3) venosys_1 is optimized away by gcc
Product: Red Hat Enterprise Linux 6 Reporter: Jan Stancek <jstancek>
Component: kernelAssignee: Dave Anderson <anderson>
Status: CLOSED NOTABUG QA Contact: Red Hat Kernel QE team <kernel-qe>
Severity: low Docs Contact:
Priority: low    
Version: 6.2CC: jburke, prarit
Target Milestone: rc   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2011-06-07 07:34:09 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:

Description Jan Stancek 2011-05-23 14:41:20 UTC
Description of problem:
4th vsyscall (venosys_1) is supposed to return -ENOSYS when called. GCC is optimizing it away, leaving random data instead.

These 2 commit might be of interest:
commit 2e8ad43ec07545780ce7992cb18e2d82c7abd24c
[PATCH] x86-64: Prevent gcc 4 from optimizing away vsyscalls

commit a4928cffe6435caf427ae673131a633c1329dbf3
"make namespacecheck" fixes

I'm not aware of any useful 4th vsyscall being ever present in kernel - initial priority and severity set to low.

Version-Release number of selected component (if applicable):
2.6.32-150.el6.x86_64

How reproducible:
100%

Steps to Reproduce:
1.
--- cut b.c ---
#include <stdio.h>
#include <stdlib.h>

#define vsyscall_venosys       0xffffffffff600000+1024*3

typedef long (* venosys_proto)(void);

int run_venosys()
{
    venosys_proto venosys;
    venosys = (venosys_proto) vsyscall_venosys;
    return venosys();
}

int main()
{
    int ret = run_venosys();
    printf("ret: %d\n", ret);
    return 0;
}
--- cut ---

2. gcc b.c
3. ./a.out
Segmentation fault

4. gdb ./a.out
Notice first 3 vsyscalls have correct function prologue, 4th is just random data.

(gdb) disassemble 0xffffffffff600000+1024*0, 0xffffffffff600000+1024*0+10
Dump of assembler code from 0xffffffffff600000 to 0xffffffffff60000a:
   0xffffffffff600000:	push   %rbp
   0xffffffffff600001:	mov    %rsp,%rbp
   0xffffffffff600004:	push   %r13
   0xffffffffff600006:	push   %r12
   0xffffffffff600008:	mov    %rdi,%r12
End of assembler dump.

(gdb) disassemble 0xffffffffff600000+1024*1, 0xffffffffff600000+1024*1+10
Dump of assembler code from 0xffffffffff600400 to 0xffffffffff60040a:
   0xffffffffff600400:	push   %rbp
   0xffffffffff600401:	mov    %rsp,%rbp
   0xffffffffff600404:	push   %rbx
   0xffffffffff600405:	mov    %rdi,%rbx
   0xffffffffff600408:	sub    $0x18,%rsp
End of assembler dump.

(gdb) disassemble 0xffffffffff600000+1024*2, 0xffffffffff600000+1024*2+10
Dump of assembler code from 0xffffffffff600800 to 0xffffffffff60080a:
   0xffffffffff600800:	push   %rbp
   0xffffffffff600801:	test   %rdx,%rdx
   0xffffffffff600804:	mov    %rdx,%r8
   0xffffffffff600807:	mov    %rsp,%rbp
End of assembler dump.

(gdb) disassemble 0xffffffffff600000+1024*3, 0xffffffffff600000+1024*3+10
Dump of assembler code from 0xffffffffff600c00 to 0xffffffffff600c0a:
   0xffffffffff600c00: (bad)
   0xffffffffff600c01: movslq 0x6f(%rcx,%rbp,2),%esi
   0xffffffffff600c05: outsb %ds%rsi),(%dx)
   0xffffffffff600c06: cmp (%rax),%ah
   0xffffffffff600c08: and $0xa646c,%eax
>>> End of assembler dump.
  
Actual results:
4th vsyscall is unusable and can crash any user space application, which tries to call it.

Expected results:
4th vsyscall is valid and returns -ENOSYS.

Additional info:
With 2.6.18-257.el5 it works as expected.

Comment 2 Dave Anderson 2011-06-02 13:31:07 UTC
> 4th vsyscall (venosys_1) is supposed to return -ENOSYS when called. GCC is
> optimizing it away, leaving random data instead.

I was about to post a patch to change venosys_1() back to not being static,
but looking a bit more, I'm wondering whether it's actually supposed to be
exported to user space.  The kernel's "arch/x86/include/asm/vsyscall.h"
doesn't create a system call number for it:

enum vsyscall_num {
        __NR_vgettimeofday,
        __NR_vtime,
        __NR_vgetcpu,
};

And it's not verified by vsyscall_init():

static int __init vsyscall_init(void)
{
        BUG_ON(((unsigned long) &vgettimeofday !=
                        VSYSCALL_ADDR(__NR_vgettimeofday)));
        BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
        BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
        BUG_ON((unsigned long) &vgetcpu != VSYSCALL_ADDR(__NR_vgetcpu));
#ifdef CONFIG_SYSCTL
        register_sysctl_table(kernel_root_table2);
#endif
        on_each_cpu(cpu_vsyscall_init, NULL, 1);
        hotcpu_notifier(cpu_vsyscall_notifier, 0);
        return 0;
}

Do you have some documentation or reference somewhere that says that
the system call should actually be made available to user-space?

Comment 3 Jan Stancek 2011-06-02 14:06:34 UTC
Can't find anything in current Documentation. 

The only reference, which I found is at top of arch/x86/kernel/vsyscall_64.c:
 *  vsyscall 1 is located at -10Mbyte, vsyscall 2 is located
 *  at virtual address -10Mbyte+1024bytes etc... There are at max 4
 *  vsyscalls. One vsyscall can reserve more than 1 slot to avoid
 *  jumping out of line if necessary. We cannot add more with this
 *  mechanism because older kernels won't return -ENOSYS.

Looking at history, vsyscall(2) also used to return -ENOSYS:
http://lwn.net/Articles/13141/

My assumption was, that user-space application could check and see if some vsyscall is supported or not, and it would do so by trying to call it.

The main reason for this bz was, that it used to work in RHEL5.

Comment 4 Dave Anderson 2011-06-02 14:19:35 UTC
> The main reason for this bz was, that it used to work in RHEL5.

OK, I understand.  I'm wondering whether that's the bug, i.e.,
that it should never have been exported to begin with.
Anyway, I sent an email off to Ingo and Andi to get their
take on the matter.

Comment 5 Dave Anderson 2011-06-03 15:28:59 UTC
Andi's response was: "The original design was that they should be there."
Ingo did not respond.  Anyway, I posted it on LKML: 

  https://lkml.org/lkml/2011/6/3/216

If it gets accepted, it can go into RHEL6; otherwise it should be
closed NOTABUG.

Comment 6 Dave Anderson 2011-06-06 13:00:24 UTC
> If it gets accepted, it can go into RHEL6; otherwise it should be
> closed NOTABUG.

As it turns out, this patch steps all over patch #6 of this 10-part
series posted on 5/31/11, which removes venosys_1() entirely:

  [PATCH v3 00/10] Remove syscall instructions at fixed addresses
  https://lkml.org/lkml/2011/5/31/181
  ...
  [PATCH v3 06/10] x86-64: Remove vsyscall number 3 (venosys)
  https://lkml.org/lkml/2011/5/31/187

Comment 7 Dave Anderson 2011-06-06 20:55:29 UTC
And also given that it's already been removed from Ingo's tip
tree, it's pretty obvious that it's never going to be resurrected.

Can we just close this BZ?

Thanks,
  Dave

Comment 8 Jan Stancek 2011-06-07 07:34:09 UTC
Agreed, closing as NOTABUG.