1 /* 2 * Copyright (C) 2008 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 "system_properties/prop_area.h" 30 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdlib.h> 34 #include <sys/cdefs.h> 35 #include <sys/stat.h> 36 #include <sys/types.h> 37 #include <sys/xattr.h> 38 #include <unistd.h> 39 40 #include <new> 41 42 #include <async_safe/log.h> 43 44 constexpr size_t PA_SIZE = 128 * 1024; 45 constexpr uint32_t PROP_AREA_MAGIC = 0x504f5250; 46 constexpr uint32_t PROP_AREA_VERSION = 0xfc6ed0ab; 47 48 size_t prop_area::pa_size_ = 0; 49 size_t prop_area::pa_data_size_ = 0; 50 51 prop_area* prop_area::map_prop_area_rw(const char* filename, const char* context, 52 bool* fsetxattr_failed) { 53 /* dev is a tmpfs that we can use to carve a shared workspace 54 * out of, so let's do that... 55 */ 56 const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); 57 58 if (fd < 0) { 59 if (errno == EACCES) { 60 /* for consistency with the case where the process has already 61 * mapped the page in and segfaults when trying to write to it 62 */ 63 abort(); 64 } 65 return nullptr; 66 } 67 68 if (context) { 69 if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) { 70 async_safe_format_log(ANDROID_LOG_ERROR, "libc", 71 "fsetxattr failed to set context (%s) for \"%s\"", context, filename); 72 /* 73 * fsetxattr() will fail during system properties tests due to selinux policy. 74 * We do not want to create a custom policy for the tester, so we will continue in 75 * this function but set a flag that an error has occurred. 76 * Init, which is the only daemon that should ever call this function will abort 77 * when this error occurs. 78 * Otherwise, the tester will ignore it and continue, albeit without any selinux 79 * property separation. 80 */ 81 if (fsetxattr_failed) { 82 *fsetxattr_failed = true; 83 } 84 } 85 } 86 87 if (ftruncate(fd, PA_SIZE) < 0) { 88 close(fd); 89 return nullptr; 90 } 91 92 pa_size_ = PA_SIZE; 93 pa_data_size_ = pa_size_ - sizeof(prop_area); 94 95 void* const memory_area = mmap(nullptr, pa_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 96 if (memory_area == MAP_FAILED) { 97 close(fd); 98 return nullptr; 99 } 100 101 prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); 102 103 close(fd); 104 return pa; 105 } 106 107 prop_area* prop_area::map_fd_ro(const int fd) { 108 struct stat fd_stat; 109 if (fstat(fd, &fd_stat) < 0) { 110 return nullptr; 111 } 112 113 if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) || 114 ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) || 115 (fd_stat.st_size < static_cast<off_t>(sizeof(prop_area)))) { 116 return nullptr; 117 } 118 119 pa_size_ = fd_stat.st_size; 120 pa_data_size_ = pa_size_ - sizeof(prop_area); 121 122 void* const map_result = mmap(nullptr, pa_size_, PROT_READ, MAP_SHARED, fd, 0); 123 if (map_result == MAP_FAILED) { 124 return nullptr; 125 } 126 127 prop_area* pa = reinterpret_cast<prop_area*>(map_result); 128 if ((pa->magic() != PROP_AREA_MAGIC) || (pa->version() != PROP_AREA_VERSION)) { 129 munmap(pa, pa_size_); 130 return nullptr; 131 } 132 133 return pa; 134 } 135 136 prop_area* prop_area::map_prop_area(const char* filename) { 137 int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); 138 if (fd == -1) return nullptr; 139 140 prop_area* map_result = map_fd_ro(fd); 141 close(fd); 142 143 return map_result; 144 } 145 146 void* prop_area::allocate_obj(const size_t size, uint_least32_t* const off) { 147 const size_t aligned = __BIONIC_ALIGN(size, sizeof(uint_least32_t)); 148 if (bytes_used_ + aligned > pa_data_size_) { 149 return nullptr; 150 } 151 152 *off = bytes_used_; 153 bytes_used_ += aligned; 154 return data_ + *off; 155 } 156 157 prop_bt* prop_area::new_prop_bt(const char* name, uint32_t namelen, uint_least32_t* const off) { 158 uint_least32_t new_offset; 159 void* const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset); 160 if (p != nullptr) { 161 prop_bt* bt = new (p) prop_bt(name, namelen); 162 *off = new_offset; 163 return bt; 164 } 165 166 return nullptr; 167 } 168 169 prop_info* prop_area::new_prop_info(const char* name, uint32_t namelen, const char* value, 170 uint32_t valuelen, uint_least32_t* const off) { 171 uint_least32_t new_offset; 172 void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset); 173 if (p == nullptr) return nullptr; 174 175 prop_info* info; 176 if (valuelen >= PROP_VALUE_MAX) { 177 uint32_t long_value_offset = 0; 178 char* long_location = reinterpret_cast<char*>(allocate_obj(valuelen + 1, &long_value_offset)); 179 if (!long_location) return nullptr; 180 181 memcpy(long_location, value, valuelen); 182 long_location[valuelen] = '\0'; 183 184 // Both new_offset and long_value_offset are offsets based off of data_, however prop_info 185 // does not know what data_ is, so we change this offset to be an offset from the prop_info 186 // pointer that contains it. 187 long_value_offset -= new_offset; 188 189 info = new (p) prop_info(name, namelen, long_value_offset); 190 } else { 191 info = new (p) prop_info(name, namelen, value, valuelen); 192 } 193 *off = new_offset; 194 return info; 195 } 196 197 void* prop_area::to_prop_obj(uint_least32_t off) { 198 if (off > pa_data_size_) return nullptr; 199 200 return (data_ + off); 201 } 202 203 inline prop_bt* prop_area::to_prop_bt(atomic_uint_least32_t* off_p) { 204 uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume); 205 return reinterpret_cast<prop_bt*>(to_prop_obj(off)); 206 } 207 208 inline prop_info* prop_area::to_prop_info(atomic_uint_least32_t* off_p) { 209 uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume); 210 return reinterpret_cast<prop_info*>(to_prop_obj(off)); 211 } 212 213 inline prop_bt* prop_area::root_node() { 214 return reinterpret_cast<prop_bt*>(to_prop_obj(0)); 215 } 216 217 static int cmp_prop_name(const char* one, uint32_t one_len, const char* two, uint32_t two_len) { 218 if (one_len < two_len) 219 return -1; 220 else if (one_len > two_len) 221 return 1; 222 else 223 return strncmp(one, two, one_len); 224 } 225 226 prop_bt* prop_area::find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen, 227 bool alloc_if_needed) { 228 prop_bt* current = bt; 229 while (true) { 230 if (!current) { 231 return nullptr; 232 } 233 234 const int ret = cmp_prop_name(name, namelen, current->name, current->namelen); 235 if (ret == 0) { 236 return current; 237 } 238 239 if (ret < 0) { 240 uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed); 241 if (left_offset != 0) { 242 current = to_prop_bt(¤t->left); 243 } else { 244 if (!alloc_if_needed) { 245 return nullptr; 246 } 247 248 uint_least32_t new_offset; 249 prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); 250 if (new_bt) { 251 atomic_store_explicit(¤t->left, new_offset, memory_order_release); 252 } 253 return new_bt; 254 } 255 } else { 256 uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed); 257 if (right_offset != 0) { 258 current = to_prop_bt(¤t->right); 259 } else { 260 if (!alloc_if_needed) { 261 return nullptr; 262 } 263 264 uint_least32_t new_offset; 265 prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); 266 if (new_bt) { 267 atomic_store_explicit(¤t->right, new_offset, memory_order_release); 268 } 269 return new_bt; 270 } 271 } 272 } 273 } 274 275 const prop_info* prop_area::find_property(prop_bt* const trie, const char* name, uint32_t namelen, 276 const char* value, uint32_t valuelen, 277 bool alloc_if_needed) { 278 if (!trie) return nullptr; 279 280 const char* remaining_name = name; 281 prop_bt* current = trie; 282 while (true) { 283 const char* sep = strchr(remaining_name, '.'); 284 const bool want_subtree = (sep != nullptr); 285 const uint32_t substr_size = (want_subtree) ? sep - remaining_name : strlen(remaining_name); 286 287 if (!substr_size) { 288 return nullptr; 289 } 290 291 prop_bt* root = nullptr; 292 uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); 293 if (children_offset != 0) { 294 root = to_prop_bt(¤t->children); 295 } else if (alloc_if_needed) { 296 uint_least32_t new_offset; 297 root = new_prop_bt(remaining_name, substr_size, &new_offset); 298 if (root) { 299 atomic_store_explicit(¤t->children, new_offset, memory_order_release); 300 } 301 } 302 303 if (!root) { 304 return nullptr; 305 } 306 307 current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); 308 if (!current) { 309 return nullptr; 310 } 311 312 if (!want_subtree) break; 313 314 remaining_name = sep + 1; 315 } 316 317 uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); 318 if (prop_offset != 0) { 319 return to_prop_info(¤t->prop); 320 } else if (alloc_if_needed) { 321 uint_least32_t new_offset; 322 prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset); 323 if (new_info) { 324 atomic_store_explicit(¤t->prop, new_offset, memory_order_release); 325 } 326 327 return new_info; 328 } else { 329 return nullptr; 330 } 331 } 332 333 bool prop_area::foreach_property(prop_bt* const trie, 334 void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { 335 if (!trie) return false; 336 337 uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed); 338 if (left_offset != 0) { 339 const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie); 340 if (err < 0) return false; 341 } 342 uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed); 343 if (prop_offset != 0) { 344 prop_info* info = to_prop_info(&trie->prop); 345 if (!info) return false; 346 propfn(info, cookie); 347 } 348 uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed); 349 if (children_offset != 0) { 350 const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie); 351 if (err < 0) return false; 352 } 353 uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed); 354 if (right_offset != 0) { 355 const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie); 356 if (err < 0) return false; 357 } 358 359 return true; 360 } 361 362 const prop_info* prop_area::find(const char* name) { 363 return find_property(root_node(), name, strlen(name), nullptr, 0, false); 364 } 365 366 bool prop_area::add(const char* name, unsigned int namelen, const char* value, 367 unsigned int valuelen) { 368 return find_property(root_node(), name, namelen, value, valuelen, true); 369 } 370 371 bool prop_area::foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { 372 return foreach_property(root_node(), propfn, cookie); 373 } 374