~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/x86/vdso/vma.c

Version: ~ [ linux-5.3-rc5 ] ~ [ linux-5.2.9 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.67 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.139 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.189 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.189 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.72 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Set up the VMAs to tell the VM about the vDSO.
  3  * Copyright 2007 Andi Kleen, SUSE Labs.
  4  * Subject to the GPL, v.2
  5  */
  6 #include <linux/mm.h>
  7 #include <linux/err.h>
  8 #include <linux/sched.h>
  9 #include <linux/slab.h>
 10 #include <linux/init.h>
 11 #include <linux/random.h>
 12 #include <linux/elf.h>
 13 #include <asm/vsyscall.h>
 14 #include <asm/vgtod.h>
 15 #include <asm/proto.h>
 16 #include <asm/vdso.h>
 17 #include <asm/page.h>
 18 
 19 unsigned int __read_mostly vdso_enabled = 1;
 20 
 21 extern char vdso_start[], vdso_end[];
 22 extern unsigned short vdso_sync_cpuid;
 23 
 24 extern struct page *vdso_pages[];
 25 static unsigned vdso_size;
 26 
 27 #ifdef CONFIG_X86_X32_ABI
 28 extern char vdsox32_start[], vdsox32_end[];
 29 extern struct page *vdsox32_pages[];
 30 static unsigned vdsox32_size;
 31 
 32 static void __init patch_vdsox32(void *vdso, size_t len)
 33 {
 34         Elf32_Ehdr *hdr = vdso;
 35         Elf32_Shdr *sechdrs, *alt_sec = 0;
 36         char *secstrings;
 37         void *alt_data;
 38         int i;
 39 
 40         BUG_ON(len < sizeof(Elf32_Ehdr));
 41         BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0);
 42 
 43         sechdrs = (void *)hdr + hdr->e_shoff;
 44         secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 45 
 46         for (i = 1; i < hdr->e_shnum; i++) {
 47                 Elf32_Shdr *shdr = &sechdrs[i];
 48                 if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) {
 49                         alt_sec = shdr;
 50                         goto found;
 51                 }
 52         }
 53 
 54         /* If we get here, it's probably a bug. */
 55         pr_warning("patch_vdsox32: .altinstructions not found\n");
 56         return;  /* nothing to patch */
 57 
 58 found:
 59         alt_data = (void *)hdr + alt_sec->sh_offset;
 60         apply_alternatives(alt_data, alt_data + alt_sec->sh_size);
 61 }
 62 #endif
 63 
 64 static void __init patch_vdso64(void *vdso, size_t len)
 65 {
 66         Elf64_Ehdr *hdr = vdso;
 67         Elf64_Shdr *sechdrs, *alt_sec = 0;
 68         char *secstrings;
 69         void *alt_data;
 70         int i;
 71 
 72         BUG_ON(len < sizeof(Elf64_Ehdr));
 73         BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0);
 74 
 75         sechdrs = (void *)hdr + hdr->e_shoff;
 76         secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 77 
 78         for (i = 1; i < hdr->e_shnum; i++) {
 79                 Elf64_Shdr *shdr = &sechdrs[i];
 80                 if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) {
 81                         alt_sec = shdr;
 82                         goto found;
 83                 }
 84         }
 85 
 86         /* If we get here, it's probably a bug. */
 87         pr_warning("patch_vdso64: .altinstructions not found\n");
 88         return;  /* nothing to patch */
 89 
 90 found:
 91         alt_data = (void *)hdr + alt_sec->sh_offset;
 92         apply_alternatives(alt_data, alt_data + alt_sec->sh_size);
 93 }
 94 
 95 static int __init init_vdso(void)
 96 {
 97         int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE;
 98         int i;
 99 
100         patch_vdso64(vdso_start, vdso_end - vdso_start);
101 
102         vdso_size = npages << PAGE_SHIFT;
103         for (i = 0; i < npages; i++)
104                 vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE);
105 
106 #ifdef CONFIG_X86_X32_ABI
107         patch_vdsox32(vdsox32_start, vdsox32_end - vdsox32_start);
108         npages = (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE;
109         vdsox32_size = npages << PAGE_SHIFT;
110         for (i = 0; i < npages; i++)
111                 vdsox32_pages[i] = virt_to_page(vdsox32_start + i*PAGE_SIZE);
112 #endif
113 
114         return 0;
115 }
116 subsys_initcall(init_vdso);
117 
118 struct linux_binprm;
119 
120 /*
121  * Put the vdso above the (randomized) stack with another randomized
122  * offset.  This way there is no hole in the middle of address space.
123  * To save memory make sure it is still in the same PTE as the stack
124  * top.  This doesn't give that many random bits.
125  *
126  * Note that this algorithm is imperfect: the distribution of the vdso
127  * start address within a PMD is biased toward the end.
128  *
129  */
130 static unsigned long vdso_addr(unsigned long start, unsigned len)
131 {
132         unsigned long addr, end;
133         unsigned offset;
134 
135         /*
136          * Round up the start address.  It can start out unaligned as a result
137          * of stack start randomization.
138          */
139         start = PAGE_ALIGN(start);
140 
141         /* Round the lowest possible end address up to a PMD boundary. */
142         end = (start + len + PMD_SIZE - 1) & PMD_MASK;
143         if (end >= TASK_SIZE_MAX)
144                 end = TASK_SIZE_MAX;
145         end -= len;
146 
147         if (end > start) {
148                 offset = get_random_int() % (((end - start) >> PAGE_SHIFT) + 1);
149                 addr = start + (offset << PAGE_SHIFT);
150         } else {
151                 addr = start;
152         }
153 
154         /*
155          * page-align it here so that get_unmapped_area doesn't
156          * align it wrongfully again to the next page. addr can come in 4K
157          * unaligned here as a result of stack start randomization.
158          */
159         addr = PAGE_ALIGN(addr);
160         addr = align_addr(addr, NULL, ALIGN_VDSO);
161 
162         return addr;
163 }
164 
165 /* Setup a VMA at program startup for the vsyscall page.
166    Not called for compat tasks */
167 static int setup_additional_pages(struct linux_binprm *bprm,
168                                   int uses_interp,
169                                   struct page **pages,
170                                   unsigned size)
171 {
172         struct mm_struct *mm = current->mm;
173         unsigned long addr;
174         int ret;
175 
176         if (!vdso_enabled)
177                 return 0;
178 
179         down_write(&mm->mmap_sem);
180         addr = vdso_addr(mm->start_stack, size);
181         addr = get_unmapped_area(NULL, addr, size, 0, 0);
182         if (IS_ERR_VALUE(addr)) {
183                 ret = addr;
184                 goto up_fail;
185         }
186 
187         current->mm->context.vdso = (void *)addr;
188 
189         ret = install_special_mapping(mm, addr, size,
190                                       VM_READ|VM_EXEC|
191                                       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
192                                       pages);
193         if (ret) {
194                 current->mm->context.vdso = NULL;
195                 goto up_fail;
196         }
197 
198 up_fail:
199         up_write(&mm->mmap_sem);
200         return ret;
201 }
202 
203 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
204 {
205         return setup_additional_pages(bprm, uses_interp, vdso_pages,
206                                       vdso_size);
207 }
208 
209 #ifdef CONFIG_X86_X32_ABI
210 int x32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
211 {
212         return setup_additional_pages(bprm, uses_interp, vdsox32_pages,
213                                       vdsox32_size);
214 }
215 #endif
216 
217 static __init int vdso_setup(char *s)
218 {
219         vdso_enabled = simple_strtoul(s, NULL, 0);
220         return 0;
221 }
222 __setup("vdso=", vdso_setup);
223 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp