1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "linker_note_gnu_property.h"
30 
31 #include <elf.h>
32 #include <link.h>
33 
34 #include "linker.h"
35 #include "linker_debug.h"
36 #include "linker_globals.h"
37 #include "linker_soinfo.h"
38 
GnuPropertySection(const soinfo * si)39 GnuPropertySection::GnuPropertySection(const soinfo* si)
40     : GnuPropertySection(si->phdr, si->phnum, si->load_bias, si->get_realpath()) {}
41 
GnuPropertySection(const ElfW (Phdr)* phdr,size_t phdr_count,const ElfW (Addr)load_bias,const char * name)42 GnuPropertySection::GnuPropertySection(const ElfW(Phdr)* phdr, size_t phdr_count,
43                                        const ElfW(Addr) load_bias, const char* name) {
44   // Try to find PT_GNU_PROPERTY segment.
45   auto note_gnu_property = FindSegment(phdr, phdr_count, load_bias, name);
46   // Perform some validity checks.
47   if (note_gnu_property && SanityCheck(note_gnu_property, name)) {
48     // Parse section.
49     Parse(note_gnu_property, name);
50   }
51 }
52 
ElfW(NhdrGNUProperty)53 const ElfW(NhdrGNUProperty)* GnuPropertySection::FindSegment(const ElfW(Phdr)* phdr,
54                                                              size_t phdr_count,
55                                                              const ElfW(Addr) load_bias,
56                                                              const char* name) const {
57   // According to Linux gABI extension this segment should contain
58   // .note.gnu.property section only.
59   if (phdr != nullptr) {
60     for (size_t i = 0; i < phdr_count; ++i) {
61       if (phdr[i].p_type != PT_GNU_PROPERTY) {
62         continue;
63       }
64 
65       TRACE("\"%s\" PT_GNU_PROPERTY: found at segment index %zu", name, i);
66 
67       // Check segment size.
68       if (phdr[i].p_memsz < sizeof(ElfW(NhdrGNUProperty))) {
69         DL_ERR_AND_LOG(
70             "\"%s\" PT_GNU_PROPERTY segment is too small. Segment "
71             "size is %zu, minimum is %zu.",
72             name, static_cast<size_t>(phdr[i].p_memsz), sizeof(ElfW(NhdrGNUProperty)));
73         return nullptr;
74       }
75 
76       // PT_GNU_PROPERTY contains .note.gnu.property which has SHF_ALLOC
77       // attribute, therefore it is loaded.
78       auto note_nhdr = reinterpret_cast<ElfW(NhdrGNUProperty)*>(load_bias + phdr[i].p_vaddr);
79 
80       // Check that the n_descsz <= p_memsz
81       if ((phdr[i].p_memsz - sizeof(ElfW(NhdrGNUProperty))) < note_nhdr->nhdr.n_descsz) {
82         DL_ERR_AND_LOG(
83             "\"%s\" PT_GNU_PROPERTY segment p_memsz (%zu) is too small for note n_descsz (%zu).",
84             name, static_cast<size_t>(phdr[i].p_memsz),
85             static_cast<size_t>(note_nhdr->nhdr.n_descsz));
86         return nullptr;
87       }
88 
89       return note_nhdr;
90     }
91   }
92 
93   TRACE("\"%s\" PT_GNU_PROPERTY: not found", name);
94   return nullptr;
95 }
96 
SanityCheck(const ElfW (NhdrGNUProperty)* note_nhdr,const char * name) const97 bool GnuPropertySection::SanityCheck(const ElfW(NhdrGNUProperty)* note_nhdr,
98                                      const char* name) const {
99   // Check .note section type
100   if (note_nhdr->nhdr.n_type != NT_GNU_PROPERTY_TYPE_0) {
101     DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected note type. Expected %u, got %u.", name,
102                    NT_GNU_PROPERTY_TYPE_0, note_nhdr->nhdr.n_type);
103     return false;
104   }
105 
106   if (note_nhdr->nhdr.n_namesz != 4) {
107     DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected name size. Expected 4, got %u.", name,
108                    note_nhdr->nhdr.n_namesz);
109     return false;
110   }
111 
112   if (strncmp(note_nhdr->n_name, "GNU", 4) != 0) {
113     DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected name. Expected 'GNU', got '%s'.", name,
114                    note_nhdr->n_name);
115     return false;
116   }
117 
118   return true;
119 }
120 
Parse(const ElfW (NhdrGNUProperty)* note_nhdr,const char * name)121 bool GnuPropertySection::Parse(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name) {
122   // The total length of the program property array is in _bytes_.
123   ElfW(Word) offset = 0;
124   while (offset < note_nhdr->nhdr.n_descsz) {
125     DEBUG("\"%s\" .note.gnu.property: processing at offset 0x%x", name, offset);
126 
127     // At least the "header" part must fit.
128     // The ABI doesn't say that pr_datasz can't be 0.
129     if ((note_nhdr->nhdr.n_descsz - offset) < sizeof(ElfW(Prop))) {
130       DL_ERR_AND_LOG(
131           "\"%s\" .note.gnu.property: no more space left for a "
132           "Program Property Note header.",
133           name);
134       return false;
135     }
136 
137     // Loop on program property array.
138     const ElfW(Prop)* property = reinterpret_cast<const ElfW(Prop)*>(&note_nhdr->n_desc[offset]);
139     const ElfW(Word) property_size =
140         align_up(sizeof(ElfW(Prop)) + property->pr_datasz, sizeof(ElfW(Addr)));
141     if ((note_nhdr->nhdr.n_descsz - offset) < property_size) {
142       DL_ERR_AND_LOG(
143           "\"%s\" .note.gnu.property: property descriptor size is "
144           "invalid. Expected at least %u bytes, got %u.",
145           name, property_size, note_nhdr->nhdr.n_descsz - offset);
146       return false;
147     }
148 
149     // Cache found properties.
150     switch (property->pr_type) {
151 #if defined(__aarch64__)
152       case GNU_PROPERTY_AARCH64_FEATURE_1_AND: {
153         if (property->pr_datasz != 4) {
154           DL_ERR_AND_LOG(
155               "\"%s\" .note.gnu.property: property descriptor size is "
156               "invalid. Expected %u bytes for GNU_PROPERTY_AARCH64_FEATURE_1_AND, got %u.",
157               name, 4, property->pr_datasz);
158           return false;
159         }
160 
161         const ElfW(Word) flags = *reinterpret_cast<const ElfW(Word)*>(&property->pr_data[0]);
162         properties_.bti_compatible = (flags & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) != 0;
163         if (properties_.bti_compatible) {
164           INFO("[ BTI compatible: \"%s\" ]", name);
165         }
166         break;
167       }
168 #endif
169       default:
170         DEBUG("\"%s\" .note.gnu.property: found property pr_type %u pr_datasz 0x%x", name,
171               property->pr_type, property->pr_datasz);
172         break;
173     }
174 
175     // Move offset, this should be safe to add because of previous checks.
176     offset += property_size;
177   }
178 
179   return true;
180 }
181 
182 #if defined(__aarch64__)
IsBTICompatible() const183 bool GnuPropertySection::IsBTICompatible() const {
184   return (g_platform_properties.bti_supported && properties_.bti_compatible);
185 }
186 #endif
187