1/* Support for GNU properties. x86 version.
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#ifndef _DL_PROP_H
20#define _DL_PROP_H
21
22#include <not-cancel.h>
23
24extern void _dl_cet_check (struct link_map *, const char *)
25 attribute_hidden;
26extern void _dl_cet_open_check (struct link_map *)
27 attribute_hidden;
28
29static inline void __attribute__ ((always_inline))
30_rtld_main_check (struct link_map *m, const char *program)
31{
32#if CET_ENABLED
33 _dl_cet_check (m, program);
34#endif
35}
36
37static inline void __attribute__ ((always_inline))
38_dl_open_check (struct link_map *m)
39{
40#if CET_ENABLED
41 _dl_cet_open_check (m);
42#endif
43}
44
45static inline void __attribute__ ((unused))
46_dl_process_cet_property_note (struct link_map *l,
47 const ElfW(Nhdr) *note,
48 const ElfW(Addr) size,
49 const ElfW(Addr) align)
50{
51#if CET_ENABLED
52 /* Skip if we have seen a NT_GNU_PROPERTY_TYPE_0 note before. */
53 if (l->l_cet != lc_unknown)
54 return;
55
56 /* The NT_GNU_PROPERTY_TYPE_0 note must be aliged to 4 bytes in
57 32-bit objects and to 8 bytes in 64-bit objects. Skip notes
58 with incorrect alignment. */
59 if (align != (__ELF_NATIVE_CLASS / 8))
60 return;
61
62 const ElfW(Addr) start = (ElfW(Addr)) note;
63
64 unsigned int feature_1 = 0;
65 unsigned int last_type = 0;
66
67 while ((ElfW(Addr)) (note + 1) - start < size)
68 {
69 /* Find the NT_GNU_PROPERTY_TYPE_0 note. */
70 if (note->n_namesz == 4
71 && note->n_type == NT_GNU_PROPERTY_TYPE_0
72 && memcmp (note + 1, "GNU", 4) == 0)
73 {
74 /* Stop if we see more than one GNU property note which may
75 be generated by the older linker. */
76 if (l->l_cet != lc_unknown)
77 return;
78
79 /* Check CET status now. */
80 l->l_cet = lc_none;
81
82 /* Check for invalid property. */
83 if (note->n_descsz < 8
84 || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
85 return;
86
87 /* Start and end of property array. */
88 unsigned char *ptr = (unsigned char *) (note + 1) + 4;
89 unsigned char *ptr_end = ptr + note->n_descsz;
90
91 do
92 {
93 unsigned int type = *(unsigned int *) ptr;
94 unsigned int datasz = *(unsigned int *) (ptr + 4);
95
96 /* Property type must be in ascending order. */
97 if (type < last_type)
98 return;
99
100 ptr += 8;
101 if ((ptr + datasz) > ptr_end)
102 return;
103
104 last_type = type;
105
106 if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
107 {
108 /* The size of GNU_PROPERTY_X86_FEATURE_1_AND is 4
109 bytes. When seeing GNU_PROPERTY_X86_FEATURE_1_AND,
110 we stop the search regardless if its size is correct
111 or not. There is no point to continue if this note
112 is ill-formed. */
113 if (datasz != 4)
114 return;
115
116 feature_1 = *(unsigned int *) ptr;
117
118 /* Keep searching for the next GNU property note
119 generated by the older linker. */
120 break;
121 }
122 else if (type > GNU_PROPERTY_X86_FEATURE_1_AND)
123 {
124 /* Stop since property type is in ascending order. */
125 return;
126 }
127
128 /* Check the next property item. */
129 ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
130 }
131 while ((ptr_end - ptr) >= 8);
132 }
133
134 /* NB: Note sections like .note.ABI-tag and .note.gnu.build-id are
135 aligned to 4 bytes in 64-bit ELF objects. */
136 note = ((const void *) note
137 + ELF_NOTE_NEXT_OFFSET (note->n_namesz, note->n_descsz,
138 align));
139 }
140
141 /* We get here only if there is one or no GNU property note. */
142 if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_IBT))
143 l->l_cet |= lc_ibt;
144 if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_SHSTK))
145 l->l_cet |= lc_shstk;
146#endif
147}
148
149#ifdef FILEBUF_SIZE
150static inline int __attribute__ ((unused))
151_dl_process_pt_note (struct link_map *l, const ElfW(Phdr) *ph,
152 int fd, struct filebuf *fbp)
153{
154# if CET_ENABLED
155 const ElfW(Nhdr) *note;
156 ElfW(Nhdr) *note_malloced = NULL;
157 ElfW(Addr) size = ph->p_filesz;
158
159 if (ph->p_offset + size <= (size_t) fbp->len)
160 note = (const void *) (fbp->buf + ph->p_offset);
161 else
162 {
163 if (size < __MAX_ALLOCA_CUTOFF)
164 note = alloca (size);
165 else
166 {
167 note_malloced = malloc (size);
168 note = note_malloced;
169 }
170 __lseek (fd, ph->p_offset, SEEK_SET);
171 if (__read_nocancel (fd, (void *) note, size) != size)
172 {
173 if (note_malloced)
174 free (note_malloced);
175 return -1;
176 }
177 }
178
179 _dl_process_cet_property_note (l, note, size, ph->p_align);
180 if (note_malloced)
181 free (note_malloced);
182# endif
183 return 0;
184}
185#endif
186
187static inline int __attribute__ ((unused))
188_rtld_process_pt_note (struct link_map *l, const ElfW(Phdr) *ph)
189{
190 const ElfW(Nhdr) *note = (const void *) (ph->p_vaddr + l->l_addr);
191 _dl_process_cet_property_note (l, note, ph->p_memsz, ph->p_align);
192 return 0;
193}
194
195#endif /* _DL_PROP_H */
196