1 /*
2 * Copyright (C) 2012 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_phdr.h"
30
31 #include <linux/prctl.h>
32 #include <sys/mman.h>
33 #include <sys/prctl.h>
34 #include <unistd.h>
35
36 #include "linker_debug.h"
37 #include "linker_dlwarning.h"
38 #include "linker_globals.h"
39
40 #include "platform/bionic/macros.h"
41 #include "platform/bionic/page.h"
42
43 #include <string>
44
45 static bool g_enable_16kb_app_compat;
46
segment_contains_prefix(const ElfW (Phdr)* segment,const ElfW (Phdr)* prefix)47 static inline bool segment_contains_prefix(const ElfW(Phdr)* segment, const ElfW(Phdr)* prefix) {
48 return segment && prefix && segment->p_vaddr == prefix->p_vaddr;
49 }
50
set_16kb_appcompat_mode(bool enable_app_compat)51 void set_16kb_appcompat_mode(bool enable_app_compat) {
52 g_enable_16kb_app_compat = enable_app_compat;
53 }
54
get_16kb_appcompat_mode()55 bool get_16kb_appcompat_mode() {
56 return g_enable_16kb_app_compat;
57 }
58
59 /*
60 * Returns true if the ELF contains at most 1 RELRO segment; and populates @relro_phdr
61 * with the relro phdr or nullptr if none.
62 *
63 * Returns false if more than 1 RELRO segments are found.
64 */
HasAtMostOneRelroSegment(const ElfW (Phdr)** relro_phdr)65 bool ElfReader::HasAtMostOneRelroSegment(const ElfW(Phdr)** relro_phdr) {
66 const ElfW(Phdr)* relro = nullptr;
67 for (size_t i = 0; i < phdr_num_; ++i) {
68 const ElfW(Phdr)* phdr = &phdr_table_[i];
69
70 if (phdr->p_type != PT_GNU_RELRO) {
71 continue;
72 }
73
74 if (relro == nullptr) {
75 relro = phdr;
76 } else {
77 return false;
78 }
79 }
80
81 *relro_phdr = relro;
82
83 return true;
84 }
85
86 /*
87 * In 16KiB compatibility mode ELFs with the following segment layout
88 * can be loaded successfully:
89 *
90 * ┌────────────┬─────────────────────────┬────────────┐
91 * │ │ │ │
92 * │ (RO|RX)* │ (RW - RELRO prefix)? │ (RW)* │
93 * │ │ │ │
94 * └────────────┴─────────────────────────┴────────────┘
95 *
96 * In other words, compatible layouts have:
97 * - zero or more RO or RX segments;
98 * - followed by zero or one RELRO prefix;
99 * - followed by zero or more RW segments (this can include the RW
100 * suffix from the segment containing the RELRO prefix, if any)
101 *
102 * In 16KiB compat mode, after relocation, the ELF is layout in virtual
103 * memory is as shown below:
104 * ┌──────────────────────────────────────┬────────────┐
105 * │ │ │
106 * │ (RX)? │ (RW)? │
107 * │ │ │
108 * └──────────────────────────────────────┴────────────┘
109 *
110 * In compat mode:
111 * - the RO and RX segments along with the RELRO prefix are protected
112 * as RX;
113 * - and the RW segments along with RW suffix from the relro segment,
114 * if any; are RW protected.
115 *
116 * This allows for the single RX|RW permission boundary to be aligned with
117 * a 16KiB page boundary; since a single page cannot share multiple
118 * permissions.
119 *
120 * IsEligibleFor16KiBAppCompat() identifies compatible ELFs and populates @vaddr
121 * with the boundary between RX|RW portions.
122 *
123 * Returns true if the ELF can be loaded in compat mode, else false.
124 */
IsEligibleFor16KiBAppCompat(ElfW (Addr)* vaddr)125 bool ElfReader::IsEligibleFor16KiBAppCompat(ElfW(Addr)* vaddr) {
126 const ElfW(Phdr)* relro_phdr = nullptr;
127 if (!HasAtMostOneRelroSegment(&relro_phdr)) {
128 DL_WARN("\"%s\": Compat loading failed: Multiple RELRO segments found", name_.c_str());
129 return false;
130 }
131
132 const ElfW(Phdr)* last_rw = nullptr;
133 const ElfW(Phdr)* first_rw = nullptr;
134
135 for (size_t i = 0; i < phdr_num_; ++i) {
136 const ElfW(Phdr)* curr = &phdr_table_[i];
137 const ElfW(Phdr)* prev = (i > 0) ? &phdr_table_[i - 1] : nullptr;
138
139 if (curr->p_type != PT_LOAD) {
140 continue;
141 }
142
143 int prot = PFLAGS_TO_PROT(curr->p_flags);
144
145 if ((prot & PROT_WRITE) && (prot & PROT_READ)) {
146 if (!first_rw) {
147 first_rw = curr;
148 }
149
150 if (last_rw && last_rw != prev) {
151 DL_WARN("\"%s\": Compat loading failed: ELF contains multiple non-adjacent RW segments",
152 name_.c_str());
153 return false;
154 }
155
156 last_rw = curr;
157 }
158 }
159
160 if (!relro_phdr) {
161 *vaddr = align_down(first_rw->p_vaddr, kCompatPageSize);
162 return true;
163 }
164
165 // The RELRO segment is present, it must be the prefix of the first RW segment.
166 if (!segment_contains_prefix(first_rw, relro_phdr)) {
167 DL_WARN("\"%s\": Compat loading failed: RELRO is not in the first RW segment",
168 name_.c_str());
169 return false;
170 }
171
172 uint64_t end;
173 if (__builtin_add_overflow(relro_phdr->p_vaddr, relro_phdr->p_memsz, &end)) {
174 DL_WARN("\"%s\": Compat loading failed: relro vaddr + memsz overflowed", name_.c_str());
175 return false;
176 }
177
178 *vaddr = align_up(end, kCompatPageSize);
179 return true;
180 }
181
182 /*
183 * Returns the offset/shift needed to align @vaddr to a page boundary.
184 */
perm_boundary_offset(const ElfW (Addr)addr)185 static inline ElfW(Addr) perm_boundary_offset(const ElfW(Addr) addr) {
186 ElfW(Addr) offset = page_offset(addr);
187
188 return offset ? page_size() - offset : 0;
189 }
190
Setup16KiBAppCompat()191 bool ElfReader::Setup16KiBAppCompat() {
192 if (!should_use_16kib_app_compat_) {
193 return true;
194 }
195
196 ElfW(Addr) rx_rw_boundary; // Permission bounadry for compat mode
197 if (!IsEligibleFor16KiBAppCompat(&rx_rw_boundary)) {
198 return false;
199 }
200
201 // Adjust the load_bias to position the RX|RW boundary on a page boundary
202 load_bias_ += perm_boundary_offset(rx_rw_boundary);
203
204 // RW region (.data, .bss ...)
205 ElfW(Addr) rw_start = load_bias_ + rx_rw_boundary;
206 ElfW(Addr) rw_size = load_size_ - (rw_start - reinterpret_cast<ElfW(Addr)>(load_start_));
207
208 CHECK(rw_start % getpagesize() == 0);
209 CHECK(rw_size % getpagesize() == 0);
210
211 // Compat RELRO (RX) region (.text, .data.relro, ...)
212 compat_relro_start_ = reinterpret_cast<ElfW(Addr)>(load_start_);
213 compat_relro_size_ = load_size_ - rw_size;
214
215 // Label the ELF VMA, since compat mode uses anonymous mappings.
216 std::string compat_name = name_ + " (compat loaded)";
217 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, load_start_, load_size_, compat_name.c_str());
218
219 return true;
220 }
221
CompatMapSegment(size_t seg_idx,size_t len)222 bool ElfReader::CompatMapSegment(size_t seg_idx, size_t len) {
223 const ElfW(Phdr)* phdr = &phdr_table_[seg_idx];
224
225 // NOTE: The compat(legacy) page size (4096) must be used when aligning
226 // the 4KiB segments for loading (reading). The larger 16KiB page size
227 // will lead to overwriting adjacent segments since the ELF's segment(s)
228 // are not 16KiB aligned.
229
230 void* start = reinterpret_cast<void*>(align_down(phdr->p_vaddr + load_bias_, kCompatPageSize));
231
232 // The ELF could be being loaded directly from a zipped APK,
233 // the zip offset must be added to find the segment offset.
234 const ElfW(Addr) offset = file_offset_ + align_down(phdr->p_offset, kCompatPageSize);
235
236 CHECK(should_use_16kib_app_compat_);
237
238 // Since the 4KiB max-page-size ELF is not properly aligned, loading it by
239 // directly mmapping the ELF file is not feasible.
240 // Instead, read the ELF contents into the anonymous RW mapping.
241 if (TEMP_FAILURE_RETRY(pread64(fd_, start, len, offset)) == -1) {
242 DL_ERR("Compat loading: \"%s\" failed to read LOAD segment %zu: %m", name_.c_str(), seg_idx);
243 return false;
244 }
245
246 return true;
247 }
248