diff options
Diffstat (limited to 'arch/x86/vdso/vdso32-setup.c')
-rw-r--r-- | arch/x86/vdso/vdso32-setup.c | 301 |
1 files changed, 81 insertions, 220 deletions
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index d6bfb876cfb0..00348980a3a6 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/slab.h> | ||
19 | 20 | ||
20 | #include <asm/cpufeature.h> | 21 | #include <asm/cpufeature.h> |
21 | #include <asm/msr.h> | 22 | #include <asm/msr.h> |
@@ -25,17 +26,14 @@ | |||
25 | #include <asm/tlbflush.h> | 26 | #include <asm/tlbflush.h> |
26 | #include <asm/vdso.h> | 27 | #include <asm/vdso.h> |
27 | #include <asm/proto.h> | 28 | #include <asm/proto.h> |
28 | 29 | #include <asm/fixmap.h> | |
29 | enum { | 30 | #include <asm/hpet.h> |
30 | VDSO_DISABLED = 0, | 31 | #include <asm/vvar.h> |
31 | VDSO_ENABLED = 1, | ||
32 | VDSO_COMPAT = 2, | ||
33 | }; | ||
34 | 32 | ||
35 | #ifdef CONFIG_COMPAT_VDSO | 33 | #ifdef CONFIG_COMPAT_VDSO |
36 | #define VDSO_DEFAULT VDSO_COMPAT | 34 | #define VDSO_DEFAULT 0 |
37 | #else | 35 | #else |
38 | #define VDSO_DEFAULT VDSO_ENABLED | 36 | #define VDSO_DEFAULT 1 |
39 | #endif | 37 | #endif |
40 | 38 | ||
41 | #ifdef CONFIG_X86_64 | 39 | #ifdef CONFIG_X86_64 |
@@ -44,13 +42,6 @@ enum { | |||
44 | #endif | 42 | #endif |
45 | 43 | ||
46 | /* | 44 | /* |
47 | * This is the difference between the prelinked addresses in the vDSO images | ||
48 | * and the VDSO_HIGH_BASE address where CONFIG_COMPAT_VDSO places the vDSO | ||
49 | * in the user address space. | ||
50 | */ | ||
51 | #define VDSO_ADDR_ADJUST (VDSO_HIGH_BASE - (unsigned long)VDSO32_PRELINK) | ||
52 | |||
53 | /* | ||
54 | * Should the kernel map a VDSO page into processes and pass its | 45 | * Should the kernel map a VDSO page into processes and pass its |
55 | * address down to glibc upon exec()? | 46 | * address down to glibc upon exec()? |
56 | */ | 47 | */ |
@@ -60,6 +51,9 @@ static int __init vdso_setup(char *s) | |||
60 | { | 51 | { |
61 | vdso_enabled = simple_strtoul(s, NULL, 0); | 52 | vdso_enabled = simple_strtoul(s, NULL, 0); |
62 | 53 | ||
54 | if (vdso_enabled > 1) | ||
55 | pr_warn("vdso32 values other than 0 and 1 are no longer allowed; vdso disabled\n"); | ||
56 | |||
63 | return 1; | 57 | return 1; |
64 | } | 58 | } |
65 | 59 | ||
@@ -76,124 +70,8 @@ __setup_param("vdso=", vdso32_setup, vdso_setup, 0); | |||
76 | EXPORT_SYMBOL_GPL(vdso_enabled); | 70 | EXPORT_SYMBOL_GPL(vdso_enabled); |
77 | #endif | 71 | #endif |
78 | 72 | ||
79 | static __init void reloc_symtab(Elf32_Ehdr *ehdr, | 73 | static struct page **vdso32_pages; |
80 | unsigned offset, unsigned size) | 74 | static unsigned vdso32_size; |
81 | { | ||
82 | Elf32_Sym *sym = (void *)ehdr + offset; | ||
83 | unsigned nsym = size / sizeof(*sym); | ||
84 | unsigned i; | ||
85 | |||
86 | for(i = 0; i < nsym; i++, sym++) { | ||
87 | if (sym->st_shndx == SHN_UNDEF || | ||
88 | sym->st_shndx == SHN_ABS) | ||
89 | continue; /* skip */ | ||
90 | |||
91 | if (sym->st_shndx > SHN_LORESERVE) { | ||
92 | printk(KERN_INFO "VDSO: unexpected st_shndx %x\n", | ||
93 | sym->st_shndx); | ||
94 | continue; | ||
95 | } | ||
96 | |||
97 | switch(ELF_ST_TYPE(sym->st_info)) { | ||
98 | case STT_OBJECT: | ||
99 | case STT_FUNC: | ||
100 | case STT_SECTION: | ||
101 | case STT_FILE: | ||
102 | sym->st_value += VDSO_ADDR_ADJUST; | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static __init void reloc_dyn(Elf32_Ehdr *ehdr, unsigned offset) | ||
108 | { | ||
109 | Elf32_Dyn *dyn = (void *)ehdr + offset; | ||
110 | |||
111 | for(; dyn->d_tag != DT_NULL; dyn++) | ||
112 | switch(dyn->d_tag) { | ||
113 | case DT_PLTGOT: | ||
114 | case DT_HASH: | ||
115 | case DT_STRTAB: | ||
116 | case DT_SYMTAB: | ||
117 | case DT_RELA: | ||
118 | case DT_INIT: | ||
119 | case DT_FINI: | ||
120 | case DT_REL: | ||
121 | case DT_DEBUG: | ||
122 | case DT_JMPREL: | ||
123 | case DT_VERSYM: | ||
124 | case DT_VERDEF: | ||
125 | case DT_VERNEED: | ||
126 | case DT_ADDRRNGLO ... DT_ADDRRNGHI: | ||
127 | /* definitely pointers needing relocation */ | ||
128 | dyn->d_un.d_ptr += VDSO_ADDR_ADJUST; | ||
129 | break; | ||
130 | |||
131 | case DT_ENCODING ... OLD_DT_LOOS-1: | ||
132 | case DT_LOOS ... DT_HIOS-1: | ||
133 | /* Tags above DT_ENCODING are pointers if | ||
134 | they're even */ | ||
135 | if (dyn->d_tag >= DT_ENCODING && | ||
136 | (dyn->d_tag & 1) == 0) | ||
137 | dyn->d_un.d_ptr += VDSO_ADDR_ADJUST; | ||
138 | break; | ||
139 | |||
140 | case DT_VERDEFNUM: | ||
141 | case DT_VERNEEDNUM: | ||
142 | case DT_FLAGS_1: | ||
143 | case DT_RELACOUNT: | ||
144 | case DT_RELCOUNT: | ||
145 | case DT_VALRNGLO ... DT_VALRNGHI: | ||
146 | /* definitely not pointers */ | ||
147 | break; | ||
148 | |||
149 | case OLD_DT_LOOS ... DT_LOOS-1: | ||
150 | case DT_HIOS ... DT_VALRNGLO-1: | ||
151 | default: | ||
152 | if (dyn->d_tag > DT_ENCODING) | ||
153 | printk(KERN_INFO "VDSO: unexpected DT_tag %x\n", | ||
154 | dyn->d_tag); | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | static __init void relocate_vdso(Elf32_Ehdr *ehdr) | ||
160 | { | ||
161 | Elf32_Phdr *phdr; | ||
162 | Elf32_Shdr *shdr; | ||
163 | int i; | ||
164 | |||
165 | BUG_ON(memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0 || | ||
166 | !elf_check_arch_ia32(ehdr) || | ||
167 | ehdr->e_type != ET_DYN); | ||
168 | |||
169 | ehdr->e_entry += VDSO_ADDR_ADJUST; | ||
170 | |||
171 | /* rebase phdrs */ | ||
172 | phdr = (void *)ehdr + ehdr->e_phoff; | ||
173 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
174 | phdr[i].p_vaddr += VDSO_ADDR_ADJUST; | ||
175 | |||
176 | /* relocate dynamic stuff */ | ||
177 | if (phdr[i].p_type == PT_DYNAMIC) | ||
178 | reloc_dyn(ehdr, phdr[i].p_offset); | ||
179 | } | ||
180 | |||
181 | /* rebase sections */ | ||
182 | shdr = (void *)ehdr + ehdr->e_shoff; | ||
183 | for(i = 0; i < ehdr->e_shnum; i++) { | ||
184 | if (!(shdr[i].sh_flags & SHF_ALLOC)) | ||
185 | continue; | ||
186 | |||
187 | shdr[i].sh_addr += VDSO_ADDR_ADJUST; | ||
188 | |||
189 | if (shdr[i].sh_type == SHT_SYMTAB || | ||
190 | shdr[i].sh_type == SHT_DYNSYM) | ||
191 | reloc_symtab(ehdr, shdr[i].sh_offset, | ||
192 | shdr[i].sh_size); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static struct page *vdso32_pages[1]; | ||
197 | 75 | ||
198 | #ifdef CONFIG_X86_64 | 76 | #ifdef CONFIG_X86_64 |
199 | 77 | ||
@@ -212,12 +90,6 @@ void syscall32_cpu_init(void) | |||
212 | wrmsrl(MSR_CSTAR, ia32_cstar_target); | 90 | wrmsrl(MSR_CSTAR, ia32_cstar_target); |
213 | } | 91 | } |
214 | 92 | ||
215 | #define compat_uses_vma 1 | ||
216 | |||
217 | static inline void map_compat_vdso(int map) | ||
218 | { | ||
219 | } | ||
220 | |||
221 | #else /* CONFIG_X86_32 */ | 93 | #else /* CONFIG_X86_32 */ |
222 | 94 | ||
223 | #define vdso32_sysenter() (boot_cpu_has(X86_FEATURE_SEP)) | 95 | #define vdso32_sysenter() (boot_cpu_has(X86_FEATURE_SEP)) |
@@ -241,64 +113,36 @@ void enable_sep_cpu(void) | |||
241 | put_cpu(); | 113 | put_cpu(); |
242 | } | 114 | } |
243 | 115 | ||
244 | static struct vm_area_struct gate_vma; | ||
245 | |||
246 | static int __init gate_vma_init(void) | ||
247 | { | ||
248 | gate_vma.vm_mm = NULL; | ||
249 | gate_vma.vm_start = FIXADDR_USER_START; | ||
250 | gate_vma.vm_end = FIXADDR_USER_END; | ||
251 | gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC; | ||
252 | gate_vma.vm_page_prot = __P101; | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | #define compat_uses_vma 0 | ||
258 | |||
259 | static void map_compat_vdso(int map) | ||
260 | { | ||
261 | static int vdso_mapped; | ||
262 | |||
263 | if (map == vdso_mapped) | ||
264 | return; | ||
265 | |||
266 | vdso_mapped = map; | ||
267 | |||
268 | __set_fixmap(FIX_VDSO, page_to_pfn(vdso32_pages[0]) << PAGE_SHIFT, | ||
269 | map ? PAGE_READONLY_EXEC : PAGE_NONE); | ||
270 | |||
271 | /* flush stray tlbs */ | ||
272 | flush_tlb_all(); | ||
273 | } | ||
274 | |||
275 | #endif /* CONFIG_X86_64 */ | 116 | #endif /* CONFIG_X86_64 */ |
276 | 117 | ||
277 | int __init sysenter_setup(void) | 118 | int __init sysenter_setup(void) |
278 | { | 119 | { |
279 | void *syscall_page = (void *)get_zeroed_page(GFP_ATOMIC); | 120 | char *vdso32_start, *vdso32_end; |
280 | const void *vsyscall; | 121 | int npages, i; |
281 | size_t vsyscall_len; | ||
282 | |||
283 | vdso32_pages[0] = virt_to_page(syscall_page); | ||
284 | |||
285 | #ifdef CONFIG_X86_32 | ||
286 | gate_vma_init(); | ||
287 | #endif | ||
288 | 122 | ||
123 | #ifdef CONFIG_COMPAT | ||
289 | if (vdso32_syscall()) { | 124 | if (vdso32_syscall()) { |
290 | vsyscall = &vdso32_syscall_start; | 125 | vdso32_start = vdso32_syscall_start; |
291 | vsyscall_len = &vdso32_syscall_end - &vdso32_syscall_start; | 126 | vdso32_end = vdso32_syscall_end; |
292 | } else if (vdso32_sysenter()){ | 127 | vdso32_pages = vdso32_syscall_pages; |
293 | vsyscall = &vdso32_sysenter_start; | 128 | } else |
294 | vsyscall_len = &vdso32_sysenter_end - &vdso32_sysenter_start; | 129 | #endif |
130 | if (vdso32_sysenter()) { | ||
131 | vdso32_start = vdso32_sysenter_start; | ||
132 | vdso32_end = vdso32_sysenter_end; | ||
133 | vdso32_pages = vdso32_sysenter_pages; | ||
295 | } else { | 134 | } else { |
296 | vsyscall = &vdso32_int80_start; | 135 | vdso32_start = vdso32_int80_start; |
297 | vsyscall_len = &vdso32_int80_end - &vdso32_int80_start; | 136 | vdso32_end = vdso32_int80_end; |
137 | vdso32_pages = vdso32_int80_pages; | ||
298 | } | 138 | } |
299 | 139 | ||
300 | memcpy(syscall_page, vsyscall, vsyscall_len); | 140 | npages = ((vdso32_end - vdso32_start) + PAGE_SIZE - 1) / PAGE_SIZE; |
301 | relocate_vdso(syscall_page); | 141 | vdso32_size = npages << PAGE_SHIFT; |
142 | for (i = 0; i < npages; i++) | ||
143 | vdso32_pages[i] = virt_to_page(vdso32_start + i*PAGE_SIZE); | ||
144 | |||
145 | patch_vdso32(vdso32_start, vdso32_size); | ||
302 | 146 | ||
303 | return 0; | 147 | return 0; |
304 | } | 148 | } |
@@ -309,48 +153,73 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
309 | struct mm_struct *mm = current->mm; | 153 | struct mm_struct *mm = current->mm; |
310 | unsigned long addr; | 154 | unsigned long addr; |
311 | int ret = 0; | 155 | int ret = 0; |
312 | bool compat; | 156 | struct vm_area_struct *vma; |
313 | 157 | ||
314 | #ifdef CONFIG_X86_X32_ABI | 158 | #ifdef CONFIG_X86_X32_ABI |
315 | if (test_thread_flag(TIF_X32)) | 159 | if (test_thread_flag(TIF_X32)) |
316 | return x32_setup_additional_pages(bprm, uses_interp); | 160 | return x32_setup_additional_pages(bprm, uses_interp); |
317 | #endif | 161 | #endif |
318 | 162 | ||
319 | if (vdso_enabled == VDSO_DISABLED) | 163 | if (vdso_enabled != 1) /* Other values all mean "disabled" */ |
320 | return 0; | 164 | return 0; |
321 | 165 | ||
322 | down_write(&mm->mmap_sem); | 166 | down_write(&mm->mmap_sem); |
323 | 167 | ||
324 | /* Test compat mode once here, in case someone | 168 | addr = get_unmapped_area(NULL, 0, vdso32_size + VDSO_OFFSET(VDSO_PREV_PAGES), 0, 0); |
325 | changes it via sysctl */ | 169 | if (IS_ERR_VALUE(addr)) { |
326 | compat = (vdso_enabled == VDSO_COMPAT); | 170 | ret = addr; |
171 | goto up_fail; | ||
172 | } | ||
173 | |||
174 | addr += VDSO_OFFSET(VDSO_PREV_PAGES); | ||
327 | 175 | ||
328 | map_compat_vdso(compat); | 176 | current->mm->context.vdso = (void *)addr; |
329 | 177 | ||
330 | if (compat) | 178 | /* |
331 | addr = VDSO_HIGH_BASE; | 179 | * MAYWRITE to allow gdb to COW and set breakpoints |
332 | else { | 180 | */ |
333 | addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0); | 181 | ret = install_special_mapping(mm, |
334 | if (IS_ERR_VALUE(addr)) { | 182 | addr, |
335 | ret = addr; | 183 | vdso32_size, |
336 | goto up_fail; | 184 | VM_READ|VM_EXEC| |
337 | } | 185 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, |
186 | vdso32_pages); | ||
187 | |||
188 | if (ret) | ||
189 | goto up_fail; | ||
190 | |||
191 | vma = _install_special_mapping(mm, | ||
192 | addr - VDSO_OFFSET(VDSO_PREV_PAGES), | ||
193 | VDSO_OFFSET(VDSO_PREV_PAGES), | ||
194 | VM_READ, | ||
195 | NULL); | ||
196 | |||
197 | if (IS_ERR(vma)) { | ||
198 | ret = PTR_ERR(vma); | ||
199 | goto up_fail; | ||
338 | } | 200 | } |
339 | 201 | ||
340 | current->mm->context.vdso = (void *)addr; | 202 | ret = remap_pfn_range(vma, |
203 | addr - VDSO_OFFSET(VDSO_VVAR_PAGE), | ||
204 | __pa_symbol(&__vvar_page) >> PAGE_SHIFT, | ||
205 | PAGE_SIZE, | ||
206 | PAGE_READONLY); | ||
207 | |||
208 | if (ret) | ||
209 | goto up_fail; | ||
341 | 210 | ||
342 | if (compat_uses_vma || !compat) { | 211 | #ifdef CONFIG_HPET_TIMER |
343 | /* | 212 | if (hpet_address) { |
344 | * MAYWRITE to allow gdb to COW and set breakpoints | 213 | ret = io_remap_pfn_range(vma, |
345 | */ | 214 | addr - VDSO_OFFSET(VDSO_HPET_PAGE), |
346 | ret = install_special_mapping(mm, addr, PAGE_SIZE, | 215 | hpet_address >> PAGE_SHIFT, |
347 | VM_READ|VM_EXEC| | 216 | PAGE_SIZE, |
348 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | 217 | pgprot_noncached(PAGE_READONLY)); |
349 | vdso32_pages); | ||
350 | 218 | ||
351 | if (ret) | 219 | if (ret) |
352 | goto up_fail; | 220 | goto up_fail; |
353 | } | 221 | } |
222 | #endif | ||
354 | 223 | ||
355 | current_thread_info()->sysenter_return = | 224 | current_thread_info()->sysenter_return = |
356 | VDSO32_SYMBOL(addr, SYSENTER_RETURN); | 225 | VDSO32_SYMBOL(addr, SYSENTER_RETURN); |
@@ -411,20 +280,12 @@ const char *arch_vma_name(struct vm_area_struct *vma) | |||
411 | 280 | ||
412 | struct vm_area_struct *get_gate_vma(struct mm_struct *mm) | 281 | struct vm_area_struct *get_gate_vma(struct mm_struct *mm) |
413 | { | 282 | { |
414 | /* | ||
415 | * Check to see if the corresponding task was created in compat vdso | ||
416 | * mode. | ||
417 | */ | ||
418 | if (mm && mm->context.vdso == (void *)VDSO_HIGH_BASE) | ||
419 | return &gate_vma; | ||
420 | return NULL; | 283 | return NULL; |
421 | } | 284 | } |
422 | 285 | ||
423 | int in_gate_area(struct mm_struct *mm, unsigned long addr) | 286 | int in_gate_area(struct mm_struct *mm, unsigned long addr) |
424 | { | 287 | { |
425 | const struct vm_area_struct *vma = get_gate_vma(mm); | 288 | return 0; |
426 | |||
427 | return vma && addr >= vma->vm_start && addr < vma->vm_end; | ||
428 | } | 289 | } |
429 | 290 | ||
430 | int in_gate_area_no_mm(unsigned long addr) | 291 | int in_gate_area_no_mm(unsigned long addr) |