From 52f1d50902f329d51b44873ab5fe8f60bc353cfd Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 28 Feb 2017 16:52:27 +0800 Subject: [PATCH] Project restructure --- .gitmodules | 5 +- jni/resetprop | 1 - jni/resetprop/Android.mk | 8 + jni/resetprop/LICENSE | 339 +++++++ jni/resetprop/_system_properties.h | 180 ++++ jni/resetprop/bionic_futex.h | 77 ++ jni/resetprop/bionic_lock.h | 79 ++ jni/resetprop/bionic_macros.h | 49 + jni/resetprop/libc_logging.cpp | 659 +++++++++++++ jni/resetprop/libc_logging.h | 115 +++ jni/resetprop/resetprop.cpp | 280 ++++++ jni/resetprop/system_properties.cpp | 1385 +++++++++++++++++++++++++++ 12 files changed, 3172 insertions(+), 5 deletions(-) delete mode 160000 jni/resetprop create mode 100644 jni/resetprop/Android.mk create mode 100644 jni/resetprop/LICENSE create mode 100644 jni/resetprop/_system_properties.h create mode 100644 jni/resetprop/bionic_futex.h create mode 100644 jni/resetprop/bionic_lock.h create mode 100644 jni/resetprop/bionic_macros.h create mode 100644 jni/resetprop/libc_logging.cpp create mode 100644 jni/resetprop/libc_logging.h create mode 100644 jni/resetprop/resetprop.cpp create mode 100644 jni/resetprop/system_properties.cpp diff --git a/.gitmodules b/.gitmodules index 483f82e26..40ee763b1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,9 @@ [submodule "jni/sepolicy-inject"] path = jni/sepolicy-inject url = https://github.com/topjohnwu/sepolicy-inject -[submodule "jni/resetprop"] - path = jni/resetprop - url = https://github.com/topjohnwu/resetprop.git [submodule "jni/selinux"] path = jni/selinux url = https://github.com/topjohnwu/selinux.git [submodule "jni/su"] path = jni/su - url = https://github.com/topjohnwu/Superuser.git + url = https://github.com/topjohnwu/MagiskSU.git diff --git a/jni/resetprop b/jni/resetprop deleted file mode 160000 index 3a6db7dba..000000000 --- a/jni/resetprop +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a6db7dbafdfb46fd09705ba1ab5477cacb87155 diff --git a/jni/resetprop/Android.mk b/jni/resetprop/Android.mk new file mode 100644 index 000000000..bc29e4892 --- /dev/null +++ b/jni/resetprop/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := resetprop +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := resetprop.cpp system_properties.cpp libc_logging.cpp +LOCAL_LDLIBS += -latomic +include $(BUILD_EXECUTABLE) diff --git a/jni/resetprop/LICENSE b/jni/resetprop/LICENSE new file mode 100644 index 000000000..23cb79033 --- /dev/null +++ b/jni/resetprop/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/jni/resetprop/_system_properties.h b/jni/resetprop/_system_properties.h new file mode 100644 index 000000000..e82c3c5ca --- /dev/null +++ b/jni/resetprop/_system_properties.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _INCLUDE_SYS__SYSTEM_PROPERTIES_H +#define _INCLUDE_SYS__SYSTEM_PROPERTIES_H + +#ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#error you should #include instead +#else +#include + +typedef struct prop_msg prop_msg; + +#define PROP_AREA_MAGIC 0x504f5250 +#define PROP_AREA_VERSION 0xfc6ed0ab +#define PROP_AREA_VERSION_COMPAT 0x45434f76 + +#define PROP_SERVICE_NAME "property_service" +#define PROP_FILENAME_MAX 1024 +#define PROP_FILENAME "/dev/__properties__" + +#define PA_SIZE (128 * 1024) + +#define SERIAL_VALUE_LEN(serial) ((serial) >> 24) +#define SERIAL_DIRTY(serial) ((serial) & 1) + +__BEGIN_DECLS + +struct prop_msg +{ + unsigned cmd; + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; +}; + +#define PROP_MSG_SETPROP 1 + +/* +** Rules: +** +** - there is only one writer, but many readers +** - prop_area.count will never decrease in value +** - once allocated, a prop_info's name will not change +** - once allocated, a prop_info's offset will not change +** - reading a value requires the following steps +** 1. serial = pi->serial +** 2. if SERIAL_DIRTY(serial), wait*, then goto 1 +** 3. memcpy(local, pi->value, SERIAL_VALUE_LEN(serial) + 1) +** 4. if pi->serial != serial, goto 2 +** +** - writing a value requires the following steps +** 1. pi->serial = pi->serial | 1 +** 2. memcpy(pi->value, local_value, value_len) +** 3. pi->serial = (value_len << 24) | ((pi->serial + 1) & 0xffffff) +*/ + +#define PROP_PATH_RAMDISK_DEFAULT "/default.prop" +#define PROP_PATH_SYSTEM_BUILD "/system/build.prop" +#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop" +#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" +#define PROP_PATH_FACTORY "/factory/factory.prop" + +/* +** Map the property area from the specified filename. This +** method is for testing only. +*/ +int __system_property_set_filename(const char *filename); + +/* +** Initialize the area to be used to store properties. Can +** only be done by a single process that has write access to +** the property area. +*/ +int __system_property_area_init(); + +/* Read the global serial number of the system properties +** +** Called to predict if a series of cached __system_property_find +** objects will have seen __system_property_serial values change. +** But also aids the converse, as changes in the global serial can +** also be used to predict if a failed __system_property_find +** could in-turn now find a new object; thus preventing the +** cycles of effort to poll __system_property_find. +** +** Typically called at beginning of a cache cycle to signal if _any_ possible +** changes have occurred since last. If there is, one may check each individual +** __system_property_serial to confirm dirty, or __system_property_find +** to check if the property now exists. If a call to __system_property_add +** or __system_property_update has completed between two calls to +** __system_property_area_serial then the second call will return a larger +** value than the first call. Beware of race conditions as changes to the +** properties are not atomic, the main value of this call is to determine +** whether the expensive __system_property_find is worth retrying to see if +** a property now exists. +** +** Returns the serial number on success, -1 on error. +*/ +unsigned int __system_property_area_serial(); + +/* Add a new system property. Can only be done by a single +** process that has write access to the property area, and +** that process must handle sequencing to ensure the property +** does not already exist and that only one property is added +** or updated at a time. +** +** Returns 0 on success, -1 if the property area is full. +*/ +int __system_property_add(const char *name, unsigned int namelen, + const char *value, unsigned int valuelen); + +int __system_property_del(const char *name); + +/* Update the value of a system property returned by +** __system_property_find. Can only be done by a single process +** that has write access to the property area, and that process +** must handle sequencing to ensure that only one property is +** updated at a time. +** +** Returns 0 on success, -1 if the parameters are incorrect. +*/ +int __system_property_update(prop_info *pi, const char *value, unsigned int len); + +/* Read the serial number of a system property returned by +** __system_property_find. +** +** Returns the serial number on success, -1 on error. +*/ +unsigned int __system_property_serial(const prop_info *pi); + +/* Wait for any system property to be updated. Caller must pass +** in 0 the first time, and the previous return value on each +** successive call. */ +unsigned int __system_property_wait_any(unsigned int serial); + +/* Compatibility functions to support using an old init with a new libc, + ** mostly for the OTA updater binary. These can be deleted once OTAs from + ** a pre-K release no longer needed to be supported. */ +// const prop_info *__system_property_find_compat(const char *name); +// int __system_property_read_compat(const prop_info *pi, char *name, char *value); +// int __system_property_foreach_compat( +// void (*propfn)(const prop_info *pi, void *cookie), +// void *cookie); + +/* Initialize the system properties area in read only mode. + * Should be done by all processes that need to read system + * properties. + * + * Returns 0 on success, -1 otherwise. + */ +int __system_properties_init(); + +__END_DECLS + +#endif +#endif diff --git a/jni/resetprop/bionic_futex.h b/jni/resetprop/bionic_futex.h new file mode 100644 index 000000000..946d9dd1f --- /dev/null +++ b/jni/resetprop/bionic_futex.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _BIONIC_FUTEX_H +#define _BIONIC_FUTEX_H + +#include +#include +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +struct timespec; + +static inline __always_inline int __futex(volatile void* ftx, int op, int value, + const struct timespec* timeout, + int bitset) { + // Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to. + int saved_errno = errno; + int result = syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset); + if (__predict_false(result == -1)) { + result = -errno; + errno = saved_errno; + } + return result; +} + +static inline int __futex_wake(volatile void* ftx, int count) { + return __futex(ftx, FUTEX_WAKE, count, NULL, 0); +} + +static inline int __futex_wake_ex(volatile void* ftx, bool shared, int count) { + return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL, 0); +} + +static inline int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) { + return __futex(ftx, FUTEX_WAIT, value, timeout, 0); +} + +static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value, + bool use_realtime_clock, const struct timespec* abs_timeout) { + return __futex(ftx, (shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE) | + (use_realtime_clock ? FUTEX_CLOCK_REALTIME : 0), value, abs_timeout, + FUTEX_BITSET_MATCH_ANY); +} + +__END_DECLS + +#endif /* _BIONIC_FUTEX_H */ diff --git a/jni/resetprop/bionic_lock.h b/jni/resetprop/bionic_lock.h new file mode 100644 index 000000000..60c440b00 --- /dev/null +++ b/jni/resetprop/bionic_lock.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _BIONIC_LOCK_H +#define _BIONIC_LOCK_H + +#include +#include "bionic_futex.h" +#include "bionic_macros.h" + +// Lock is used in places like pthread_rwlock_t, which can be initialized without calling +// an initialization function. So make sure Lock can be initialized by setting its memory to 0. +class Lock { + private: + enum LockState { + Unlocked = 0, + LockedWithoutWaiter, + LockedWithWaiter, + }; + _Atomic(LockState) state; + bool process_shared; + + public: + void init(bool process_shared) { + atomic_init(&state, Unlocked); + this->process_shared = process_shared; + } + + bool trylock() { + LockState old_state = Unlocked; + return __predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state, + LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed)); + } + + void lock() { + LockState old_state = Unlocked; + if (__predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state, + LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed))) { + return; + } + while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) { + // TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping. + __futex_wait_ex(&state, process_shared, LockedWithWaiter, false, nullptr); + } + return; + } + + void unlock() { + if (atomic_exchange_explicit(&state, Unlocked, memory_order_release) == LockedWithWaiter) { + __futex_wake_ex(&state, process_shared, 1); + } + } +}; + +#endif // _BIONIC_LOCK_H diff --git a/jni/resetprop/bionic_macros.h b/jni/resetprop/bionic_macros.h new file mode 100644 index 000000000..4969bd95f --- /dev/null +++ b/jni/resetprop/bionic_macros.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BIONIC_MACROS_H_ +#define _BIONIC_MACROS_H_ + +// Frameworks OpenGL code currently leaks this header and allows +// collisions with other declarations, e.g., from libnativehelper. +// TODO: Remove once cleaned up. b/18334516 +#if !defined(DISALLOW_COPY_AND_ASSIGN) +// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. +// It goes in the private: declarations in a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#endif // !defined(DISALLOW_COPY_AND_ASSIGN) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +#define BIONIC_ALIGN(value, alignment) \ + (((value) + (alignment) - 1) & ~((alignment) - 1)) + +#define BIONIC_ROUND_UP_POWER_OF_2(value) \ + ((sizeof(value) == 8) \ + ? (1UL << (64 - __builtin_clzl(static_cast(value)))) \ + : (1UL << (32 - __builtin_clz(static_cast(value))))) + +#endif // _BIONIC_MACROS_H_ diff --git a/jni/resetprop/libc_logging.cpp b/jni/resetprop/libc_logging.cpp new file mode 100644 index 000000000..72098fad7 --- /dev/null +++ b/jni/resetprop/libc_logging.cpp @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libc_logging.h" // Relative path so we can #include this .cpp file for testing. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include "_system_properties.h" + +static pthread_mutex_t g_abort_msg_lock = PTHREAD_MUTEX_INITIALIZER; + +__LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common. + +// Must be kept in sync with frameworks/base/core/java/android/util/EventLog.java. +enum AndroidEventLogType { + EVENT_TYPE_INT = 0, + EVENT_TYPE_LONG = 1, + EVENT_TYPE_STRING = 2, + EVENT_TYPE_LIST = 3, + EVENT_TYPE_FLOAT = 4, +}; + +struct BufferOutputStream { + public: + BufferOutputStream(char* buffer, size_t size) : total(0) { + buffer_ = buffer; + end_ = buffer + size - 1; + pos_ = buffer_; + pos_[0] = '\0'; + } + + ~BufferOutputStream() { + } + + void Send(const char* data, int len) { + if (len < 0) { + len = strlen(data); + } + + total += len; + + while (len > 0) { + int avail = end_ - pos_; + if (avail == 0) { + return; + } + if (avail > len) { + avail = len; + } + memcpy(pos_, data, avail); + pos_ += avail; + pos_[0] = '\0'; + len -= avail; + } + } + + size_t total; + + private: + char* buffer_; + char* pos_; + char* end_; +}; + +struct FdOutputStream { + public: + FdOutputStream(int fd) : total(0), fd_(fd) { + } + + void Send(const char* data, int len) { + if (len < 0) { + len = strlen(data); + } + + total += len; + + while (len > 0) { + int rc = TEMP_FAILURE_RETRY(write(fd_, data, len)); + if (rc == -1) { + return; + } + data += rc; + len -= rc; + } + } + + size_t total; + + private: + int fd_; +}; + +/*** formatted output implementation + ***/ + +/* Parse a decimal string from 'format + *ppos', + * return the value, and writes the new position past + * the decimal string in '*ppos' on exit. + * + * NOTE: Does *not* handle a sign prefix. + */ +static unsigned parse_decimal(const char *format, int *ppos) { + const char* p = format + *ppos; + unsigned result = 0; + + for (;;) { + int ch = *p; + unsigned d = static_cast(ch - '0'); + + if (d >= 10U) { + break; + } + + result = result*10 + d; + p++; + } + *ppos = p - format; + return result; +} + +// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes. +// Assumes that buf_size > 0. +static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) { + char* p = buf; + char* end = buf + buf_size - 1; + + // Generate digit string in reverse order. + while (value) { + unsigned d = value % base; + value /= base; + if (p != end) { + char ch; + if (d < 10) { + ch = '0' + d; + } else { + ch = (caps ? 'A' : 'a') + (d - 10); + } + *p++ = ch; + } + } + + // Special case for 0. + if (p == buf) { + if (p != end) { + *p++ = '0'; + } + } + *p = '\0'; + + // Reverse digit string in-place. + size_t length = p - buf; + for (size_t i = 0, j = length - 1; i < j; ++i, --j) { + char ch = buf[i]; + buf[i] = buf[j]; + buf[j] = ch; + } +} + +static void format_integer(char* buf, size_t buf_size, uint64_t value, char conversion) { + // Decode the conversion specifier. + int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o'); + int base = 10; + if (conversion == 'x' || conversion == 'X') { + base = 16; + } else if (conversion == 'o') { + base = 8; + } + bool caps = (conversion == 'X'); + + if (is_signed && static_cast(value) < 0) { + buf[0] = '-'; + buf += 1; + buf_size -= 1; + value = static_cast(-static_cast(value)); + } + format_unsigned(buf, buf_size, value, base, caps); +} + +template +static void SendRepeat(Out& o, char ch, int count) { + char pad[8]; + memset(pad, ch, sizeof(pad)); + + const int pad_size = static_cast(sizeof(pad)); + while (count > 0) { + int avail = count; + if (avail > pad_size) { + avail = pad_size; + } + o.Send(pad, avail); + count -= avail; + } +} + +/* Perform formatted output to an output target 'o' */ +template +static void out_vformat(Out& o, const char* format, va_list args) { + int nn = 0; + + for (;;) { + int mm; + int padZero = 0; + int padLeft = 0; + char sign = '\0'; + int width = -1; + int prec = -1; + size_t bytelen = sizeof(int); + int slen; + char buffer[32]; /* temporary buffer used to format numbers */ + + char c; + + /* first, find all characters that are not 0 or '%' */ + /* then send them to the output directly */ + mm = nn; + do { + c = format[mm]; + if (c == '\0' || c == '%') + break; + mm++; + } while (1); + + if (mm > nn) { + o.Send(format+nn, mm-nn); + nn = mm; + } + + /* is this it ? then exit */ + if (c == '\0') + break; + + /* nope, we are at a '%' modifier */ + nn++; // skip it + + /* parse flags */ + for (;;) { + c = format[nn++]; + if (c == '\0') { /* single trailing '%' ? */ + c = '%'; + o.Send(&c, 1); + return; + } + else if (c == '0') { + padZero = 1; + continue; + } + else if (c == '-') { + padLeft = 1; + continue; + } + else if (c == ' ' || c == '+') { + sign = c; + continue; + } + break; + } + + /* parse field width */ + if ((c >= '0' && c <= '9')) { + nn --; + width = static_cast(parse_decimal(format, &nn)); + c = format[nn++]; + } + + /* parse precision */ + if (c == '.') { + prec = static_cast(parse_decimal(format, &nn)); + c = format[nn++]; + } + + /* length modifier */ + switch (c) { + case 'h': + bytelen = sizeof(short); + if (format[nn] == 'h') { + bytelen = sizeof(char); + nn += 1; + } + c = format[nn++]; + break; + case 'l': + bytelen = sizeof(long); + if (format[nn] == 'l') { + bytelen = sizeof(long long); + nn += 1; + } + c = format[nn++]; + break; + case 'z': + bytelen = sizeof(size_t); + c = format[nn++]; + break; + case 't': + bytelen = sizeof(ptrdiff_t); + c = format[nn++]; + break; + default: + ; + } + + /* conversion specifier */ + const char* str = buffer; + if (c == 's') { + /* string */ + str = va_arg(args, const char*); + if (str == NULL) { + str = "(null)"; + } + } else if (c == 'c') { + /* character */ + /* NOTE: char is promoted to int when passed through the stack */ + buffer[0] = static_cast(va_arg(args, int)); + buffer[1] = '\0'; + } else if (c == 'p') { + uint64_t value = reinterpret_cast(va_arg(args, void*)); + buffer[0] = '0'; + buffer[1] = 'x'; + format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x'); + } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') { + /* integers - first read value from stack */ + uint64_t value; + int is_signed = (c == 'd' || c == 'i' || c == 'o'); + + /* NOTE: int8_t and int16_t are promoted to int when passed + * through the stack + */ + switch (bytelen) { + case 1: value = static_cast(va_arg(args, int)); break; + case 2: value = static_cast(va_arg(args, int)); break; + case 4: value = va_arg(args, uint32_t); break; + case 8: value = va_arg(args, uint64_t); break; + default: return; /* should not happen */ + } + + /* sign extension, if needed */ + if (is_signed) { + int shift = 64 - 8*bytelen; + value = static_cast((static_cast(value << shift)) >> shift); + } + + /* format the number properly into our buffer */ + format_integer(buffer, sizeof(buffer), value, c); + } else if (c == '%') { + buffer[0] = '%'; + buffer[1] = '\0'; + } else { + __assert(__FILE__, __LINE__, "conversion specifier unsupported"); + } + + /* if we are here, 'str' points to the content that must be + * outputted. handle padding and alignment now */ + + slen = strlen(str); + + if (sign != '\0' || prec != -1) { + __assert(__FILE__, __LINE__, "sign/precision unsupported"); + } + + if (slen < width && !padLeft) { + char padChar = padZero ? '0' : ' '; + SendRepeat(o, padChar, width - slen); + } + + o.Send(str, slen); + + if (slen < width && padLeft) { + char padChar = padZero ? '0' : ' '; + SendRepeat(o, padChar, width - slen); + } + } +} + +int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) { + BufferOutputStream os(buffer, buffer_size); + va_list args; + va_start(args, format); + out_vformat(os, format, args); + va_end(args); + return os.total; +} + +int __libc_format_fd(int fd, const char* format, ...) { + FdOutputStream os(fd); + va_list args; + va_start(args, format); + out_vformat(os, format, args); + va_end(args); + return os.total; +} + +static int __libc_write_stderr(const char* tag, const char* msg) { + int fd = TEMP_FAILURE_RETRY(open("/dev/stderr", O_CLOEXEC | O_WRONLY | O_APPEND)); + if (fd == -1) { + return -1; + } + + iovec vec[4]; + vec[0].iov_base = const_cast(tag); + vec[0].iov_len = strlen(tag); + vec[1].iov_base = const_cast(": "); + vec[1].iov_len = 2; + vec[2].iov_base = const_cast(msg); + vec[2].iov_len = strlen(msg); + vec[3].iov_base = const_cast("\n"); + vec[3].iov_len = 1; + + int result = TEMP_FAILURE_RETRY(writev(fd, vec, 4)); + close(fd); + return result; +} + +static int __libc_open_log_socket() { + // ToDo: Ideally we want this to fail if the gid of the current + // process is AID_LOGD, but will have to wait until we have + // registered this in private/android_filesystem_config.h. We have + // found that all logd crashes thus far have had no problem stuffing + // the UNIX domain socket and moving on so not critical *today*. + + int log_fd = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)); + if (log_fd < 0) { + return -1; + } + + if (fcntl(log_fd, F_SETFL, O_NONBLOCK) == -1) { + close(log_fd); + return -1; + } + + union { + struct sockaddr addr; + struct sockaddr_un addrUn; + } u; + memset(&u, 0, sizeof(u)); + u.addrUn.sun_family = AF_UNIX; + strlcpy(u.addrUn.sun_path, "/dev/socket/logdw", sizeof(u.addrUn.sun_path)); + + if (TEMP_FAILURE_RETRY(connect(log_fd, &u.addr, sizeof(u.addrUn))) != 0) { + close(log_fd); + return -1; + } + + return log_fd; +} + +struct cache { + const prop_info* pinfo; + uint32_t serial; + char c; +}; + +static void refresh_cache(struct cache *cache, const char *key) +{ + if (!cache->pinfo) { + cache->pinfo = __system_property_find(key); + if (!cache->pinfo) { + return; + } + } + uint32_t serial = __system_property_serial(cache->pinfo); + if (serial == cache->serial) { + return; + } + cache->serial = serial; + + char buf[PROP_VALUE_MAX]; + __system_property_read(cache->pinfo, 0, buf); + cache->c = buf[0]; +} + +// Timestamp state generally remains constant, since a change is +// rare, we can accept a trylock failure gracefully. +static pthread_mutex_t lock_clockid = PTHREAD_MUTEX_INITIALIZER; + +static clockid_t __android_log_clockid() +{ + static struct cache r_time_cache = { NULL, static_cast(-1), 0 }; + static struct cache p_time_cache = { NULL, static_cast(-1), 0 }; + char c; + + if (pthread_mutex_trylock(&lock_clockid)) { + // We are willing to accept some race in this context + if (!(c = p_time_cache.c)) { + c = r_time_cache.c; + } + } else { + static uint32_t serial; + uint32_t current_serial = __system_property_area_serial(); + if (current_serial != serial) { + refresh_cache(&r_time_cache, "ro.logd.timestamp"); + refresh_cache(&p_time_cache, "persist.logd.timestamp"); + serial = current_serial; + } + if (!(c = p_time_cache.c)) { + c = r_time_cache.c; + } + + pthread_mutex_unlock(&lock_clockid); + } + + return (tolower(c) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME; +} + +struct log_time { // Wire format + uint32_t tv_sec; + uint32_t tv_nsec; +}; + +int __libc_write_log(int priority, const char* tag, const char* msg) { + int main_log_fd = __libc_open_log_socket(); + if (main_log_fd == -1) { + // Try stderr instead. + return __libc_write_stderr(tag, msg); + } + + iovec vec[6]; + char log_id = (priority == ANDROID_LOG_FATAL) ? LOG_ID_CRASH : LOG_ID_MAIN; + vec[0].iov_base = &log_id; + vec[0].iov_len = sizeof(log_id); + uint16_t tid = gettid(); + vec[1].iov_base = &tid; + vec[1].iov_len = sizeof(tid); + timespec ts; + clock_gettime(__android_log_clockid(), &ts); + log_time realtime_ts; + realtime_ts.tv_sec = ts.tv_sec; + realtime_ts.tv_nsec = ts.tv_nsec; + vec[2].iov_base = &realtime_ts; + vec[2].iov_len = sizeof(realtime_ts); + + vec[3].iov_base = &priority; + vec[3].iov_len = 1; + vec[4].iov_base = const_cast(tag); + vec[4].iov_len = strlen(tag) + 1; + vec[5].iov_base = const_cast(msg); + vec[5].iov_len = strlen(msg) + 1; + + int result = TEMP_FAILURE_RETRY(writev(main_log_fd, vec, sizeof(vec) / sizeof(vec[0]))); + close(main_log_fd); + return result; +} + +int __libc_format_log_va_list(int priority, const char* tag, const char* format, va_list args) { + char buffer[1024]; + BufferOutputStream os(buffer, sizeof(buffer)); + out_vformat(os, format, args); + return __libc_write_log(priority, tag, buffer); +} + +int __libc_format_log(int priority, const char* tag, const char* format, ...) { + va_list args; + va_start(args, format); + int result = __libc_format_log_va_list(priority, tag, format, args); + va_end(args); + return result; +} + +static int __libc_android_log_event(int32_t tag, char type, const void* payload, size_t len) { + iovec vec[6]; + char log_id = LOG_ID_EVENTS; + vec[0].iov_base = &log_id; + vec[0].iov_len = sizeof(log_id); + uint16_t tid = gettid(); + vec[1].iov_base = &tid; + vec[1].iov_len = sizeof(tid); + timespec ts; + clock_gettime(__android_log_clockid(), &ts); + log_time realtime_ts; + realtime_ts.tv_sec = ts.tv_sec; + realtime_ts.tv_nsec = ts.tv_nsec; + vec[2].iov_base = &realtime_ts; + vec[2].iov_len = sizeof(realtime_ts); + + vec[3].iov_base = &tag; + vec[3].iov_len = sizeof(tag); + vec[4].iov_base = &type; + vec[4].iov_len = sizeof(type); + vec[5].iov_base = const_cast(payload); + vec[5].iov_len = len; + + int event_log_fd = __libc_open_log_socket(); + + if (event_log_fd == -1) { + return -1; + } + int result = TEMP_FAILURE_RETRY(writev(event_log_fd, vec, sizeof(vec) / sizeof(vec[0]))); + close(event_log_fd); + return result; +} + +void __libc_android_log_event_int(int32_t tag, int value) { + __libc_android_log_event(tag, EVENT_TYPE_INT, &value, sizeof(value)); +} + +void __libc_android_log_event_uid(int32_t tag) { + __libc_android_log_event_int(tag, getuid()); +} + +void __fortify_chk_fail(const char* msg, uint32_t tag) { + if (tag != 0) { + __libc_android_log_event_uid(tag); + } + __libc_fatal("FORTIFY: %s", msg); +} + +void __libc_fatal_no_abort(const char* format, ...) { + va_list args; + va_start(args, format); + __libc_fatal(format, args); + va_end(args); +} + +void __libc_fatal(const char* format, ...) { + va_list args; + va_start(args, format); + __libc_fatal(format, args); + va_end(args); + abort(); +} \ No newline at end of file diff --git a/jni/resetprop/libc_logging.h b/jni/resetprop/libc_logging.h new file mode 100644 index 000000000..31abc38a5 --- /dev/null +++ b/jni/resetprop/libc_logging.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBC_LOGGING_H +#define _LIBC_LOGGING_H + +#include +#include +#include +#include + +__BEGIN_DECLS + +enum { + ANDROID_LOG_UNKNOWN = 0, + ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ + + ANDROID_LOG_VERBOSE, + ANDROID_LOG_DEBUG, + ANDROID_LOG_INFO, + ANDROID_LOG_WARN, + ANDROID_LOG_ERROR, + ANDROID_LOG_FATAL, + + ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ +}; + +enum { + LOG_ID_MIN = 0, + + LOG_ID_MAIN = 0, + LOG_ID_RADIO = 1, + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + LOG_ID_CRASH = 4, + + LOG_ID_MAX +}; + +struct abort_msg_t { + size_t size; + char msg[0]; +}; + +// +// Formats a message to the log (priority 'fatal'), then aborts. +// + +__LIBC_HIDDEN__ __noreturn void __libc_fatal(const char* format, ...) __printflike(1, 2); + +// +// Formats a message to the log (priority 'fatal'), but doesn't abort. +// Used by the malloc implementation to ensure that debuggerd dumps memory +// around the bad address. +// + +__LIBC_HIDDEN__ void __libc_fatal_no_abort(const char* format, ...) + __printflike(1, 2); + +// +// Formatting routines for the C library's internal debugging. +// Unlike the usual alternatives, these don't allocate, and they don't drag in all of stdio. +// + +__LIBC_HIDDEN__ int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) + __printflike(3, 4); + +__LIBC_HIDDEN__ int __libc_format_fd(int fd, const char* format, ...) + __printflike(2, 3); + +__LIBC_HIDDEN__ int __libc_format_log(int priority, const char* tag, const char* format, ...) + __printflike(3, 4); + +__LIBC_HIDDEN__ int __libc_format_log_va_list(int priority, const char* tag, const char* format, + va_list ap); + +__LIBC_HIDDEN__ int __libc_write_log(int priority, const char* tag, const char* msg); + +// +// Event logging. +// + +__LIBC_HIDDEN__ void __libc_android_log_event_int(int32_t tag, int value); +__LIBC_HIDDEN__ void __libc_android_log_event_uid(int32_t tag); + +__LIBC_HIDDEN__ __noreturn void __fortify_chk_fail(const char* msg, uint32_t event_tag); + +__END_DECLS + +#endif diff --git a/jni/resetprop/resetprop.cpp b/jni/resetprop/resetprop.cpp new file mode 100644 index 000000000..ecbb75838 --- /dev/null +++ b/jni/resetprop/resetprop.cpp @@ -0,0 +1,280 @@ +/* + * + * resetprop.cpp + * + * Copyright 2016 nkk71 + * Copyright 2016 topjohnwu + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include "_system_properties.h" +#include + +/* Info: + * + * all changes are in + * + * bionic/libc/bionic/system_properties.cpp + * + * Functions that need to be patched/added in system_properties.cpp + * + * int __system_properties_init() + * on android 7, first tear down the everything then let it initialize again: + * if (initialized) { + * //list_foreach(contexts, [](context_node* l) { l->reset_access(); }); + * //return 0; + * free_and_unmap_contexts(); + * initialized = false; + * } + * + * + * static prop_area* map_prop_area(const char* filename, bool is_legacy) + * we dont want this read only so change: 'O_RDONLY' to 'O_RDWR' + * + * static prop_area* map_fd_ro(const int fd) + * we dont want this read only so change: 'PROT_READ' to 'PROT_READ | PROT_WRITE' + * + * + * Copy the code of prop_info *prop_area::find_property, and modify to delete props + * const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name) + * { + * ... + * ... Do not alloc a new prop_bt here, remove all code involve alloc_if_needed + * ... + * + * if (prop_offset != 0) { + * atomic_store_explicit(¤t->prop, 0, memory_order_release); // Add this line to nullify the prop entry + * return to_prop_info(¤t->prop); + * } else { + * + * .... + * } + * + * + * by patching just those functions directly, all other functions should be ok + * as is. + * + * + */ + +int verbose = 0, del = 0, file = 0, trigger = 1; + +static bool is_legal_property_name(const char* name, size_t namelen) +{ + size_t i; + if (namelen >= PROP_NAME_MAX) return false; + if (namelen < 1) return false; + if (name[0] == '.') return false; + if (name[namelen - 1] == '.') return false; + + /* Only allow alphanumeric, plus '.', '-', or '_' */ + /* Don't allow ".." to appear in a property name */ + for (i = 0; i < namelen; i++) { + if (name[i] == '.') { + // i=0 is guaranteed to never have a dot. See above. + if (name[i-1] == '.') return false; + continue; + } + if (name[i] == '_' || name[i] == '-') continue; + if (name[i] >= 'a' && name[i] <= 'z') continue; + if (name[i] >= 'A' && name[i] <= 'Z') continue; + if (name[i] >= '0' && name[i] <= '9') continue; + return false; + } + + return true; +} + +int x_property_set(const char *name, const char *value) +{ + int ret; + char value_read[PROP_VALUE_MAX]; + + size_t namelen = strlen(name); + size_t valuelen = strlen(value); + + if (trigger) { + printf("Set with property_service: '%s'='%s'\n", name, value); + } else { + printf("Modify data structure: '%s'='%s'\n", name, value); + } + + __system_property_get(name, value_read); + + if(strlen(value_read)) { + printf("Existing property: '%s'='%s'\n", name, value_read); + if (trigger) { + if (!strncmp(name, "ro.", 3)) __system_property_del(name); // Only delete ro props + ret = __system_property_set(name, value); + } else { + ret = __system_property_update((prop_info*) __system_property_find(name), value, valuelen); + } + } else { + if (trigger) { + ret = __system_property_set(name, value); + } else { + ret = __system_property_add(name, namelen, value, valuelen); + } + } + + if (ret != 0) { + fprintf(stderr, "Failed to set '%s'='%s'\n", name, value); + return ret; + } + + __system_property_get(name, value_read); + printf("Recheck property: '%s'='%s'\n", name, value_read); + + return 0; +} + +int read_prop_file(const char* filename) { + printf(" Attempting to read props from \'%s\'\n", filename); + FILE *fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, "Cannot open \'%s\'\n", filename); + return 1; + } + char *line = NULL, *pch, name[PROP_NAME_MAX], value[PROP_VALUE_MAX]; + size_t len; + ssize_t read; + int comment = 0, i; + while ((read = getline(&line, &len, fp)) != -1) { + // Remove the trailing newline + if (line[read - 1] == '\n') { + line[read - 1] = '\0'; + --read; + } + comment = 0; + for (i = 0; i < read; ++i) { + // Ignore starting spaces + if (line[i] == ' ') continue; + else { + // A line starting with # is ignored + if (line[i] == '#') comment = 1; + break; + } + } + if (comment) continue; + pch = strchr(line, '='); + // Ignore ivalid formats + if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue; + // Separate the string + *pch = '\0'; + x_property_set(line + i, pch + 1); + } + free(line); + fclose(fp); + return 0; +} + +int usage(char* name) { + fprintf(stderr, "usage: %s [-v] [-n] [--file propfile] [--delete name] [ name value ] \n", name); + fprintf(stderr, " -v :\n"); + fprintf(stderr, " verbose output (Default: Disabled)\n"); + fprintf(stderr, " -n :\n"); + fprintf(stderr, " no event triggers when changing props (Default: Will trigger events)\n"); + fprintf(stderr, " --file propfile :\n"); + fprintf(stderr, " Read props from prop files (e.g. build.prop)\n"); + fprintf(stderr, " --delete name :\n"); + fprintf(stderr, " Remove a prop entry\n\n"); + return 1; +} + +int main(int argc, char *argv[]) +{ + + int exp_arg = 2, stdout_bak, null; + char *name, *value, *filename; + + if (argc < 3) { + return usage(argv[0]); + } + + for (int i = 1; i < argc; ++i) { + if (!strcmp("-v", argv[i])) { + verbose = 1; + } else if (!strcmp("-n", argv[i])) { + trigger = 0; + } else if (!strcmp("--file", argv[i])) { + file = 1; + exp_arg = 1; + } else if (!strcmp("--delete", argv[i])) { + del = 1; + exp_arg = 1; + } else { + if (i + exp_arg > argc) { + return usage(argv[0]); + } + if (file) { + filename = argv[i]; + break; + } else { + if(!is_legal_property_name(argv[i], strlen(argv[i]))) { + fprintf(stderr, "Illegal property name \'%s\'\n", argv[i]); + return 1; + } + name = argv[i]; + if (exp_arg > 1) { + if (strlen(argv[i + 1]) >= PROP_VALUE_MAX) { + fprintf(stderr, "Value too long \'%s\'\n", argv[i + 1]); + return 1; + } + value = argv[i + 1]; + } + break; + } + } + } + + if (!verbose) { + fflush(stdout); + stdout_bak = dup(1); + null = open("/dev/null", O_WRONLY); + dup2(null, 1); + } + + printf("resetprop by nkk71 & topjohnwu\n"); + + printf("Initializing...\n"); + if (__system_properties_init()) { + fprintf(stderr, "Error during init\n"); + return 1; + } + + if (file) { + if (read_prop_file(filename)) return 1; + } else if (del) { + printf("Attempting to delete '%s'\n", name); + if (__system_property_del(name)) return 1; + } else { + if(x_property_set(name, value)) return 1; + } + printf("Done!\n\n"); + return 0; + +} diff --git a/jni/resetprop/system_properties.cpp b/jni/resetprop/system_properties.cpp new file mode 100644 index 000000000..4e99e15ef --- /dev/null +++ b/jni/resetprop/system_properties.cpp @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include "_system_properties.h" +#include + +#include "bionic_futex.h" +#include "bionic_lock.h" +#include "bionic_macros.h" +#include "libc_logging.h" + +static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; + + +/* + * Properties are stored in a hybrid trie/binary tree structure. + * Each property's name is delimited at '.' characters, and the tokens are put + * into a trie structure. Siblings at each level of the trie are stored in a + * binary tree. For instance, "ro.secure"="1" could be stored as follows: + * + * +-----+ children +----+ children +--------+ + * | |-------------->| ro |-------------->| secure | + * +-----+ +----+ +--------+ + * / \ / | + * left / \ right left / | prop +===========+ + * v v v +-------->| ro.secure | + * +-----+ +-----+ +-----+ +-----------+ + * | net | | sys | | com | | 1 | + * +-----+ +-----+ +-----+ +===========+ + */ + +// Represents a node in the trie. +struct prop_bt { + uint8_t namelen; + uint8_t reserved[3]; + + // The property trie is updated only by the init process (single threaded) which provides + // property service. And it can be read by multiple threads at the same time. + // As the property trie is not protected by locks, we use atomic_uint_least32_t types for the + // left, right, children "pointers" in the trie node. To make sure readers who see the + // change of "pointers" can also notice the change of prop_bt structure contents pointed by + // the "pointers", we always use release-consume ordering pair when accessing these "pointers". + + // prop "points" to prop_info structure if there is a propery associated with the trie node. + // Its situation is similar to the left, right, children "pointers". So we use + // atomic_uint_least32_t and release-consume ordering to protect it as well. + + // We should also avoid rereading these fields redundantly, since not + // all processor implementations ensure that multiple loads from the + // same field are carried out in the right order. + atomic_uint_least32_t prop; + + atomic_uint_least32_t left; + atomic_uint_least32_t right; + + atomic_uint_least32_t children; + + char name[0]; + + prop_bt(const char *name, const uint8_t name_length) { + this->namelen = name_length; + memcpy(this->name, name, name_length); + this->name[name_length] = '\0'; + } + +private: + DISALLOW_COPY_AND_ASSIGN(prop_bt); +}; + +class prop_area { +public: + + prop_area(const uint32_t magic, const uint32_t version) : + magic_(magic), version_(version) { + atomic_init(&serial_, 0); + memset(reserved_, 0, sizeof(reserved_)); + // Allocate enough space for the root node. + bytes_used_ = sizeof(prop_bt); + } + + const prop_info *find(const char *name); + bool add(const char *name, unsigned int namelen, + const char *value, unsigned int valuelen); + const prop_info *del(const char *name); + + bool foreach(void (*propfn)(const prop_info *pi, void *cookie), void *cookie); + + atomic_uint_least32_t *serial() { return &serial_; } + uint32_t magic() const { return magic_; } + uint32_t version() const { return version_; } + +private: + void *allocate_obj(const size_t size, uint_least32_t *const off); + prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off); + prop_info *new_prop_info(const char *name, uint8_t namelen, + const char *value, uint8_t valuelen, + uint_least32_t *const off); + void *to_prop_obj(uint_least32_t off); + prop_bt *to_prop_bt(atomic_uint_least32_t *off_p); + prop_info *to_prop_info(atomic_uint_least32_t *off_p); + + prop_bt *root_node(); + + prop_bt *find_prop_bt(prop_bt *const bt, const char *name, + uint8_t namelen, bool alloc_if_needed); + + const prop_info *find_property(prop_bt *const trie, const char *name, + uint8_t namelen, const char *value, + uint8_t valuelen, bool alloc_if_needed); + + const prop_info *find_property_and_del(prop_bt *const trie, const char *name); + + bool foreach_property(prop_bt *const trie, + void (*propfn)(const prop_info *pi, void *cookie), + void *cookie); + + uint32_t bytes_used_; + atomic_uint_least32_t serial_; + uint32_t magic_; + uint32_t version_; + uint32_t reserved_[28]; + char data_[0]; + + DISALLOW_COPY_AND_ASSIGN(prop_area); +}; + +struct prop_info { + atomic_uint_least32_t serial; + char value[PROP_VALUE_MAX]; + char name[0]; + + prop_info(const char *name, const uint8_t namelen, const char *value, + const uint8_t valuelen) { + memcpy(this->name, name, namelen); + this->name[namelen] = '\0'; + atomic_init(&this->serial, valuelen << 24); + memcpy(this->value, value, valuelen); + this->value[valuelen] = '\0'; + } +private: + DISALLOW_COPY_AND_ASSIGN(prop_info); +}; + +struct find_nth_cookie { + uint32_t count; + const uint32_t n; + const prop_info *pi; + + find_nth_cookie(uint32_t n) : count(0), n(n), pi(NULL) { + } +}; + +static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME; +static bool compat_mode = false; +static size_t pa_data_size; +static size_t pa_size; +static bool initialized = false; + +// NOTE: This isn't static because system_properties_compat.c +// requires it. +prop_area *__system_property_area__ = NULL; + +static int get_fd_from_env(void) +{ + // This environment variable consistes of two decimal integer + // values separated by a ",". The first value is a file descriptor + // and the second is the size of the system properties area. The + // size is currently unused. + char *env = getenv("ANDROID_PROPERTY_WORKSPACE"); + + if (!env) { + return -1; + } + + return atoi(env); +} + +static prop_area* map_prop_area_rw(const char* filename, const char* context, + bool* fsetxattr_failed) { + /* dev is a tmpfs that we can use to carve a shared workspace + * out of, so let's do that... + */ + const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); + + if (fd < 0) { + if (errno == EACCES) { + /* for consistency with the case where the process has already + * mapped the page in and segfaults when trying to write to it + */ + abort(); + } + return nullptr; + } + + if (context) { + if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", + "fsetxattr failed to set context (%s) for \"%s\"", context, filename); + /* + * fsetxattr() will fail during system properties tests due to selinux policy. + * We do not want to create a custom policy for the tester, so we will continue in + * this function but set a flag that an error has occurred. + * Init, which is the only daemon that should ever call this function will abort + * when this error occurs. + * Otherwise, the tester will ignore it and continue, albeit without any selinux + * property separation. + */ + if (fsetxattr_failed) { + *fsetxattr_failed = true; + } + } + } + + if (ftruncate(fd, PA_SIZE) < 0) { + close(fd); + return nullptr; + } + + pa_size = PA_SIZE; + pa_data_size = pa_size - sizeof(prop_area); + compat_mode = false; + + void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (memory_area == MAP_FAILED) { + close(fd); + return nullptr; + } + + prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); + + close(fd); + return pa; +} + +static prop_area* map_fd_ro(const int fd) { + struct stat fd_stat; + if (fstat(fd, &fd_stat) < 0) { + return nullptr; + } + + if ((fd_stat.st_uid != 0) + || (fd_stat.st_gid != 0) + || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) + || (fd_stat.st_size < static_cast(sizeof(prop_area))) ) { + return nullptr; + } + + pa_size = fd_stat.st_size; + pa_data_size = pa_size - sizeof(prop_area); + + void* const map_result = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // xsetprop changed PROT_READ to PROT_READ | PROT_WRITE + if (map_result == MAP_FAILED) { + return nullptr; + } + + prop_area* pa = reinterpret_cast(map_result); + if ((pa->magic() != PROP_AREA_MAGIC) || + (pa->version() != PROP_AREA_VERSION && + pa->version() != PROP_AREA_VERSION_COMPAT)) { + munmap(pa, pa_size); + return nullptr; + } + + if (pa->version() == PROP_AREA_VERSION_COMPAT) { + compat_mode = true; + } + + return pa; +} + +static prop_area* map_prop_area(const char* filename, bool is_legacy) { + int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); // xsetprop changed O_RDONLY to O_RDWR + bool close_fd = true; + if (fd == -1 && errno == ENOENT && is_legacy) { + /* + * For backwards compatibility, if the file doesn't + * exist, we use the environment to get the file descriptor. + * For security reasons, we only use this backup if the kernel + * returns ENOENT. We don't want to use the backup if the kernel + * returns other errors such as ENOMEM or ENFILE, since it + * might be possible for an external program to trigger this + * condition. + * Only do this for the legacy prop file, secured prop files + * do not have a backup + */ + fd = get_fd_from_env(); + close_fd = false; + } + + if (fd < 0) { + return nullptr; + } + + prop_area* map_result = map_fd_ro(fd); + if (close_fd) { + close(fd); + } + + return map_result; +} + +void *prop_area::allocate_obj(const size_t size, uint_least32_t *const off) +{ + const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t)); + if (bytes_used_ + aligned > pa_data_size) { + return NULL; + } + + *off = bytes_used_; + bytes_used_ += aligned; + return data_ + *off; +} + +prop_bt *prop_area::new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off) +{ + uint_least32_t new_offset; + void *const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset); + if (p != NULL) { + prop_bt* bt = new(p) prop_bt(name, namelen); + *off = new_offset; + return bt; + } + + return NULL; +} + +prop_info *prop_area::new_prop_info(const char *name, uint8_t namelen, + const char *value, uint8_t valuelen, uint_least32_t *const off) +{ + uint_least32_t new_offset; + void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset); + if (p != NULL) { + prop_info* info = new(p) prop_info(name, namelen, value, valuelen); + *off = new_offset; + return info; + } + + return NULL; +} + +void *prop_area::to_prop_obj(uint_least32_t off) +{ + if (off > pa_data_size) + return NULL; + + return (data_ + off); +} + +inline prop_bt *prop_area::to_prop_bt(atomic_uint_least32_t* off_p) { + uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume); + return reinterpret_cast(to_prop_obj(off)); +} + +inline prop_info *prop_area::to_prop_info(atomic_uint_least32_t* off_p) { + uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume); + return reinterpret_cast(to_prop_obj(off)); +} + +inline prop_bt *prop_area::root_node() +{ + return reinterpret_cast(to_prop_obj(0)); +} + +static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, + uint8_t two_len) +{ + if (one_len < two_len) + return -1; + else if (one_len > two_len) + return 1; + else + return strncmp(one, two, one_len); +} + +prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name, + uint8_t namelen, bool alloc_if_needed) +{ + + prop_bt* current = bt; + while (true) { + if (!current) { + return NULL; + } + + const int ret = cmp_prop_name(name, namelen, current->name, current->namelen); + if (ret == 0) { + return current; + } + + if (ret < 0) { + uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed); + if (left_offset != 0) { + current = to_prop_bt(¤t->left); + } else { + if (!alloc_if_needed) { + return NULL; + } + + uint_least32_t new_offset; + prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); + if (new_bt) { + atomic_store_explicit(¤t->left, new_offset, memory_order_release); + } + return new_bt; + } + } else { + uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed); + if (right_offset != 0) { + current = to_prop_bt(¤t->right); + } else { + if (!alloc_if_needed) { + return NULL; + } + + uint_least32_t new_offset; + prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); + if (new_bt) { + atomic_store_explicit(¤t->right, new_offset, memory_order_release); + } + return new_bt; + } + } + } +} + +const prop_info *prop_area::find_property(prop_bt *const trie, const char *name, + uint8_t namelen, const char *value, uint8_t valuelen, + bool alloc_if_needed) +{ + if (!trie) return NULL; + + const char *remaining_name = name; + prop_bt* current = trie; + while (true) { + const char *sep = strchr(remaining_name, '.'); + const bool want_subtree = (sep != NULL); + const uint8_t substr_size = (want_subtree) ? + sep - remaining_name : strlen(remaining_name); + + if (!substr_size) { + return NULL; + } + + prop_bt* root = NULL; + uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); + if (children_offset != 0) { + root = to_prop_bt(¤t->children); + } else if (alloc_if_needed) { + uint_least32_t new_offset; + root = new_prop_bt(remaining_name, substr_size, &new_offset); + if (root) { + atomic_store_explicit(¤t->children, new_offset, memory_order_release); + } + } + + if (!root) { + return NULL; + } + + current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); + if (!current) { + return NULL; + } + + if (!want_subtree) + break; + + remaining_name = sep + 1; + } + + uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); + if (prop_offset != 0) { + return to_prop_info(¤t->prop); + } else if (alloc_if_needed) { + uint_least32_t new_offset; + prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset); + if (new_info) { + atomic_store_explicit(¤t->prop, new_offset, memory_order_release); + } + + return new_info; + } else { + return NULL; + } +} + +const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name) +{ + if (!trie) return NULL; + + const char *remaining_name = name; + prop_bt* current = trie; + while (true) { + const char *sep = strchr(remaining_name, '.'); + const bool want_subtree = (sep != NULL); + const uint8_t substr_size = (want_subtree) ? + sep - remaining_name : strlen(remaining_name); + + if (!substr_size) { + return NULL; + } + + prop_bt* root = NULL; + uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); + if (children_offset != 0) { + root = to_prop_bt(¤t->children); + } + + if (!root) { + return NULL; + } + + current = find_prop_bt(root, remaining_name, substr_size, false); + if (!current) { + return NULL; + } + + if (!want_subtree) + break; + + remaining_name = sep + 1; + } + + uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); + if (prop_offset != 0) { + printf("[bt] found at %p: name='%s' prop='%llu' left='%llu' right='%llu' children='%llu'\n", + static_cast(current), current->name, + static_cast(atomic_load_explicit(¤t->prop, memory_order_relaxed)), + static_cast(atomic_load_explicit(¤t->left, memory_order_relaxed)), + static_cast(atomic_load_explicit(¤t->right, memory_order_relaxed)), + static_cast(atomic_load_explicit(¤t->children, memory_order_relaxed)) + ); + atomic_store_explicit(¤t->prop, 0, memory_order_release); + return to_prop_info(¤t->prop); + } else { + printf("[bt] property not found\n"); + return NULL; + } +} + +static int send_prop_msg(const prop_msg *msg) +{ + const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd == -1) { + return -1; + } + + const size_t namelen = strlen(property_service_socket); + + sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path)); + addr.sun_family = AF_LOCAL; + socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1; + if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast(&addr), alen)) < 0) { + close(fd); + return -1; + } + + const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0)); + + int result = -1; + if (num_bytes == sizeof(prop_msg)) { + // We successfully wrote to the property server but now we + // wait for the property server to finish its work. It + // acknowledges its completion by closing the socket so we + // poll here (on nothing), waiting for the socket to close. + // If you 'adb shell setprop foo bar' you'll see the POLLHUP + // once the socket closes. Out of paranoia we cap our poll + // at 250 ms. + pollfd pollfds[1]; + pollfds[0].fd = fd; + pollfds[0].events = 0; + const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); + if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) { + result = 0; + } else { + // Ignore the timeout and treat it like a success anyway. + // The init process is single-threaded and its property + // service is sometimes slow to respond (perhaps it's off + // starting a child process or something) and thus this + // times out and the caller thinks it failed, even though + // it's still getting around to it. So we fake it here, + // mostly for ctl.* properties, but we do try and wait 250 + // ms so callers who do read-after-write can reliably see + // what they've written. Most of the time. + // TODO: fix the system properties design. + result = 0; + } + } + + close(fd); + return result; +} + +static void find_nth_fn(const prop_info *pi, void *ptr) +{ + find_nth_cookie *cookie = reinterpret_cast(ptr); + + if (cookie->n == cookie->count) + cookie->pi = pi; + + cookie->count++; +} + +bool prop_area::foreach_property(prop_bt *const trie, + void (*propfn)(const prop_info *pi, void *cookie), void *cookie) +{ + if (!trie) + return false; + + uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed); + if (left_offset != 0) { + const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie); + if (err < 0) + return false; + } + uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed); + if (prop_offset != 0) { + prop_info *info = to_prop_info(&trie->prop); + if (!info) + return false; + propfn(info, cookie); + } + uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed); + if (children_offset != 0) { + const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie); + if (err < 0) + return false; + } + uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed); + if (right_offset != 0) { + const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie); + if (err < 0) + return false; + } + + return true; +} + +const prop_info *prop_area::find(const char *name) { + return find_property(root_node(), name, strlen(name), nullptr, 0, false); +} + +bool prop_area::add(const char *name, unsigned int namelen, + const char *value, unsigned int valuelen) { + return find_property(root_node(), name, namelen, value, valuelen, true); +} + +const prop_info *prop_area::del(const char *name) { + return find_property_and_del(root_node(), name); +} + +bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { + return foreach_property(root_node(), propfn, cookie); +} + +class context_node { +public: + context_node(context_node* next, const char* context, prop_area* pa) + : next(next), context_(strdup(context)), pa_(pa), no_access_(false) { + lock_.init(false); + } + ~context_node() { + unmap(); + free(context_); + } + bool open(bool access_rw, bool* fsetxattr_failed); + bool check_access_and_open(); + void reset_access(); + + const char* context() const { return context_; } + prop_area* pa() { return pa_; } + + context_node* next; + +private: + bool check_access(); + void unmap(); + + Lock lock_; + char* context_; + prop_area* pa_; + bool no_access_; +}; + +struct prefix_node { + prefix_node(struct prefix_node* next, const char* prefix, context_node* context) + : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) { + } + ~prefix_node() { + free(prefix); + } + char* prefix; + const size_t prefix_len; + context_node* context; + struct prefix_node* next; +}; + +template +static inline void list_add(List** list, Args... args) { + *list = new List(*list, args...); +} + +static void list_add_after_len(prefix_node** list, const char* prefix, context_node* context) { + size_t prefix_len = strlen(prefix); + + auto next_list = list; + + while (*next_list) { + if ((*next_list)->prefix_len < prefix_len || (*next_list)->prefix[0] == '*') { + list_add(next_list, prefix, context); + return; + } + next_list = &(*next_list)->next; + } + list_add(next_list, prefix, context); +} + +template +static void list_foreach(List* list, Func func) { + while (list) { + func(list); + list = list->next; + } +} + +template +static List* list_find(List* list, Func func) { + while (list) { + if (func(list)) { + return list; + } + list = list->next; + } + return nullptr; +} + +template +static void list_free(List** list) { + while (*list) { + auto old_list = *list; + *list = old_list->next; + delete old_list; + } +} + +static prefix_node* prefixes = nullptr; +static context_node* contexts = nullptr; + +/* + * pthread_mutex_lock() calls into system_properties in the case of contention. + * This creates a risk of dead lock if any system_properties functions + * use pthread locks after system_property initialization. + * + * For this reason, the below three functions use a bionic Lock and static + * allocation of memory for each filename. + */ + +bool context_node::open(bool access_rw, bool* fsetxattr_failed) { + lock_.lock(); + if (pa_) { + lock_.unlock(); + return true; + } + + char filename[PROP_FILENAME_MAX]; + int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s", + property_filename, context_); + if (len < 0 || len > PROP_FILENAME_MAX) { + lock_.unlock(); + return false; + } + + if (access_rw) { + pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed); + } else { + pa_ = map_prop_area(filename, false); + } + lock_.unlock(); + return pa_; +} + +bool context_node::check_access_and_open() { + if (!pa_ && !no_access_) { + if (!check_access() || !open(false, nullptr)) { + no_access_ = true; + } + } + return pa_; +} + +void context_node::reset_access() { + if (!check_access()) { + unmap(); + no_access_ = true; + } else { + no_access_ = false; + } +} + +bool context_node::check_access() { + char filename[PROP_FILENAME_MAX]; + int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s", + property_filename, context_); + if (len < 0 || len > PROP_FILENAME_MAX) { + return false; + } + + return access(filename, R_OK) == 0; +} + +void context_node::unmap() { + if (!pa_) { + return; + } + + munmap(pa_, pa_size); + if (pa_ == __system_property_area__) { + __system_property_area__ = nullptr; + } + pa_ = nullptr; +} + +static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) { + char filename[PROP_FILENAME_MAX]; + int len = __libc_format_buffer(filename, sizeof(filename), + "%s/properties_serial", property_filename); + if (len < 0 || len > PROP_FILENAME_MAX) { + __system_property_area__ = nullptr; + return false; + } + + if (access_rw) { + __system_property_area__ = + map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed); + } else { + __system_property_area__ = map_prop_area(filename, false); + } + return __system_property_area__; +} + +static prop_area* get_prop_area_for_name(const char* name) { + auto entry = list_find(prefixes, [name](prefix_node* l) { + return l->prefix[0] == '*' || !strncmp(l->prefix, name, l->prefix_len); + }); + if (!entry) { + return nullptr; + } + + auto cnode = entry->context; + if (!cnode->pa()) { + /* + * We explicitly do not check no_access_ in this case because unlike the + * case of foreach(), we want to generate an selinux audit for each + * non-permitted property access in this function. + */ + cnode->open(false, nullptr); + } + return cnode->pa(); +} + +/* + * The below two functions are duplicated from label_support.c in libselinux. + * TODO: Find a location suitable for these functions such that both libc and + * libselinux can share a common source file. + */ + +/* + * The read_spec_entries and read_spec_entry functions may be used to + * replace sscanf to read entries from spec files. The file and + * property services now use these. + */ + +/* Read an entry from a spec file (e.g. file_contexts) */ +static inline int read_spec_entry(char **entry, char **ptr, int *len) +{ + *entry = NULL; + char *tmp_buf = NULL; + + while (isspace(**ptr) && **ptr != '\0') + (*ptr)++; + + tmp_buf = *ptr; + *len = 0; + + while (!isspace(**ptr) && **ptr != '\0') { + (*ptr)++; + (*len)++; + } + + if (*len) { + *entry = strndup(tmp_buf, *len); + if (!*entry) + return -1; + } + + return 0; +} + +/* + * line_buf - Buffer containing the spec entries . + * num_args - The number of spec parameter entries to process. + * ... - A 'char **spec_entry' for each parameter. + * returns - The number of items processed. + * + * This function calls read_spec_entry() to do the actual string processing. + */ +static int read_spec_entries(char *line_buf, int num_args, ...) +{ + char **spec_entry, *buf_p; + int len, rc, items, entry_len = 0; + va_list ap; + + len = strlen(line_buf); + if (line_buf[len - 1] == '\n') + line_buf[len - 1] = '\0'; + else + /* Handle case if line not \n terminated by bumping + * the len for the check below (as the line is NUL + * terminated by getline(3)) */ + len++; + + buf_p = line_buf; + while (isspace(*buf_p)) + buf_p++; + + /* Skip comment lines and empty lines. */ + if (*buf_p == '#' || *buf_p == '\0') + return 0; + + /* Process the spec file entries */ + va_start(ap, num_args); + + items = 0; + while (items < num_args) { + spec_entry = va_arg(ap, char **); + + if (len - 1 == buf_p - line_buf) { + va_end(ap); + return items; + } + + rc = read_spec_entry(spec_entry, &buf_p, &entry_len); + if (rc < 0) { + va_end(ap); + return rc; + } + if (entry_len) + items++; + } + va_end(ap); + return items; +} + +static bool initialize_properties() { + FILE* file = fopen("/property_contexts", "re"); + + if (!file) { + return false; + } + + char* buffer = nullptr; + size_t line_len; + char* prop_prefix = nullptr; + char* context = nullptr; + + while (getline(&buffer, &line_len, file) > 0) { + int items = read_spec_entries(buffer, 2, &prop_prefix, &context); + if (items <= 0) { + continue; + } + if (items == 1) { + free(prop_prefix); + continue; + } + /* + * init uses ctl.* properties as an IPC mechanism and does not write them + * to a property file, therefore we do not need to create property files + * to store them. + */ + if (!strncmp(prop_prefix, "ctl.", 4)) { + free(prop_prefix); + free(context); + continue; + } + + auto old_context = list_find( + contexts, [context](context_node* l) { return !strcmp(l->context(), context); }); + if (old_context) { + list_add_after_len(&prefixes, prop_prefix, old_context); + } else { + list_add(&contexts, context, nullptr); + list_add_after_len(&prefixes, prop_prefix, contexts); + } + free(prop_prefix); + free(context); + } + + free(buffer); + fclose(file); + return true; +} + +static bool is_dir(const char* pathname) { + struct stat info; + if (stat(pathname, &info) == -1) { + return false; + } + return S_ISDIR(info.st_mode); +} + +static void free_and_unmap_contexts() { + list_free(&prefixes); + list_free(&contexts); + if (__system_property_area__) { + munmap(__system_property_area__, pa_size); + __system_property_area__ = nullptr; + } +} + +int __system_properties_init() +{ + if (initialized) { + //list_foreach(contexts, [](context_node* l) { l->reset_access(); }); // xsetprop removed + //return 0; // xsetprop removed + free_and_unmap_contexts(); // xsetprop added + initialized = false; // xsetprop added + } + if (is_dir(property_filename)) { + if (!initialize_properties()) { + return -1; + } + if (!map_system_property_area(false, nullptr)) { + free_and_unmap_contexts(); + return -1; + } + } else { + __system_property_area__ = map_prop_area(property_filename, true); + if (!__system_property_area__) { + return -1; + } + list_add(&contexts, "legacy_system_prop_area", __system_property_area__); + list_add_after_len(&prefixes, "*", contexts); + } + initialized = true; + return 0; +} + +int __system_property_set_filename(const char *filename) +{ + size_t len = strlen(filename); + if (len >= sizeof(property_filename)) + return -1; + + strcpy(property_filename, filename); + return 0; +} + +int __system_property_area_init() +{ + free_and_unmap_contexts(); + mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (!initialize_properties()) { + return -1; + } + bool open_failed = false; + bool fsetxattr_failed = false; + list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) { + if (!l->open(true, &fsetxattr_failed)) { + open_failed = true; + } + }); + if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) { + free_and_unmap_contexts(); + return -1; + } + initialized = true; + return fsetxattr_failed ? -2 : 0; +} + +unsigned int __system_property_area_serial() +{ + prop_area *pa = __system_property_area__; + if (!pa) { + return -1; + } + // Make sure this read fulfilled before __system_property_serial + return atomic_load_explicit(pa->serial(), memory_order_acquire); +} + +const prop_info *__system_property_find(const char *name) +{ + if (!__system_property_area__) { + return nullptr; + } + + // if (__predict_false(compat_mode)) { + // return __system_property_find_compat(name); + // } + + prop_area* pa = get_prop_area_for_name(name); + if (!pa) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name); + return nullptr; + } + + return pa->find(name); +} + +int __system_property_del(const char *name) +{ + if (!__system_property_area__) { + return -1; + } + + prop_area* pa = get_prop_area_for_name(name); + if (!pa) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied deleting property \"%s\"", name); + return -1; + } + + bool ret = pa->del(name); + if (!ret) + return -1; + + // There is only a single mutator, but we want to make sure that + // updates are visible to a reader waiting for the update. + atomic_store_explicit( + __system_property_area__->serial(), + atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(__system_property_area__->serial(), INT32_MAX); + return 0; + +} + +// The C11 standard doesn't allow atomic loads from const fields, +// though C++11 does. Fudge it until standards get straightened out. +static inline uint_least32_t load_const_atomic(const atomic_uint_least32_t* s, + memory_order mo) { + atomic_uint_least32_t* non_const_s = const_cast(s); + return atomic_load_explicit(non_const_s, mo); +} + +int __system_property_read(const prop_info *pi, char *name, char *value) +{ + // if (__predict_false(compat_mode)) { + // return __system_property_read_compat(pi, name, value); + // } + + while (true) { + uint32_t serial = __system_property_serial(pi); // acquire semantics + size_t len = SERIAL_VALUE_LEN(serial); + memcpy(value, pi->value, len + 1); + // TODO: Fix the synchronization scheme here. + // There is no fully supported way to implement this kind + // of synchronization in C++11, since the memcpy races with + // updates to pi, and the data being accessed is not atomic. + // The following fence is unintuitive, but would be the + // correct one if memcpy used memory_order_relaxed atomic accesses. + // In practice it seems unlikely that the generated code would + // would be any different, so this should be OK. + atomic_thread_fence(memory_order_acquire); + if (serial == + load_const_atomic(&(pi->serial), memory_order_relaxed)) { + if (name != 0) { + strcpy(name, pi->name); + } + return len; + } + } +} + +int __system_property_get(const char *name, char *value) +{ + const prop_info *pi = __system_property_find(name); + + if (pi != 0) { + return __system_property_read(pi, 0, value); + } else { + value[0] = 0; + return 0; + } +} + +int __system_property_set(const char *key, const char *value) +{ + if (key == 0) return -1; + if (value == 0) value = ""; + if (strlen(key) >= PROP_NAME_MAX) return -1; + if (strlen(value) >= PROP_VALUE_MAX) return -1; + + prop_msg msg; + memset(&msg, 0, sizeof msg); + msg.cmd = PROP_MSG_SETPROP; + strlcpy(msg.name, key, sizeof msg.name); + strlcpy(msg.value, value, sizeof msg.value); + + const int err = send_prop_msg(&msg); + if (err < 0) { + return err; + } + + return 0; +} + +int __system_property_update(prop_info *pi, const char *value, unsigned int len) +{ + if (len >= PROP_VALUE_MAX) + return -1; + + prop_area* pa = __system_property_area__; + + if (!pa) { + return -1; + } + + uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed); + serial |= 1; + atomic_store_explicit(&pi->serial, serial, memory_order_relaxed); + // The memcpy call here also races. Again pretend it + // used memory_order_relaxed atomics, and use the analogous + // counterintuitive fence. + atomic_thread_fence(memory_order_release); + memcpy(pi->value, value, len + 1); + atomic_store_explicit( + &pi->serial, + (len << 24) | ((serial + 1) & 0xffffff), + memory_order_release); + __futex_wake(&pi->serial, INT32_MAX); + + atomic_store_explicit( + pa->serial(), + atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(pa->serial(), INT32_MAX); + + return 0; +} + +int __system_property_add(const char *name, unsigned int namelen, + const char *value, unsigned int valuelen) +{ + if (namelen >= PROP_NAME_MAX) + return -1; + if (valuelen >= PROP_VALUE_MAX) + return -1; + if (namelen < 1) + return -1; + + if (!__system_property_area__) { + return -1; + } + + prop_area* pa = get_prop_area_for_name(name); + + if (!pa) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name); + return -1; + } + + bool ret = pa->add(name, namelen, value, valuelen); + if (!ret) + return -1; + + // There is only a single mutator, but we want to make sure that + // updates are visible to a reader waiting for the update. + atomic_store_explicit( + __system_property_area__->serial(), + atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(__system_property_area__->serial(), INT32_MAX); + return 0; +} + +// Wait for non-locked serial, and retrieve it with acquire semantics. +unsigned int __system_property_serial(const prop_info *pi) +{ + uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire); + while (SERIAL_DIRTY(serial)) { + __futex_wait(const_cast( + reinterpret_cast(&pi->serial)), + serial, NULL); + serial = load_const_atomic(&pi->serial, memory_order_acquire); + } + return serial; +} + +unsigned int __system_property_wait_any(unsigned int serial) +{ + prop_area *pa = __system_property_area__; + uint32_t my_serial; + + if (!pa) { + return 0; + } + + do { + __futex_wait(pa->serial(), serial, NULL); + my_serial = atomic_load_explicit(pa->serial(), memory_order_acquire); + } while (my_serial == serial); + + return my_serial; +} + +const prop_info *__system_property_find_nth(unsigned n) +{ + find_nth_cookie cookie(n); + + const int err = __system_property_foreach(find_nth_fn, &cookie); + if (err < 0) { + return NULL; + } + + return cookie.pi; +} + +int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie), + void *cookie) +{ + if (!__system_property_area__) { + return -1; + } + + // if (__predict_false(compat_mode)) { + // return __system_property_foreach_compat(propfn, cookie); + // } + + list_foreach(contexts, [propfn, cookie](context_node* l) { + if (l->check_access_and_open()) { + l->pa()->foreach(propfn, cookie); + } + }); + return 0; +}