diff --git a/KernelSU-Next/.gitignore b/KernelSU-Next/.gitignore new file mode 100644 index 000000000000..6ae4ba179834 --- /dev/null +++ b/KernelSU-Next/.gitignore @@ -0,0 +1,2 @@ +userspace +manager \ No newline at end of file diff --git a/KernelSU-Next/LICENSE b/KernelSU-Next/LICENSE new file mode 100644 index 000000000000..f288702d2fa1 --- /dev/null +++ b/KernelSU-Next/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. diff --git a/KernelSU-Next/kernel/.clang-format b/KernelSU-Next/kernel/.clang-format new file mode 100644 index 000000000000..10dc5a9a61b3 --- /dev/null +++ b/KernelSU-Next/kernel/.clang-format @@ -0,0 +1,548 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 4. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +#AlignEscapedNewlines: Left # Unknown to clang-format-4.0 +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + #AfterExternBlock: false # Unknown to clang-format-5.0 + BeforeCatch: false + BeforeElse: false + IndentBraces: false + #SplitEmptyFunction: true # Unknown to clang-format-4.0 + #SplitEmptyRecord: true # Unknown to clang-format-4.0 + #SplitEmptyNamespace: true # Unknown to clang-format-4.0 +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +#CompactNamespaces: false # Unknown to clang-format-4.0 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +#FixNamespaceComments: false # Unknown to clang-format-4.0 + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +ForEachMacros: + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - '__ata_qc_for_each' + - 'ata_qc_for_each' + - 'ata_qc_for_each_raw' + - 'ata_qc_for_each_with_internal' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - '__bio_for_each_bvec' + - 'bio_for_each_bvec' + - 'bio_for_each_bvec_all' + - 'bio_for_each_integrity_vec' + - '__bio_for_each_segment' + - 'bio_for_each_segment' + - 'bio_for_each_segment_all' + - 'bio_list_for_each' + - 'bip_for_each_vec' + - 'bitmap_for_each_clear_region' + - 'bitmap_for_each_set_region' + - 'blkg_for_each_descendant_post' + - 'blkg_for_each_descendant_pre' + - 'blk_queue_for_each_rl' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - 'bpf_for_each_spilled_reg' + - 'btree_for_each_safe128' + - 'btree_for_each_safe32' + - 'btree_for_each_safe64' + - 'btree_for_each_safel' + - 'card_for_each_dev' + - 'cgroup_taskset_for_each' + - 'cgroup_taskset_for_each_leader' + - 'cpufreq_for_each_entry' + - 'cpufreq_for_each_entry_idx' + - 'cpufreq_for_each_valid_entry' + - 'cpufreq_for_each_valid_entry_idx' + - 'css_for_each_child' + - 'css_for_each_descendant_post' + - 'css_for_each_descendant_pre' + - 'device_for_each_child_node' + - 'dma_fence_chain_for_each' + - 'do_for_each_ftrace_op' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_atomic_for_each_plane_damage' + - 'drm_client_for_each_connector_iter' + - 'drm_client_for_each_modeset' + - 'drm_connector_for_each_possible_encoder' + - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_for_each_privobj' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'flow_action_for_each' + - 'for_each_active_dev_scope' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_aggr_pgid' + - 'for_each_available_child_of_node' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_bvec' + - 'for_each_card_auxs' + - 'for_each_card_auxs_safe' + - 'for_each_card_components' + - 'for_each_card_dapms' + - 'for_each_card_pre_auxs' + - 'for_each_card_prelinks' + - 'for_each_card_rtds' + - 'for_each_card_rtds_safe' + - 'for_each_card_widgets' + - 'for_each_card_widgets_safe' + - 'for_each_cgroup_storage_type' + - 'for_each_child_of_node' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_cmsghdr' + - 'for_each_compatible_node' + - 'for_each_component_dais' + - 'for_each_component_dais_safe' + - 'for_each_comp_order' + - 'for_each_console' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_not' + - 'for_each_cpu_wrap' + - 'for_each_dapm_widgets' + - 'for_each_dev_addr' + - 'for_each_dev_scope' + - 'for_each_displayid_db' + - 'for_each_dma_cap_mask' + - 'for_each_dpcm_be' + - 'for_each_dpcm_be_rollback' + - 'for_each_dpcm_be_safe' + - 'for_each_dpcm_fe' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_element' + - 'for_each_element_extid' + - 'for_each_element_id' + - 'for_each_endpoint_of_node' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - 'for_each_free_mem_pfn_range_in_zone' + - 'for_each_free_mem_pfn_range_in_zone_from' + - 'for_each_free_mem_range' + - 'for_each_free_mem_range_reverse' + - 'for_each_func_rsrc' + - 'for_each_hstate' + - 'for_each_if' + - 'for_each_iommu' + - 'for_each_ip_tunnel_rcu' + - 'for_each_irq_nr' + - 'for_each_link_codecs' + - 'for_each_link_cpus' + - 'for_each_link_platforms' + - 'for_each_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_member' + - 'for_each_mem_region' + - 'for_each_memblock_type' + - 'for_each_memcg_cache_index' + - 'for_each_mem_pfn_range' + - '__for_each_mem_range' + - 'for_each_mem_range' + - '__for_each_mem_range_rev' + - 'for_each_mem_range_rev' + - 'for_each_migratetype_order' + - 'for_each_msi_entry' + - 'for_each_msi_entry_safe' + - 'for_each_net' + - 'for_each_net_continue_reverse' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - 'for_each_netdev_continue_reverse' + - 'for_each_netdev_feature' + - 'for_each_netdev_in_bond_rcu' + - 'for_each_netdev_rcu' + - 'for_each_netdev_reverse' + - 'for_each_netdev_safe' + - 'for_each_net_rcu' + - 'for_each_new_connector_in_state' + - 'for_each_new_crtc_in_state' + - 'for_each_new_mst_mgr_in_state' + - 'for_each_new_plane_in_state' + - 'for_each_new_private_obj_in_state' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_nonreserved_multicast_dest_pgid' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_cpu_node' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_old_mst_mgr_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_mst_mgr_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_plane_in_state_reverse' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pci_msi_entry' + - 'for_each_pcm_streams' + - 'for_each_physmem_range' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_present_cpu' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_property_of_node' + - 'for_each_registered_fb' + - 'for_each_requested_gpio' + - 'for_each_requested_gpio_in_range' + - 'for_each_reserved_mem_range' + - 'for_each_reserved_mem_region' + - 'for_each_rtd_codec_dais' + - 'for_each_rtd_codec_dais_rollback' + - 'for_each_rtd_components' + - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_cpu_dais_rollback' + - 'for_each_rtd_dais' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_set_clump8' + - 'for_each_sg' + - 'for_each_sg_dma_page' + - 'for_each_sg_page' + - 'for_each_sgtable_dma_page' + - 'for_each_sgtable_dma_sg' + - 'for_each_sgtable_page' + - 'for_each_sgtable_sg' + - 'for_each_sibling_event' + - 'for_each_subelement' + - 'for_each_subelement_extid' + - 'for_each_subelement_id' + - '__for_each_thread' + - 'for_each_thread' + - 'for_each_unicast_dest_pgid' + - 'for_each_wakeup_source' + - 'for_each_zone' + - 'for_each_zone_zonelist' + - 'for_each_zone_zonelist_nodemask' + - 'fwnode_for_each_available_child_node' + - 'fwnode_for_each_child_node' + - 'fwnode_graph_for_each_endpoint' + - 'gadget_for_each_ep' + - 'genradix_for_each' + - 'genradix_for_each_from' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hctx_for_each_ctx' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - '__hlist_for_each_rcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - 'i3c_bus_for_each_i2cdev' + - 'i3c_bus_for_each_i3cdev' + - 'ide_host_for_each_port' + - 'ide_port_for_each_dev' + - 'ide_port_for_each_present_dev' + - 'idr_for_each_entry' + - 'idr_for_each_entry_continue' + - 'idr_for_each_entry_continue_ul' + - 'idr_for_each_entry_ul' + - 'in_dev_for_each_ifa_rcu' + - 'in_dev_for_each_ifa_rtnl' + - 'inet_bind_bucket_for_each' + - 'inet_lhash2_for_each_icsk_rcu' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_func_safe' + - 'klp_for_each_func_static' + - 'klp_for_each_object' + - 'klp_for_each_object_safe' + - 'klp_for_each_object_static' + - 'kunit_suite_for_each_test_case' + - 'kvm_for_each_memslot' + - 'kvm_for_each_vcpu' + - 'list_for_each' + - 'list_for_each_codec' + - 'list_for_each_codec_safe' + - 'list_for_each_continue' + - 'list_for_each_entry' + - 'list_for_each_entry_continue' + - 'list_for_each_entry_continue_rcu' + - 'list_for_each_entry_continue_reverse' + - 'list_for_each_entry_from' + - 'list_for_each_entry_from_rcu' + - 'list_for_each_entry_from_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'mci_for_each_dimm' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'nanddev_io_for_each_page' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'pcm_for_each_format' + - 'ping_portaddr_for_each_entry' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rbtree_postorder_for_each_entry_safe' + - 'rdma_for_each_block' + - 'rdma_for_each_port' + - 'rdma_umem_for_each_dma_block' + - 'resource_list_for_each_entry' + - 'resource_list_for_each_entry_safe' + - 'rhl_for_each_entry_rcu' + - 'rhl_for_each_rcu' + - 'rht_for_each' + - 'rht_for_each_entry' + - 'rht_for_each_entry_from' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_from' + - 'rht_for_each_entry_safe' + - 'rht_for_each_from' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_from' + - '__rq_for_each_bio' + - 'rq_for_each_bvec' + - 'rq_for_each_segment' + - 'scsi_for_each_prot_sg' + - 'scsi_for_each_sg' + - 'sctp_for_each_hentry' + - 'sctp_skb_for_each' + - 'shdma_for_each_chan' + - '__shost_for_each_device' + - 'shost_for_each_device' + - 'sk_for_each' + - 'sk_for_each_bound' + - 'sk_for_each_entry_offset_rcu' + - 'sk_for_each_from' + - 'sk_for_each_rcu' + - 'sk_for_each_safe' + - 'sk_nulls_for_each' + - 'sk_nulls_for_each_from' + - 'sk_nulls_for_each_rcu' + - 'snd_array_for_each' + - 'snd_pcm_group_for_each_entry' + - 'snd_soc_dapm_widget_for_each_path' + - 'snd_soc_dapm_widget_for_each_path_safe' + - 'snd_soc_dapm_widget_for_each_sink_path' + - 'snd_soc_dapm_widget_for_each_source_path' + - 'tb_property_for_each' + - 'tcf_exts_for_each_action' + - 'udp_portaddr_for_each_entry' + - 'udp_portaddr_for_each_entry_rcu' + - 'usb_hub_for_each_child' + - 'v4l2_device_for_each_subdev' + - 'v4l2_m2m_for_each_dst_buf' + - 'v4l2_m2m_for_each_dst_buf_safe' + - 'v4l2_m2m_for_each_src_buf' + - 'v4l2_m2m_for_each_src_buf_safe' + - 'virtio_device_for_each_vq' + - 'while_for_each_ftrace_op' + - 'xa_for_each' + - 'xa_for_each_marked' + - 'xa_for_each_range' + - 'xa_for_each_start' + - 'xas_for_each' + - 'xas_for_each_conflict' + - 'xas_for_each_marked' + - 'xbc_array_for_each_value' + - 'xbc_for_each_key_value' + - 'xbc_node_for_each_array_value' + - 'xbc_node_for_each_child' + - 'xbc_node_for_each_key_value' + - 'zorro_for_each_dev' + +#IncludeBlocks: Preserve # Unknown to clang-format-5.0 +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +#IndentPPDirectives: None # Unknown to clang-format-5.0 +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +#SortUsingDeclarations: false # Unknown to clang-format-4.0 +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 +#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 +SpaceBeforeParens: ControlStatements +#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/KernelSU-Next/kernel/.gitignore b/KernelSU-Next/kernel/.gitignore new file mode 100644 index 000000000000..72fda848f641 --- /dev/null +++ b/KernelSU-Next/kernel/.gitignore @@ -0,0 +1,24 @@ +.cache/ +.thinlto-cache/ +compile_commands.json +*.ko +*.o +*.mod +*.lds +*.mod.o +.*.o* +.*.mod* +*.ko* +*.mod.c +*.symvers* +*.order +.*.ko.cmd +.tmp_versions/ +libs/ +obj/ + +CLAUDE.md +.ddk-version +.vscode/settings.json +check_symbol +ksu_uapi.h diff --git a/KernelSU-Next/kernel/Kbuild b/KernelSU-Next/kernel/Kbuild new file mode 100644 index 000000000000..2ffdcd6e99e7 --- /dev/null +++ b/KernelSU-Next/kernel/Kbuild @@ -0,0 +1,313 @@ +kernelsu-objs := core/init.o + +kernelsu-objs += compat/kernel_compat.o +kernelsu-objs += feature/kernel_umount.o +kernelsu-objs += feature/sucompat.o + +kernelsu-objs += hook/hook_manager.o +kernelsu-objs += hook/lsm_hooks.o +kernelsu-objs += hook/setuid_hook.o +kernelsu-objs += extras.o +kernelsu-objs += tiny_sulog.o + +kernelsu-objs += infra/file_wrapper.o +kernelsu-objs += infra/seccomp_cache.o +kernelsu-objs += infra/su_mount_ns.o + +ifneq ($(CONFIG_KSU_DISABLE_MANAGER),y) +kernelsu-objs += manager/apk_sign.o +kernelsu-objs += manager/throne_tracker.o +kernelsu-objs += manager/pkg_observer.o +endif + +kernelsu-objs += policy/allowlist.o +kernelsu-objs += policy/app_profile.o +kernelsu-objs += policy/feature.o + +kernelsu-objs += runtime/boot_event.o +kernelsu-objs += runtime/ksud_integration.o + +kernelsu-objs += selinux/selinux.o +kernelsu-objs += selinux/rules.o +kernelsu-objs += selinux/sepolicy.o + +kernelsu-objs += supercall/dispatch.o +kernelsu-objs += supercall/perm.o +kernelsu-objs += supercall/supercall.o + +ifdef KBUILD_EXTMOD +ifeq ($(CONFIG_KSU_DISABLE_MANAGER),y) +ccflags-y += -DCONFIG_KSU_DISABLE_MANAGER=1 +endif +ifeq ($(CONFIG_KSU_DISABLE_POLICY),y) +ccflags-y += -DCONFIG_KSU_DISABLE_POLICY=1 +endif +ifeq ($(CONFIG_KSU_DEBUG),y) +ccflags-y += -DCONFIG_KSU_DEBUG=1 +endif +endif + +ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include +ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h + +ifeq ($(filter /%,$(src)),) +KSU_KERNEL_DIR := $(srctree)/$(src) +else +KSU_KERNEL_DIR := $(src) +endif + +ccflags-y += -I$(KSU_KERNEL_DIR) -I$(KSU_KERNEL_DIR)/include + +obj-$(CONFIG_KSU) += kernelsu.o + +LPATH := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin +MDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +# Check if this is a git repository +# Try to detect Git repo intelligently +GIT_ROOT := $(shell cd $(MDIR) && $(LPATH) git rev-parse --show-toplevel 2>/dev/null) +ifneq ($(GIT_ROOT),) +KERNEL_GIT_ROOT := $(shell cd $(srctree) && $(LPATH) git rev-parse --show-toplevel 2>/dev/null) +ifneq ($(GIT_ROOT),$(KERNEL_GIT_ROOT)) +# Only set version if it's a different repo from kernel +$(shell cd $(GIT_ROOT) && [ -f .git/shallow ] && $(LPATH) git fetch --unshallow 2>/dev/null || true) +KSU_GIT_VERSION := $(shell cd $(GIT_ROOT) && $(LPATH) git rev-list --count HEAD 2>/dev/null) +KSU_GIT_TAG := $(shell cd $(GIT_ROOT) && $(LPATH) git describe --tags --abbrev=0 2>/dev/null) +KSU_GIT_VERSION_VALID := 1 +$(info -- KernelSU-Next Git repo detected at: $(GIT_ROOT)) +endif +endif + +# Calculate version if git version is available +ifdef KSU_GIT_VERSION_VALID +# ksu_version: major * 30000 + git version for historical reasons +$(eval KSU_VERSION=$(shell expr 30000 + $(KSU_GIT_VERSION) + 150)) +$(info -- KernelSU-Next version: $(KSU_VERSION)) +ccflags-y += -DKSU_VERSION=$(KSU_VERSION) +else +# If there is no .git directory, use default version +$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU-Next a git repository!") +KSU_VERSION_FALLBACK := 1 +$(info -- KernelSU-Next version fallback: $(KSU_VERSION_FALLBACK)) +ccflags-y += -DKSU_VERSION=$(KSU_VERSION_FALLBACK) +endif + +ifdef KSU_GIT_VERSION_VALID +$(eval KSU_VERSION_TAG=$(KSU_GIT_TAG)) +$(info -- KernelSU-Next tag: $(KSU_VERSION_TAG)) +ccflags-y += -DKSU_VERSION_TAG=\"$(KSU_VERSION_TAG)\" +else +$(warning "KSU_VERSION_TAG not defined! It is better to make KernelSU-Next a git submodule!") +KSU_VERSION_TAG_FALLBACK := v0.0.1 +$(info -- KernelSU-Next tag fallback: $(KSU_VERSION_TAG_FALLBACK)) +ccflags-y += -DKSU_VERSION_TAG=\"$(KSU_VERSION_TAG_FALLBACK)\" +endif + +ifndef KSU_NEXT_MANAGER_SIZE +KSU_NEXT_MANAGER_SIZE := 0x3e6 +endif + +ifndef KSU_NEXT_MANAGER_HASH +KSU_NEXT_MANAGER_HASH := 79e590113c4c4c0c222978e413a5faa801666957b1212a328e46c00c69821bf7 +endif + +ifdef KSU_MANAGER_PACKAGE +ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\" +$(info -- KernelSU-Next Manager package name: $(KSU_MANAGER_PACKAGE)) +endif + +$(info -- KernelSU-Next Manager signature size: $(KSU_NEXT_MANAGER_SIZE)) +$(info -- KernelSU-Next Manager signature hash: $(KSU_NEXT_MANAGER_HASH)) + +# RKSU: checks for available hook +## Logic flipped for HAVE_KSU_HOOK: 0 is success, 1 is failure +HAVE_KSU_HOOK ?= 1 + +# Checks hooks state +ifeq ($(CONFIG_KSU_KPROBES_HOOK), y) +$(info -- KernelSU-Next: Hook mode: Kprobes) +ccflags-y += -DKSU_KPROBES_HOOK +# Let's make it 0, so it would pass. +HAVE_KSU_HOOK := 0 +endif + +ifeq ($(CONFIG_KSU_MANUAL_HOOK), y) +HAVE_KSU_HOOK := $(shell grep -q "ksu_handle_sys_reboot" $(srctree)/kernel/reboot.c && echo 0 || echo 1) +ifeq ($(HAVE_KSU_HOOK),0) +$(info -- KernelSU-Next: Hook mode: Manual) +endif +endif + +ifneq ($(HAVE_KSU_HOOK),0) +$(error -- KernelSU-Next: No hooks were defined, please integrate manual hooks in your kernel!) +endif + +# some backports +ifneq ($(shell grep -Eq "^static int can_umount" $(srctree)/fs/namespace.c; echo $$?),0) +$(info -- KSU_NEXT: adding function 'static int can_umount(const struct path *path, int flags);' to $(srctree)/fs/namespace.c) +CAN_UMOUNT = static int can_umount(const struct path *path, int flags)\n\ +{\n\t\ + struct mount *mnt = real_mount(path->mnt);\n\t\ + if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))\n\t\t\ + return -EINVAL;\n\t\ + if (!may_mount())\n\t\t\ + return -EPERM;\n\t\ + if (path->dentry != path->mnt->mnt_root)\n\t\t\ + return -EINVAL;\n\t\ + if (!check_mnt(mnt))\n\t\t\ + return -EINVAL;\n\t\ + if (mnt->mnt.mnt_flags & MNT_LOCKED)\n\t\t\ + return -EINVAL;\n\t\ + if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN))\n\t\t\ + return -EPERM;\n\t\ + return 0;\n\ +}\n +$(shell sed -i '/^static bool is_mnt_ns_file/i $(CAN_UMOUNT)' $(srctree)/fs/namespace.c;) +endif + +ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/namespace.c; echo $$?),0) +$(info -- KSU_NEXT: adding function 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/namespace.c) +PATH_UMOUNT = int path_umount(struct path *path, int flags)\n\ +{\n\t\ + struct mount *mnt = real_mount(path->mnt);\n\t\ + int ret;\n\t\ + ret = can_umount(path, flags);\n\t\ + if (!ret)\n\t\t\ + ret = do_umount(mnt, flags);\n\t\ + dput(path->dentry);\n\t\ + mntput_no_expire(mnt);\n\t\ + return ret;\n\ +}\n +$(shell sed -i '/^static bool is_mnt_ns_file/i $(PATH_UMOUNT)' $(srctree)/fs/namespace.c;) +endif + +ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/internal.h; echo $$?),0) +$(shell sed -i '/^extern void __init mnt_init/a int path_umount(struct path *path, int flags);' $(srctree)/fs/internal.h;) +$(info -- KSU_NEXT: adding 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/internal.h) +endif + +ifneq ($(shell grep -q "atomic_t filter_count;" $(srctree)/include/linux/seccomp.h; echo $$?),0) +$(info -- KSU_NEXT: patching struct seccomp for filter_count) +$(shell sed -i '/int mode;/a\ atomic_t filter_count;' $(srctree)/include/linux/seccomp.h) +$(shell sed -i '/#include /a\#include ' $(srctree)/include/linux/seccomp.h) +endif + +# security/selinux backports +ifneq ($(shell grep -q "selinux_inode(inode)" $(srctree)/security/selinux/hooks.c; echo $$?),0) +$(info -- KSU_NEXT: patching selinux/hooks.c for selinux_inode) +$(shell sed -i 's/struct inode_security_struct \*isec = inode->i_security/struct inode_security_struct *isec = selinux_inode(inode)/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/return inode->i_security/return selinux_inode(inode)/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/return inode->i_security/return selinux_inode(inode)/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/\bisec = inode->i_security;/isec = selinux_inode(inode);/' $(srctree)/security/selinux/hooks.c) +endif + +ifneq ($(shell grep -q "selinux_cred" $(srctree)/security/selinux/hooks.c; echo $$?),0) +$(info -- KSU_NEXT: patching selinux/hooks.c for selinux_cred) +$(shell sed -i 's/tsec = cred->security;/tsec = selinux_cred(cred);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/const struct task_security_struct \*tsec = cred->security;/const struct task_security_struct *tsec = selinux_cred(cred);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/const struct task_security_struct \*tsec = current_security();/const struct task_security_struct *tsec = selinux_cred(current_cred());/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/rc = selinux_determine_inode_label(current_security())/rc = selinux_determine_inode_label(selinux_cred(current_cred()))/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/old_tsec = current_security();/old_tsec = selinux_cred(current_cred());/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/new_tsec = bprm->cred->security;/new_tsec = selinux_cred(bprm->cred);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/rc = selinux_determine_inode_label(old->security)/rc = selinux_determine_inode_label(selinux_cred(old))/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/tsec = new->security;/tsec = selinux_cred(new);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/tsec = new_creds->security;/tsec = selinux_cred(new_creds);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/old_tsec = old->security;/old_tsec = selinux_cred(old);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/const struct task_security_struct \*old_tsec = old->security;/const struct task_security_struct *old_tsec = selinux_cred(old);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/struct task_security_struct \*tsec = new->security;/struct task_security_struct *tsec = selinux_cred(new);/g' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/__tsec = current_security();/__tsec = selinux_cred(current_cred());/' $(srctree)/security/selinux/hooks.c) +$(shell sed -i 's/__tsec = __task_cred(p)->security;/__tsec = selinux_cred(__task_cred(p));/' $(srctree)/security/selinux/hooks.c) +endif + +ifneq ($(shell grep -q "selinux_inode(inode)" $(srctree)/security/selinux/selinuxfs.c; echo $$?),0) +$(info -- KSU_NEXT: patching selinux/selinuxfs.c for selinux_inode) +$(shell sed -i 's/(struct inode_security_struct \*)inode->i_security/selinux_inode(inode)/g' $(srctree)/security/selinux/selinuxfs.c) +endif + +ifneq ($(shell grep -q "selinux_cred" $(srctree)/security/selinux/xfrm.c; echo $$?),0) +$(info -- KSU_NEXT: patching selinux/xfrm.c for selinux_cred) +$(shell sed -i 's/const struct task_security_struct \*tsec = current_security();/const struct task_security_struct *tsec = selinux_cred(current_cred());/g' $(srctree)/security/selinux/xfrm.c) +endif + +ifneq ($(shell grep -q "selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +$(info -- KSU_NEXT: patching selinux/include/objsec.h for selinux_inode) +$(shell sed -i '/#endif \/\* _SELINUX_OBJSEC_H_ \*\//i\static inline struct inode_security_struct *selinux_inode(\n\t\t\t\t\t\tconst struct inode *inode)\n{\n\treturn inode->i_security;\n}\n' $(srctree)/security/selinux/include/objsec.h) +endif + +ifneq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +$(info -- KSU_NEXT: patching selinux/include/objsec.h for selinux_cred) +$(shell sed -i '/#endif \/\* _SELINUX_OBJSEC_H_ \*\//i\static inline struct task_security_struct *selinux_cred(const struct cred *cred)\n{\n\treturn cred->security;\n}\n' $(srctree)/security/selinux/include/objsec.h) +endif + +# SELinux drivers check +ifeq ($(shell grep -q "current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID +endif +ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_USE_SELINUX_STATE +endif +ifeq ($(shell grep -q "^DEFINE_RWLOCK(policy_rwlock);" $(srctree)/security/selinux/ss/services.c; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_EXPORTED_POLICY_RWLOCK +endif +ifeq ($(shell grep -q "cpus_ptr;" $(srctree)/include/linux/sched.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_BACKPORTED_CPUS_PTR +endif + +# Handle optional backports +ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0) +ccflags-y += -DKSU_OPTIONAL_STRNCPY +endif + +ifeq ($(shell grep -q "ssize_t kernel_read" $(srctree)/fs/read_write.c; echo $$?),0) +ccflags-y += -DKSU_OPTIONAL_KERNEL_READ +endif + +ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0) +ccflags-y += -DKSU_OPTIONAL_KERNEL_WRITE +endif + +ifeq ($(shell grep -q "int\s\+path_mount" $(srctree)/fs/namespace.c; echo $$?),0) +ccflags-y += -DKSU_HAS_PATH_MOUNT +endif + +ifeq ($(shell grep -q "int\s\+path_umount" $(srctree)/fs/namespace.c; echo $$?),0) +ccflags-y += -DKSU_HAS_PATH_UMOUNT +endif + +# some old kernel backport this, let's check if put_seccomp_filter still exist +ifneq ($(shell grep -wq "put_seccomp_filter" $(srctree)/kernel/seccomp.c $(srctree)/include/linux/seccomp.h; echo $$?),0) +ifeq ($(shell grep -wq "seccomp_filter_release" $(srctree)/kernel/seccomp.c $(srctree)/include/linux/seccomp.h; echo $$?),0) +ccflags-y += -DKSU_OPTIONAL_SECCOMP_FILTER_RELEASE +endif +endif + +ifeq ($(shell grep -q "security_inode_init_security_anon" $(srctree)/include/linux/security.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_INIT_SEC_ANON +endif + +# Checks Samsung +ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0) +ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST +endif + +ifeq ($(shell grep -q "SEC_SELINUX_PORTING_COMMON" $(srctree)/security/selinux/avc.c; echo $$?),0) +ccflags-y += -DSAMSUNG_SELINUX_PORTING +endif + +ccflags-y += -DEXPECTED_MANAGER_SIZE=$(KSU_NEXT_MANAGER_SIZE) +ccflags-y += -DEXPECTED_MANAGER_HASH=\"$(KSU_NEXT_MANAGER_HASH)\" + +ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes +ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable + +## For susfs stuff ## +ifeq ($(shell test -e $(srctree)/fs/susfs.c; echo $$?),0) +$(eval SUSFS_VERSION=$(shell cat $(srctree)/include/linux/susfs.h | grep -E '^#define SUSFS_VERSION' | cut -d' ' -f3 | sed 's/"//g')) +$(info ) +$(info -- SUSFS_VERSION: $(SUSFS_VERSION)) +else +$(info -- You have not integrated susfs in your kernel yet.) +$(info -- Read: https://gitlab.com/simonpunk/susfs4ksu) +endif + +# Keep a new line here!! Because someone may append config diff --git a/KernelSU-Next/kernel/Kconfig b/KernelSU-Next/kernel/Kconfig new file mode 100644 index 000000000000..cf74ec54ca74 --- /dev/null +++ b/KernelSU-Next/kernel/Kconfig @@ -0,0 +1,160 @@ +menu "KernelSU" + +config KSU + tristate "KernelSU function support" + default y + help + Enable kernel-level root privileges on Android System. + Requires CONFIG_KPROBES for kernel hooking support. + To compile as a module, choose M here: the + module will be called kernelsu. + +config KSU_DEBUG + bool "KernelSU debug mode" + depends on KSU + default n + help + Enable KernelSU debug mode. + +config KSU_DISABLE_MANAGER + bool "Disable KernelSU manager integration" + depends on KSU + default n + help + Disable manager APK detection and manager-specific handling. + Root will be used in place of manager-only functionality. + +config KSU_DISABLE_POLICY + bool "Disable KernelSU policy profiles" + depends on KSU + default n + help + Disable per-app root/non-root profile customization. + Escalation will always use the default full root profile, and + non-root handling will follow the global umount policy only. + +config KSU_ALLOWLIST_WORKAROUND + bool "KernelSU allowlist workaround" + depends on KSU + default n + help + Enable workaround for broken allowlist save + +# For easier extern ifdef handling +config KSU_MANUAL_HOOK + bool "KernelSU manual hook mode." + depends on KSU && KSU != m + default y if !KPROBES + default n + help + Enable manual hook support. + +config KSU_KPROBES_HOOK + bool "KernelSU tracepoint+kretprobe hook" + depends on KSU && !KSU_MANUAL_HOOK + depends on KRETPROBES && KPROBES && HAVE_SYSCALL_TRACEPOINTS + default y if KPROBES && KRETPROBES && HAVE_SYSCALL_TRACEPOINTS + default y if !KSU_MANUAL_HOOK + help + Enable KPROBES, KRETPROBES and TRACEPOINT hook for KernelSU core. + This should not be used on kernel below 5.10. + +menu "KernelSU - SUSFS" +config KSU_SUSFS + bool "KernelSU addon - SUSFS" + depends on KSU + depends on THREAD_INFO_IN_TASK + default y + help + Patch and Enable SUSFS to kernel with KernelSU. + +config KSU_SUSFS_SUS_PATH + bool "Enable to hide suspicious path (NOT recommended)" + depends on KSU_SUSFS + default y + help + - Allow hiding the user-defined path and all its sub-paths from various system calls. + - Includes temp fix for the leaks of app path in /sdcard/Android/data directory. + - Effective only on zygote spawned user app process. + - Use with cautious as it may cause performance loss and will be vulnerable to side channel attacks, + just disable this feature if it doesn't work for you or you don't need it at all. + +config KSU_SUSFS_SUS_MOUNT + bool "Enable to hide suspicious mounts" + depends on KSU_SUSFS + default y + help + - Allow hiding the user-defined mount paths from /proc/self/[mounts|mountinfo|mountstat]. + - Effective on all processes for hiding mount entries. + - mnt_id and mnt_group_id of the sus mount will be assigned to a much bigger number to solve the ssue of id not being contiguous. + +config KSU_SUSFS_SUS_KSTAT + bool "Enable to spoof suspicious kstat" + depends on KSU_SUSFS + default y + help + - Allow spoofing the kstat of user-defined file/directory. + - Effective only on zygote spawned user app process. + +config KSU_SUSFS_TRY_UMOUNT + bool "Enable to use ksu's try_umount" + depends on KSU_SUSFS + default y + help + - Allow using try_umount to umount other user-defined mount paths prior to ksu's default umount paths. + - Effective only on zygote spawned umounted user app process. + +config KSU_SUSFS_SPOOF_UNAME + bool "Enable to spoof uname" + depends on KSU_SUSFS + default y + help + - Allow spoofing the string returned by uname syscall to user-defined string. + - Effective on all processes. + +config KSU_SUSFS_ENABLE_LOG + bool "Enable logging susfs log to kernel" + depends on KSU_SUSFS + default y + help + - Allow logging susfs log to kernel, uncheck it to completely disable all susfs log. + +config KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS + bool "Enable to automatically hide ksu and susfs symbols from /proc/kallsyms" + depends on KSU_SUSFS + default y + help + - Automatically hide ksu and susfs symbols from '/proc/kallsyms'. + - Effective on all processes. + +config KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + bool "Enable to spoof /proc/bootconfig (gki) or /proc/cmdline (non-gki)" + depends on KSU_SUSFS + default y + help + - Spoof the output of /proc/bootconfig (gki) or /proc/cmdline (non-gki) with a user-defined file. + - Effective on all processes. + +config KSU_SUSFS_OPEN_REDIRECT + bool "Enable to redirect a path to be opened with another path (experimental)" + depends on KSU_SUSFS + default y + help + - Allow redirecting a target path to be opened with another user-defined path. + - Effective only on processes with uid < 2000. + - Please be reminded that process with open access to the target and redirected path can be detected. + +config KSU_SUSFS_SUS_MAP + bool "Enable to hide some mmapped real file from different proc maps interfaces" + depends on KSU_SUSFS + default y + help + - Allow hiding mmapped real file from /proc//[maps|smaps|smaps_rollup|map_files|mem|pagemap] + - It does NOT support hiding for anon memory. + - It does NOT hide any inline hooks or plt hooks cause by the injected library itself. + - It may not be able to evade detections by apps that implement a good injection detection. + - Effective only on zygote spawned umounted user app process. + +endmenu + +endmenu diff --git a/KernelSU-Next/kernel/LICENSE b/KernelSU-Next/kernel/LICENSE new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/KernelSU-Next/kernel/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. + + + Copyright (C) + + 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. + + , 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/KernelSU-Next/kernel/Makefile b/KernelSU-Next/kernel/Makefile new file mode 100644 index 000000000000..38164088275f --- /dev/null +++ b/KernelSU-Next/kernel/Makefile @@ -0,0 +1,25 @@ +KDIR := $(KDIR) +MDIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) + +$(info -- KDIR: $(KDIR)) +$(info -- MDIR: $(MDIR)) + +.PHONY: all compdb clean format check-format + +all: check_symbol + make -C $(KDIR) M=$(MDIR) modules + ./check_symbol kernelsu.ko $(KDIR)/vmlinux + +compdb: + python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR) +clean: + make -C $(KDIR) M=$(MDIR) clean + rm check_symbol +check_symbol: tools/check_symbol.c + $(CC) tools/check_symbol.c -o check_symbol +format: + find . \( -name "*.c" -o -name "*.h" \) -print0 | xargs -0 clang-format -i +check-format: + find . \( -name "*.c" -o -name "*.h" \) -print0 | xargs -0 clang-format --dry-run --Werror + +# Keep a new line here!! Because someone may append config \ No newline at end of file diff --git a/KernelSU-Next/kernel/compat/kernel_compat.c b/KernelSU-Next/kernel/compat/kernel_compat.c new file mode 100644 index 000000000000..6deb054feca5 --- /dev/null +++ b/KernelSU-Next/kernel/compat/kernel_compat.c @@ -0,0 +1,174 @@ +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#include +#else +#include +#endif +#include +#include +#include "klog.h" // IWYU pragma: keep +#include "kernel_compat.h" + +struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) +{ + return filp_open(filename, flags, mode); +} + +ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, + loff_t *pos) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ + defined(KSU_OPTIONAL_KERNEL_READ) + return kernel_read(p, buf, count, pos); +#else + loff_t offset = pos ? *pos : 0; + ssize_t result = kernel_read(p, offset, (char *)buf, count); + if (pos && result > 0) { + *pos = offset + result; + } + return result; +#endif +} + +ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, + loff_t *pos) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ + defined(KSU_OPTIONAL_KERNEL_WRITE) + return kernel_write(p, buf, count, pos); +#else + loff_t offset = pos ? *pos : 0; + ssize_t result = kernel_write(p, buf, count, offset); + if (pos && result > 0) { + *pos = offset + result; + } + return result; +#endif +} + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) && !defined(KSU_HAS_PATH_MOUNT)) +extern long do_mount(const char *dev_name, const char __user *dir_name, + const char *type_page, unsigned long flags, + void *data_page); + +int path_mount(const char *dev_name, struct path *path, const char *type_page, + unsigned long flags, void *data_page) +{ + mm_segment_t old_fs; + long ret = 0; + char buf[384]; + + char *realpath = d_path(path, buf, sizeof(buf)); + if (IS_ERR(realpath)) { + pr_err("ksu_mount: d_path failed, err: %lu\n", + PTR_ERR(realpath)); + return PTR_ERR(realpath); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = do_mount(dev_name, (const char __user *)realpath, type_page, + flags, data_page); + set_fs(old_fs); + return ret; +} +#endif + +long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + return copy_from_user_nofault(dst, src, size); +#else + // https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205 + long ret = -EFAULT; + mm_segment_t old_fs = get_fs(); + + set_fs(USER_DS); + // tweaked to use ksu_access_ok + if (ksu_access_ok(src, size)) { + pagefault_disable(); + ret = __copy_from_user_inatomic(dst, src, size); + pagefault_enable(); + } + set_fs(old_fs); + + if (ret) + return -EFAULT; + return 0; +#endif +} + +#ifndef KSU_OPTIONAL_STRNCPY +long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, + long count) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) + return strncpy_from_unsafe_user(dst, unsafe_addr, count); +#else + mm_segment_t old_fs = get_fs(); + long ret; + + if (unlikely(count <= 0)) + return 0; + + set_fs(USER_DS); + pagefault_disable(); + ret = strncpy_from_user(dst, unsafe_addr, count); + pagefault_enable(); + set_fs(old_fs); + + if (ret >= count) { + ret = count; + dst[ret - 1] = '\0'; + } else if (ret > 0) { + ret++; + } + + return ret; +#endif +} +#endif // #ifndef KSU_OPTIONAL_STRNCPY + +static void *__kvmalloc(size_t size, gfp_t flags) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +// https://elixir.bootlin.com/linux/v4.4.302/source/security/apparmor/lib.c#L79 + void *buffer = NULL; + + if (size == 0) + return NULL; + + /* do not attempt kmalloc if we need more than 16 pages at once */ + if (size <= (16 * PAGE_SIZE)) + buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); + if (!buffer) { + if (flags & __GFP_ZERO) + buffer = vzalloc(size); + else + buffer = vmalloc(size); + } + return buffer; +#else + return kvmalloc(size, flags); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0) +// https://elixir.bootlin.com/linux/v5.10.247/source/mm/util.c#L664 +void *ksu_compat_kvrealloc(const void *p, size_t oldsize, size_t newsize, + gfp_t flags) +{ + void *newp; + + if (oldsize >= newsize) + return (void *)p; + newp = __kvmalloc(newsize, flags); + if (!newp) + return NULL; + memcpy(newp, p, oldsize); + kvfree(p); + return newp; +} +#endif diff --git a/KernelSU-Next/kernel/compat/kernel_compat.h b/KernelSU-Next/kernel/compat/kernel_compat.h new file mode 100644 index 000000000000..43fb86ce14b3 --- /dev/null +++ b/KernelSU-Next/kernel/compat/kernel_compat.h @@ -0,0 +1,130 @@ +#ifndef __KSU_H_KERNEL_COMPAT +#define __KSU_H_KERNEL_COMPAT + +#include +#include +#include +#include "ss/policydb.h" +#include "linux/key.h" + +/* + * Adapt to Huawei HISI kernel without affecting other kernels , + * Huawei Hisi Kernel EBITMAP Enable or Disable Flag , + * From ss/ebitmap.h + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) +#ifdef HISI_SELINUX_EBITMAP_RO +#define CONFIG_IS_HW_HISI +#endif +#endif + +// Checks for UH, KDP and RKP +#ifdef SAMSUNG_UH_DRIVER_EXIST +#if defined(CONFIG_UH) || defined(CONFIG_KDP) || defined(CONFIG_RKP) +#error "CONFIG_UH, CONFIG_KDP and CONFIG_RKP is enabled! Please disable or remove it before compile a kernel with KernelSU!" +#endif +#endif + +extern struct file *ksu_filp_open_compat(const char *filename, int flags, + umode_t mode); +extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, + loff_t *pos); +extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, + size_t count, loff_t *pos); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) +extern struct key *init_session_keyring; +#endif + +extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size); +/* + * ksu_copy_from_user_retry + * try nofault copy first, if it fails, try with plain + * paramters are the same as copy_from_user + * 0 = success + */ +static inline long ksu_copy_from_user_retry(void *to, + const void __user *from, unsigned long count) +{ + long ret = ksu_copy_from_user_nofault(to, from, count); + if (likely(!ret)) + return ret; + + // we faulted! fallback to slow path + return copy_from_user(to, from, count); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0) +extern void *ksu_compat_kvrealloc(const void *p, size_t oldsize, size_t newsize, + gfp_t flags); +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +static inline void *ksu_kvmalloc(size_t size, gfp_t flags) +{ + void *buf = kmalloc(size, flags); + if (!buf) + buf = vmalloc(size); + + return buf; +} + +static inline void ksu_kvfree(const void *buf) +{ + if (is_vmalloc_addr(buf)) + vfree(buf); + else + kfree(buf); +} +#define kvmalloc ksu_kvmalloc +#define kvfree ksu_kvfree +#endif + +// https://elixir.bootlin.com/linux/v4.14.222/source/lib/string.c#L282 +static inline ssize_t __strscpy_pad(char *dest, const char *src, size_t count) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 222) + return strscpy_pad(dest, src, count); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + ssize_t res = strscpy(dest, src, count); + if (res >= 0 && (size_t)res < count) { + memset(dest + res, 0, count - res); + } + return res; +#else + if (count == 0) + return -E2BIG; + + strncpy(dest, src, count); + dest[count - 1] = '\0'; + return strlen(dest); +#endif +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) +#define ksu_access_ok(addr, size) access_ok(addr, size) +#else +#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size) +#endif + +#ifndef KSU_OPTIONAL_STRNCPY +extern long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, + long count); +#endif // #ifndef KSU_OPTIONAL_STRNCPY + +// Linux >= 5.7 +// task_work_add (struct, struct, enum) +// Linux pre-5.7 +// task_work_add (struct, struct, bool) +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) +#ifndef TWA_RESUME +#define TWA_RESUME true +#endif +#endif + +#endif // #ifndef __KSU_H_KERNEL_COMPAT diff --git a/KernelSU-Next/kernel/core/init.c b/KernelSU-Next/kernel/core/init.c new file mode 100644 index 000000000000..f1b63c393f3c --- /dev/null +++ b/KernelSU-Next/kernel/core/init.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "policy/allowlist.h" +#include "policy/app_profile.h" +#include "policy/feature.h" +#include "klog.h" // IWYU pragma: keep +#include "manager/manager_observer.h" +#include "manager/throne_tracker.h" +#include "hook/hook_manager.h" +#include "runtime/ksud.h" +#include "runtime/ksud_boot.h" +#include "supercall/supercall.h" +#include "ksu.h" +#include "infra/file_wrapper.h" +#ifdef CONFIG_KSU_SUSFS +#include +#endif // #ifdef CONFIG_KSU_SUSFS +#include "selinux/selinux.h" + +extern void __init ksu_lsm_hook_init(void); +extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, + void *argv, void *envp, int *flags); +extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, + void *argv, void *envp, int *flags); +int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, + void *envp, int *flags) +{ + ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags); + return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, + flags); +} + +// workaround for A12-5.10 kernel +// Some third-party kernel (e.g. linegaeOS) uses wrong toolchain, which supports +// CC_HAVE_STACKPROTECTOR_SYSREG while gki's toolchain doesn't. +// Therefore, ksu lkm, which uses gki toolchain, requires this __stack_chk_guard, +// while those third-party kernel can't provide. +// Thus, we manually provide it instead of using kernel's +#if defined(CONFIG_STACKPROTECTOR) && \ + (defined(CONFIG_ARM64) && defined(MODULE) && \ + !defined(CONFIG_STACKPROTECTOR_PER_TASK)) +#include +#include +unsigned long __stack_chk_guard __ro_after_init + __attribute__((visibility("hidden"))); + +__attribute__((no_stack_protector)) void __init ksu_setup_stack_chk_guard() +{ + unsigned long canary; + + /* Try to get a semi random initial value. */ + get_random_bytes(&canary, sizeof(canary)); + canary ^= LINUX_VERSION_CODE; + canary &= CANARY_MASK; + __stack_chk_guard = canary; +} + +__attribute__((naked)) int __init kernelsu_init_early(void) +{ + asm("mov x19, x30;\n" + "bl ksu_setup_stack_chk_guard;\n" + "mov x30, x19;\n" + "b kernelsu_init;\n"); +} +#define NEED_OWN_STACKPROTECTOR 1 +#else +#define NEED_OWN_STACKPROTECTOR 0 +#endif + +struct cred *ksu_cred; +bool ksu_late_loaded; + +int __init kernelsu_init(void) +{ +#ifdef MODULE + ksu_late_loaded = (current->pid != 1); +#else + ksu_late_loaded = false; +#endif + +#ifdef CONFIG_KSU_DEBUG + pr_alert("*************************************************************"); + pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); + pr_alert("** **"); + pr_alert("** You are running KernelSU in DEBUG mode **"); + pr_alert("** **"); + pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); + pr_alert("*************************************************************"); +#endif + + ksu_cred = prepare_creds(); + if (!ksu_cred) { + pr_err("prepare cred failed!\n"); + } + + ksu_feature_init(); + + ksu_supercalls_init(); + + + + if (ksu_late_loaded) { + pr_info("late load mode, skipping kprobe hooks\n"); + + apply_kernelsu_rules(); + cache_sid(); + setup_ksu_cred(); + + // Grant current process (ksud late-load) root + // with KSU SELinux domain before enforcing SELinux, so it + // can continue to access /data/app etc. after enforcement. + escape_to_root_for_init(); + + ksu_allowlist_init(); + ksu_load_allow_list(); + + ksu_syscall_hook_manager_init(); + + ksu_throne_tracker_init(); + ksu_observer_init(); + ksu_file_wrapper_init(); + + ksu_boot_completed = true; + track_throne(false); + + if (!getenforce()) { + pr_info("Permissive SELinux, enforcing\n"); + setenforce(true); + } + + } else { + ksu_syscall_hook_manager_init(); + + ksu_lsm_hook_init(); + + ksu_allowlist_init(); + + ksu_throne_tracker_init(); + +#ifdef CONFIG_KSU_SUSFS + susfs_init(); +#endif // #ifdef CONFIG_KSU_SUSFS + + ksu_ksud_init(); + + ksu_file_wrapper_init(); + } + +#ifdef MODULE +#ifndef CONFIG_KSU_DEBUG + kobject_del(&THIS_MODULE->mkobj.kobj); +#endif +#endif + return 0; +} + +void __exit kernelsu_exit(void) +{ + // Phase 1: Stop all hooks first to prevent new callbacks + ksu_syscall_hook_manager_exit(); + + ksu_supercalls_exit(); + + if (!ksu_late_loaded) + ksu_ksud_exit(); + + // Wait for any in-flight RCU readers (e.g. handler traversing allow_list) + synchronize_rcu(); + + // Phase 2: Now safe to release data structures + ksu_observer_exit(); + + ksu_throne_tracker_exit(); + + ksu_allowlist_exit(); + + ksu_feature_exit(); + + if (ksu_cred) { + put_cred(ksu_cred); + } +} + +#if NEED_OWN_STACKPROTECTOR +module_init(kernelsu_init_early); +#else +module_init(kernelsu_init); +#endif +module_exit(kernelsu_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("weishu"); +MODULE_DESCRIPTION("Android KernelSU"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) +MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver"); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif diff --git a/KernelSU-Next/kernel/extras.c b/KernelSU-Next/kernel/extras.c new file mode 100644 index 000000000000..0e8d55a6d2fd --- /dev/null +++ b/KernelSU-Next/kernel/extras.c @@ -0,0 +1,213 @@ +#include +#include +#include + +#include "policy/feature.h" +#include "uapi/feature.h" +#include "klog.h" +#include "runtime/ksud.h" +#include "infra/seccomp_cache.h" + +// sorry for the ifdef hell +// but im too lazy to fragment this out. +// theres only one feature so far anyway +// - xx, 20251019 + +static u32 su_sid = 0; +static u32 priv_app_sid = 0; + +// init as disabled by default +static atomic_t disable_spoof = ATOMIC_INIT(1); + +void ksu_avc_spoof_enable(); +void ksu_avc_spoof_disable(); + +static bool ksu_avc_spoof_enabled = true; +static bool boot_completed = false; + +static int avc_spoof_feature_get(u64 *value) +{ + *value = ksu_avc_spoof_enabled ? 1 : 0; + return 0; +} + +static int avc_spoof_feature_set(u64 value) +{ + bool enable = value != 0; + + if (enable == ksu_avc_spoof_enabled) { + pr_info("avc_spoof: no need to change\n"); + return 0; + } + + ksu_avc_spoof_enabled = enable; + + if (boot_completed) { + if (enable) { + ksu_avc_spoof_enable(); + } else { + ksu_avc_spoof_disable(); + } + } + + pr_info("avc_spoof: set to %d\n", enable); + + return 0; +} + +static const struct ksu_feature_handler avc_spoof_handler = { + .feature_id = KSU_FEATURE_AVC_SPOOF, + .name = "avc_spoof", + .get_handler = avc_spoof_feature_get, + .set_handler = avc_spoof_feature_set, +}; + +static int get_sid() +{ + // dont load at all if we cant get sids + int err = security_secctx_to_secid("u:r:su:s0", strlen("u:r:su:s0"), &su_sid); + if (err) { + pr_info("avc_spoof/get_sid: su_sid not found!\n"); + return -1; + } + pr_info("avc_spoof/get_sid: su_sid: %u\n", su_sid); + + err = security_secctx_to_secid("u:r:priv_app:s0:c512,c768", strlen("u:r:priv_app:s0:c512,c768"), &priv_app_sid); + if (err) { + pr_info("avc_spoof/get_sid: priv_app_sid not found!\n"); + return -1; + } + pr_info("avc_spoof/get_sid: priv_app_sid: %u\n", priv_app_sid); + return 0; +} + +int ksu_handle_slow_avc_audit(u32 *tsid) +{ + if (atomic_read(&disable_spoof)) + return 0; + + // if tsid is su, we just replace it + // unsure if its enough, but this is how it is aye? + if (*tsid == su_sid) { + pr_info("avc_spoof/slow_avc_audit: replacing su_sid: %u with priv_app_sid: %u\n", su_sid, priv_app_sid); + *tsid = priv_app_sid; + } + + return 0; +} + +#ifdef KSU_KPROBES_HOOK +#include +#include +#include "arch.h" +static struct kprobe *slow_avc_audit_kp; +// .symbol_name = "slow_avc_audit", +// .pre_handler = slow_avc_audit_pre_handler, +static int slow_avc_audit_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + if (atomic_read(&disable_spoof)) + return 0; + + /* + * for < 4.17 int slow_avc_audit(u32 ssid, u32 tsid + * for >= 4.17 int slow_avc_audit(struct selinux_state *state, u32 ssid, u32 tsid + * for >= 6.4 int slow_avc_audit(u32 ssid, u32 tsid + * not to mention theres also DKSU_HAS_SELINUX_STATE + * since its hard to make sure this selinux state thing + * cross crossing with 4.17 ~ 6.4's where slow_avc_audit + * changes abi (tsid in arg2 vs arg3) + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) + u32 *tsid = (u32 *)&PT_REGS_PARM2(regs); + ksu_handle_slow_avc_audit(tsid); +#else + u32 *tsid = (u32 *)&PT_REGS_PARM3(regs); + ksu_handle_slow_avc_audit(tsid); +#endif + + return 0; +} + +// copied from upstream +static struct kprobe *init_kprobe(const char *name, + kprobe_pre_handler_t handler) +{ + struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL); + if (!kp) + return NULL; + kp->symbol_name = name; + kp->pre_handler = handler; + + int ret = register_kprobe(kp); + pr_info("sucompat: register_%s kprobe: %d\n", name, ret); + if (ret) { + kfree(kp); + return NULL; + } + + return kp; +} +static void destroy_kprobe(struct kprobe **kp_ptr) +{ + struct kprobe *kp = *kp_ptr; + if (!kp) + return; + unregister_kprobe(kp); + synchronize_rcu(); + kfree(kp); + *kp_ptr = NULL; +} +#endif // KSU_KPROBES_HOOK + +void ksu_avc_spoof_disable(void) +{ +#ifdef KSU_KPROBES_HOOK + pr_info("avc_spoof/exit: unregister slow_avc_audit kprobe!\n"); + destroy_kprobe(&slow_avc_audit_kp); +#endif + atomic_set(&disable_spoof, 1); + pr_info("avc_spoof/exit: slow_avc_audit spoofing disabled!\n"); +} + +void ksu_avc_spoof_enable(void) +{ + int ret = get_sid(); + if (ret) { + pr_info("avc_spoof/init: sid grab fail!\n"); + return; + } + +#ifdef KSU_KPROBES_HOOK + pr_info("avc_spoof/init: register slow_avc_audit kprobe!\n"); + slow_avc_audit_kp = init_kprobe("slow_avc_audit", slow_avc_audit_pre_handler); +#endif + // once we get the sids, we can now enable the hook handler + atomic_set(&disable_spoof, 0); + + pr_info("avc_spoof/init: slow_avc_audit spoofing enabled!\n"); +} + +void ksu_avc_spoof_late_init(void) +{ + boot_completed = true; + + if (ksu_avc_spoof_enabled) { + ksu_avc_spoof_enable(); + } +} + +void __init ksu_avc_spoof_init(void) +{ + if (ksu_register_feature_handler(&avc_spoof_handler)) { + pr_err("Failed to register avc spoof feature handler\n"); + } +} + +void __exit ksu_avc_spoof_exit(void) +{ + if (ksu_avc_spoof_enabled) { + ksu_avc_spoof_disable(); + } + ksu_unregister_feature_handler(KSU_FEATURE_AVC_SPOOF); +} diff --git a/KernelSU-Next/kernel/feature/kernel_umount.c b/KernelSU-Next/kernel/feature/kernel_umount.c new file mode 100644 index 000000000000..7037efdf19dc --- /dev/null +++ b/KernelSU-Next/kernel/feature/kernel_umount.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef KSU_HAS_PATH_UMOUNT +#include +#endif + +#include "kernel_umount.h" +#include "klog.h" // IWYU pragma: keep +#include "policy/allowlist.h" +#include "selinux/selinux.h" +#include "policy/feature.h" +#include "runtime/ksud_boot.h" +#include "ksu.h" +#include "compat/kernel_compat.h" + +#ifndef CONFIG_KSU_SUSFS +static bool ksu_kernel_umount_enabled = true; +#else +bool ksu_kernel_umount_enabled = true; +#endif // #ifndef CONFIG_KSU_SUSFS + +static int kernel_umount_feature_get(u64 *value) +{ + *value = ksu_kernel_umount_enabled ? 1 : 0; + return 0; +} + +static int kernel_umount_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_kernel_umount_enabled = enable; + pr_info("kernel_umount: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler kernel_umount_handler = { + .feature_id = KSU_FEATURE_KERNEL_UMOUNT, + .name = "kernel_umount", + .get_handler = kernel_umount_feature_get, + .set_handler = kernel_umount_feature_set, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ + defined(KSU_HAS_PATH_UMOUNT) +extern int path_umount(struct path *path, int flags); +static void ksu_umount_mnt(const char *mnt, struct path *path, int flags) +{ + int err = path_umount(path, flags); + if (err) { + pr_info("umount %s failed: %d\n", mnt, err); + } +} +#else +static void ksu_sys_umount(const char *mnt, int flags) +{ + char __user *usermnt = (char __user *)mnt; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ksys_umount(usermnt, flags); +#else + sys_umount(usermnt, flags); // cuz asmlinkage long sys##name +#endif + set_fs(old_fs); +} + +#define ksu_umount_mnt(mnt, __unused, flags) \ + ({ \ + path_put(__unused); \ + ksu_sys_umount(mnt, flags); \ + }) + +#endif + +#if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) +static void try_umount(const char *mnt, int flags) +#else +void try_umount(const char *mnt, int flags) +#endif +{ + struct path path; + int err = kern_path(mnt, 0, &path); + if (err) { + return; + } + + if (path.dentry != path.mnt->mnt_root) { + // it is not root mountpoint, maybe umounted by others already. + path_put(&path); + return; + } + ksu_umount_mnt(mnt, &path, flags); +} + +struct umount_tw { + struct callback_head cb; +}; + +#if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) +static void umount_tw_func(struct callback_head *cb) +{ + struct umount_tw *tw = container_of(cb, struct umount_tw, cb); + const struct cred *saved = override_creds(ksu_cred); + + struct mount_entry *entry; + down_read(&mount_list_lock); + list_for_each_entry(entry, &mount_list, list) { + pr_info("%s: unmounting: %s flags: 0x%x\n", __func__, entry->umountable, entry->flags); + try_umount(entry->umountable, entry->flags); + } + up_read(&mount_list_lock); + + revert_creds(saved); + + kfree(tw); +} + +int ksu_handle_umount(uid_t old_uid, uid_t new_uid) +{ + struct umount_tw *tw; +#if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) + // if there isn't any module mounted, just ignore it! + if (!ksu_module_mounted) { + return 0; + } + + if (!ksu_kernel_umount_enabled) { + return 0; + } + + if (!ksu_cred) { + return 0; + } + + // There are 6 scenarios: + // 1. Normal app: zygote -> appuid + // 2. Isolated process forked from zygote: zygote -> isolated_process + // 3. App zygote forked from zygote: zygote -> appuid + // 4. Webview zygote forked from zygote: zygote -> WEBVIEW_ZYGOTE_UID (no need to handle, app cannot run custom code) + // 5. Isolated process forked from app zygote: appuid -> isolated_process (already handled by 3) + // 6. Isolated process forked from webview zygote (no need to handle, app cannot run custom code) + if (!is_appuid(new_uid) && !is_isolated_process(new_uid)) { + return 0; + } + + if (!ksu_uid_should_umount(new_uid) && !is_isolated_process(new_uid)) { + return 0; + } + + // check old process's selinux context, if it is not zygote, ignore it! + // because some su apps may setuid to untrusted_app but they are in global mount namespace + // when we umount for such process, that is a disaster! + // also handle case 4 and 5 + bool is_zygote_child = is_zygote(current_cred()); + if (!is_zygote_child) { + pr_info("handle umount ignore non zygote child: %d\n", + current->pid); + return 0; + } +#endif // #if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) + // umount the target mnt + pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid); + + tw = kzalloc(sizeof(*tw), GFP_ATOMIC); + if (!tw) + return 0; + + tw->cb.func = umount_tw_func; + + int err = task_work_add(current, &tw->cb, TWA_RESUME); + if (err) { + kfree(tw); + pr_warn("unmount add task_work failed\n"); + } + + return 0; +} +#endif // #if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) + +void __init ksu_kernel_umount_init(void) +{ + if (ksu_register_feature_handler(&kernel_umount_handler)) { + pr_err("Failed to register kernel_umount feature handler\n"); + } +} + +void __exit ksu_kernel_umount_exit(void) +{ + ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); +} diff --git a/KernelSU-Next/kernel/feature/kernel_umount.h b/KernelSU-Next/kernel/feature/kernel_umount.h new file mode 100644 index 000000000000..623976d99e78 --- /dev/null +++ b/KernelSU-Next/kernel/feature/kernel_umount.h @@ -0,0 +1,25 @@ +#ifndef __KSU_H_KERNEL_UMOUNT +#define __KSU_H_KERNEL_UMOUNT + +#include +#include +#include + +void ksu_kernel_umount_init(void); +void ksu_kernel_umount_exit(void); + +#if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) + // Handler function to be called from setresuid hook + int ksu_handle_umount(uid_t old_uid, uid_t new_uid); +#endif + +// for the umount list +struct mount_entry { + char *umountable; + unsigned int flags; + struct list_head list; +}; +extern struct list_head mount_list; +extern struct rw_semaphore mount_list_lock; + +#endif diff --git a/KernelSU-Next/kernel/feature/sucompat.c b/KernelSU-Next/kernel/feature/sucompat.c new file mode 100644 index 000000000000..ef079a27b9d8 --- /dev/null +++ b/KernelSU-Next/kernel/feature/sucompat.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#include +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif +#include + +#include "objsec.h" + +#include "policy/allowlist.h" +#include "policy/feature.h" +#include "klog.h" // IWYU pragma: keep +#include "runtime/ksud.h" +#include "compat/kernel_compat.h" +#include "sucompat.h" +#include "policy/app_profile.h" +#include "selinux/selinux.h" +#include "tiny_sulog.h" + +#define SU_PATH "/system/bin/su" +#define SH_PATH "/system/bin/sh" + +bool ksu_su_compat_enabled __read_mostly = true; + +static int su_compat_feature_get(u64 *value) +{ + *value = ksu_su_compat_enabled ? 1 : 0; + return 0; +} + +static int su_compat_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_su_compat_enabled = enable; + pr_info("su_compat: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler su_compat_handler = { + .feature_id = KSU_FEATURE_SU_COMPAT, + .name = "su_compat", + .get_handler = su_compat_feature_get, + .set_handler = su_compat_feature_set, +}; + +static void __user *userspace_stack_buffer(const void *d, size_t len) +{ + // Stack Pointer must be 16-byte aligned. + // We also subtract a safe margin (256 bytes) + // to avoid corrupting local variables or smth + unsigned long sp = current_user_stack_pointer(); + sp = (sp - len - 256) & ~0xFUL; // Align downwards to nearest 16 bytes + + char __user *p = (char __user *)sp; + + return copy_to_user(p, d, len) ? NULL : p; +} + +static char __user *sh_user_path(void) +{ + static const char sh_path[] = "/system/bin/sh"; + + return userspace_stack_buffer(sh_path, sizeof(sh_path)); +} + +static char __user *ksud_user_path(void) +{ + static const char ksud_path[] = KSUD_PATH; + + return userspace_stack_buffer(ksud_path, sizeof(ksud_path)); +} + +int ksu_handle_faccessat(int *dfd, const char __user **filename_user, + int *mode, int *__unused_flags) +{ + const char su[] = SU_PATH; + + if (!ksu_is_allow_uid_for_current(current_uid().val)) { + return 0; + } + + char path[sizeof(su) + 1]; + memset(path, 0, sizeof(path)); + strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (unlikely(!memcmp(path, su, sizeof(su)))) { + write_sulog('a'); + pr_info("faccessat su->sh!\n"); + *filename_user = sh_user_path(); + } + + return 0; +} + +int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) +{ + // const char sh[] = SH_PATH; + const char su[] = SU_PATH; + + if (!ksu_is_allow_uid_for_current(current_uid().val)) { + return 0; + } + + if (unlikely(!filename_user)) { + return 0; + } + + char path[sizeof(su) + 1]; + memset(path, 0, sizeof(path)); + strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (unlikely(!memcmp(path, su, sizeof(su)))) { + write_sulog('s'); + pr_info("newfstatat su->sh!\n"); + *filename_user = sh_user_path(); + } + + return 0; +} + +long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr, const struct pt_regs *regs) +{ + const char su[] = SU_PATH; + const char __user *fn; + char path[sizeof(su) + 1]; + long ret; + unsigned long addr; + + if (unlikely(!filename_user)) + goto do_orig_execve; + + if (!ksu_is_allow_uid_for_current(current_uid().val)) + goto do_orig_execve; + + addr = untagged_addr((unsigned long)*filename_user); + fn = (const char __user *)addr; + memset(path, 0, sizeof(path)); + + ret = strncpy_from_user_nofault(path, fn, sizeof(path)); + if (ret < 0 && preempt_count()) { + preempt_enable_no_resched_notrace(); + ret = strncpy_from_user(path, fn, sizeof(path)); + preempt_disable_notrace(); + } + + if (ret < 0) { + goto do_orig_execve; + } + + if (likely(memcmp(path, su, sizeof(su)))) + goto do_orig_execve; + + write_sulog('x'); + + pr_info("sys_execve su found\n"); + *filename_user = ksud_user_path(); + + ret = escape_with_root_profile(); + if (ret) { + pr_err("escape_with_root_profile failed: %ld\n", ret); + goto do_orig_execve; + } + if (preempt_count() > 0) { + *filename_user = ksud_user_path(); + } else { + struct file *f = ksu_filp_open_compat(KSUD_PATH, O_RDONLY, 0); + if (IS_ERR(f)) { + pr_warn("ksud inaccesible, aplicando fallback a sh\n"); + *filename_user = sh_user_path(); + } else { + filp_close(f, NULL); + *filename_user = ksud_user_path(); + } + } +do_orig_execve: + return 0; +} + +int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags) +{ + struct filename *filename; + const char su[] = SU_PATH; + static const char ksud_path[] = KSUD_PATH; + + if (unlikely(!filename_ptr)) + return 0; + + if (!ksu_is_allow_uid_for_current(current_uid().val)) + return 0; + + filename = *filename_ptr; + if (IS_ERR(filename)) + return 0; + + if (likely(memcmp(filename->name, su, sizeof(su)))) + return 0; + + pr_info("do_execveat_common su found\n"); + memcpy((void *)filename->name, ksud_path, sizeof(ksud_path)); + + escape_with_root_profile(); + + return 0; +} + +int __ksu_handle_devpts(struct inode *inode) +{ +#ifndef KSU_KPROBES_HOOK + if (!ksu_su_compat_enabled) + return 0; +#endif + + if (!current->mm) { + return 0; + } + + uid_t uid = current_uid().val; + if (uid % 100000 < 10000) { + // not untrusted_app, ignore it + return 0; + } + + if (likely(!ksu_is_allow_uid(uid))) + return 0; + + struct inode_security_struct *sec = selinux_inode(inode); + + if (ksu_file_sid && sec) + sec->sid = ksu_file_sid; + return 0; +} + +// dead code: devpts handling +int __maybe_unused ksu_handle_devpts(struct inode *inode) +{ + return __ksu_handle_devpts(inode); +} + +// sucompat: permitted process can execute 'su' to gain root access. +void __init ksu_sucompat_init() +{ + if (ksu_register_feature_handler(&su_compat_handler)) { + pr_err("Failed to register su_compat feature handler\n"); + } +} + +void __exit ksu_sucompat_exit() +{ + ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT); +} diff --git a/KernelSU-Next/kernel/feature/sucompat.h b/KernelSU-Next/kernel/feature/sucompat.h new file mode 100644 index 000000000000..642de191a1c9 --- /dev/null +++ b/KernelSU-Next/kernel/feature/sucompat.h @@ -0,0 +1,16 @@ +#ifndef __KSU_H_SUCOMPAT +#define __KSU_H_SUCOMPAT +#include + +extern bool ksu_su_compat_enabled; + +void ksu_sucompat_init(void); +void ksu_sucompat_exit(void); + +// Handler functions exported for hook_manager +int ksu_handle_faccessat(int *dfd, const char __user **filename_user, + int *mode, int *__unused_flags); +int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); +long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr, const struct pt_regs *regs); + +#endif \ No newline at end of file diff --git a/KernelSU-Next/kernel/hook/hook_manager.c b/KernelSU-Next/kernel/hook/hook_manager.c new file mode 100644 index 000000000000..21f42d0c08db --- /dev/null +++ b/KernelSU-Next/kernel/hook/hook_manager.c @@ -0,0 +1,429 @@ +#ifdef KSU_KPROBES_HOOK +#include "linux/printk.h" +#include +#include +#include +#include +#include +#include +#include + +#include "policy/allowlist.h" +#include "arch.h" +#include "klog.h" // IWYU pragma: keep +#include "hook_manager.h" +#include "feature/sucompat.h" +#include "setuid_hook.h" +#include "selinux/selinux.h" +#include "compat/kernel_compat.h" +#include "runtime/ksud.h" + +// Tracepoint registration count management +// == 1: just us +// > 1: someone else is also using syscall tracepoint e.g. ftrace +static int tracepoint_reg_count = 0; +static DEFINE_SPINLOCK(tracepoint_reg_lock); + +void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t) +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count <= 1) { + ksu_clear_task_tracepoint_flag(t); + } + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); +} + +// Process marking management +static void handle_process_mark(bool mark) +{ + struct task_struct *p, *t; + read_lock(&tasklist_lock); + for_each_process_thread(p, t) { + if (mark) + ksu_set_task_tracepoint_flag(t); + else + ksu_clear_task_tracepoint_flag(t); + } + read_unlock(&tasklist_lock); +} + +void ksu_mark_all_process(void) +{ + handle_process_mark(true); + pr_info("hook_manager: mark all user process done!\n"); +} + +void ksu_unmark_all_process(void) +{ + handle_process_mark(false); + pr_info("hook_manager: unmark all user process done!\n"); +} + +static void ksu_mark_running_process_locked() +{ + struct task_struct *p, *t; + read_lock(&tasklist_lock); + for_each_process_thread (p, t) { + if (t->pid != 1 && !t->mm) { + // skip kernel threads, but always allow pid 1 + continue; + } + int uid = task_uid(t).val; + const struct cred *cred = get_task_cred(t); + bool ksu_root_process = + uid == 0 && is_task_ksu_domain(cred); + bool is_zygote_process = is_zygote(cred); + bool is_shell = uid == 2000; + // before boot completed, we shall mark init for marking zygote + bool is_init = t->pid == 1; + if (ksu_root_process || is_zygote_process || is_shell || is_init + || ksu_is_allow_uid(uid)) { + ksu_set_task_tracepoint_flag(t); + pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n", + t->pid, uid, t->comm); + } else { + ksu_clear_task_tracepoint_flag(t); + pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n", + t->pid, uid, t->comm); + } + put_cred(cred); + } + read_unlock(&tasklist_lock); +} + +void ksu_mark_running_process() +{ + unsigned long flags; + bool should_mark = false; + + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count <= 1) { + should_mark = true; + } else { + pr_info("hook_manager: not mark running process since syscall tracepoint is in use\n"); + } + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + + // Call this outside of tracepoint_reg_lock + if (should_mark) { + ksu_mark_running_process_locked(); + } +} + +// Get task mark status +// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found +int ksu_get_task_mark(pid_t pid) +{ + struct task_struct *task; + int marked = -ESRCH; + + rcu_read_lock(); + task = find_task_by_vpid(pid); + if (task) { + get_task_struct(task); + rcu_read_unlock(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 : 0; +#else + marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ? 1 : 0; +#endif + put_task_struct(task); + } else { + rcu_read_unlock(); + } + + return marked; +} + +// Set task mark status +// Returns: 0 on success, -ESRCH if task not found +int ksu_set_task_mark(pid_t pid, bool mark) +{ + struct task_struct *task; + int ret = -ESRCH; + + rcu_read_lock(); + task = find_task_by_vpid(pid); + if (task) { + get_task_struct(task); + rcu_read_unlock(); + if (mark) { + ksu_set_task_tracepoint_flag(task); + pr_info("hook_manager: marked task pid=%d comm=%s\n", pid, task->comm); + } else { + ksu_clear_task_tracepoint_flag(task); + pr_info("hook_manager: unmarked task pid=%d comm=%s\n", pid, task->comm); + } + put_task_struct(task); + ret = 0; + } else { + rcu_read_unlock(); + } + + return ret; +} + +#ifdef CONFIG_KRETPROBES + +static struct kretprobe *init_kretprobe(const char *name, + kretprobe_handler_t handler) +{ + struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL); + if (!rp) + return NULL; + rp->kp.symbol_name = name; + rp->handler = handler; + rp->data_size = 0; + rp->maxactive = 0; + + int ret = register_kretprobe(rp); + pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret); + if (ret) { + kfree(rp); + return NULL; + } + + return rp; +} + +static void destroy_kretprobe(struct kretprobe **rp_ptr) +{ + struct kretprobe *rp = *rp_ptr; + if (!rp) + return; + unregister_kretprobe(rp); + synchronize_rcu(); + kfree(rp); + *rp_ptr = NULL; +} + +static int syscall_regfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + unsigned long flags; + int count; + + spin_lock_irqsave(&tracepoint_reg_lock, flags); + count = tracepoint_reg_count; + tracepoint_reg_count++; + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + + // Execute marking logic outside the spinlock + if (count < 1) { + ksu_mark_running_process_locked(); + } else if (count == 1) { + ksu_mark_all_process(); + } + return 0; +} + +static int syscall_unregfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + unsigned long flags; + int count; + + spin_lock_irqsave(&tracepoint_reg_lock, flags); + tracepoint_reg_count--; + count = tracepoint_reg_count; + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + + // Execute marking logic outside the spinlock + if (count <= 0) { + ksu_unmark_all_process(); + } else if (count == 1) { + ksu_mark_running_process_locked(); + } + return 0; +} + +static struct kretprobe *syscall_regfunc_rp = NULL; +static struct kretprobe *syscall_unregfunc_rp = NULL; +#endif + +static inline bool check_syscall_fastpath(int nr) +{ + switch (nr) { + case __NR_newfstatat: +#ifdef __NR_fstatat64 + case __NR_fstatat64: +#endif + case __NR_faccessat: + case __NR_execve: +#ifdef __NR_execveat + case __NR_execveat: +#endif + case __NR_setresuid: + return true; + default: + return false; + } +} + +// Unmark init's child that are not zygote, adbd or ksud +int ksu_handle_init_mark_tracker(const char __user **filename_user) +{ + char path[64]; + unsigned long addr; + const char __user *fn; + long ret; + + if (unlikely(!filename_user)) + return 0; + + addr = untagged_addr((unsigned long)*filename_user); + fn = (const char __user *)addr; + + memset(path, 0, sizeof(path)); + + // Safe no-fault reading, no try_set_access_flag hacks! + ret = strncpy_from_user_nofault(path, fn, sizeof(path)); + if (ret < 0 && preempt_count()) { + preempt_enable_no_resched_notrace(); + ret = strncpy_from_user(path, fn, sizeof(path)); + preempt_disable_notrace(); + } + + if (ret < 0) { + // unreadable path; keep mark to avoid wrongly unmarking zygote + return 0; + } + + if (unlikely(strcmp(path, KSUD_PATH) == 0)) { + pr_info("hook_manager: escape to root for init executing ksud: %d\n", current->pid); + escape_to_root_for_init(); + } else if (likely(strstr(path, "/app_process") == NULL && strstr(path, "/adbd") == NULL)) { + pr_info("hook_manager: unmark %d exec %s\n", current->pid, path); + ksu_clear_task_tracepoint_flag_if_needed(current); + } + + return 0; +} + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS +// Generic sys_enter handler that dispatches to specific handlers +static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id) +{ + if (unlikely(check_syscall_fastpath(id))) { + if (ksu_su_compat_enabled) { + // Handle newfstatat (y compatibilidad con arquitecturas híbridas) +#ifdef __NR_fstatat64 + if (id == __NR_newfstatat || id == __NR_fstatat64) { +#else + if (id == __NR_newfstatat) { +#endif + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM2(regs); + int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs); + ksu_handle_stat(dfd, filename_user, flags); + return; + } + + // Handle faccessat + if (id == __NR_faccessat) { + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM2(regs); + int *mode = (int *)&PT_REGS_PARM3(regs); + ksu_handle_faccessat(dfd, filename_user, mode, NULL); + return; + } + + // Handle execve (y compatibilidad con arquitecturas híbridas) +#ifdef __NR_execveat + if (id == __NR_execve || id == __NR_execveat) { +#else + if (id == __NR_execve) { +#endif + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM1(regs); + if (current->pid != 1 && is_init(current_cred())) { + ksu_handle_init_mark_tracker(filename_user); + } else { + ksu_handle_execve_sucompat(filename_user, NULL, NULL); + } + return; + } + } + + // Handle setresuid + if (id == __NR_setresuid) { + uid_t ruid = (uid_t)PT_REGS_PARM1(regs); + uid_t euid = (uid_t)PT_REGS_PARM2(regs); + uid_t suid = (uid_t)PT_REGS_PARM3(regs); + ksu_handle_setresuid(ruid, euid, suid); + return; + } + } +} +#endif + +void __init ksu_syscall_hook_manager_init(void) +{ + int ret; + pr_info("hook_manager: ksu_hook_manager_init called\n"); + +#ifdef CONFIG_KRETPROBES + // Register kretprobe for syscall_regfunc + syscall_regfunc_rp = init_kretprobe("syscall_regfunc", syscall_regfunc_handler); + // Register kretprobe for syscall_unregfunc + syscall_unregfunc_rp = init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler); +#endif + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL); +#ifndef CONFIG_KRETPROBES + ksu_mark_running_process_locked(); +#endif + if (ret) { + pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n", ret); + } else { + pr_info("hook_manager: sys_enter tracepoint registered\n"); + } +#endif + + ksu_setuid_hook_init(); + ksu_sucompat_init(); + ksu_avc_spoof_init(); +} + +void __exit ksu_syscall_hook_manager_exit(void) +{ + pr_info("hook_manager: ksu_hook_manager_exit called\n"); +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + unregister_trace_sys_enter(ksu_sys_enter_handler, NULL); + tracepoint_synchronize_unregister(); + pr_info("hook_manager: sys_enter tracepoint unregistered\n"); +#endif + +#ifdef CONFIG_KRETPROBES + destroy_kretprobe(&syscall_regfunc_rp); + destroy_kretprobe(&syscall_unregfunc_rp); +#endif + + ksu_sucompat_exit(); + ksu_setuid_hook_exit(); + ksu_avc_spoof_exit(); +} +#else +#include "klog.h" // IWYU pragma: keep +#include "hook_manager.h" +#include "feature/sucompat.h" +#include "setuid_hook.h" + +void __init ksu_syscall_hook_manager_init(void) +{ + pr_info("hook_manager: initializing..\n"); + ksu_setuid_hook_init(); + ksu_sucompat_init(); + ksu_avc_spoof_init(); +} + +void __exit ksu_syscall_hook_manager_exit(void) +{ + pr_info("hook_manager: exiting..\n"); + ksu_sucompat_exit(); + ksu_setuid_hook_exit(); + ksu_avc_spoof_exit(); +} +#endif \ No newline at end of file diff --git a/KernelSU-Next/kernel/hook/hook_manager.h b/KernelSU-Next/kernel/hook/hook_manager.h new file mode 100644 index 000000000000..14c8de16f2b7 --- /dev/null +++ b/KernelSU-Next/kernel/hook/hook_manager.h @@ -0,0 +1,48 @@ +#ifndef __KSU_H_HOOK_MANAGER +#define __KSU_H_HOOK_MANAGER + +#include +#include +#include + +// Hook manager initialization and cleanup +void ksu_syscall_hook_manager_init(void); +void ksu_syscall_hook_manager_exit(void); + +// extras.c +void ksu_avc_spoof_init(void); +void ksu_avc_spoof_exit(void); + +#ifdef KSU_KPROBES_HOOK +// Process marking for tracepoint +void ksu_mark_all_process(void); +void ksu_unmark_all_process(void); +void ksu_mark_running_process(void); + +// Per-task mark operations +int ksu_get_task_mark(pid_t pid); +int ksu_set_task_mark(pid_t pid, bool mark); + +static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + set_task_syscall_work(t, SYSCALL_TRACEPOINT); +#else + set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); +#endif +} + +static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + clear_task_syscall_work(t, SYSCALL_TRACEPOINT); +#else + clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); +#endif +} + +void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t); + +#endif + +#endif \ No newline at end of file diff --git a/KernelSU-Next/kernel/hook/lsm_hooks.c b/KernelSU-Next/kernel/hook/lsm_hooks.c new file mode 100644 index 000000000000..2d5fb8a6b2b4 --- /dev/null +++ b/KernelSU-Next/kernel/hook/lsm_hooks.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include + +#include "klog.h" // IWYU pragma: keep +#include "runtime/ksud_boot.h" +#include "compat/kernel_compat.h" +#include "setuid_hook.h" +#include "manager/throne_tracker.h" + +#ifndef KSU_KPROBES_HOOK + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) +struct key *init_session_keyring = NULL; + +static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, + unsigned perm) +{ + if (init_session_keyring != NULL) { + return 0; + } + if (strcmp(current->comm, "init")) { + // we are only interested in `init` process + return 0; + } + init_session_keyring = cred->session_keyring; + pr_info("kernel_compat: got init_session_keyring\n"); + return 0; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) +static int ksu_inode_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +static int ksu_inode_rename(struct user_namespace *mnt_userns, struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +#else +static int ksu_inode_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +#endif +{ + // skip kernel threads + if (!current->mm) { + return 0; + } + + // skip non system uid + if (current_uid().val != 1000) { + return 0; + } + + if (!old_dentry || !new_dentry) { + return 0; + } + + // Use d_name.name instead of the dangerous d_iname + // which can cause OOPS when the dentry is in an inconsistent state during rename + if (strcmp(new_dentry->d_name.name, "packages.list")) { + return 0; + } + + char path[128]; + char *buf = dentry_path_raw(new_dentry, path, sizeof(path)); + if (IS_ERR(buf)) { + pr_err("dentry_path_raw failed.\n"); + return 0; + } + + if (!strstr(buf, "/system/packages.list")) { + return 0; + } + + // Do not track anything until the system has fully booted. + // Parsing files during early boot from an LSM hook can causes VFS deadlocks + if (!ksu_boot_completed) { + return 0; + } + + pr_debug("renameat: %s -> %s, new path: %s\n", old_dentry->d_name.name, + new_dentry->d_name.name, buf); + + // Thread-safe execution using atomic operations to prevent race conditions + // if system_server threads execute this hook concurrently. + static atomic_t first_time = ATOMIC_INIT(1); + + // atomic_xchg swaps the value to 0 and returns the old value. + // If the old value was 1, we are the first thread to reach here. + if (atomic_xchg(&first_time, 0) == 1) { + track_throne(true); + } else { + track_throne(false); + } + + return 0; +} + +static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, + int flags) +{ + kuid_t new_uid = new->uid; + kuid_t new_euid = new->euid; + + return ksu_handle_setresuid((uid_t)new_uid.val, (uid_t)new_euid.val, + (uid_t)new_uid.val); +} + +#ifndef DEVPTS_SUPER_MAGIC +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#endif + +extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) +int ksu_inode_permission(struct mnt_idmap *idmap, struct inode *inode, int mask) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +int ksu_inode_permission(struct user_namespace *mnt_userns, struct inode *inode, int mask) +#else +int ksu_inode_permission(struct inode *inode, int mask) +#endif +{ + if (unlikely(inode && inode->i_sb && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) { + __ksu_handle_devpts(inode); + } + return 0; +} + +static struct security_hook_list ksu_hooks[] = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) + LSM_HOOK_INIT(key_permission, ksu_key_permission), +#endif + LSM_HOOK_INIT(inode_permission, ksu_inode_permission), + LSM_HOOK_INIT(inode_rename, ksu_inode_rename), + LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid) +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +static const struct lsm_id ksu_lsmid = { + .name = "ksu", + .id = 912, +}; +#endif + +void __init ksu_lsm_hook_init(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); +#else + // https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892 + security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks)); +#endif + pr_info("LSM hooks initialized.\n"); +} +#else +void __init ksu_lsm_hook_init(void) +{ + return; +} +#endif diff --git a/KernelSU-Next/kernel/hook/setuid_hook.c b/KernelSU-Next/kernel/hook/setuid_hook.c new file mode 100644 index 000000000000..f52882365dc9 --- /dev/null +++ b/KernelSU-Next/kernel/hook/setuid_hook.c @@ -0,0 +1,204 @@ +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "policy/allowlist.h" +#include "setuid_hook.h" +#include "klog.h" // IWYU pragma: keep +#include "manager/manager_identity.h" +#include "selinux/selinux.h" +#include "infra/seccomp_cache.h" +#include "supercall/supercall.h" +#include "hook_manager.h" +#include "feature/kernel_umount.h" +#include "compat/kernel_compat.h" +#ifdef CONFIG_KSU_SUSFS +#include +#include +#endif // #ifdef CONFIG_KSU_SUSFS + +extern void disable_seccomp(struct task_struct *tsk); + +#ifdef CONFIG_KSU_SUSFS +static inline bool is_zygote_isolated_service_uid(uid_t uid) +{ + uid %= 100000; + return (uid >= 99000 && uid < 100000); +} + +static inline bool is_zygote_normal_app_uid(uid_t uid) +{ + uid %= 100000; + return (uid >= 10000 && uid < 19999); +} + +extern u32 susfs_zygote_sid; +extern struct cred *ksu_cred; + +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +extern void susfs_run_sus_path_loop(void); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH + +struct susfs_handle_setuid_tw { + struct callback_head cb; +}; + +static void susfs_handle_setuid_tw_func(struct callback_head *cb) +{ + struct susfs_handle_setuid_tw *tw = container_of(cb, struct susfs_handle_setuid_tw, cb); + const struct cred *saved = override_creds(ksu_cred); + +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + susfs_run_sus_path_loop(); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH + + revert_creds(saved); + kfree(tw); +} + +static void ksu_handle_extra_susfs_work(void) +{ + struct susfs_handle_setuid_tw *tw = kzalloc(sizeof(*tw), GFP_ATOMIC); + + if (!tw) { + pr_err("susfs: No enough memory\n"); + return; + } + + tw->cb.func = susfs_handle_setuid_tw_func; + + int err = task_work_add(current, &tw->cb, TWA_RESUME); + if (err) { + kfree(tw); + pr_err("susfs: Failed adding task_work 'susfs_handle_setuid_tw', err: %d\n", err); + } +} +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +extern void susfs_try_umount(uid_t uid); +#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +#endif // #ifdef CONFIG_KSU_SUSFS + +static void ksu_install_manager_fd_tw_func(struct callback_head *cb) +{ + ksu_install_fd(); + kfree(cb); +} + +int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + // we rely on the fact that zygote always call setresuid(3) with same uids + uid_t new_uid = ruid; + uid_t old_uid = current_uid().val; + + // We only interest in process spwaned by zygote + if (!susfs_is_sid_equal(current_cred(), susfs_zygote_sid)) { + return 0; + } + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + // Check if spawned process is isolated service first, and force to do umount if so + if (is_zygote_isolated_service_uid(new_uid)) { + goto do_umount; + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + + pr_debug("handle_setresuid from %d to %d\n", old_uid, new_uid); + + if (unlikely(is_uid_manager(new_uid))) { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (current->seccomp.mode == SECCOMP_MODE_FILTER && current->seccomp.filter) { + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + } +#else + disable_seccomp(current); +#endif + +#ifdef KSU_KPROBES_HOOK + ksu_set_task_tracepoint_flag(current); +#endif + + pr_info("install fd for manager: %d\n", new_uid); + struct callback_head *cb = kzalloc(sizeof(*cb), GFP_ATOMIC); + if (!cb) + return 0; + cb->func = ksu_install_manager_fd_tw_func; + if (task_work_add(current, cb, TWA_RESUME)) { + kfree(cb); + pr_warn("install manager fd add task_work failed\n"); + } + return 0; + } + +// Check if spawned process is normal user app and needs to be umounted + if (likely(is_zygote_normal_app_uid(new_uid) && ksu_uid_should_umount(new_uid))) { + goto do_umount; + } + + if (ksu_is_allow_uid_for_current(new_uid)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (current->seccomp.mode == SECCOMP_MODE_FILTER && current->seccomp.filter) { + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + } +#else + disable_seccomp(current); +#endif + +#ifdef KSU_KPROBES_HOOK + ksu_set_task_tracepoint_flag(current); +#endif + } else { +#ifdef KSU_KPROBES_HOOK + ksu_clear_task_tracepoint_flag_if_needed(current); +#endif + } + + // Handle kernel umount + //ksu_handle_umount(old_uid, new_uid); + + return 0; + +do_umount: + // Handle kernel umount +#ifndef CONFIG_KSU_SUSFS_TRY_UMOUNT + ksu_handle_umount(old_uid, new_uid); +#else + susfs_try_umount(new_uid); +#endif // #ifndef CONFIG_KSU_SUSFS_TRY_UMOUNT + +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + //susfs_run_sus_path_loop(new_uid); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH + + ksu_handle_extra_susfs_work(); + + susfs_set_current_proc_umounted(); + + return 0; +} + +extern void ksu_lsm_hook_init(void); +void __init ksu_setuid_hook_init(void) +{ + ksu_kernel_umount_init(); +} + +void __exit ksu_setuid_hook_exit(void) +{ + pr_info("ksu_core_exit\n"); + ksu_kernel_umount_exit(); +} \ No newline at end of file diff --git a/KernelSU-Next/kernel/hook/setuid_hook.h b/KernelSU-Next/kernel/hook/setuid_hook.h new file mode 100644 index 000000000000..da338298ec2b --- /dev/null +++ b/KernelSU-Next/kernel/hook/setuid_hook.h @@ -0,0 +1,13 @@ +#ifndef __KSU_H_KSU_CORE +#define __KSU_H_KSU_CORE + +#include +#include + +void ksu_setuid_hook_init(void); +void ksu_setuid_hook_exit(void); + +// Handler functions for hook_manager +int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid); + +#endif diff --git a/KernelSU-Next/kernel/include/arch.h b/KernelSU-Next/kernel/include/arch.h new file mode 100644 index 000000000000..dc2c16345b6f --- /dev/null +++ b/KernelSU-Next/kernel/include/arch.h @@ -0,0 +1,96 @@ +#ifndef __KSU_H_ARCH +#define __KSU_H_ARCH + +#include + +#if defined(__aarch64__) + +#define __PT_PARM1_REG regs[0] +#define __PT_PARM2_REG regs[1] +#define __PT_PARM3_REG regs[2] +#define __PT_SYSCALL_PARM4_REG regs[3] +#define __PT_CCALL_PARM4_REG regs[3] +#define __PT_PARM5_REG regs[4] +#define __PT_PARM6_REG regs[5] +#define __PT_RET_REG regs[30] +#define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */ +#define __PT_RC_REG regs[0] +#define __PT_SP_REG sp +#define __PT_IP_REG pc + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#define REBOOT_SYMBOL "__arm64_sys_reboot" +#define SYS_READ_SYMBOL "__arm64_sys_read" +#define SYS_EXECVE_SYMBOL "__arm64_sys_execve" +#define SYS_SETNS_SYMBOL __arm64_sys_setns +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/scripts/syscalltbl.sh;l=57;drc=9142be9e6443fd641ca37f820efe00d9cd890eb1 +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/scripts/syscall.tbl;l=104;drc=b36d4b6aa88ef039647228b98c59a875e92f8c8e +#define SYS_FSTAT_SYMBOL "__arm64_sys_newfstat" +#else +#define REBOOT_SYMBOL "sys_reboot" +#define SYS_READ_SYMBOL "sys_read" +#define SYS_EXECVE_SYMBOL "sys_execve" +#define SYS_SETNS_SYMBOL sys_setns +#define SYS_FSTAT_SYMBOL "sys_newfstat" +#endif + +#elif defined(__x86_64__) + +#define __PT_PARM1_REG di +#define __PT_PARM2_REG si +#define __PT_PARM3_REG dx +/* syscall uses r10 for PARM4 */ +#define __PT_SYSCALL_PARM4_REG r10 +#define __PT_CCALL_PARM4_REG cx +#define __PT_PARM5_REG r8 +#define __PT_PARM6_REG r9 +#define __PT_RET_REG sp +#define __PT_FP_REG bp +#define __PT_RC_REG ax +#define __PT_SP_REG sp +#define __PT_IP_REG ip + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#define REBOOT_SYMBOL "__x64_sys_reboot" +#define SYS_READ_SYMBOL "__x64_sys_read" +#define SYS_EXECVE_SYMBOL "__x64_sys_execve" +#define SYS_SETNS_SYMBOL __x64_sys_setns +#define SYS_FSTAT_SYMBOL "__x64_sys_newfstat" +#else +#define REBOOT_SYMBOL "sys_reboot" +#define SYS_READ_SYMBOL "sys_read" +#define SYS_EXECVE_SYMBOL "sys_execve" +#define SYS_SETNS_SYMBOL sys_setns +#define SYS_FSTAT_SYMBOL "sys_newfstat" +#endif + +#else +#error "Unsupported arch" +#endif + +/* allow some architecutres to override `struct pt_regs` */ +#ifndef __PT_REGS_CAST +#define __PT_REGS_CAST(x) (x) +#endif + +#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG) +#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) +#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG) +#define PT_REGS_SYSCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_SYSCALL_PARM4_REG) +#define PT_REGS_CCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_CCALL_PARM4_REG) +#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG) +#define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG) +#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG) +#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG) +#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG) +#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) +#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs)) +#else +#define PT_REAL_REGS(regs) ((regs)) +#endif + + +#endif diff --git a/KernelSU-Next/kernel/include/klog.h b/KernelSU-Next/kernel/include/klog.h new file mode 100644 index 000000000000..a934027fbeeb --- /dev/null +++ b/KernelSU-Next/kernel/include/klog.h @@ -0,0 +1,11 @@ +#ifndef __KSU_H_KLOG +#define __KSU_H_KLOG + +#include + +#ifdef pr_fmt +#undef pr_fmt +#define pr_fmt(fmt) "KernelSU: " fmt +#endif + +#endif diff --git a/KernelSU-Next/kernel/include/ksu.h b/KernelSU-Next/kernel/include/ksu.h new file mode 100644 index 000000000000..a996d2ff17c0 --- /dev/null +++ b/KernelSU-Next/kernel/include/ksu.h @@ -0,0 +1,32 @@ +#ifndef __KSU_H_KSU +#define __KSU_H_KSU + +#include +#include +#include + +#define KERNEL_SU_VERSION KSU_VERSION +#define KERNEL_SU_VERSION_TAG KSU_VERSION_TAG + +#define EVENT_POST_FS_DATA 1 +#define EVENT_BOOT_COMPLETED 2 +#define EVENT_MODULE_MOUNTED 3 + +static inline int startswith(char *s, char *prefix) +{ + return strncmp(s, prefix, strlen(prefix)); +} + +static inline int endswith(const char *s, const char *t) +{ + size_t slen = strlen(s); + size_t tlen = strlen(t); + if (tlen > slen) + return 1; + return strcmp(s + slen - tlen, t); +} + +extern struct cred* ksu_cred; +extern bool ksu_late_loaded; + +#endif diff --git a/KernelSU-Next/kernel/include/uapi b/KernelSU-Next/kernel/include/uapi new file mode 120000 index 000000000000..8fd1b18bf2b7 --- /dev/null +++ b/KernelSU-Next/kernel/include/uapi @@ -0,0 +1 @@ +../../uapi \ No newline at end of file diff --git a/KernelSU-Next/kernel/infra/file_wrapper.c b/KernelSU-Next/kernel/infra/file_wrapper.c new file mode 100644 index 000000000000..eb84662de4b4 --- /dev/null +++ b/KernelSU-Next/kernel/infra/file_wrapper.c @@ -0,0 +1,625 @@ +#include +#include +#include +#include +#include // kernel 3.18 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "objsec.h" + +#include "klog.h" // IWYU pragma: keep +#include "selinux/selinux.h" + +#include "file_wrapper.h" + +struct ksu_file_wrapper { + struct file *orig; + struct file_operations ops; +}; + +static struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp); + +static int ksu_wrapper_open(struct inode *ino, struct file *fp) +{ + struct path *orig_path = fp->f_path.dentry->d_fsdata; + struct file *orig_file = + dentry_open(orig_path, fp->f_flags, current_cred()); + if (IS_ERR(orig_file)) { + return PTR_ERR(orig_file); + } + struct ksu_file_wrapper *wrapper = ksu_create_file_wrapper(orig_file); + if (IS_ERR(wrapper)) { + filp_close(orig_file, current->files); + return PTR_ERR(wrapper); + } + fp->private_data = wrapper; + const struct file_operations *new_fops = fops_get(&wrapper->ops); + replace_fops(fp, new_fops); + return 0; +} + +static const struct file_operations ksu_file_wrapper_inode_fops = { + .owner = THIS_MODULE, + .open = ksu_wrapper_open +}; + +static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->llseek(data->orig, off, flags); +} + +static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->read(orig, ptr, sz, off); +} + +static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->write(orig, ptr, sz, off); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) { + struct ksu_file_wrapper* data = iocb->ki_filp->private_data; + struct file* orig = data->orig; + iocb->ki_filp = orig; + return orig->f_op->read_iter(iocb, iovi); +} + +static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) { + struct ksu_file_wrapper* data = iocb->ki_filp->private_data; + struct file* orig = data->orig; + iocb->ki_filp = orig; + return orig->f_op->write_iter(iocb, iovi); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) { + struct ksu_file_wrapper* data = kiocb->ki_filp->private_data; + struct file* orig = data->orig; + kiocb->ki_filp = orig; + return orig->f_op->iopoll(kiocb, icb, v); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) +static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) { + struct ksu_file_wrapper* data = kiocb->ki_filp->private_data; + struct file* orig = data->orig; + kiocb->ki_filp = orig; + return orig->f_op->iopoll(kiocb, spin); +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) +static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->iterate(orig, dc); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->iterate_shared(orig, dc); +} +#endif + +// typedef unsigned __bitwise __poll_t; +static unsigned __bitwise ksu_wrapper_poll(struct file *fp, + struct poll_table_struct *pts) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->poll(orig, pts); +} + +static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->unlocked_ioctl(orig, cmd, arg); +} + +static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->compat_ioctl(orig, cmd, arg); +} + +static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->mmap(orig, vma); +} + +static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->flush(orig, id); +} + + +static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->fsync(orig, off1, off2, datasync); +} + +static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->fasync(arg, orig, arg2); +} + +static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->lock(orig, arg1, fl); +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) +static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->sendpage) { + return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2); + } + return -EINVAL; +} +#endif + +static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->get_unmapped_area) { + return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4); + } + return -EINVAL; +} + +// static int ksu_wrapper_check_flags(int arg) {} + +static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->flock) { + return orig->f_op->flock(orig, arg1, fl); + } + return -EINVAL; +} + +static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->splice_write) { + return orig->f_op->splice_write(pii, orig, off, sz, arg1); + } + return -EINVAL; +} + +static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->splice_read) { + return orig->f_op->splice_read(orig, off, pii, sz, arg1); + } + return -EINVAL; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) +void ksu_wrapper_splice_eof(struct file *fp) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->splice_eof) { + return orig->f_op->splice_eof(orig); + } +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->setlease) { + return orig->f_op->setlease(orig, arg1, fl, p); + } + return -EINVAL; +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) +static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->setlease) { + return orig->f_op->setlease(orig, arg1, fl, p); + } + return -EINVAL; +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) // int (*setlease)(struct file *, long, struct file_lock **, void **); +static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->setlease) { + return orig->f_op->setlease(orig, arg1, fl, p); + } + return -EINVAL; +} +#else // int (*setlease)(struct file *, long, struct file_lock **); +static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->setlease) { + return orig->f_op->setlease(orig, arg1, fl); + } + return -EINVAL; +} +#endif + +static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->fallocate) { + return orig->f_op->fallocate(orig, mode, offset, len); + } + return -EINVAL; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) { + struct ksu_file_wrapper* data = f->private_data; + struct file* orig = data->orig; + if (orig->f_op->show_fdinfo) { + orig->f_op->show_fdinfo(m, orig); + } +} +#else +static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) { + struct ksu_file_wrapper* data = f->private_data; + struct file* orig = data->orig; + if (orig->f_op->show_fdinfo) { + orig->f_op->show_fdinfo(m, orig); + } + return -EINVAL; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1593-1606;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +static ssize_t ksu_wrapper_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, + loff_t pos_out, size_t len, unsigned int flags) { + struct ksu_file_wrapper* data = file_out->private_data; + struct file* orig = data->orig; + return orig->f_op->copy_file_range(file_in, pos_in, orig, pos_out, len, flags); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) +// no REMAP_FILE_DEDUP: use file_in +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1598-1599;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=403-404;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +// REMAP_FILE_DEDUP: use file_out +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=483-484;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + loff_t len, unsigned int remap_flags) { + if (remap_flags & REMAP_FILE_DEDUP) { + struct ksu_file_wrapper* data = file_out->private_data; + struct file* orig = data->orig; + return orig->f_op->remap_file_range(file_in, pos_in, orig, pos_out, len, remap_flags); + } else { + struct ksu_file_wrapper* data = file_in->private_data; + struct file* orig = data->orig; + return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags); + } +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + if (orig->f_op->fadvise) { + return orig->f_op->fadvise(orig, off1, off2, flags); + } + return -EINVAL; +} +#endif + +static void ksu_release_file_wrapper(struct ksu_file_wrapper *data); + +static int ksu_wrapper_release(struct inode *inode, struct file *filp) { + // https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/file_table.c;l=467-473;drc=3be0b283b562eabbc2b1f3bb534dc8903079bbaa + // f_op->release is called before fops_put(f_op), so we put it manually. + fops_put(filp->f_op); + // prevent it from being put again + filp->f_op = NULL; + ksu_release_file_wrapper(filp->private_data); + return 0; +} + +static struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) { + struct ksu_file_wrapper* p = kcalloc(1, sizeof(struct ksu_file_wrapper), GFP_KERNEL); + if (!p) { + return ERR_PTR(-ENOMEM); + } + + get_file(fp); + + p->orig = fp; + p->ops.owner = THIS_MODULE; + p->ops.llseek = fp->f_op->llseek ? ksu_wrapper_llseek : NULL; + p->ops.read = fp->f_op->read ? ksu_wrapper_read : NULL; + p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL; + p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL; +#endif + p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL; + p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL; + p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL; + p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) + p->ops.fop_flags = fp->f_op->fop_flags; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + p->ops.mmap_supported_flags = fp->f_op->mmap_supported_flags; +#endif + p->ops.flush = fp->f_op->flush ? ksu_wrapper_flush : NULL; + p->ops.release = ksu_wrapper_release; + p->ops.fsync = fp->f_op->fsync ? ksu_wrapper_fsync : NULL; + p->ops.fasync = fp->f_op->fasync ? ksu_wrapper_fasync : NULL; + p->ops.lock = fp->f_op->lock ? ksu_wrapper_lock : NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) + p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL; +#endif + p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? + ksu_wrapper_get_unmapped_area : + NULL; + p->ops.check_flags = fp->f_op->check_flags; + p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL; + p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL; + p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL; + p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL; + p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL; + p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + p->ops.remap_file_range = fp->f_op->remap_file_range ? + ksu_wrapper_remap_file_range : + NULL; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL; +#endif + + return p; +} + +static void ksu_release_file_wrapper(struct ksu_file_wrapper *data) { + fput((struct file*) data->orig); + kfree(data); +} + +static char *ksu_wrapper_d_dname(struct dentry *dentry, char *buffer, + int buflen) +{ + struct path *orig_path = dentry->d_fsdata; + return d_path(orig_path, buffer, buflen); +} + +static void ksu_wrapper_d_release(struct dentry *dentry) +{ + struct path *orig_path = dentry->d_fsdata; + path_put(orig_path); + kfree(orig_path); +} + +static const struct dentry_operations ksu_file_wrapper_d_ops = { + .d_dname = ksu_wrapper_d_dname, + .d_release = ksu_wrapper_d_release +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +#define ksu_anon_inode_create_getfile_compat anon_inode_create_getfile +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) +#define ksu_anon_inode_create_getfile_compat anon_inode_getfile_secure +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +// There is no anon_inode_create_getfile before 4.19, but it's not difficult to implement it. +// https://cs.android.com/android/kernel/superproject/+/common-android12-5.10:common/fs/anon_inodes.c;l=58-125;drc=0d34ce8aa78e38affbb501690bcabec4df88620e + +// Borrow kernel's anon_inode_mnt, so that we don't need to mount one by ourselves. +static struct vfsmount *anon_inode_mnt __read_mostly; + +static struct inode * +ksu_anon_inode_make_secure_inode(const char *name, + const struct inode *context_inode) +{ + struct inode *inode; +#ifdef KSU_COMPAT_HAS_INIT_SEC_ANON + int error; + const struct qstr qname = QSTR_INIT(name, strlen(name)); +#endif + + if (unlikely(!anon_inode_mnt)) { + return ERR_PTR(-ENODEV); + } + + inode = alloc_anon_inode(anon_inode_mnt->mnt_sb); + if (IS_ERR(inode)) + return inode; + inode->i_flags &= ~S_PRIVATE; +#ifdef KSU_COMPAT_HAS_INIT_SEC_ANON + error = security_inode_init_security_anon(inode, &qname, context_inode); + if (error) { + iput(inode); + return ERR_PTR(error); + } +#endif + return inode; +} + +static struct file *ksu_anon_inode_create_getfile_compat( + const char *name, const struct file_operations *fops, void *priv, + int flags, const struct inode *context_inode) +{ + struct inode *inode; + struct file *file; + + if (fops->owner && !try_module_get(fops->owner)) + return ERR_PTR(-ENOENT); + + inode = ksu_anon_inode_make_secure_inode(name, context_inode); + if (IS_ERR(inode)) { + file = ERR_CAST(inode); + goto err; + } + + file = alloc_file_pseudo(inode, anon_inode_mnt, name, + flags & (O_ACCMODE | O_NONBLOCK), fops); + if (IS_ERR(file)) + goto err_iput; + + file->f_mapping = inode->i_mapping; + + file->private_data = priv; + + return file; + +err_iput: + iput(inode); +err: + module_put(fops->owner); + return file; +} +#else +struct file *ksu_anon_inode_create_getfile_compat( + const char *name, const struct file_operations *fops, void *priv, + int flags, const struct inode *context_inode) +{ + return anon_inode_getfile(name, fops, priv, flags); +} +#endif + +int ksu_install_file_wrapper(int fd) +{ + int out_fd, ret; + struct file *orig_file = fget(fd); + if (!orig_file) { + return -EBADF; + } + + out_fd = get_unused_fd_flags(O_CLOEXEC); + if (out_fd < 0) { + ret = out_fd; + goto done; + } + + struct ksu_file_wrapper *file_wrapper_data = + ksu_create_file_wrapper(orig_file); + if (IS_ERR(file_wrapper_data)) { + ret = PTR_ERR(file_wrapper_data); + goto out_put_fd; + } + + struct file *wrapper_file = ksu_anon_inode_create_getfile_compat( + "[ksu_fdwrapper]", &file_wrapper_data->ops, file_wrapper_data, + orig_file->f_flags, NULL); + if (IS_ERR(wrapper_file)) { + pr_err("ksu_fdwrapper: getfile failed: %ld\n", + PTR_ERR(wrapper_file)); + ret = PTR_ERR(wrapper_file); + goto out_release_wrapper; + } + + // Now do magic on inode and dentry. + // It should be safe to modify them since the file hasn't been published. + + struct inode *wrapper_inode = file_inode(wrapper_file); + // libc's stdio relies on the fstat() result of the fd to determine its buffer type. + wrapper_inode->i_mode = file_inode(orig_file)->i_mode; + + struct inode_security_struct *wrapper_sec = + selinux_inode(wrapper_inode); + + // Use ksu_file_sid to bypass SELinux check. + // When we call `su` from terminal app, this is useful. + if (wrapper_sec) { + wrapper_sec->sid = ksu_file_sid; + } + // Install open file operation for inode. + wrapper_inode->i_fop = &ksu_file_wrapper_inode_fops; + + struct path *orig_path = kmalloc(sizeof(struct path), GFP_KERNEL); + if (!orig_path) { + ret = -ENOMEM; + goto out_put_wrapper_file; + } + *orig_path = orig_file->f_path; + path_get(orig_path); + // Some applications (such as screen) won't work if the tty's path is weird, + // Therefore, we use d_dname to spoof it to return the path to the original file. + wrapper_file->f_path.dentry->d_fsdata = orig_path; + wrapper_file->f_path.dentry->d_op = &ksu_file_wrapper_d_ops; + + fd_install(out_fd, wrapper_file); + ret = out_fd; + goto done; + +out_put_wrapper_file: + fput(wrapper_file); + // file_wrapper will be released by fput + goto out_put_fd; +out_release_wrapper: + ksu_release_file_wrapper(file_wrapper_data); +out_put_fd: + put_unused_fd(out_fd); +done: + fput(orig_file); + + return ret; +} + +void __init ksu_file_wrapper_init(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) + static const struct file_operations tmp = { .owner = THIS_MODULE }; + struct file *dummy = anon_inode_getfile("dummy", &tmp, NULL, 0); + if (IS_ERR(dummy)) { + pr_err("file_wrapper: initialize anon_inode_mnt failed, can't get file: %ld\n", + PTR_ERR(dummy)); + return; + } + anon_inode_mnt = dummy->f_path.mnt; + if (unlikely(!anon_inode_mnt)) { + pr_err("file_wrapper: initialize anon_inode_mnt failed, got NULL\n"); + } + fput(dummy); +#endif +} diff --git a/KernelSU-Next/kernel/infra/file_wrapper.h b/KernelSU-Next/kernel/infra/file_wrapper.h new file mode 100644 index 000000000000..faae4dded301 --- /dev/null +++ b/KernelSU-Next/kernel/infra/file_wrapper.h @@ -0,0 +1,10 @@ +#ifndef KSU_FILE_WRAPPER_H +#define KSU_FILE_WRAPPER_H + +#include +#include + +int ksu_install_file_wrapper(int fd); +void ksu_file_wrapper_init(void); + +#endif // KSU_FILE_WRAPPER_H diff --git a/KernelSU-Next/kernel/infra/seccomp_cache.c b/KernelSU-Next/kernel/infra/seccomp_cache.c new file mode 100644 index 000000000000..74fea37bfb0a --- /dev/null +++ b/KernelSU-Next/kernel/infra/seccomp_cache.c @@ -0,0 +1,67 @@ +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#include +#include +#include +#include +#include +#include +#include "klog.h" // IWYU pragma: keep +#include "seccomp_cache.h" + +struct action_cache { + DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR); +#ifdef SECCOMP_ARCH_COMPAT + DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR); +#endif +}; + +struct seccomp_filter { + refcount_t refs; + refcount_t users; + bool log; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + bool wait_killable_recv; +#endif + struct action_cache cache; + struct seccomp_filter *prev; + struct bpf_prog *prog; + struct notification *notif; + struct mutex notify_lock; + wait_queue_head_t wqh; +}; + +void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr) +{ + if (!filter) { + return; + } + + if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { + clear_bit(nr, filter->cache.allow_native); + } + +#ifdef SECCOMP_ARCH_COMPAT + if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { + clear_bit(nr, filter->cache.allow_compat); + } +#endif +} + +void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr) +{ + if (!filter) { + return; + } + + if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { + set_bit(nr, filter->cache.allow_native); + } + +#ifdef SECCOMP_ARCH_COMPAT + if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { + set_bit(nr, filter->cache.allow_compat); + } +#endif +} +#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) \ No newline at end of file diff --git a/KernelSU-Next/kernel/infra/seccomp_cache.h b/KernelSU-Next/kernel/infra/seccomp_cache.h new file mode 100644 index 000000000000..5900f288d35f --- /dev/null +++ b/KernelSU-Next/kernel/infra/seccomp_cache.h @@ -0,0 +1,12 @@ +#ifndef __KSU_H_SECCOMP_CACHE +#define __KSU_H_SECCOMP_CACHE + +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr); +extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr); +#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + +#endif diff --git a/KernelSU-Next/kernel/infra/su_mount_ns.c b/KernelSU-Next/kernel/infra/su_mount_ns.c new file mode 100644 index 000000000000..ed3088c26b43 --- /dev/null +++ b/KernelSU-Next/kernel/infra/su_mount_ns.c @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#include +#else +#include +#endif +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#include +#else +#include +#endif +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) +#include +#else +#include +#endif +#endif + +#include "arch.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "su_mount_ns.h" +#include "compat/kernel_compat.h" + +extern int path_mount(const char *dev_name, struct path *path, + const char *type_page, unsigned long flags, + void *data_page); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) +#if defined(__aarch64__) +extern long __arm64_sys_setns(const struct pt_regs *regs); +#elif defined(__x86_64__) +extern long __x64_sys_setns(const struct pt_regs *regs); +#elif defined(__arm__) // https://syscalls.mebeim.net/?table=arm/32/eabi/latest +extern long sys_setns(const struct pt_regs *regs); +#endif + +static long ksu_sys_setns(int fd, int flags) +{ + struct pt_regs regs; + memset(®s, 0, sizeof(regs)); + + PT_REGS_PARM1(®s) = fd; + PT_REGS_PARM2(®s) = flags; + +#if defined(__aarch64__) + return __arm64_sys_setns(®s); +#elif defined(__x86_64__) + return __x64_sys_setns(®s); +#elif defined(__arm__) + return sys_setns(®s); +#else + return -ENOSYS; +#endif +} + +static int ksu_sys_unshare(unsigned long flags) +{ + return ksys_unshare(flags); +} + +#else +static long ksu_sys_setns(int fd, int nstype) +{ + return sys_setns(fd, nstype); +} + +static long ksu_sys_unshare(unsigned long flags) +{ + return sys_unshare(flags); +} +#endif + +// global mode, need CAP_SYS_ADMIN and CAP_SYS_CHROOT to perform setns +static void ksu_mnt_ns_global(void) +{ + // save current working directory as absolute path before setns + char *pwd_path = NULL; + char *pwd_buf = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pwd_buf) { + pr_warn("no mem for pwd buffer, skip restore pwd!!\n"); + goto try_setns; + } + + struct path saved_pwd; + get_fs_pwd(current->fs, &saved_pwd); + pwd_path = d_path(&saved_pwd, pwd_buf, PATH_MAX); + path_put(&saved_pwd); + + if (IS_ERR(pwd_path)) { + if (PTR_ERR(pwd_path) == -ENAMETOOLONG) { + pr_warn("absolute pwd longer than: %d, skip restore pwd!!\n", + PATH_MAX); + } else { + pr_warn("get absolute pwd failed: %ld\n", PTR_ERR(pwd_path)); + } + pwd_path = NULL; + } + +try_setns: + + rcu_read_lock(); + // &init_task is not init, but swapper/idle, which forks the init process + // so we need find init process + struct pid *pid_struct = find_pid_ns(1, &init_pid_ns); + if (unlikely(!pid_struct)) { + rcu_read_unlock(); + pr_warn("failed to find pid_struct for PID 1\n"); + goto out; + } + + struct task_struct *pid1_task = get_pid_task(pid_struct, PIDTYPE_PID); + rcu_read_unlock(); + if (unlikely(!pid1_task)) { + pr_warn("failed to get task_struct for PID 1\n"); + goto out; + } + struct path ns_path; + long ret = ns_get_path(&ns_path, pid1_task, &mntns_operations); + put_task_struct(pid1_task); + if (ret) { + pr_warn("failed get path for init mount namespace: %ld\n", ret); + goto out; + } + struct file *ns_file = dentry_open(&ns_path, O_RDONLY, ksu_cred); + + path_put(&ns_path); + if (IS_ERR(ns_file)) { + pr_warn("failed open file for init mount namespace: %ld\n", + PTR_ERR(ns_file)); + goto out; + } + + int fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + pr_warn("failed to get an unused fd: %d\n", fd); + fput(ns_file); + goto out; + } + + fd_install(fd, ns_file); + ret = ksu_sys_setns(fd, CLONE_NEWNS); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + close_fd(fd); +#else + __close_fd(current->files, fd); +#endif + + if (ret) { + pr_warn("call setns failed: %ld\n", ret); + goto out; + } + // try to restore working directory using absolute path after setns + if (pwd_path) { + struct path new_pwd; + int err = kern_path(pwd_path, 0, &new_pwd); + if (!err) { + set_fs_pwd(current->fs, &new_pwd); + path_put(&new_pwd); + } else { + pr_warn("restore pwd failed: %d, path: %s\n", err, pwd_path); + } + } +out: + kfree(pwd_buf); +} + +// individual mode , need CAP_SYS_ADMIN to perform unshare and remount +static void ksu_mnt_ns_individual(void) +{ + long ret = ksu_sys_unshare(CLONE_NEWNS); + if (ret) { + pr_warn("call ksys_unshare failed: %ld\n", ret); + return; + } + + // make root mount private + struct path root_path; + get_fs_root(current->fs, &root_path); + int pm_ret = path_mount(NULL, &root_path, NULL, MS_PRIVATE | MS_REC, NULL); + path_put(&root_path); + + if (pm_ret < 0) { + pr_err("failed to make root private, err: %d\n", pm_ret); + } +} + +static void ksu_setup_mount_ns_tw_func(struct callback_head *cb) +{ + struct ksu_mns_tw *tw = container_of(cb, struct ksu_mns_tw, cb); + const struct cred *old_cred = override_creds(ksu_cred); + if (tw->ns_mode == KSU_NS_GLOBAL) { + ksu_mnt_ns_global(); + } else { + ksu_mnt_ns_individual(); + } + revert_creds(old_cred); + kfree(tw); +} + +void setup_mount_ns(int32_t ns_mode) +{ + // inherit mode + if (ns_mode == KSU_NS_INHERITED) { + // do nothing + return; + } + + if (ns_mode != KSU_NS_GLOBAL && ns_mode != KSU_NS_INDIVIDUAL) { + pr_warn("pid: %d ,unknown mount namespace mode: %d\n", current->pid, + ns_mode); + return; + } + + if (!ksu_cred) { + pr_err("no ksu cred! skip mnt_ns magic for pid: %d.\n", current->pid); + return; + } + + struct ksu_mns_tw *tw = kzalloc(sizeof(*tw), GFP_ATOMIC); + if (!tw) { + pr_err("no mem for tw! skip mnt_ns magic for pid: %d.\n", current->pid); + return; + } + tw->cb.func = ksu_setup_mount_ns_tw_func; + tw->ns_mode = ns_mode; + if (task_work_add(current, &tw->cb, TWA_RESUME)) { + kfree(tw); + pr_err("add task work failed! skip mnt_ns magic for pid: %d.\n", + current->pid); + } +} diff --git a/KernelSU-Next/kernel/infra/su_mount_ns.h b/KernelSU-Next/kernel/infra/su_mount_ns.h new file mode 100644 index 000000000000..2411e6190c71 --- /dev/null +++ b/KernelSU-Next/kernel/infra/su_mount_ns.h @@ -0,0 +1,17 @@ +#ifndef __KSU_SU_MOUNT_NS_H +#define __KSU_SU_MOUNT_NS_H + +#include + +#define KSU_NS_INHERITED 0 +#define KSU_NS_GLOBAL 1 +#define KSU_NS_INDIVIDUAL 2 + +struct ksu_mns_tw { + struct callback_head cb; + int32_t ns_mode; +}; + +void setup_mount_ns(int32_t ns_mode); + +#endif diff --git a/KernelSU-Next/kernel/manager/apk_sign.c b/KernelSU-Next/kernel/manager/apk_sign.c new file mode 100644 index 000000000000..e1bf47236a1e --- /dev/null +++ b/KernelSU-Next/kernel/manager/apk_sign.c @@ -0,0 +1,453 @@ +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KSU_DEBUG +#include +#endif +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) +#include +#else +#include +#endif + +#include "apk_sign.h" +#include "policy/app_profile.h" +#include "klog.h" // IWYU pragma: keep +#include "compat/kernel_compat.h" + +struct sdesc { + struct shash_desc shash; + char ctx[]; +}; + +static struct sdesc *init_sdesc(struct crypto_shash *alg) +{ + struct sdesc *sdesc; + int size; + + size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); + sdesc = kzalloc(size, GFP_KERNEL); + if (!sdesc) + return ERR_PTR(-ENOMEM); + sdesc->shash.tfm = alg; + return sdesc; +} + +static int calc_hash(struct crypto_shash *alg, const unsigned char *data, + unsigned int datalen, unsigned char *digest) +{ + struct sdesc *sdesc; + int ret; + + sdesc = init_sdesc(alg); + if (IS_ERR(sdesc)) { + pr_info("can't alloc sdesc\n"); + return PTR_ERR(sdesc); + } + + ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); + kfree(sdesc); + return ret; +} + +static int ksu_sha256(const unsigned char *data, unsigned int datalen, + unsigned char *digest) +{ + struct crypto_shash *alg; + char *hash_alg_name = "sha256"; + int ret; + + alg = crypto_alloc_shash(hash_alg_name, 0, 0); + if (IS_ERR(alg)) { + pr_info("can't alloc alg %s\n", hash_alg_name); + return PTR_ERR(alg); + } + ret = calc_hash(alg, data, datalen, digest); + crypto_free_shash(alg); + return ret; +} + +static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, + unsigned expected_size, const char *expected_sha256) +{ + ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length + + *offset += 0x4 * 3; + + ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length + + *pos += *size4; + *offset += 0x4 + *size4; + + ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length + *offset += 0x4 * 2; + + if (*size4 == expected_size) { + *offset += *size4; + +#define CERT_MAX_LENGTH 1024 + char cert[CERT_MAX_LENGTH]; + if (*size4 > CERT_MAX_LENGTH) { + pr_info("cert length overlimit\n"); + return false; + } + ksu_kernel_read_compat(fp, cert, *size4, pos); + unsigned char digest[SHA256_DIGEST_SIZE]; + if (ksu_sha256(cert, *size4, digest) < 0 ) { + pr_info("sha256 error\n"); + return false; + } + + char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; + hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; + + bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); + pr_info("sha256: %s, expected: %s\n", hash_str, + expected_sha256); + if (strcmp(expected_sha256, hash_str) == 0) { + return true; + } + } + return false; +} + +struct zip_entry_header { + uint32_t signature; + uint16_t version; + uint16_t flags; + uint16_t compression; + uint16_t mod_time; + uint16_t mod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_length; + uint16_t extra_field_length; +} __attribute__((packed)); + +struct ksu_buf_reader { + struct file *fp; + loff_t file_pos; + char buf[4096]; + size_t buf_len; +}; + +static inline ssize_t ksu_bread(struct ksu_buf_reader *br, void *dst, + size_t count, loff_t *pos) +{ + if (*pos >= br->file_pos && + *pos + count <= br->file_pos + br->buf_len) { + memcpy(dst, br->buf + (*pos - br->file_pos), count); + *pos += count; + return count; + } + + br->file_pos = *pos; + loff_t read_pos = br->file_pos; + ssize_t res = ksu_kernel_read_compat(br->fp, br->buf, sizeof(br->buf), + &read_pos); + if (res <= 0) { + br->buf_len = 0; + return res; + } + br->buf_len = res; + + if (count <= br->buf_len) { + memcpy(dst, br->buf, count); + *pos += count; + return count; + } + + return 0; +} + +// This is a necessary but not sufficient condition, but it is enough for us +static bool has_v1_signature_file(struct file *fp) +{ + struct zip_entry_header header; + const char MANIFEST[] = "META-INF/MANIFEST.MF"; + bool found = false; + loff_t pos = 0; + + struct ksu_buf_reader *br = + kzalloc(sizeof(struct ksu_buf_reader), GFP_KERNEL); + if (!br) { + pr_err("ksu_buf_reader alloc failed\n"); + return false; + } + + br->fp = fp; + br->file_pos = 0; + br->buf_len = 0; + while (ksu_bread(br, &header, + sizeof(struct zip_entry_header), &pos) == + sizeof(struct zip_entry_header)) { + if (header.signature != 0x04034b50) { + // ZIP magic: 'PK' + break; + } + // Read the entry file name + if (header.file_name_length == sizeof(MANIFEST) - 1) { + char fileName[sizeof(MANIFEST)]; + if (ksu_bread(br, fileName, header.file_name_length, + &pos) == header.file_name_length) { + fileName[header.file_name_length] = '\0'; + + // Check if the entry matches META-INF/MANIFEST.MF + if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == 0) { + found = true; + break; + } + } else { + break; + } + } else { + // Skip the entry file name + pos += header.file_name_length; + } + + // Skip to the next entry + pos += header.extra_field_length + header.compressed_size; + } + + kfree(br); + return found; +} + +static __always_inline bool check_v2_signature(char *path, + unsigned expected_size, + const char *expected_sha256) +{ + unsigned char buffer[0x11] = { 0 }; + u32 size4; + u64 size8, size_of_block; + + loff_t pos; + + bool v2_signing_valid = false; + int v2_signing_blocks = 0; + bool v3_signing_exist = false; + bool v3_1_signing_exist = false; + + int i; + struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("open %s error.\n", path); + return false; + } + + // disable inotify for this file + fp->f_mode |= FMODE_NONOTIFY; + + // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) + { + unsigned char *eocd_buffer; + loff_t file_size; + long search_size; + long max_comment_size = 0xffff; + long eocd_min_size = 22; + long eocd_found = 0; + + file_size = generic_file_llseek(fp, 0, SEEK_END); + search_size = max_comment_size + eocd_min_size; + if (search_size > file_size) { + search_size = file_size; + } + + eocd_buffer = kvmalloc(search_size, GFP_KERNEL); + if (!eocd_buffer) { + pr_err("error: cannot allocate memory for eocd\n"); + goto clean; + } + + pos = file_size - search_size; + ksu_kernel_read_compat(fp, eocd_buffer, search_size, &pos); + + if (search_size >= eocd_min_size) { + long j; + for (j = search_size - eocd_min_size; j >= 0; j--) { + if (eocd_buffer[j] == 0x50 && + eocd_buffer[j + 1] == 0x4b && + eocd_buffer[j + 2] == 0x05 && + eocd_buffer[j + 3] == 0x06) { + unsigned short comment_len = + eocd_buffer[j + 20] | (eocd_buffer[j + 21] << 8); + if (comment_len == search_size - j - eocd_min_size) { + pos = file_size - search_size + j; + eocd_found = 1; + break; + } + } + } + } + + kvfree(eocd_buffer); + + if (!eocd_found) { + pr_info("error: cannot find eocd\n"); + goto clean; + } + } + + pos += 16; // skip 4 bytes signature + 12 bytes + // offset + ksu_kernel_read_compat(fp, &size4, 0x4, &pos); + pos = size4 - 0x18; + + ksu_kernel_read_compat(fp, &size8, 0x8, &pos); + ksu_kernel_read_compat(fp, buffer, 0x10, &pos); + if (strcmp((char *)buffer, "APK Sig Block 42")) { + goto clean; + } + + pos = size4 - (size8 + 0x8); + ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos); + if (size_of_block != size8) { + goto clean; + } + + int loop_count = 0; + while (loop_count++ < 10) { + uint32_t id; + uint32_t offset; + ksu_kernel_read_compat(fp, &size8, 0x8, + &pos); // sequence length + if (size8 == size_of_block) { + break; + } + ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id + offset = 4; + if (id == 0x7109871au) { + v2_signing_blocks++; + v2_signing_valid = + check_block(fp, &size4, &pos, &offset, + expected_size, expected_sha256); + } else if (id == 0xf05368c0u) { + // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73 + v3_signing_exist = true; + } else if (id == 0x1b93ad61u) { + // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74 + v3_1_signing_exist = true; + } else { +#ifdef CONFIG_KSU_DEBUG + pr_info("Unknown id: 0x%08x\n", id); +#endif + } + pos += (size8 - offset); + } + + if (v2_signing_blocks != 1) { +#ifdef CONFIG_KSU_DEBUG + pr_err("Unexpected v2 signature count: %d\n", + v2_signing_blocks); +#endif + v2_signing_valid = false; + } + + if (v2_signing_valid) { + int has_v1_signing = has_v1_signature_file(fp); + if (has_v1_signing) { + pr_err("Unexpected v1 signature scheme found!\n"); + filp_close(fp, 0); + return false; + } + } +clean: + filp_close(fp, 0); + + if (v3_signing_exist || v3_1_signing_exist) { +#ifdef CONFIG_KSU_DEBUG + pr_err("Unexpected v3 signature scheme found!\n"); +#endif + return false; + } + + return v2_signing_valid; +} + +#ifdef CONFIG_KSU_DEBUG + +int ksu_debug_manager_appid = -1; + +#include "manager/manager_identity.h" + +static int set_expected_size(const char *val, const struct kernel_param *kp) +{ + int rv = param_set_uint(val, kp); + ksu_set_manager_appid(ksu_debug_manager_appid); + pr_info("ksu_manager_appid set to %d\n", ksu_debug_manager_appid); + return rv; +} + +static struct kernel_param_ops expected_size_ops = { + .set = set_expected_size, + .get = param_get_uint, +}; + +module_param_cb(ksu_debug_manager_appid, &expected_size_ops, + &ksu_debug_manager_appid, S_IRUSR | S_IWUSR); + +#endif + +int get_pkg_from_apk_path(char *pkg, const char *path) +{ + int len = strlen(path); + if (len >= KSU_MAX_PACKAGE_NAME || len < 1) + return -1; + + const char *last_slash = NULL; + const char *second_last_slash = NULL; + + int i; + for (i = len - 1; i >= 0; i--) { + if (path[i] == '/') { + if (!last_slash) { + last_slash = &path[i]; + } else { + second_last_slash = &path[i]; + break; + } + } + } + + if (!last_slash || !second_last_slash) + return -1; + + const char *last_hyphen = strchr(second_last_slash, '-'); + if (!last_hyphen || last_hyphen > last_slash) + return -1; + + int pkg_len = last_hyphen - second_last_slash - 1; + if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) + return -1; + + // Copying the package name + strncpy(pkg, second_last_slash + 1, pkg_len); + pkg[pkg_len] = '\0'; + + return 0; +} + +bool is_manager_apk(char *path) +{ +#ifdef KSU_MANAGER_PACKAGE + char pkg[KSU_MAX_PACKAGE_NAME]; + if (get_pkg_from_apk_path(pkg, path) < 0) { + pr_err("Failed to get package name from apk path: %s\n", path); + return false; + } + + // pkg is `` + if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { + return false; + } +#endif + return check_v2_signature(path, EXPECTED_MANAGER_SIZE, EXPECTED_MANAGER_HASH); +} diff --git a/KernelSU-Next/kernel/manager/apk_sign.h b/KernelSU-Next/kernel/manager/apk_sign.h new file mode 100644 index 000000000000..d3a44bd207c2 --- /dev/null +++ b/KernelSU-Next/kernel/manager/apk_sign.h @@ -0,0 +1,9 @@ +#ifndef __KSU_H_APK_V2_SIGN +#define __KSU_H_APK_V2_SIGN + +#include + +bool is_manager_apk(char *path); +int get_pkg_from_apk_path(char *pkg, const char *path); + +#endif diff --git a/KernelSU-Next/kernel/manager/manager_identity.h b/KernelSU-Next/kernel/manager/manager_identity.h new file mode 100644 index 000000000000..ca3f80a7bb04 --- /dev/null +++ b/KernelSU-Next/kernel/manager/manager_identity.h @@ -0,0 +1,73 @@ +#ifndef __KSU_H_KSU_MANAGER +#define __KSU_H_KSU_MANAGER + +#include +#include + +#define KSU_INVALID_APPID -1 +#define KSU_PER_USER_RANGE 100000 + +#ifdef CONFIG_KSU_DISABLE_MANAGER +static inline bool ksu_is_manager_appid_valid() +{ + return true; +} + +static inline bool is_manager() +{ + return current_uid().val == 0; +} + +static inline bool is_uid_manager(uid_t uid) +{ + return uid == 0; +} + +static inline uid_t ksu_get_manager_appid() +{ + return 0; +} + +static inline void ksu_set_manager_appid(uid_t appid) +{ + (void)appid; +} + +static inline void ksu_invalidate_manager_uid() +{ +} +#else +extern uid_t ksu_manager_appid; // DO NOT DIRECT USE + +static inline bool ksu_is_manager_appid_valid() +{ + return ksu_manager_appid != KSU_INVALID_APPID; +} + +static inline bool is_manager() +{ + return unlikely(ksu_manager_appid == current_uid().val % KSU_PER_USER_RANGE); +} + +static inline bool is_uid_manager(uid_t uid) +{ + return unlikely(ksu_manager_appid == uid % KSU_PER_USER_RANGE); +} + +static inline uid_t ksu_get_manager_appid() +{ + return ksu_manager_appid; +} + +static inline void ksu_set_manager_appid(uid_t appid) +{ + ksu_manager_appid = appid; +} + +static inline void ksu_invalidate_manager_uid() +{ + ksu_manager_appid = KSU_INVALID_APPID; +} +#endif + +#endif diff --git a/KernelSU-Next/kernel/manager/manager_observer.h b/KernelSU-Next/kernel/manager/manager_observer.h new file mode 100644 index 000000000000..03f3b12787d9 --- /dev/null +++ b/KernelSU-Next/kernel/manager/manager_observer.h @@ -0,0 +1,18 @@ +#ifndef __KSU_H_MANAGER_OBSERVER +#define __KSU_H_MANAGER_OBSERVER + +#ifdef CONFIG_KSU_DISABLE_MANAGER +static inline int ksu_observer_init(void) +{ + return 0; +} + +static inline void ksu_observer_exit(void) +{ +} +#else +int ksu_observer_init(void); +void ksu_observer_exit(void); +#endif + +#endif // __KSU_H_MANAGER_OBSERVER diff --git a/KernelSU-Next/kernel/manager/pkg_observer.c b/KernelSU-Next/kernel/manager/pkg_observer.c new file mode 100644 index 000000000000..fb4f01994ac0 --- /dev/null +++ b/KernelSU-Next/kernel/manager/pkg_observer.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include "klog.h" // IWYU pragma: keep +#include "throne_tracker.h" + +#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD) + +struct watch_dir { + const char *path; + u32 mask; + struct path kpath; + struct inode *inode; + struct fsnotify_mark *mark; +}; + +static struct fsnotify_group *g; + +#include "pkg_observer_defs.h" // KSU_DECL_FSNOTIFY_OPS +static KSU_DECL_FSNOTIFY_OPS(ksu_handle_inode_event) +{ + if (!file_name) + return 0; + if (mask & FS_ISDIR) + return 0; + if (ksu_fname_len(file_name) == 13 && + !memcmp(ksu_fname_arg(file_name), "packages.list", 13)) { + pr_info("packages.list detected: %d\n", mask); + track_throne(false); + } + return 0; +} + +static const struct fsnotify_ops ksu_ops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .handle_inode_event = ksu_handle_inode_event, +#else + .handle_event = ksu_handle_inode_event, +#endif +}; + +static void __maybe_unused m_free(struct fsnotify_mark *m) +{ + if (m) { + kfree(m); + } +} + +static int add_mark_on_inode(struct inode *inode, u32 mask, + struct fsnotify_mark **out) +{ + struct fsnotify_mark *m; + int ret; + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) + fsnotify_init_mark(m, g); + m->mask = mask; + ret = fsnotify_add_inode_mark(m, inode, 0); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + fsnotify_init_mark(m, g); + m->mask = mask; + ret = fsnotify_add_mark(m, inode, NULL, 0); +#else + fsnotify_init_mark(m, m_free); + m->mask = mask; + ret = fsnotify_add_mark(m, g, inode, NULL, 0); +#endif + + if (ret) { + fsnotify_put_mark(m); + return -EINVAL; + } + *out = m; + return 0; +} + +static int watch_one_dir(struct watch_dir *wd) +{ + int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath); + if (ret) { + pr_info("path not ready: %s (%d)\n", wd->path, ret); + return ret; + } + wd->inode = d_inode(wd->kpath.dentry); + ihold(wd->inode); + + ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark); + if (ret) { + pr_err("Add mark failed for %s (%d)\n", wd->path, ret); + path_put(&wd->kpath); + iput(wd->inode); + wd->inode = NULL; + return ret; + } + pr_info("watching %s\n", wd->path); + return 0; +} + +static void unwatch_one_dir(struct watch_dir *wd) +{ + if (wd->mark) { + fsnotify_destroy_mark(wd->mark, g); + fsnotify_put_mark(wd->mark); + wd->mark = NULL; + } + if (wd->inode) { + iput(wd->inode); + wd->inode = NULL; + } + if (wd->kpath.dentry) { + path_put(&wd->kpath); + memset(&wd->kpath, 0, sizeof(wd->kpath)); + } +} + +static struct watch_dir g_watch = { .path = "/data/system", + .mask = MASK_SYSTEM }; + +int ksu_observer_init(void) +{ + int ret = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + g = fsnotify_alloc_group(&ksu_ops, 0); +#else + g = fsnotify_alloc_group(&ksu_ops); +#endif + if (IS_ERR(g)) + return PTR_ERR(g); + + ret = watch_one_dir(&g_watch); + pr_info("observer init done\n"); + return 0; +} + +void __exit ksu_observer_exit(void) +{ + unwatch_one_dir(&g_watch); + fsnotify_put_group(g); + pr_info("observer exit done\n"); +} diff --git a/KernelSU-Next/kernel/manager/throne_tracker.c b/KernelSU-Next/kernel/manager/throne_tracker.c new file mode 100644 index 000000000000..bf5b8b6958ee --- /dev/null +++ b/KernelSU-Next/kernel/manager/throne_tracker.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "policy/allowlist.h" +#include "apk_sign.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "manager_identity.h" +#include "throne_tracker.h" +#include "compat/kernel_compat.h" + +uid_t ksu_manager_appid = KSU_INVALID_APPID; + +#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" + +struct uid_data { + struct list_head list; + u32 uid; + char package[KSU_MAX_PACKAGE_NAME]; +}; + +static void crown_manager(const char *apk, struct list_head *uid_data) +{ + char pkg[KSU_MAX_PACKAGE_NAME]; + if (get_pkg_from_apk_path(pkg, apk) < 0) { + pr_err("Failed to get package name from apk path: %s\n", apk); + return; + } + + pr_info("manager pkg: %s\n", pkg); + + struct list_head *list = (struct list_head *)uid_data; + struct uid_data *np; + + list_for_each_entry (np, list, list) { + if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { + pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid); + ksu_set_manager_appid(np->uid); + break; + } + } +} + +#define DATA_PATH_LEN 384 // 384 is enough for /data/app//base.apk + +struct data_path { + char dirpath[DATA_PATH_LEN]; + int depth; + struct list_head list; +}; + +struct apk_path_hash { + unsigned int hash; + bool exists; + struct list_head list; +}; + +struct my_dir_context { + struct dir_context ctx; + struct list_head *data_path_list; + char *parent_dir; + void *private_data; + int depth; + int *stop; +}; +// https://docs.kernel.org/filesystems/porting.html +// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted. +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +#define FILLDIR_RETURN_TYPE bool +#define FILLDIR_ACTOR_CONTINUE true +#define FILLDIR_ACTOR_STOP false +#else +#define FILLDIR_RETURN_TYPE int +#define FILLDIR_ACTOR_CONTINUE 0 +#define FILLDIR_ACTOR_STOP -EINVAL +#endif +extern bool is_manager_apk(char *path); +FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, + int namelen, loff_t off, u64 ino, + unsigned int d_type) +{ + struct my_dir_context *my_ctx = + container_of(ctx, struct my_dir_context, ctx); + + // we put the apk path we collected here + char *candidate_path = (char *)my_ctx->private_data; + + char dirpath[DATA_PATH_LEN]; + + if (!my_ctx) { + pr_err("Invalid context\n"); + return FILLDIR_ACTOR_STOP; + } + if (my_ctx->stop && *my_ctx->stop) { + pr_info("Stop searching\n"); + return FILLDIR_ACTOR_STOP; + } + + if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) + return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." + + if ((d_type == DT_DIR || d_type == DT_UNKNOWN) && namelen >= 8 && !strncmp(name, "vmdl", 4) && + !strncmp(name + namelen - 4, ".tmp", 4)) { + pr_info("Skipping directory: %.*s\n", namelen, name); + return FILLDIR_ACTOR_CONTINUE; // Skip staging package + } + + if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, namelen, + name) >= DATA_PATH_LEN) { + pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, name); + return FILLDIR_ACTOR_CONTINUE; + } + + if ((d_type == DT_DIR || d_type == DT_UNKNOWN) && my_ctx->depth > 0 && + (my_ctx->stop && !*my_ctx->stop)) { + struct data_path *data = kzalloc(sizeof(struct data_path), GFP_KERNEL); + + if (!data) { + pr_err("Failed to allocate memory for %s\n", dirpath); + return FILLDIR_ACTOR_CONTINUE; + } + + strscpy(data->dirpath, dirpath, DATA_PATH_LEN); + data->depth = my_ctx->depth - 1; + list_add_tail(&data->list, my_ctx->data_path_list); + + return FILLDIR_ACTOR_CONTINUE; + } + + // now put this on candidate_path + if (d_type == DT_REG && !strncmp(name, "base.apk", 8)) { + snprintf(candidate_path, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, namelen, name); + } + + return FILLDIR_ACTOR_CONTINUE; +} + +void search_manager(const char *path, int depth, struct list_head *uid_data) +{ + int i, stop = 0; + struct list_head data_path_list; + INIT_LIST_HEAD(&data_path_list); + + // First depth + struct data_path data; + strscpy(data.dirpath, path, DATA_PATH_LEN); + data.depth = depth; + list_add_tail(&data.list, &data_path_list); + + // we put the apk path we collected here + char candidate_path[DATA_PATH_LEN]; + + for (i = depth; i >= 0; i--) { + struct data_path *pos, *n; + + list_for_each_entry_safe (pos, n, &data_path_list, list) { + struct my_dir_context ctx = { .ctx.actor = my_actor, + .data_path_list = &data_path_list, + .parent_dir = pos->dirpath, + .private_data = candidate_path, + .depth = pos->depth, + .stop = &stop }; + + // make sure to clean buffer on every iteration + memset(candidate_path, 0, DATA_PATH_LEN); + + struct file *file; + + if (!stop) { + file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); + if (IS_ERR(file)) { + pr_err("Failed to open directory: %s, err: %ld\n", + pos->dirpath, PTR_ERR(file)); + goto skip_iterate; + } + + iterate_dir(file, &ctx.ctx); + filp_close(file, NULL); + + // ^ oh so thats the issue! + // we were calling is_manager_apk inside iterate_dir + // now we defer file opens after iterate_dir + // this way we dont open apks while inside that + if (!strstarts(candidate_path, "/data/ap") ) + goto skip_iterate; + + bool is_manager = is_manager_apk(candidate_path); + pr_info("Found new base.apk at path: %s, is_manager: %d\n", candidate_path, is_manager); + + if (likely(!is_manager)) + goto skip_iterate; + + crown_manager(candidate_path, uid_data); + stop = 1; + } + skip_iterate: + list_del(&pos->list); + if (pos != &data) + kfree(pos); + } + } +} + +static bool is_uid_exist(uid_t uid, char *package, void *data) +{ + struct list_head *list = (struct list_head *)data; + struct uid_data *np; + + bool exist = false; + list_for_each_entry (np, list, list) { + if (np->uid == uid % PER_USER_RANGE && + strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { + exist = true; + break; + } + } + return exist; +} + +// Helper to know if Android is modifying the file +static bool is_lock_held(const char *path) +{ + struct path kpath; + + if (kern_path(path, 0, &kpath)) + return true; // If we cannot find the route, we assume it is not safe + + if (!kpath.dentry) { + path_put(&kpath); + return true; + } + + // Check the VFS lock (d_lock) without blocking ourselves + if (!spin_trylock(&kpath.dentry->d_lock)) { + pr_info("%s: lock held on %s, bail out!\n", __func__, path); + path_put(&kpath); + return true; + } + + spin_unlock(&kpath.dentry->d_lock); + path_put(&kpath); + return false; +} + +struct ksu_throne_work_data { + struct delayed_work dwork; + bool prune_only; + int retries; +}; + +static struct ksu_throne_work_data throne_data; +static DEFINE_MUTEX(throne_tracker_mutex); + +static bool do_track_throne_core(bool prune_only) +{ + if (is_lock_held(SYSTEM_PACKAGES_LIST_PATH)) { + return false; // The file is blocked by Android, we ask for a retry + } + + struct file *fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_info("throne_tracker: %s not ready yet: %ld\n", SYSTEM_PACKAGES_LIST_PATH, PTR_ERR(fp)); + return false; // It does not yet exist or cannot be read, we ask for a retry + } + + struct list_head uid_list; + INIT_LIST_HEAD(&uid_list); + + char chr = 0; + loff_t pos = 0; + loff_t line_start = 0; + char buf[KSU_MAX_PACKAGE_NAME]; + for (;;) { + ssize_t count = ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); + if (count != sizeof(chr)) + break; + if (chr != '\n') + continue; + + count = ksu_kernel_read_compat(fp, buf, sizeof(buf), &line_start); + + struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_KERNEL); + if (!data) { + filp_close(fp, 0); + goto out; + } + + char *tmp = buf; + const char *delim = " "; + char *package = strsep(&tmp, delim); + char *uid = strsep(&tmp, delim); + if (!uid || !package) { + kfree(data); + pr_err("update_uid: package or uid is NULL!\n"); + break; + } + + u32 res; + if (kstrtou32(uid, 10, &res)) { + kfree(data); + pr_err("update_uid: uid parse err\n"); + break; + } + data->uid = res; + strncpy(data->package, package, KSU_MAX_PACKAGE_NAME); + list_add_tail(&data->list, &uid_list); + // reset line start + line_start = pos; + } + filp_close(fp, 0); + + // now update uid list + struct uid_data *np; + struct uid_data *n; + + if (prune_only) + goto prune; + + // first, check if manager_uid exist! + bool manager_exist = false; + list_for_each_entry (np, &uid_list, list) { + if (np->uid == ksu_get_manager_appid()) { + manager_exist = true; + break; + } + } + + if (!manager_exist) { + if (ksu_is_manager_appid_valid()) { + pr_info("manager is uninstalled, invalidate it!\n"); + ksu_invalidate_manager_uid(); + goto prune; + } + pr_info("Searching manager...\n"); + search_manager("/data/app", 2, &uid_list); + pr_info("Search manager finished\n"); + } + +prune: + // then prune the allowlist + ksu_prune_allowlist(is_uid_exist, &uid_list); +out: + // free uid_list + list_for_each_entry_safe (np, n, &uid_list, list) { + list_del(&np->list); + kfree(np); + } + + return true; // success +} + +// kworker +static void ksu_throne_work_fn(struct work_struct *work) +{ + struct ksu_throne_work_data *data = container_of(to_delayed_work(work), struct ksu_throne_work_data, dwork); + bool success; + + mutex_lock(&throne_tracker_mutex); + + // Temporarily lend root credentials to the kworker + const struct cred *saved_cred = override_creds(ksu_cred); + + success = do_track_throne_core(data->prune_only); + + revert_creds(saved_cred); + mutex_unlock(&throne_tracker_mutex); + + if (!success && data->retries < 10) { + data->retries++; + pr_info("throne_tracker: retrying (%d/10) in 100ms...\n", data->retries); + // Reschedule exactly this work instance + schedule_delayed_work(&data->dwork, msecs_to_jiffies(100)); + } else { + if (!success) { + pr_warn("throne_tracker: giving up after 10 retries.\n"); + } + data->retries = 0; // Resets for future triggers + } +} + +void track_throne(bool prune_only) +{ + static bool throne_tracker_first_run __read_mostly = true; + + // First scan must be synchronous to not break FDE/FBEv1 on older kernels + if (unlikely(throne_tracker_first_run)) { + mutex_lock(&throne_tracker_mutex); + + const struct cred *saved_cred = override_creds(ksu_cred); + do_track_throne_core(prune_only); + revert_creds(saved_cred); + + mutex_unlock(&throne_tracker_mutex); + throne_tracker_first_run = false; + return; + } + + // For asynchronous runs, if a work is already pending, canceling it + // ensures we don't clobber the prune_only state while it's waiting. + cancel_delayed_work_sync(&throne_data.dwork); + + // Update state safely and queue the new work + throne_data.prune_only = prune_only; + throne_data.retries = 0; + schedule_delayed_work(&throne_data.dwork, 0); +} + +void __init ksu_throne_tracker_init(void) +{ + INIT_DELAYED_WORK(&throne_data.dwork, ksu_throne_work_fn); +} + +void __exit ksu_throne_tracker_exit(void) +{ + cancel_delayed_work_sync(&throne_data.dwork); +} diff --git a/KernelSU-Next/kernel/manager/throne_tracker.h b/KernelSU-Next/kernel/manager/throne_tracker.h new file mode 100644 index 000000000000..3ca346c12f28 --- /dev/null +++ b/KernelSU-Next/kernel/manager/throne_tracker.h @@ -0,0 +1,26 @@ +#ifndef __KSU_H_UID_OBSERVER +#define __KSU_H_UID_OBSERVER + +#include +#ifdef CONFIG_KSU_DISABLE_MANAGER +static inline void ksu_throne_tracker_init() +{ +} + +static inline void ksu_throne_tracker_exit() +{ +} + +static inline void track_throne(bool prune_only) +{ + (void)prune_only; +} +#else +void ksu_throne_tracker_init(); + +void ksu_throne_tracker_exit(); + +void track_throne(bool prune_only); +#endif + +#endif diff --git a/KernelSU-Next/kernel/policy/allowlist.c b/KernelSU-Next/kernel/policy/allowlist.c new file mode 100644 index 000000000000..8f44d800ef91 --- /dev/null +++ b/KernelSU-Next/kernel/policy/allowlist.c @@ -0,0 +1,628 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#include +#endif + +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "runtime/ksud_boot.h" +#include "selinux/selinux.h" +#include "policy/allowlist.h" +#include "manager/manager_identity.h" +#include "infra/su_mount_ns.h" +#include "compat/kernel_compat.h" + +#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 +#define FILE_FORMAT_VERSION 3 // u32 + +#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID +#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:" KERNEL_SU_DOMAIN ":s0" + +static DEFINE_MUTEX(allowlist_mutex); + +// default profiles, these may be used frequently, so we cache it +static struct root_profile default_root_profile; +static struct non_root_profile default_non_root_profile; + +static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly + __aligned(PAGE_SIZE); +static int allow_list_pointer __read_mostly = 0; + +static void remove_uid_from_arr(uid_t uid) +{ + int i; + for (i = 0; i < allow_list_pointer; i++) { + if (allow_list_arr[i] == uid) { + int remaining = allow_list_pointer - 1 - i; + if (remaining > 0) { + memmove(&allow_list_arr[i], &allow_list_arr[i + 1], + remaining * sizeof(allow_list_arr[0])); + } + allow_list_pointer--; + allow_list_arr[allow_list_pointer] = -1; + return; + } + } +} + +static void init_default_profiles() +{ + kernel_cap_t full_cap = CAP_FULL_SET; + + default_root_profile.uid = 0; + default_root_profile.gid = 0; + default_root_profile.groups_count = 1; + default_root_profile.groups[0] = 0; + memcpy(&default_root_profile.capabilities.effective, &full_cap, + sizeof(default_root_profile.capabilities.effective)); + default_root_profile.namespaces = KSU_NS_INHERITED; + strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); + + // This means that we will umount modules by default! + default_non_root_profile.umount_modules = true; +} + +struct perm_data { + struct list_head list; + struct rcu_head rcu; + struct app_profile profile; +}; + +static struct list_head allow_list; + +static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE); +#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1) + +#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist" + +void ksu_persistent_allow_list(void); + +void ksu_show_allow_list(void) +{ + struct perm_data *p = NULL; + pr_info("ksu_show_allow_list\n"); + rcu_read_lock(); + list_for_each_entry_rcu (p, &allow_list, list) { + pr_info("uid :%d, allow: %d\n", p->profile.current_uid, + p->profile.allow_su); + } + rcu_read_unlock(); +} + +#ifdef CONFIG_KSU_DEBUG +static void ksu_grant_root_to_shell() +{ + struct app_profile profile = { + .version = KSU_APP_PROFILE_VER, + .allow_su = true, + .current_uid = 2000, + }; + strcpy(profile.key, "com.android.shell"); + strcpy(profile.rp_config.profile.selinux_domain, + KSU_DEFAULT_SELINUX_DOMAIN); + ksu_set_app_profile(&profile); +} +#endif + +bool ksu_get_app_profile(struct app_profile *profile) +{ + struct perm_data *p = NULL; + bool found = false; + + rcu_read_lock(); + list_for_each_entry_rcu (p, &allow_list, list) { + bool uid_match = profile->current_uid == p->profile.current_uid; + if (uid_match) { + // found it, override it with ours + memcpy(profile, &p->profile, sizeof(*profile)); + found = true; + goto exit; + } + } + +exit: + rcu_read_unlock(); + return found; +} + +static inline bool forbid_system_uid(uid_t uid) +{ +#define SHELL_UID 2000 +#define SYSTEM_UID 1000 + return uid < SHELL_UID && uid != SYSTEM_UID; +} + +static bool profile_valid(struct app_profile *profile) +{ + if (!profile) { + return false; + } + + bool need_migrate_su_domain = false; + + if (unlikely(profile->version == 2)) { + profile->version = KSU_APP_PROFILE_VER; + need_migrate_su_domain = true; + } + + if (strnlen(profile->key, sizeof(profile->key)) >= sizeof(profile->key)) { + pr_err("invalid app_profile key\n"); + return false; + } + + if (profile->version < KSU_APP_PROFILE_VER) { + pr_info("Unsupported profile version: %d\n", profile->version); + return false; + } + + if (profile->allow_su) { +#ifndef CONFIG_KSU_DISABLE_POLICY + if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) { + pr_err("invalid groups_count in app_profile: %s\n", profile->key); + return false; + } + + char *domain = profile->rp_config.profile.selinux_domain; + static const size_t domain_len = sizeof(profile->rp_config.profile.selinux_domain); + if (unlikely(need_migrate_su_domain)) { + if (strncmp(domain, "u:r:su:s0", domain_len) == 0) { + __strscpy_pad(domain, KSU_DEFAULT_SELINUX_DOMAIN, domain_len); + pr_info("migrated profile domain: %s\n", profile->key); + } + } + size_t len = strnlen(domain, domain_len); + + if (len == 0 || len >= domain_len) { + pr_err("invalid selinux_domain in app_profile: %s\n", profile->key); + return false; + } +#endif + } + + return true; +} + +int ksu_set_app_profile(struct app_profile *profile) +{ + struct perm_data *p = NULL, *np; + int result = 0; + u16 count = 0; + + if (!profile_valid(profile)) { + pr_err("Failed to set app profile: invalid profile!\n"); + return -EINVAL; + } + +#ifdef CONFIG_KSU_DISABLE_POLICY + if (profile->allow_su) { + profile->rp_config.use_default = true; + memset(profile->rp_config.template_name, 0, sizeof(profile->rp_config.template_name)); + memset(&profile->rp_config.profile, 0, sizeof(profile->rp_config.profile)); + } else { + profile->nrp_config.use_default = true; + memset(&profile->nrp_config.profile, 0, sizeof(profile->nrp_config.profile)); + } +#endif + + mutex_lock(&allowlist_mutex); + + list_for_each_entry (p, &allow_list, list) { + ++count; + // both uid and package must match, otherwise it will break multiple package with different user id + if (profile->current_uid == p->profile.current_uid && + !strcmp(profile->key, p->profile.key)) { + // found it, just override it all! + np = (struct perm_data *)kzalloc(sizeof(struct perm_data), + GFP_KERNEL); + if (!np) { + result = -ENOMEM; + goto out_unlock; + } + memcpy(&np->profile, profile, sizeof(*profile)); + list_replace_rcu(&p->list, &np->list); + kfree_rcu(p, rcu); + goto out; + } + } + + if (unlikely(count == U16_MAX)) { + pr_err("too many app profile\n"); + result = -E2BIG; + goto out_unlock; + } + + // not found, alloc a new node! + p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL); + if (!p) { + pr_err("ksu_set_app_profile alloc failed\n"); + result = -ENOMEM; + goto out_unlock; + } + + memcpy(&p->profile, profile, sizeof(*profile)); + if (profile->allow_su) { + pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n", + profile->key, profile->current_uid, + profile->rp_config.profile.gid, + profile->rp_config.profile.selinux_domain); + } else { + pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n", + profile->key, profile->current_uid, + profile->nrp_config.profile.umount_modules); + } + + list_add_tail_rcu(&p->list, &allow_list); + +out: + result = 0; + + // check if the default profiles is changed, cache it to a single struct to accelerate access. + if (unlikely(!strcmp(profile->key, "$"))) { +#ifndef CONFIG_KSU_DISABLE_POLICY + // set default non root profile + memcpy(&default_non_root_profile, &profile->nrp_config.profile, sizeof(default_non_root_profile)); +#endif + } else if (unlikely(!strcmp(profile->key, "#"))) { +#ifndef CONFIG_KSU_DISABLE_POLICY + // set default root profile + // TODO: Do we really need this? + memcpy(&default_root_profile, &profile->rp_config.profile, sizeof(default_root_profile)); +#endif + } else if (profile->current_uid <= BITMAP_UID_MAX) { + if (profile->allow_su) + allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= + 1 << (profile->current_uid % BITS_PER_BYTE); + else + allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= + ~(1 << (profile->current_uid % BITS_PER_BYTE)); + } else { + if (profile->allow_su) { + /* + * 1024 apps with uid higher than BITMAP_UID_MAX + * registered to request superuser? + */ + if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) { + pr_err("too many apps registered\n"); + WARN_ON(1); + } else { + allow_list_arr[allow_list_pointer++] = profile->current_uid; + } + } else { + remove_uid_from_arr(profile->current_uid); + } + } + +out_unlock: + mutex_unlock(&allowlist_mutex); + return result; +} + +bool __ksu_is_allow_uid(uid_t uid) +{ + int i; + + if (forbid_system_uid(uid)) { + // do not bother going through the list if it's system + return false; + } + + if (unlikely(is_uid_manager(uid))) { + // manager is always allowed! + return true; + } + + if (likely(uid <= BITMAP_UID_MAX)) { + return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & + (1 << (uid % BITS_PER_BYTE))); + } else { + for (i = 0; i < allow_list_pointer; i++) { + if (allow_list_arr[i] == uid) + return true; + } + } + + return false; +} + +bool __ksu_is_allow_uid_for_current(uid_t uid) +{ + if (unlikely(uid == 0)) { + // already root, but only allow our domain. + return is_ksu_domain(); + } + return __ksu_is_allow_uid(uid); +} + +bool ksu_uid_should_umount(uid_t uid) +{ +#ifndef CONFIG_KSU_DISABLE_POLICY + struct app_profile profile = { .current_uid = uid }; +#endif + if (unlikely(is_uid_manager(uid))) { + // we should not umount on manager! + return false; + } + if (unlikely(uid == WEBVIEW_ZYGOTE_UID)) { + // we should not umount for webview zygote + return false; + } +#ifdef CONFIG_KSU_DISABLE_POLICY + return !__ksu_is_allow_uid(uid); +#else + bool found = ksu_get_app_profile(&profile); + if (!found) { + // no app profile found, it must be non root app + return default_non_root_profile.umount_modules; + } + if (profile.allow_su) { + // if found and it is granted to su, we shouldn't umount for it + return false; + } else { + // found an app profile + if (profile.nrp_config.use_default) { + return default_non_root_profile.umount_modules; + } else { + return profile.nrp_config.profile.umount_modules; + } + } +#endif +} + +void ksu_get_root_profile(uid_t uid, struct root_profile *profile) +{ +#ifdef CONFIG_KSU_DISABLE_POLICY + (void)uid; + memcpy(profile, &default_root_profile, sizeof(*profile)); + return; +#else + struct perm_data *p = NULL; + + if (is_uid_manager(uid)) { + goto use_default; + } + + if (!__ksu_is_allow_uid(uid)) { + goto use_default; + } + + rcu_read_lock(); + list_for_each_entry_rcu (p, &allow_list, list) { + if (uid == p->profile.current_uid && p->profile.allow_su) { + if (!p->profile.rp_config.use_default) { + memcpy(profile, &p->profile.rp_config.profile, + sizeof(*profile)); + rcu_read_unlock(); + return; + } + } + } + rcu_read_unlock(); + +use_default: + // use default profile + memcpy(profile, &default_root_profile, sizeof(*profile)); +#endif +} + +bool ksu_get_allow_list(int *array, u16 length, u16 *out_length, u16 *out_total, + bool allow) +{ + struct perm_data *p = NULL; + u16 i = 0, j = 0; + rcu_read_lock(); + list_for_each_entry_rcu (p, &allow_list, list) { + // pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow); + if (p->profile.allow_su == allow && + !is_uid_manager(p->profile.current_uid)) { + if (j < length) { + array[j++] = p->profile.current_uid; + } + ++i; + } + } + rcu_read_unlock(); + if (out_length) { + *out_length = j; + } + if (out_total) { + *out_total = i; + } + + return true; +} + +static struct work_struct ksu_save_allow_list_work; + +static void do_persistent_allow_list(struct work_struct *work) +{ + u32 magic = FILE_MAGIC; + u32 version = FILE_FORMAT_VERSION; + struct perm_data *p = NULL; + loff_t off = 0; + + const struct cred *saved = override_creds(ksu_cred); + struct file *fp = + ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp)); + goto out; + } + + // store magic and version + if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { + pr_err("save_allow_list write magic failed.\n"); + goto close_file; + } + + if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { + pr_err("save_allow_list write version failed.\n"); + goto close_file; + } + + mutex_lock(&allowlist_mutex); + list_for_each_entry (p, &allow_list, list) { + pr_info("save allow list, name: %s uid :%d, allow: %d\n", + p->profile.key, p->profile.current_uid, p->profile.allow_su); + + ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile), &off); + } + mutex_unlock(&allowlist_mutex); + +close_file: + filp_close(fp, 0); +out: + revert_creds(saved); +} + +void ksu_persistent_allow_list(void) +{ + schedule_work(&ksu_save_allow_list_work); +} + +void ksu_load_allow_list() +{ +#ifdef CONFIG_KSU_DISABLE_POLICY + pr_info("allowlist load skipped because policy is disabled\n"); + return; +#endif + loff_t off = 0; + ssize_t ret = 0; + struct file *fp = NULL; + u32 magic; + u32 version; + +#ifdef CONFIG_KSU_DEBUG + // always allow adb shell by default + ksu_grant_root_to_shell(); +#endif + + // load allowlist now! + fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp)); + return; + } + + // verify magic + if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || + magic != FILE_MAGIC) { + pr_err("allowlist file invalid: %d!\n", magic); + goto exit; + } + + if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { + pr_err("allowlist read version: %d failed\n", version); + goto exit; + } + + pr_info("allowlist version: %d\n", version); + + while (true) { + struct app_profile profile; + + ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile), &off); + + if (ret <= 0) { + pr_info("load_allow_list read err: %zd\n", ret); + break; + } + + pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", profile.key, + profile.current_uid, profile.allow_su); + ksu_set_app_profile(&profile); + } + +exit: + ksu_show_allow_list(); + filp_close(fp, 0); +} + +void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), + void *data) +{ + struct perm_data *np = NULL; + struct perm_data *n = NULL; + + if (!ksu_boot_completed) { + pr_info("boot not completed, skip prune\n"); + return; + } + + bool modified = false; + mutex_lock(&allowlist_mutex); + list_for_each_entry_safe (np, n, &allow_list, list) { + uid_t uid = np->profile.current_uid; + char *package = np->profile.key; + // we use this uid for special cases, don't prune it! + bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID; + if (!is_preserved_uid && !is_uid_valid(uid, package, data)) { + modified = true; + pr_info("prune uid: %d, package: %s\n", uid, package); + list_del_rcu(&np->list); + kfree_rcu(np, rcu); + if (likely(uid <= BITMAP_UID_MAX)) { + allow_list_bitmap[uid / BITS_PER_BYTE] &= + ~(1 << (uid % BITS_PER_BYTE)); + } + remove_uid_from_arr(uid); + } + } + mutex_unlock(&allowlist_mutex); + + if (modified) { + smp_mb(); + ksu_persistent_allow_list(); + } +} + +void __init ksu_allowlist_init(void) +{ + int i; + + BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE); + BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE); + + for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++) + allow_list_arr[i] = -1; + + INIT_LIST_HEAD(&allow_list); + + INIT_WORK(&ksu_save_allow_list_work, do_persistent_allow_list); + + init_default_profiles(); +} + +void __exit ksu_allowlist_exit(void) +{ + struct perm_data *np = NULL; + struct perm_data *n = NULL; + + cancel_work_sync(&ksu_save_allow_list_work); + + // free allowlist + mutex_lock(&allowlist_mutex); + list_for_each_entry_safe (np, n, &allow_list, list) { + list_del(&np->list); + kfree(np); + } + mutex_unlock(&allowlist_mutex); +} diff --git a/KernelSU-Next/kernel/policy/allowlist.h b/KernelSU-Next/kernel/policy/allowlist.h new file mode 100644 index 000000000000..1d52ea64192d --- /dev/null +++ b/KernelSU-Next/kernel/policy/allowlist.h @@ -0,0 +1,54 @@ +#ifndef __KSU_H_ALLOWLIST +#define __KSU_H_ALLOWLIST + +#include +#include +#include "app_profile.h" + +#define PER_USER_RANGE 100000 +#define WEBVIEW_ZYGOTE_UID 1053 +#define FIRST_APPLICATION_UID 10000 +#define LAST_APPLICATION_UID 19999 +#define FIRST_ISOLATED_UID 99000 +#define LAST_ISOLATED_UID 99999 + +void ksu_allowlist_init(void); + +void ksu_allowlist_exit(void); + +void ksu_load_allow_list(void); + +void ksu_show_allow_list(void); + +// Check if the uid is in allow list +bool __ksu_is_allow_uid(uid_t uid); +#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid)) + +// Check if the uid is in allow list, or current is ksu domain root +bool __ksu_is_allow_uid_for_current(uid_t uid); +#define ksu_is_allow_uid_for_current(uid) unlikely(__ksu_is_allow_uid_for_current(uid)) + +bool ksu_get_allow_list(int *array, u16 length, u16 *out_length, u16 *out_total, + bool allow); + +void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data); +void ksu_persistent_allow_list(); + +bool ksu_get_app_profile(struct app_profile *); +int ksu_set_app_profile(struct app_profile *); + +bool ksu_uid_should_umount(uid_t uid); +void ksu_get_root_profile(uid_t uid, struct root_profile *); + +static inline bool is_appuid(uid_t uid) +{ + uid_t appid = uid % PER_USER_RANGE; + return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID; +} + +static inline bool is_isolated_process(uid_t uid) +{ + uid_t appid = uid % PER_USER_RANGE; + return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID; +} +#endif diff --git a/KernelSU-Next/kernel/policy/app_profile.c b/KernelSU-Next/kernel/policy/app_profile.c new file mode 100644 index 000000000000..29815c500d29 --- /dev/null +++ b/KernelSU-Next/kernel/policy/app_profile.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#include // signal_struct +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "policy/allowlist.h" +#include "policy/app_profile.h" +#include "arch.h" +#include "compat/kernel_compat.h" +#include "klog.h" // IWYU pragma: keep +#include "selinux/selinux.h" +#include "infra/su_mount_ns.h" +#include "hook/hook_manager.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0) +static struct group_info root_groups = { .usage = REFCOUNT_INIT(2) }; +#else +static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; +#endif + +void setup_groups(struct root_profile *profile, struct cred *cred) +{ + if (profile->groups_count > KSU_MAX_GROUPS) { + pr_warn("Failed to setgroups, too large group: %d!\n", profile->uid); + return; + } + + if (profile->groups_count == 1 && profile->groups[0] == 0) { + // setgroup to root and return early. + if (cred->group_info) + put_group_info(cred->group_info); + cred->group_info = get_group_info(&root_groups); + return; + } + + u32 ngroups = profile->groups_count; + struct group_info *group_info = groups_alloc(ngroups); + if (!group_info) { + pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid); + return; + } + + int i; + for (i = 0; i < ngroups; i++) { + gid_t gid = profile->groups[i]; + kgid_t kgid = make_kgid(current_user_ns(), gid); + if (!gid_valid(kgid)) { + pr_warn("Failed to setgroups, invalid gid: %d\n", gid); + put_group_info(group_info); + return; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) + group_info->gid[i] = kgid; +#else + GROUP_AT(group_info, i) = kgid; +#endif + } + + groups_sort(group_info); + set_groups(cred, group_info); + put_group_info(group_info); +} + +void seccomp_filter_release(struct task_struct *tsk); + +void disable_seccomp(void) +{ + // https://github.com/backslashxx/KernelSU/tree/e28930645e764b9f0e5d0d1b0d5e236464939075/kernel/app_profile.c + if (!!!current->seccomp.mode) { + return; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ + defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)) + struct task_struct *fake; + fake = kmalloc(sizeof(*fake), GFP_ATOMIC); + if (!fake) { + pr_err("%s: cannot allocate fake struct!\n", __func__); + return; + } +#endif + + // Refer to kernel/seccomp.c: seccomp_set_mode_strict + // When disabling Seccomp, ensure that current->sighand->siglock is held during the operation. + spin_lock_irq(¤t->sighand->siglock); + // disable seccomp +#if defined(CONFIG_GENERIC_ENTRY) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + clear_syscall_work(SECCOMP); +#else + clear_thread_flag(TIF_SECCOMP); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ + defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)) + memcpy(fake, current, sizeof(*fake)); + atomic_set(¤t->seccomp.filter_count, 0); +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) && \ + !defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)) + // put_seccomp_filter is allowed while we holding sighand + put_seccomp_filter(current); +#endif + current->seccomp.mode = 0; + current->seccomp.filter = NULL; + + spin_unlock_irq(¤t->sighand->siglock); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ + defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0) + // https://github.com/torvalds/linux/commit/bfafe5efa9754ebc991750da0bcca2a6694f3ed3#diff-45eb79a57536d8eccfc1436932f093eb5c0b60d9361c39edb46581ad313e8987R576-R577 + fake->flags |= PF_EXITING; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + // https://github.com/torvalds/linux/commit/0d8315dddd2899f519fe1ca3d4d5cdaf44ea421e#diff-45eb79a57536d8eccfc1436932f093eb5c0b60d9361c39edb46581ad313e8987R556-R558 + fake->sighand = NULL; +#endif + seccomp_filter_release(fake); + kfree(fake); +#endif +} + +int escape_with_root_profile(void) +{ + struct cred *cred; + struct root_profile profile; + struct user_struct *new_user; + + cred = prepare_creds(); + if (!cred) { + pr_warn("prepare_creds failed!\n"); + return 0; + } + + if (cred->euid.val == 0) { + pr_warn("Already root, don't escape!\n"); + goto out_abort_creds; + } + + ksu_get_root_profile(cred->uid.val, &profile); + + cred->uid.val = profile.uid; + cred->suid.val = profile.uid; + cred->euid.val = profile.uid; + cred->fsuid.val = profile.uid; + + cred->gid.val = profile.gid; + cred->fsgid.val = profile.gid; + cred->sgid.val = profile.gid; + cred->egid.val = profile.gid; + cred->securebits = 0; + + BUILD_BUG_ON(sizeof(profile.capabilities.effective) != + sizeof(kernel_cap_t)); + + /* + * Mirror the kernel set*uid path: update cred->user first, then + * cred->ucounts, before commit_creds(). commit_creds() moves + * RLIMIT_NPROC accounting based on cred->user; if uid changes while + * user/ucounts stay stale, the old charge can remain pinned to the + * previous UID. + * See kernel/sys.c:set_user() and kernel/cred.c:set_cred_ucounts() / + * commit_creds(): + * https://github.com/torvalds/linux/blob/v5.14/kernel/sys.c + * https://github.com/torvalds/linux/blob/v5.14/kernel/cred.c + */ + new_user = alloc_uid(cred->uid); + if (!new_user) { + goto out_abort_creds; + } + + free_uid(cred->user); + cred->user = new_user; + + // v5.14+ added cred->ucounts, so we must refresh it after changing uid/user: + // https://github.com/torvalds/linux/commit/905ae01c4ae2ae3df05bb141801b1db4b7d83c61#diff-ff6060da281bd9ef3f24e17b77a9b0b5b2ed2d7208bb69b29107bee69732bd31 + // on older kernels, per-UID process accounting lives in user_struct. +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) + if (set_cred_ucounts(cred)) { + goto out_abort_creds; + } +#endif + + // setup capabilities + // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process + // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec! + u64 cap_for_ksud = profile.capabilities.effective | CAP_DAC_READ_SEARCH; + memcpy(&cred->cap_effective, &cap_for_ksud, sizeof(cred->cap_effective)); + memcpy(&cred->cap_permitted, &profile.capabilities.effective, + sizeof(cred->cap_permitted)); + memcpy(&cred->cap_bset, &profile.capabilities.effective, + sizeof(cred->cap_bset)); + + setup_groups(&profile, cred); + setup_selinux(profile.selinux_domain, cred); + + commit_creds(cred); + + disable_seccomp(); + +#ifdef KSU_KPROBES_HOOK + struct task_struct *p = current; + struct task_struct *t; + for_each_thread (p, t) { + ksu_set_task_tracepoint_flag(t); + } +#endif + + setup_mount_ns(profile.namespaces); + return 0; + +out_abort_creds: + abort_creds(cred); + return 0; +} + +void escape_to_root_for_init(void) { + struct cred *cred = prepare_creds(); + if (!cred) { + pr_err("Failed to prepare init's creds!\n"); + return; + } + + setup_selinux(KERNEL_SU_CONTEXT, cred); + commit_creds(cred); +} diff --git a/KernelSU-Next/kernel/policy/app_profile.h b/KernelSU-Next/kernel/policy/app_profile.h new file mode 100644 index 000000000000..18e9c74fcd83 --- /dev/null +++ b/KernelSU-Next/kernel/policy/app_profile.h @@ -0,0 +1,11 @@ +#ifndef __KSU_H_APP_PROFILE +#define __KSU_H_APP_PROFILE + +#include "uapi/app_profile.h" // IWYU pragma: keep + +// Escalate current process to root with the appropriate profile +int escape_with_root_profile(void); + +void escape_to_root_for_init(void); + +#endif diff --git a/KernelSU-Next/kernel/policy/feature.c b/KernelSU-Next/kernel/policy/feature.c new file mode 100644 index 000000000000..d19b65439ef3 --- /dev/null +++ b/KernelSU-Next/kernel/policy/feature.c @@ -0,0 +1,173 @@ +#include "feature.h" +#include "klog.h" // IWYU pragma: keep + +#include + +static const struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX]; + +static DEFINE_MUTEX(feature_mutex); + +int ksu_register_feature_handler(const struct ksu_feature_handler *handler) +{ + if (!handler) { + pr_err("feature: register handler is NULL\n"); + return -EINVAL; + } + + if (handler->feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", handler->feature_id); + return -EINVAL; + } + + if (!handler->get_handler && !handler->set_handler) { + pr_err("feature: no handler provided for feature %u\n", handler->feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + if (feature_handlers[handler->feature_id]) { + pr_warn("feature: handler for %u already registered, overwriting\n", + handler->feature_id); + } + + feature_handlers[handler->feature_id] = handler; + + pr_info("feature: registered handler for %s (id=%u)\n", + handler->name ? handler->name : "unknown", handler->feature_id); + + mutex_unlock(&feature_mutex); + return 0; +} + +int ksu_unregister_feature_handler(u32 feature_id) +{ + int ret = 0; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + if (!feature_handlers[feature_id]) { + pr_warn("feature: no handler registered for %u\n", feature_id); + ret = -ENOENT; + goto out; + } + + feature_handlers[feature_id] = NULL; + + pr_info("feature: unregistered handler for id=%u\n", feature_id); + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_get_feature(u32 feature_id, u64 *value, bool *supported) +{ + int ret = 0; + const struct ksu_feature_handler *handler; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + if (!value || !supported) { + pr_err("feature: invalid parameters\n"); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + handler = feature_handlers[feature_id]; + + if (!handler) { + *supported = false; + *value = 0; + pr_debug("feature: feature %u not supported\n", feature_id); + goto out; + } + + *supported = true; + + if (!handler->get_handler) { + pr_warn("feature: no get_handler for feature %u\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + ret = handler->get_handler(value); + if (ret) { + pr_err("feature: get_handler for %u failed: %d\n", feature_id, ret); + } + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_set_feature(u32 feature_id, u64 value) +{ + int ret = 0; + const struct ksu_feature_handler *handler; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + handler = feature_handlers[feature_id]; + + if (!handler) { + pr_err("feature: feature %u not registered\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + if (!handler->set_handler) { + pr_warn("feature: no set_handler for feature %u\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + ret = handler->set_handler(value); + if (ret) { + pr_err("feature: set_handler for %u failed: %d\n", feature_id, ret); + } + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +void __init ksu_feature_init(void) +{ + int i; + + for (i = 0; i < KSU_FEATURE_MAX; i++) { + feature_handlers[i] = NULL; + } + + pr_info("feature: feature management initialized\n"); +} + +void __exit ksu_feature_exit(void) +{ + int i; + + mutex_lock(&feature_mutex); + + for (i = 0; i < KSU_FEATURE_MAX; i++) { + feature_handlers[i] = NULL; + } + + mutex_unlock(&feature_mutex); + + pr_info("feature: feature management cleaned up\n"); +} diff --git a/KernelSU-Next/kernel/policy/feature.h b/KernelSU-Next/kernel/policy/feature.h new file mode 100644 index 000000000000..7c3d5cfcc068 --- /dev/null +++ b/KernelSU-Next/kernel/policy/feature.h @@ -0,0 +1,29 @@ +#ifndef __KSU_H_FEATURE +#define __KSU_H_FEATURE + +#include +#include "uapi/feature.h" // IWYU pragma: keep + +typedef int (*ksu_feature_get_t)(u64 *value); +typedef int (*ksu_feature_set_t)(u64 value); + +struct ksu_feature_handler { + u32 feature_id; + const char *name; + ksu_feature_get_t get_handler; + ksu_feature_set_t set_handler; +}; + +int ksu_register_feature_handler(const struct ksu_feature_handler *handler); + +int ksu_unregister_feature_handler(u32 feature_id); + +int ksu_get_feature(u32 feature_id, u64 *value, bool *supported); + +int ksu_set_feature(u32 feature_id, u64 value); + +void ksu_feature_init(void); + +void ksu_feature_exit(void); + +#endif // __KSU_H_FEATURE diff --git a/KernelSU-Next/kernel/runtime/boot_event.c b/KernelSU-Next/kernel/runtime/boot_event.c new file mode 100644 index 000000000000..fb44d971debc --- /dev/null +++ b/KernelSU-Next/kernel/runtime/boot_event.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include "policy/allowlist.h" +#include "klog.h" // IWYU pragma: keep +#include "runtime/ksud_boot.h" +#include "runtime/ksud.h" +#include "manager/manager_observer.h" +#include "manager/throne_tracker.h" + +bool ksu_module_mounted __read_mostly = false; +bool ksu_boot_completed __read_mostly = false; +extern void stop_input_hook(); + +extern void ksu_avc_spoof_late_init(); + +void on_post_fs_data(void) +{ + static bool done = false; + if (done) { + pr_info("on_post_fs_data already done\n"); + return; + } + done = true; + pr_info("on_post_fs_data!\n"); + + ksu_load_allow_list(); + ksu_observer_init(); + // sanity check, this may influence the performance + stop_input_hook(); +} + +extern void ext4_unregister_sysfs(struct super_block *sb); + +int nuke_ext4_sysfs(const char *mnt) +{ + struct path path; + int err = kern_path(mnt, 0, &path); + if (err) { + pr_err("nuke path err: %d\n", err); + return err; + } + + struct super_block *sb = path.dentry->d_inode->i_sb; + const char *name = sb->s_type->name; + if (strcmp(name, "ext4") != 0) { + pr_info("nuke but module aren't mounted\n"); + path_put(&path); + return -EINVAL; + } + + ext4_unregister_sysfs(sb); + path_put(&path); + return 0; +} + +void on_module_mounted(void) +{ + pr_info("on_module_mounted!\n"); + ksu_module_mounted = true; +} + +void on_boot_completed(void) +{ + ksu_boot_completed = true; + pr_info("on_boot_completed!\n"); + track_throne(true); + ksu_avc_spoof_late_init(); +} diff --git a/KernelSU-Next/kernel/runtime/ksud.h b/KernelSU-Next/kernel/runtime/ksud.h new file mode 100644 index 000000000000..981a1706f2e9 --- /dev/null +++ b/KernelSU-Next/kernel/runtime/ksud.h @@ -0,0 +1,13 @@ +#ifndef __KSU_H_KSUD +#define __KSU_H_KSUD + +#include + +#define KSUD_PATH "/data/adb/ksud" + +void ksu_ksud_init(); +void ksu_ksud_exit(); + +extern bool ksu_execveat_hook __read_mostly; + +#endif diff --git a/KernelSU-Next/kernel/runtime/ksud_boot.h b/KernelSU-Next/kernel/runtime/ksud_boot.h new file mode 100644 index 000000000000..39d9e768abd4 --- /dev/null +++ b/KernelSU-Next/kernel/runtime/ksud_boot.h @@ -0,0 +1,17 @@ +#ifndef __KSU_H_KSUD_BOOT +#define __KSU_H_KSUD_BOOT + +#include + +void on_post_fs_data(void); +void on_module_mounted(void); +void on_boot_completed(void); + +bool ksu_is_safe_mode(void); + +int nuke_ext4_sysfs(const char* mnt); + +extern bool ksu_module_mounted; +extern bool ksu_boot_completed; + +#endif // __KSU_H_KSUD_BOOT \ No newline at end of file diff --git a/KernelSU-Next/kernel/runtime/ksud_integration.c b/KernelSU-Next/kernel/runtime/ksud_integration.c new file mode 100644 index 000000000000..3d93d659ced2 --- /dev/null +++ b/KernelSU-Next/kernel/runtime/ksud_integration.c @@ -0,0 +1,912 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +#include +#else +#include +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#include +#endif +#ifdef KSU_KPROBES_HOOK +#include +#endif +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif + +#include "arch.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "ksud.h" +#include "ksud_boot.h" +#include "selinux/selinux.h" +#include "compat/kernel_compat.h" + +static const char KERNEL_SU_RC[] = + "\n" + + "on post-fs-data\n" + " start logd\n" + // We should wait for the post-fs-data finish + " exec u:r:" KERNEL_SU_DOMAIN ":s0 root -- " KSUD_PATH " post-fs-data\n" + "\n" + + "on nonencrypted\n" + " exec u:r:" KERNEL_SU_DOMAIN ":s0 root -- " KSUD_PATH " services\n" + "\n" + + "on property:vold.decrypt=trigger_restart_framework\n" + " exec u:r:" KERNEL_SU_DOMAIN ":s0 root -- " KSUD_PATH " services\n" + "\n" + + "on property:sys.boot_completed=1\n" + " exec u:r:" KERNEL_SU_DOMAIN ":s0 root -- " KSUD_PATH " boot-completed\n" + "\n" + + "\n"; + +void stop_init_rc_hook(); +void stop_execve_hook(); +void stop_input_hook(); + +#ifdef KSU_KPROBES_HOOK +static struct work_struct __maybe_unused stop_init_rc_hook_work; +static struct work_struct __maybe_unused stop_execve_hook_work; +static struct work_struct __maybe_unused stop_input_hook_work; +#else +bool ksu_init_rc_hook __read_mostly = true; +bool __maybe_unused ksu_vfs_read_hook = true; +bool ksu_input_hook __read_mostly = true; +bool ksu_execveat_hook __read_mostly = true; +#endif + +#define MAX_ARG_STRINGS 0x7FFFFFFF +struct user_arg_ptr { +#ifdef CONFIG_COMPAT + bool is_compat; +#endif + union { + const char __user *const __user *native; +#ifdef CONFIG_COMPAT + const compat_uptr_t __user *compat; +#endif + } ptr; +}; + +static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) +{ + const char __user *native; + +#ifdef CONFIG_COMPAT + if (unlikely(argv.is_compat)) { + compat_uptr_t compat; + + if (get_user(compat, argv.ptr.compat + nr)) + return ERR_PTR(-EFAULT); + + return compat_ptr(compat); + } +#endif + + if (get_user(native, argv.ptr.native + nr)) + return ERR_PTR(-EFAULT); + + return native; +} + +/* + * count() counts the number of strings in array ARGV. + */ + +/* + * Make sure old GCC compiler can use __maybe_unused, + * Test passed in 4.4.x ~ 4.9.x when use GCC. + */ + +static int __maybe_unused count(struct user_arg_ptr argv, int max) +{ + int i = 0; + + if (argv.ptr.native != NULL) { + for (;;) { + const char __user *p = get_user_arg_ptr(argv, i); + + if (!p) + break; + + if (IS_ERR(p)) + return -EFAULT; + + if (i >= max) + return -E2BIG; + ++i; + + if (fatal_signal_pending(current)) + return -ERESTARTNOHAND; + } + } + return i; +} + +static void on_post_fs_data_cbfun(struct callback_head *cb) +{ + on_post_fs_data(); +} + +static struct callback_head on_post_fs_data_cb = { .func = + on_post_fs_data_cbfun }; + +static bool check_argv(struct user_arg_ptr argv, int index, + const char *expected, char *buf, size_t buf_len) +{ + const char __user *p; + int argc; + + argc = count(argv, MAX_ARG_STRINGS); + if (argc <= index) + return false; + + p = get_user_arg_ptr(argv, index); + if (!p || IS_ERR(p)) + return false; + + if (strncpy_from_user_nofault(buf, p, buf_len) <= 0) + return false; + + buf[buf_len - 1] = '\0'; + return !strcmp(buf, expected); +} + +static void ksu_initialize_selinux_tw_func(struct callback_head *cb) +{ + apply_kernelsu_rules(); + cache_sid(); + setup_ksu_cred(); + kfree(cb); +} + +// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version +int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, + struct user_arg_ptr *argv, + struct user_arg_ptr *envp, int *flags) +{ +#ifndef KSU_KPROBES_HOOK + if (!ksu_execveat_hook) { + return 0; + } +#endif + struct filename *filename; + + static const char app_process[] = "/system/bin/app_process"; + static bool first_zygote = true; + + /* This applies to versions Android 10+ */ + static const char system_bin_init[] = "/system/bin/init"; + /* This applies to versions between Android 6 ~ 9 */ + static const char old_system_init[] = "/init"; + static bool init_second_stage_executed = false; + + if (!filename_ptr) + return 0; + + filename = *filename_ptr; + if (IS_ERR(filename)) { + return 0; + } + + if (unlikely(!memcmp(filename->name, system_bin_init, + sizeof(system_bin_init) - 1) && + argv)) { + char buf[16]; + if (!init_second_stage_executed && + check_argv(*argv, 1, "second_stage", buf, sizeof(buf))) { + pr_info("/system/bin/init second_stage executed\n"); + struct callback_head *cb = kzalloc(sizeof(*cb), GFP_ATOMIC); + if (cb) { + cb->func = ksu_initialize_selinux_tw_func; + if (task_work_add(current, cb, TWA_RESUME)) { + kfree(cb); + pr_warn("ksu_initialize_selinux failed to add task work\n"); + } + } else { + pr_warn( + "ksu_initialize_selinux failed to allocate task work\n"); + } + init_second_stage_executed = true; + } + } else if (unlikely(!memcmp(filename->name, old_system_init, + sizeof(old_system_init) - 1) && + argv)) { + char buf[16]; + if (!init_second_stage_executed && + check_argv(*argv, 1, "--second-stage", buf, sizeof(buf))) { + /* This applies to versions between Android 6 ~ 7 */ + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + setup_ksu_cred(); + init_second_stage_executed = true; + } else if (count(*argv, MAX_ARG_STRINGS) == 1 && + !init_second_stage_executed && envp) { + /* This applies to versions between Android 8 ~ 9 */ + int envc = count(*envp, MAX_ARG_STRINGS); + if (envc > 0) { + int n; + for (n = 1; n <= envc; n++) { + const char __user *p = get_user_arg_ptr(*envp, n); + if (!p || IS_ERR(p)) { + continue; + } + char env[256]; + // Reading environment variable strings from user space + if (strncpy_from_user_nofault(env, p, sizeof(env)) < 0) + continue; + // Parsing environment variable names and values + char *env_name = env; + char *env_value = strchr(env, '='); + if (env_value == NULL) + continue; + // Replace equal sign with string terminator + *env_value = '\0'; + env_value++; + // Check if the environment variable name and value are matching + if (!strcmp(env_name, "INIT_SECOND_STAGE") && + (!strcmp(env_value, "1") || + !strcmp(env_value, "true"))) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + setup_ksu_cred(); + init_second_stage_executed = true; + } + } + } + } + } + + if (unlikely(first_zygote && !memcmp(filename->name, app_process, + sizeof(app_process) - 1) && argv)) { + char buf[16]; + if (check_argv(*argv, 1, "-Xzygote", buf, sizeof(buf))) { + pr_info("exec zygote, /data prepared, second_stage: %d\n", + init_second_stage_executed); + rcu_read_lock(); + struct task_struct *init_task = + rcu_dereference(current->real_parent); + if (init_task) + task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); + rcu_read_unlock(); + first_zygote = false; + stop_execve_hook(); + } + } + + return 0; +} + +static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); +static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *); +static struct file_operations fops_proxy; +static ssize_t ksu_rc_pos = 0; +const size_t ksu_rc_len = sizeof(KERNEL_SU_RC) - 1; + +// https://cs.android.com/android/platform/superproject/main/+/main:system/core/init/parser.cpp;l=144;drc=61197364367c9e404c7da6900658f1b16c42d0da +// https://cs.android.com/android/platform/superproject/main/+/main:system/libbase/file.cpp;l=241-243;drc=61197364367c9e404c7da6900658f1b16c42d0da +// The system will read init.rc file until EOF, whenever read() returns 0, +// so we begin append ksu rc when we meet EOF. + +static ssize_t read_proxy(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + ssize_t ret = 0; + size_t append_count; + if (ksu_rc_pos && ksu_rc_pos < ksu_rc_len) + goto append_ksu_rc; + + ret = orig_read(file, buf, count, pos); + if (ret != 0 || ksu_rc_pos >= ksu_rc_len) { + return ret; + } else { + pr_info("read_proxy: orig read finished, start append rc\n"); + } +append_ksu_rc: + append_count = ksu_rc_len - ksu_rc_pos; + if (append_count > count - ret) + append_count = count - ret; + // copy_to_user returns the number of not copied + if (copy_to_user(buf + ret, KERNEL_SU_RC + ksu_rc_pos, append_count)) { + pr_info("read_proxy: append error, totally appended %ld\n", ksu_rc_pos); + } else { + pr_info("read_proxy: append %ld\n", append_count); + + ksu_rc_pos += append_count; + if (ksu_rc_pos == ksu_rc_len) { + pr_info("read_proxy: append done\n"); + } + ret += append_count; + } + + return ret; +} + +static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to) +{ + ssize_t ret = 0; + size_t append_count; + if (ksu_rc_pos && ksu_rc_pos < ksu_rc_len) + goto append_ksu_rc; + + ret = orig_read_iter(iocb, to); + if (ret != 0 || ksu_rc_pos >= ksu_rc_len) { + return ret; + } else { + pr_info("read_iter_proxy: orig read finished, start append rc\n"); + } +append_ksu_rc: + // copy_to_iter returns the number of copied bytes + append_count = + copy_to_iter(KERNEL_SU_RC + ksu_rc_pos, ksu_rc_len - ksu_rc_pos, to); + if (!append_count) { + pr_info("read_iter_proxy: append error, totally appended %ld\n", + ksu_rc_pos); + } else { + pr_info("read_iter_proxy: append %ld\n", append_count); + + ksu_rc_pos += append_count; + if (ksu_rc_pos == ksu_rc_len) { + pr_info("read_iter_proxy: append done\n"); + } + ret += append_count; + } + return ret; +} + +static bool check_init_path(char *dpath) +{ + const char *valid_paths[] = { "/system/etc/init/hw/init.rc", + "/init.rc" }; + bool path_match = false; + int i; + + for (i = 0; i < ARRAY_SIZE(valid_paths); i++) { + if (strcmp(dpath, valid_paths[i]) == 0) { + path_match = true; + break; + } + } + + if (!path_match) { + pr_err("vfs_read: couldn't determine init.rc path for %s\n", + dpath); + return false; + } + + pr_info("vfs_read: got init.rc path: %s\n", dpath); + return true; +} + +static bool is_init_rc(struct file *fp) +{ + if (strcmp(current->comm, "init")) { + // we are only interest in `init` process + return false; + } + + if (!d_is_reg(fp->f_path.dentry)) { + return false; + } + + const char *short_name = fp->f_path.dentry->d_name.name; + if (strcmp(short_name, "init.rc")) { + // we are only interest `init.rc` file name file + return false; + } + char path[256]; + char *dpath = d_path(&fp->f_path, path, sizeof(path)); + + if (IS_ERR(dpath)) { + return false; + } + + if (!!strcmp(dpath, "/init.rc") && !!strcmp(dpath, "/system/etc/init/hw/init.rc")) { + return false; + } + + return true; +} + +static void ksu_apply_init_rc_proxy(struct file *file) +{ + // we only process the first read + static bool rc_hooked = false; + if (rc_hooked) { + // we don't need these kprobe, unregister it! + stop_init_rc_hook(); + return; + } + rc_hooked = true; + + // now we can sure that the init process is reading + // `/system/etc/init/init.rc` + + pr_info("read init.rc, comm: %s, rc_count: %zu\n", current->comm, + ksu_rc_len); + + // Now we need to proxy the read and modify the result! + // But, we can not modify the file_operations directly, because it's in read-only memory. + // We just replace the whole file_operations with a proxy one. + memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations)); + orig_read = file->f_op->read; + if (orig_read) { + fops_proxy.read = read_proxy; + } + orig_read_iter = file->f_op->read_iter; + if (orig_read_iter) { + fops_proxy.read_iter = read_iter_proxy; + } + // replace the file_operations + file->f_op = &fops_proxy; +} + +void ksu_handle_sys_read(unsigned int fd) +{ + struct file *file = fget(fd); + if (!file) return; + + if (is_init_rc(file)) { + ksu_apply_init_rc_proxy(file); + } + + fput(file); +} + +static unsigned int volumedown_pressed_count = 0; + +static bool is_volumedown_enough(unsigned int count) +{ + return count >= 3; +} + +int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, + int *value) +{ +#ifndef KSU_KPROBES_HOOK + if (!ksu_input_hook) { + return 0; + } +#endif + if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { + int val = *value; + pr_info("KEY_VOLUMEDOWN val: %d\n", val); + if (val) { + // key pressed, count it + volumedown_pressed_count += 1; + if (is_volumedown_enough(volumedown_pressed_count)) { + stop_input_hook(); + } + } + } + + return 0; +} + +bool ksu_is_safe_mode() +{ + static bool safe_mode = false; + if (safe_mode) { + // don't need to check again, userspace may call multiple times + return true; + } + + if (ksu_late_loaded) { + return false; + } + + // stop hook first! + stop_input_hook(); + + pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count); + if (is_volumedown_enough(volumedown_pressed_count)) { + // pressed over 3 times + pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n"); + safe_mode = true; + return true; + } + + return false; +} + +#ifdef KSU_KPROBES_HOOK + +static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM1(real_regs); + const char __user *const __user *__argv = + (const char __user *const __user *)PT_REGS_PARM2(real_regs); + struct user_arg_ptr argv = { .ptr.native = __argv }; + struct filename filename_in, *filename_p; + char path[32]; + long ret; + unsigned long addr; + const char __user *fn; + + if (!filename_user) + return 0; + + addr = untagged_addr((unsigned long)*filename_user); + fn = (const char __user *)addr; + + memset(path, 0, sizeof(path)); + ret = strncpy_from_user_nofault(path, fn, 32); + if (ret < 0 && preempt_count()) { + preempt_enable_no_resched_notrace(); + ret = strncpy_from_user(path, fn, 32); + preempt_disable_notrace(); + } + + if (ret < 0) { + pr_err("Access filename failed for execve_handler_pre\n"); + return 0; + } + filename_in.name = path; + + filename_p = &filename_in; + return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL); +} + +static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + unsigned int fd = PT_REGS_PARM1(real_regs); + + ksu_handle_sys_read(fd); + return 0; +} + +static int sys_fstat_handler_pre(struct kretprobe_instance *p, + struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + unsigned int fd = PT_REGS_PARM1(real_regs); + void *statbuf = (void *)PT_REGS_PARM2(real_regs); + *(void **)&p->data = NULL; + + struct file *file = fget(fd); + if (!file) + return 1; + if (is_init_rc(file)) { + pr_info("stat init.rc"); + fput(file); + *(void **)&p->data = statbuf; + return 0; + } + fput(file); + return 1; +} + +static int sys_fstat_handler_post(struct kretprobe_instance *p, + struct pt_regs *regs) +{ + void __user *statbuf = *(void **)&p->data; + size_t size_offset; + size_t size_bytes; + long size = 0; + long new_size = 0; + + if (!statbuf) return 0; + +#ifdef CONFIG_COMPAT + // Check if the process (like init) is 32-bit running on a 64-bit kernel + if (in_compat_syscall()) { + size_offset = offsetof(struct compat_stat, st_size); + size_bytes = sizeof(compat_off_t); + } else +#endif + { + // Native 64-bit or pure 32-bit kernel + size_offset = offsetof(struct stat, st_size); + size_bytes = sizeof(off_t); + } + + void __user *st_size_ptr = statbuf + size_offset; + + // Kretprobes run in Atomic Context. We MUST disable pagefaults + // to prevent copy_to_user from sleeping and causing a Kernel Panic. + pagefault_disable(); + + if (!ksu_copy_from_user_nofault(&size, st_size_ptr, size_bytes)) { + new_size = size + ksu_rc_len; + pr_info("adding ksu_rc_len: %ld -> %ld", size, new_size); + + // Attempt to overwrite the file size in userspace safely + if (!copy_to_user(st_size_ptr, &new_size, size_bytes)) { + pr_info("added ksu_rc_len"); + } else { + pr_err("add ksu_rc_len failed: statbuf 0x%lx", + (unsigned long)st_size_ptr); + } + } + + pagefault_enable(); + + return 0; +} + +static int input_handle_event_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ + unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); + unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); + int *value = (int *)&PT_REGS_CCALL_PARM4(regs); + return ksu_handle_input_handle_event(type, code, value); +} + +static struct kprobe execve_kp = { + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, +}; +static struct kprobe sys_read_kp = { + .symbol_name = SYS_READ_SYMBOL, + .pre_handler = sys_read_handler_pre, +}; + +static struct kretprobe sys_fstat_kp = { + .kp.symbol_name = SYS_FSTAT_SYMBOL, + .entry_handler = sys_fstat_handler_pre, + .handler = sys_fstat_handler_post, + .data_size = sizeof(void *), +}; + +static struct kprobe input_event_kp = { + .symbol_name = "input_event", + .pre_handler = input_handle_event_handler_pre, +}; + +static void do_stop_init_rc_hook(struct work_struct *work) +{ + unregister_kprobe(&sys_read_kp); + unregister_kretprobe(&sys_fstat_kp); +} + +static void do_stop_execve_hook(struct work_struct *work) +{ + unregister_kprobe(&execve_kp); +} + +static void do_stop_input_hook(struct work_struct *work) +{ + unregister_kprobe(&input_event_kp); +} +#else +static int ksu_execve_ksud_common(const char __user *filename_user, + struct user_arg_ptr *argv) +{ + struct filename filename_in, *filename_p; + char path[32]; + long len; + + // return early if disabled. + if (!ksu_execveat_hook) { + return 0; + } + + if (!filename_user) + return 0; + + len = strncpy_from_user_nofault(path, filename_user, 32); + if (len <= 0) + return 0; + + path[sizeof(path) - 1] = '\0'; + + // this is because ksu_handle_execveat_ksud calls it filename->name + filename_in.name = path; + filename_p = &filename_in; + + return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, argv, NULL, + NULL); +} + +int __maybe_unused +ksu_handle_execve_ksud(const char __user *filename_user, + const char __user *const __user *__argv) +{ + struct user_arg_ptr argv = { .ptr.native = __argv }; + return ksu_execve_ksud_common(filename_user, &argv); +} + +#if defined(CONFIG_COMPAT) && defined(CONFIG_64BIT) +int __maybe_unused ksu_handle_compat_execve_ksud( + const char __user *filename_user, const compat_uptr_t __user *__argv) +{ + struct user_arg_ptr argv = { .ptr.compat = __argv }; + return ksu_execve_ksud_common(filename_user, &argv); +} +#endif /* COMPAT & 64BIT */ + +// working dummies for manual hooks +int __maybe_unused ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, + size_t *count_ptr, loff_t **pos) +{ + struct file *file = *file_ptr; + + if (IS_ERR_OR_NULL(file)) return 0; + + if (is_init_rc(file)) { + ksu_apply_init_rc_proxy(file); + } + + return 0; +} + +#define STAT_NATIVE 0 +#define STAT_STAT64 1 + +__attribute__((cold)) +static noinline void ksu_common_newfstat_ret(unsigned int fd_int, void **statbuf_ptr, + const int type, const char *syscall_name) +{ + if (!is_init(current_cred())) + return; + + struct file *file = fget(fd_int); + if (!file) + return; + + if (!is_init_rc(file)) { + fput(file); + return; + } + fput(file); + + pr_info("%s: stat init.rc \n", syscall_name); + + uintptr_t statbuf_ptr_local = (uintptr_t)*(void **)statbuf_ptr; + void __user *statbuf = (void __user *)statbuf_ptr_local; + if (!statbuf) + return; + + void __user *st_size_ptr; + long size, new_size; + size_t len; + + st_size_ptr = statbuf + offsetof(struct stat, st_size); + len = sizeof(long); + +#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) + if (type) { + st_size_ptr = statbuf + offsetof(struct stat64, st_size); + len = sizeof(long long); + } +#endif + + if (copy_from_user(&size, st_size_ptr, len)) { + pr_info("%s: read statbuf 0x%lx failed \n", syscall_name, (unsigned long)st_size_ptr); + return; + } + + new_size = size + ksu_rc_len; + pr_info("%s: adding ksu_rc_len: %ld -> %ld \n", syscall_name, size, new_size); + + if (!copy_to_user(st_size_ptr, &new_size, len)) + pr_info("%s: added ksu_rc_len \n", syscall_name); + else + pr_info("%s: add ksu_rc_len failed: statbuf 0x%lx \n", syscall_name, (unsigned long)st_size_ptr); + + return; +} + +void ksu_handle_newfstat_ret(unsigned int *fd, struct stat __user **statbuf_ptr) +{ + if (likely(!ksu_vfs_read_hook)) + return; + + ksu_common_newfstat_ret(*fd, (void **)statbuf_ptr, STAT_NATIVE, "sys_newfstat"); +} + +#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) +void ksu_handle_fstat64_ret(unsigned long *fd, struct stat64 __user **statbuf_ptr) +{ + + if (likely(!ksu_vfs_read_hook)) + return; + + // WARNING: LE-only!!! + ksu_common_newfstat_ret(*(unsigned int *)fd, (void **)statbuf_ptr, STAT_STAT64, "sys_fstat64"); +} +#endif + +#endif + +void stop_init_rc_hook() +{ +#ifdef KSU_KPROBES_HOOK + bool ret = schedule_work(&stop_init_rc_hook_work); + pr_info("unregister init_rc_hook kprobe: %d!\n", ret); +#else + ksu_init_rc_hook = false; + pr_info("stop init_rc_hook\n"); +#endif +} + +void stop_execve_hook() +{ +#ifdef KSU_KPROBES_HOOK + bool ret = schedule_work(&stop_execve_hook_work); + pr_info("unregister execve kprobe: %d!\n", ret); +#else + pr_info("stop execve_hook\n"); + ksu_execveat_hook = false; +#endif +} + +void stop_input_hook() +{ +#ifdef KSU_KPROBES_HOOK + static bool input_hook_stopped = false; + if (input_hook_stopped) { + return; + } + input_hook_stopped = true; + bool ret = schedule_work(&stop_input_hook_work); + pr_info("unregister input kprobe: %d!\n", ret); +#else + if (!ksu_input_hook) { + return; + } + ksu_input_hook = false; + pr_info("stop input_hook\n"); +#endif +} + + +// ksud: module support +void __init ksu_ksud_init() +{ +#ifdef KSU_KPROBES_HOOK + int ret; + + ret = register_kprobe(&execve_kp); + pr_info("ksud: execve_kp: %d\n", ret); + + ret = register_kprobe(&sys_read_kp); + pr_info("ksud: sys_read_kp: %d\n", ret); + + ret = register_kretprobe(&sys_fstat_kp); + pr_info("ksud: sys_fstat_kp: %d\n", ret); + + ret = register_kprobe(&input_event_kp); + pr_info("ksud: input_event_kp: %d\n", ret); + + INIT_WORK(&stop_init_rc_hook_work, do_stop_init_rc_hook); + INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); + INIT_WORK(&stop_input_hook_work, do_stop_input_hook); +#endif +} + +void __exit ksu_ksud_exit() +{ +#ifdef KSU_KPROBES_HOOK + unregister_kprobe(&execve_kp); + // this should be done before unregister sys_read_kp + // unregister_kprobe(&sys_read_kp); + unregister_kprobe(&input_event_kp); +#endif +} diff --git a/KernelSU-Next/kernel/selinux/Makefile b/KernelSU-Next/kernel/selinux/Makefile new file mode 100644 index 000000000000..39c71fff686e --- /dev/null +++ b/KernelSU-Next/kernel/selinux/Makefile @@ -0,0 +1,9 @@ +obj-y += selinux.o +obj-y += sepolicy.o +obj-y += rules.o + +ccflags-y += -Wno-strict-prototypes -Wno-int-conversion +ccflags-y += -Wno-declaration-after-statement -Wno-unused-function +ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include +ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h +ccflags-y += -I$(src) -I$(src)/include diff --git a/KernelSU-Next/kernel/selinux/rules.c b/KernelSU-Next/kernel/selinux/rules.c new file mode 100644 index 000000000000..7b9d7db4938b --- /dev/null +++ b/KernelSU-Next/kernel/selinux/rules.c @@ -0,0 +1,806 @@ +#include "linux/rcupdate.h" +#include "security.h" +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) +#include +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +#include +#else +#include +#endif +#include + +#include "uapi/selinux.h" +#include "klog.h" // IWYU pragma: keep +#include "selinux.h" +#include "sepolicy.h" +#include "ss/services.h" +#include "linux/lsm_audit.h" // IWYU pragma: keep +#include "xfrm.h" +#include "compat/kernel_compat.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#define SELINUX_POLICY_INSTEAD_SELINUX_SS +#endif + +#define ALL NULL + +static DEFINE_MUTEX(ksu_rules); + +static struct policydb *get_policydb(void) +{ + struct policydb *db; +#ifdef KSU_COMPAT_USE_SELINUX_STATE +#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS + struct selinux_policy *policy = selinux_state.policy; + db = &policy->policydb; +#else + struct selinux_ss *ss = selinux_state.ss; + db = &ss->policydb; +#endif +#else + db = &policydb; +#endif + return db; +} + +#if ((!defined(KSU_COMPAT_USE_SELINUX_STATE)) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) +extern int avc_ss_reset(u32 seqno); +#else +extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno); +#endif +// reset avc cache table, otherwise the new rules will not take effect if already denied +static void reset_avc_cache() +{ +#if ((!defined(KSU_COMPAT_USE_SELINUX_STATE)) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) + avc_ss_reset(0); + selnl_notify_policyload(0); + selinux_status_update_policyload(0); +#else + struct selinux_avc *avc = selinux_state.avc; + avc_ss_reset(avc, 0); + selnl_notify_policyload(0); + selinux_status_update_policyload(&selinux_state, 0); +#endif + selinux_xfrm_notify_policyload(); +} + +#ifndef SELINUX_POLICY_INSTEAD_SELINUX_SS + +// rwlock +#if defined(KSU_COMPAT_USE_SELINUX_STATE) +static inline rwlock_t *ksu_get_policy_rwlock(void) { return &selinux_state.ss->policy_rwlock; } +#elif defined(KSU_COMPAT_HAS_EXPORTED_POLICY_RWLOCK) +static inline rwlock_t *ksu_get_policy_rwlock(void) { extern rwlock_t policy_rwlock; return &policy_rwlock; } +#else +static inline rwlock_t *ksu_get_policy_rwlock(void) { return NULL; } +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) || defined(KSU_COMPAT_HAS_BACKPORTED_CPUS_PTR) +static inline const cpumask_t *ksu_get_current_cpumask_t() { return current->cpus_ptr; } +#else +static inline cpumask_t *ksu_get_current_cpumask_t() { return ¤t->cpus_allowed; } +#endif + +#endif // #ifndef SELINUX_POLICY_INSTEAD_SELINUX_SS + +static int apply_kernelsu_rules_fn(void *ptr) +{ + struct policydb *db = (struct policydb *)ptr; + + ksu_type(db, KERNEL_SU_DOMAIN, "domain"); + ksu_permissive(db, KERNEL_SU_DOMAIN); + ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject"); + ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain"); + ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain"); + + // Create unconstrained file type + ksu_type(db, KERNEL_SU_FILE, "file_type"); + ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject"); + ksu_allow(db, "domain", KERNEL_SU_FILE, ALL, ALL); + + // allow all! + ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL); + + // allow us do any ioctl + if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) { + ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL); + ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL); + ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL); + ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL); + } + + // we need to save allowlist in /data/adb/ksu + ksu_allow(db, "kernel", "adb_data_file", "dir", ALL); + ksu_allow(db, "kernel", "adb_data_file", "file", ALL); + // we need to search /data/app + ksu_allow(db, "kernel", "apk_data_file", "file", "open"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "open"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "read"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "search"); + // we may need to do mount on shell + ksu_allow(db, "kernel", "shell_data_file", "file", ALL); + // we need to read /data/system/packages.list + ksu_allow(db, "kernel", "kernel", "capability", "dac_override"); + // Android 10+: + // http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512 + ksu_allow(db, "kernel", "packages_list_file", "file", ALL); + // Kernel 4.4 + ksu_allow(db, "kernel", "packages_list_file", "dir", ALL); + // Android 9-: + // http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360 + ksu_allow(db, "kernel", "system_data_file", "file", ALL); + ksu_allow(db, "kernel", "system_data_file", "dir", ALL); + // our ksud triggered by init + ksu_allow(db, "init", "adb_data_file", "file", ALL); + ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289 + ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL); + // we need to umount modules in zygote + ksu_allow(db, "zygote", "adb_data_file", "dir", "search"); + + // copied from Magisk rules + // suRights + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search"); + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read"); + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open"); + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read"); + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr"); + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "process", "sigchld"); + + // allowLog + ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search"); + ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read"); + ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open"); + ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr"); + + // dumpsys, send fd + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "fd", "use"); + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "fifo_file", "write"); + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "fifo_file", "read"); + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "fifo_file", "open"); + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "fifo_file", "getattr"); + + // bootctl + ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search"); + ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read"); + ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open"); + ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process", "getattr"); + + // For mounting loop devices, mirrors, tmpfs + ksu_allow(db, "kernel", ALL, "file", "read"); + ksu_allow(db, "kernel", ALL, "file", "write"); + + // Allow all binder transactions + ksu_allow(db, "domain", KERNEL_SU_DOMAIN, "binder", ALL); + + // Allow system server kill su process + ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); + ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); + + return 0; +} + +void apply_kernelsu_rules() +{ + struct policydb *db; + + if (!getenforce()) { + pr_info("SELinux permissive or disabled, apply rules!\n"); + } + +#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS + struct selinux_policy *pol, *old_pol = selinux_state.policy; + mutex_lock(&selinux_state.policy_mutex); + pol = ksu_dup_sepolicy(rcu_dereference_protected(old_pol, lockdep_is_held(&selinux_state.policy_mutex))); + if (!pol) { + pr_err("failed to dup selinux_policy\n"); + goto out_unlock; + } + db = &pol->policydb; + + apply_kernelsu_rules_fn((void *)db); + + rcu_assign_pointer(selinux_state.policy, pol); + synchronize_rcu(); + ksu_destroy_sepolicy(old_pol); + + reset_avc_cache(); +out_unlock: + mutex_unlock(&selinux_state.policy_mutex); +#else + + cpumask_t old_mask; + db = get_policydb(); + rwlock_t *lock = ksu_get_policy_rwlock(); + + if (!lock) + goto do_stop_machine; + + /* + * HACK: write_lock() is held with preempt enabled. DO NOT let the + * task be migrated to any other CPU than the current CPU. And since + * set_cpus_allowed_ptr() can sleep, use raw_smp_processor_id() to get + * current CPU and bypass preemption checks. + */ + cpumask_copy(&old_mask, ksu_get_current_cpumask_t()); + set_cpus_allowed_ptr(current, cpumask_of(raw_smp_processor_id())); + + write_lock(lock); + preempt_enable(); + + // we do this dance since both kernel and userspace can trigger this + if (likely(current && current->mm)) + goto has_current_mm; + + apply_kernelsu_rules_fn((void *)db); + goto out_unlock; + +has_current_mm: + ; + + // HACK: raise priority of this to the heavens + int old_policy = current->policy; + struct sched_param old_param = { .sched_priority = current->rt_priority }; + struct sched_param new_param = { .sched_priority = 50 }; + + sched_setscheduler_nocheck(current, 1, &new_param); // raise, fifo, 50 + apply_kernelsu_rules_fn((void *)db); + sched_setscheduler_nocheck(current, old_policy, &old_param); // restore + +out_unlock: + preempt_disable(); + write_unlock(lock); + set_cpus_allowed_ptr(current, &old_mask); + goto out_flush; + +do_stop_machine: + stop_machine(apply_kernelsu_rules_fn, (void *)db, NULL); + +out_flush: + smp_mb(); + reset_avc_cache(); +#ifdef CONFIG_KSU_SUSFS + // Allow umount in zygote process without installing zygisk + //ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount"); + susfs_set_priv_app_sid(); + susfs_set_init_sid(); + susfs_set_ksu_sid(); + susfs_set_zygote_sid(); +#endif // #ifdef CONFIG_KSU_SUSFS +#endif +} + +#define KSU_SEPOLICY_MAX_BATCH_SIZE (8U * 1024U * 1024U) +#define KSU_SEPOLICY_MAX_ARGS 5 + +struct sepol_data { + u32 cmd; + u32 subcmd; +}; + +struct sepol_batch_cursor { + const u8 *cur; + const u8 *end; +}; + +static size_t sepol_remaining(const struct sepol_batch_cursor *cursor) +{ + return (size_t)(cursor->end - cursor->cur); +} + +static int sepol_read_cmd_header(struct sepol_batch_cursor *cursor, + struct sepol_data *header) +{ + if (sepol_remaining(cursor) < sizeof(*header)) { + return -EINVAL; + } + + memcpy(header, cursor->cur, sizeof(*header)); + cursor->cur += sizeof(*header); + + return 0; +} + +static int sepol_read_string(struct sepol_batch_cursor *cursor, + const char **out) +{ + u32 len; + const char *str; + + if (sepol_remaining(cursor) < sizeof(len)) { + return -EINVAL; + } + + memcpy(&len, cursor->cur, sizeof(len)); + cursor->cur += sizeof(len); + + if (len >= sepol_remaining(cursor)) { + return -EINVAL; + } + + str = (const char *)cursor->cur; + if (memchr(str, '\0', len) != NULL || str[len] != '\0') { + return -EINVAL; + } + + cursor->cur += len + 1; + if (len == 0) { + *out = ALL; + return 0; + } + + *out = str; + return 0; +} + +static int sepol_require_not_all(const char *value, const char *name) +{ + if (value != ALL) { + return 0; + } + + pr_err("sepol: %s cannot be ALL.\n", name); + return -EINVAL; +} + +static int sepol_expected_argc(u32 cmd) +{ + switch (cmd) { + case KSU_SEPOLICY_CMD_NORMAL_PERM: + return 4; + case KSU_SEPOLICY_CMD_XPERM: + return 5; + case KSU_SEPOLICY_CMD_TYPE_STATE: + return 1; + case KSU_SEPOLICY_CMD_TYPE: + case KSU_SEPOLICY_CMD_TYPE_ATTR: + return 2; + case KSU_SEPOLICY_CMD_ATTR: + return 1; + case KSU_SEPOLICY_CMD_TYPE_TRANSITION: + return 5; + case KSU_SEPOLICY_CMD_TYPE_CHANGE: + return 4; + case KSU_SEPOLICY_CMD_GENFSCON: + return 3; + default: + return -EINVAL; + } +} + +static int apply_one_sepolicy_cmd(struct policydb *db, + const struct sepol_data *header, + const char **args) +{ + bool success = false; + int ret; + + switch (header->cmd) { + case KSU_SEPOLICY_CMD_NORMAL_PERM: + if (header->subcmd == KSU_SEPOLICY_SUBCMD_NORMAL_PERM_ALLOW) { + success = ksu_allow(db, args[0], args[1], args[2], args[3]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_NORMAL_PERM_DENY) { + success = ksu_deny(db, args[0], args[1], args[2], args[3]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_NORMAL_PERM_AUDITALLOW) { + success = ksu_auditallow(db, args[0], args[1], args[2], args[3]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_NORMAL_PERM_DONTAUDIT) { + success = ksu_dontaudit(db, args[0], args[1], args[2], args[3]); + } else { + pr_err("sepol: unknown subcmd: %d\n", header->subcmd); + } + return success ? 0 : -EINVAL; + + case KSU_SEPOLICY_CMD_XPERM: + ret = sepol_require_not_all(args[3], "operation"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[4], "perm_set"); + if (ret < 0) { + return ret; + } + + if (header->subcmd == KSU_SEPOLICY_SUBCMD_XPERM_ALLOW) { + success = ksu_allowxperm(db, args[0], args[1], args[2], args[4]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_XPERM_AUDITALLOW) { + success = + ksu_auditallowxperm(db, args[0], args[1], args[2], args[4]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_XPERM_DONTAUDIT) { + success = + ksu_dontauditxperm(db, args[0], args[1], args[2], args[4]); + } else { + pr_err("sepol: unknown subcmd: %d\n", header->subcmd); + } + return success ? 0 : -EINVAL; + + case KSU_SEPOLICY_CMD_TYPE_STATE: + ret = sepol_require_not_all(args[0], "type"); + if (ret < 0) { + return ret; + } + + if (header->subcmd == KSU_SEPOLICY_SUBCMD_TYPE_STATE_PERMISSIVE) { + success = ksu_permissive(db, args[0]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_TYPE_STATE_ENFORCE) { + success = ksu_enforce(db, args[0]); + } else { + pr_err("sepol: unknown subcmd: %d\n", header->subcmd); + } + return success ? 0 : -EINVAL; + + case KSU_SEPOLICY_CMD_TYPE: + case KSU_SEPOLICY_CMD_TYPE_ATTR: + ret = sepol_require_not_all(args[0], "type"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[1], "attribute"); + if (ret < 0) { + return ret; + } + + if (header->cmd == KSU_SEPOLICY_CMD_TYPE) { + success = ksu_type(db, args[0], args[1]); + } else { + success = ksu_typeattribute(db, args[0], args[1]); + } + if (!success) { + pr_err("sepol: %d failed.\n", header->cmd); + return -EINVAL; + } + return 0; + + case KSU_SEPOLICY_CMD_ATTR: + ret = sepol_require_not_all(args[0], "attribute"); + if (ret < 0) { + return ret; + } + + if (!ksu_attribute(db, args[0])) { + pr_err("sepol: %d failed.\n", header->cmd); + return -EINVAL; + } + return 0; + + case KSU_SEPOLICY_CMD_TYPE_TRANSITION: { + const char *object = ALL; + + ret = sepol_require_not_all(args[0], "src"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[1], "tgt"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[2], "cls"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[3], "default_type"); + if (ret < 0) { + return ret; + } + + object = args[4]; + + success = + ksu_type_transition(db, args[0], args[1], args[2], args[3], object); + return success ? 0 : -EINVAL; + } + + case KSU_SEPOLICY_CMD_TYPE_CHANGE: + ret = sepol_require_not_all(args[0], "src"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[1], "tgt"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[2], "cls"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[3], "default_type"); + if (ret < 0) { + return ret; + } + + if (header->subcmd == KSU_SEPOLICY_SUBCMD_TYPE_CHANGE_CHANGE) { + success = ksu_type_change(db, args[0], args[1], args[2], args[3]); + } else if (header->subcmd == KSU_SEPOLICY_SUBCMD_TYPE_CHANGE_MEMBER) { + success = ksu_type_member(db, args[0], args[1], args[2], args[3]); + } else { + pr_err("sepol: unknown subcmd: %d\n", header->subcmd); + } + return success ? 0 : -EINVAL; + + case KSU_SEPOLICY_CMD_GENFSCON: + ret = sepol_require_not_all(args[0], "name"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[1], "path"); + if (ret < 0) { + return ret; + } + ret = sepol_require_not_all(args[2], "context"); + if (ret < 0) { + return ret; + } + + if (!ksu_genfscon(db, args[0], args[1], args[2])) { + pr_err("sepol: %d failed.\n", header->cmd); + return -EINVAL; + } + return 0; + + default: + pr_err("sepol: unknown cmd: %d\n", header->cmd); + return -EINVAL; + } +} + +#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS +int handle_sepolicy(void __user *user_data, u64 data_len) +{ + struct selinux_policy *pol, *old_pol; + struct policydb *db; + struct sepol_batch_cursor cursor; + u8 *payload; + int ret; + int success_cmd_count; + u32 cmd_index; + + if (!user_data || !data_len) { + return -EINVAL; + } + + if (data_len > KSU_SEPOLICY_MAX_BATCH_SIZE) { + return -E2BIG; + } + + payload = kvmalloc((size_t)data_len, GFP_KERNEL); + if (!payload) { + return -ENOMEM; + } + + if (copy_from_user(payload, user_data, (size_t)data_len)) { + ret = -EFAULT; + goto out_free; + } + + if (!getenforce()) { + pr_info("SELinux permissive or disabled when handle policy!\n"); + } + + mutex_lock(&selinux_state.policy_mutex); + + old_pol = selinux_state.policy; + pol = ksu_dup_sepolicy(rcu_dereference_protected( + old_pol, lockdep_is_held(&selinux_state.policy_mutex))); + if (!pol) { + ret = -ENOMEM; + goto out_unlock; + } + db = &pol->policydb; + + cursor.cur = payload; + cursor.end = payload + (size_t)data_len; + + ret = 0; + success_cmd_count = 0; + cmd_index = 0; + while (cursor.cur < cursor.end) { + struct sepol_data header; + const char *args[KSU_SEPOLICY_MAX_ARGS] = { 0 }; + int expected_argc; + u32 arg_index; + + ret = sepol_read_cmd_header(&cursor, &header); + if (ret < 0) { + pr_err("sepol: failed to read cmd header #%u.\n", cmd_index); + goto out_drop_new_policy; + } + + expected_argc = sepol_expected_argc(header.cmd); + if (expected_argc < 0 || expected_argc > KSU_SEPOLICY_MAX_ARGS) { + ret = -EINVAL; + pr_err("sepol: invalid cmd header #%u.\n", cmd_index); + goto out_drop_new_policy; + } + + for (arg_index = 0; arg_index < (u32)expected_argc; arg_index++) { + ret = sepol_read_string(&cursor, &args[arg_index]); + if (ret < 0) { + pr_err("sepol: failed to read cmd #%u arg #%u.\n", cmd_index, arg_index); + goto out_drop_new_policy; + } + } + + ret = apply_one_sepolicy_cmd(db, &header, args); + if (ret < 0) { + pr_err("sepol: cmd #%u failed, cmd=%u subcmd=%u.\n", cmd_index, header.cmd, header.subcmd); + } else { + success_cmd_count++; + } + cmd_index++; + } + + rcu_assign_pointer(selinux_state.policy, pol); + synchronize_rcu(); + ksu_destroy_sepolicy(old_pol); + + reset_avc_cache(); + ret = success_cmd_count; + goto out_unlock; + +out_drop_new_policy: + ksu_destroy_sepolicy(pol); +out_unlock: + mutex_unlock(&selinux_state.policy_mutex); +out_free: + kvfree(payload); + + return ret; +} +#else + +struct handle_sepolicy_args { + void *ctx_success_cmd_count; + void *ctx_payload; + u64 ctx_data_len; +}; + +static int handle_sepolicy_fn(void *data) +{ + struct sepol_batch_cursor cursor; + int ret = 0; + u32 cmd_index = 0; + int success_cmd_count = 0; + + struct policydb *db = get_policydb(); + struct handle_sepolicy_args *ctx = (struct handle_sepolicy_args *)data; + u8 *payload = (u8 *)ctx->ctx_payload; + u64 data_len = ctx->ctx_data_len; + + cursor.cur = payload; + cursor.end = payload + (size_t)data_len; + + while (cursor.cur < cursor.end) { + struct sepol_data header; + const char *args[KSU_SEPOLICY_MAX_ARGS] = { 0 }; + int expected_argc; + u32 arg_index; + + ret = sepol_read_cmd_header(&cursor, &header); + if (ret < 0) { + pr_err("sepol: failed to read cmd header #%u.\n", cmd_index); + goto out; + } + + expected_argc = sepol_expected_argc(header.cmd); + if (expected_argc < 0 || expected_argc > KSU_SEPOLICY_MAX_ARGS) { + ret = -EINVAL; + pr_err("sepol: invalid cmd header #%u.\n", cmd_index); + goto out; + } + + for (arg_index = 0; arg_index < (u32)expected_argc; arg_index++) { + ret = sepol_read_string(&cursor, &args[arg_index]); + if (ret < 0) { + pr_err("sepol: failed to read cmd #%u arg #%u.\n", cmd_index, arg_index); + goto out; + } + } + + ret = apply_one_sepolicy_cmd(db, &header, args); + if (ret < 0) + pr_err("sepol: cmd #%u failed, cmd=%u subcmd=%u.\n", cmd_index, header.cmd, header.subcmd); + else { + success_cmd_count++; + } + + cmd_index++; + } + +out: + *(int *)(ctx->ctx_success_cmd_count) = success_cmd_count; + return ret; +} + +int handle_sepolicy(void __user *user_data, u64 data_len) +{ + u8 *payload; + int ret = 0; + int success_cmd_count = 0; + cpumask_t old_mask; + + if (!user_data || !data_len) + return -EINVAL; + + if (data_len > KSU_SEPOLICY_MAX_BATCH_SIZE) + return -E2BIG; + + payload = kvmalloc((size_t)data_len, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + if (copy_from_user(payload, user_data, (size_t)data_len)) { + ret = -EFAULT; + goto out_free; + } + + if (!getenforce()) { + pr_info("SELinux permissive or disabled when handle policy!\n"); + } + + struct handle_sepolicy_args ctx = { 0 }; + ctx.ctx_success_cmd_count = (void *)&success_cmd_count; + ctx.ctx_payload = (void *)payload; + ctx.ctx_data_len = (u64)data_len; + + rwlock_t *lock = ksu_get_policy_rwlock(); + if (!lock) + goto do_stop_machine; + + /* + * HACK: write_lock() is held with preempt enabled. DO NOT let the + * task be migrated to any other CPU than the current CPU. And since + * set_cpus_allowed_ptr() can sleep, use raw_smp_processor_id() to get + * current CPU and bypass preemption checks. + */ + cpumask_copy(&old_mask, ksu_get_current_cpumask_t()); + set_cpus_allowed_ptr(current, cpumask_of(raw_smp_processor_id())); + + write_lock(lock); + preempt_enable(); + + if (likely(current && current->mm)) + goto has_current_mm; + + ret = handle_sepolicy_fn((void *)&ctx); + goto out_unlock; + +has_current_mm: + ; + + int old_policy = current->policy; + struct sched_param old_param = { .sched_priority = current->rt_priority }; + struct sched_param new_param = { .sched_priority = 50 }; + + sched_setscheduler_nocheck(current, 1, &new_param); + ret = handle_sepolicy_fn((void *)&ctx); + sched_setscheduler_nocheck(current, old_policy, &old_param); + +out_unlock: + preempt_disable(); + write_unlock(lock); + set_cpus_allowed_ptr(current, &old_mask); + goto out_done; + +do_stop_machine: + ret = stop_machine(handle_sepolicy_fn, (void *)&ctx, NULL); + +out_done: + if (ret) + goto out_free; + + smp_mb(); + reset_avc_cache(); + ret = success_cmd_count; + +out_free: + kvfree(payload); + + return ret; +} +#endif // SELINUX_POLICY_INSTEAD_SELINUX_SS diff --git a/KernelSU-Next/kernel/selinux/selinux.c b/KernelSU-Next/kernel/selinux/selinux.c new file mode 100644 index 000000000000..dc1da539dcba --- /dev/null +++ b/KernelSU-Next/kernel/selinux/selinux.c @@ -0,0 +1,342 @@ +#include "selinux.h" +#include "linux/cred.h" +#include "linux/sched.h" +#include "linux/security.h" +#include "objsec.h" +#include "linux/version.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" + +/* + * Cached SID values for frequently checked contexts. + * These are resolved once at init and used for fast u32 comparison + * instead of expensive string operations on every check. + * + * A value of 0 means "no cached SID is available" for that context. + * This covers both the initial "not yet cached" state and any case + * where resolving the SID (e.g. via security_secctx_to_secid) failed. + * In all such cases we intentionally fall back to the slower + * string-based comparison path; this degrades performance only and + * does not cause a functional failure. + */ +static u32 cached_su_sid __read_mostly = 0; +static u32 cached_zygote_sid __read_mostly = 0; +static u32 cached_init_sid __read_mostly = 0; +u32 ksu_file_sid __read_mostly = 0; + +static int transive_to_domain(const char *domain, struct cred *cred) +{ + struct task_security_struct *tsec; + u32 sid; + int error; + + tsec = selinux_cred(cred); + if (!tsec) { + pr_err("tsec == NULL!\n"); + return -1; + } + + error = security_secctx_to_secid(domain, strlen(domain), &sid); + if (error) { + pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", domain, + sid, error); + } + if (!error) { + tsec->sid = sid; + tsec->create_sid = 0; + tsec->keycreate_sid = 0; + tsec->sockcreate_sid = 0; + } + return error; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0) +bool __maybe_unused +is_ksu_transition(const struct task_security_struct *old_tsec, + const struct task_security_struct *new_tsec) +{ + static u32 ksu_sid; + char *secdata; + u32 seclen; + bool allowed = false; + + if (!ksu_sid) + security_secctx_to_secid(KERNEL_SU_CONTEXT, + strlen(KERNEL_SU_CONTEXT), &ksu_sid); + + if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen)) + return false; + + allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid); + security_release_secctx(secdata, seclen); + return allowed; +} +#endif + +void setup_selinux(const char *domain, struct cred *cred) +{ + if (transive_to_domain(domain, cred)) { + pr_err("transive domain failed.\n"); + return; + } +} + +void setup_ksu_cred(void) +{ + if (ksu_cred && transive_to_domain(KERNEL_SU_CONTEXT, ksu_cred)) { + pr_err("setup ksu cred failed.\n"); + } +} + +void setenforce(bool enforce) +{ +#ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef KSU_COMPAT_USE_SELINUX_STATE + selinux_state.enforcing = enforce; +#else + selinux_enforcing = enforce; +#endif +#endif +} + +bool getenforce(void) +{ +#ifdef CONFIG_SECURITY_SELINUX_DISABLE +#ifdef KSU_COMPAT_USE_SELINUX_STATE + if (selinux_state.disabled) { + return false; + } +#else + if (selinux_disabled) { + return false; + } +#endif // KSU_COMPAT_USE_SELINUX_STATE +#endif // CONFIG_SECURITY_SELINUX_DISABLE + +#ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef KSU_COMPAT_USE_SELINUX_STATE + return selinux_state.enforcing; +#else + return selinux_enforcing; +#endif +#else + return true; +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0) +struct lsm_context { + char *context; + u32 len; +}; + +static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp) +{ + return security_secid_to_secctx(secid, &cp->context, &cp->len); +} +static void __security_release_secctx(struct lsm_context *cp) +{ + security_release_secctx(cp->context, cp->len); +} +#else +#define __security_secid_to_secctx security_secid_to_secctx +#define __security_release_secctx security_release_secctx +#endif + +/* + * Initialize cached SID values for frequently checked SELinux contexts. + * Called once after SELinux policy is loaded (post-fs-data). + * This eliminates expensive string comparisons in hot paths. + */ + +void cache_sid(void) +{ + int err; + + err = security_secctx_to_secid(KERNEL_SU_CONTEXT, strlen(KERNEL_SU_CONTEXT), + &cached_su_sid); + if (err) { + pr_warn("Failed to cache kernel su domain SID: %d\n", err); + cached_su_sid = 0; + } else { + pr_info("Cached su SID: %u\n", cached_su_sid); + } + + err = security_secctx_to_secid(ZYGOTE_CONTEXT, strlen(ZYGOTE_CONTEXT), + &cached_zygote_sid); + if (err) { + pr_warn("Failed to cache zygote SID: %d\n", err); + cached_zygote_sid = 0; + } else { + pr_info("Cached zygote SID: %u\n", cached_zygote_sid); + } + + err = security_secctx_to_secid(INIT_CONTEXT, strlen(INIT_CONTEXT), + &cached_init_sid); + if (err) { + pr_warn("Failed to cache init SID: %d\n", err); + cached_init_sid = 0; + } else { + pr_info("Cached init SID: %u\n", cached_init_sid); + } + + err = security_secctx_to_secid(KSU_FILE_CONTEXT, strlen(KSU_FILE_CONTEXT), + &ksu_file_sid); + if (err) { + pr_warn("Failed to cache ksu_file SID: %d\n", err); + ksu_file_sid = 0; + } else { + pr_info("Cached ksu_file SID: %u\n", ksu_file_sid); + } +} + +/* + * Fast path: compare task's SID directly against cached value. + * Falls back to string comparison if cache is not initialized. + */ +static bool is_sid_match(const struct cred *cred, u32 cached_sid, + const char *fallback_context) +{ + if (!cred) { + return false; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 18, 0) + const struct task_security_struct *tsec = selinux_cred(cred); +#else + const struct cred_security_struct *tsec = selinux_cred(cred); +#endif + if (!tsec) { + return false; + } + + // Fast path: use cached SID if available + if (likely(cached_sid != 0)) { + return tsec->sid == cached_sid; + } + + // Slow path fallback: string comparison (only before cache is initialized) + struct lsm_context ctx; + bool result; + if (__security_secid_to_secctx(tsec->sid, &ctx)) { + return false; + } + result = strncmp(fallback_context, ctx.context, ctx.len) == 0; + __security_release_secctx(&ctx); + return result; +} + +bool is_task_ksu_domain(const struct cred *cred) +{ + return is_sid_match(cred, cached_su_sid, KERNEL_SU_CONTEXT); +} + +bool is_ksu_domain(void) +{ + return is_task_ksu_domain(current_cred()); +} + +bool is_zygote(const struct cred *cred) +{ + return is_sid_match(cred, cached_zygote_sid, ZYGOTE_CONTEXT); +} + +bool is_init(const struct cred *cred) +{ + return is_sid_match(cred, cached_init_sid, INIT_CONTEXT); +} + +#ifdef CONFIG_KSU_SUSFS +#define KERNEL_INIT_DOMAIN "u:r:init:s0" +#define KERNEL_ZYGOTE_DOMAIN "u:r:zygote:s0" +#define KERNEL_PRIV_APP_DOMAIN "u:r:priv_app:s0:c512,c768" + +u32 susfs_ksu_sid = 0; +u32 susfs_init_sid = 0; +u32 susfs_zygote_sid = 0; +u32 susfs_priv_app_sid = 0; + +static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid) +{ + int err; + + if (!secctx_name || !out_sid) { + pr_err("secctx_name || out_sid is NULL\n"); + return; + } + + err = security_secctx_to_secid(secctx_name, strlen(secctx_name), + out_sid); + if (err) { + pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err); + return; + } + pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name); +} + +bool susfs_is_sid_equal(const struct cred *cred, u32 sid2) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 18, 0) + const struct task_security_struct *tsec = selinux_cred(cred); +#else + const struct cred_security_struct *tsec = selinux_cred(cred); +#endif + + if (!tsec) { + return false; + } + return tsec->sid == sid2; +} + +u32 susfs_get_sid_from_name(const char *secctx_name) +{ + u32 out_sid = 0; + int err; + + if (!secctx_name) { + pr_err("secctx_name is NULL\n"); + return 0; + } + err = security_secctx_to_secid(secctx_name, strlen(secctx_name), + &out_sid); + if (err) { + pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err); + return 0; + } + return out_sid; +} + +u32 susfs_get_current_sid(void) { + return current_sid(); +} + +void susfs_set_zygote_sid(void) +{ + susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid); +} + +bool susfs_is_current_zygote_domain(void) { + return unlikely(current_sid() == susfs_zygote_sid); +} + +void susfs_set_ksu_sid(void) +{ + susfs_set_sid(KERNEL_SU_CONTEXT, &susfs_ksu_sid); +} + +bool susfs_is_current_ksu_domain(void) { + return unlikely(current_sid() == susfs_ksu_sid); +} + +void susfs_set_init_sid(void) +{ + susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid); +} + +bool susfs_is_current_init_domain(void) { + return unlikely(current_sid() == susfs_init_sid); +} + +void susfs_set_priv_app_sid(void) +{ + susfs_set_sid(KERNEL_PRIV_APP_DOMAIN, &susfs_priv_app_sid); +} +#endif // #ifdef CONFIG_KSU_SUSFS \ No newline at end of file diff --git a/KernelSU-Next/kernel/selinux/selinux.h b/KernelSU-Next/kernel/selinux/selinux.h new file mode 100644 index 000000000000..929953510842 --- /dev/null +++ b/KernelSU-Next/kernel/selinux/selinux.h @@ -0,0 +1,79 @@ +#ifndef __KSU_H_SELINUX +#define __KSU_H_SELINUX + +#include +#include +#include + +#include "objsec.h" +#include "security.h" // Samsung SELinux Porting + +#ifndef KSU_COMPAT_USE_SELINUX_STATE +#include "avc.h" +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 18, 0) +typedef struct task_security_struct taskcred_sec_t; +#else +typedef struct cred_security_struct taskcred_sec_t; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \ + !defined(KSU_COMPAT_HAS_CURRENT_SID) +/* + * get the subjective security ID of the current task + */ +static inline u32 current_sid(void) +{ + const struct task_security_struct *tsec = current_security(); + + return tsec->sid; +} +#endif + +#define KERNEL_SU_DOMAIN "ksu" +#define KERNEL_SU_FILE "ksu_file" + +#define KERNEL_SU_CONTEXT "u:r:" KERNEL_SU_DOMAIN ":s0" +#define KSU_FILE_CONTEXT "u:object_r:" KERNEL_SU_FILE ":s0" +#define ZYGOTE_CONTEXT "u:r:zygote:s0" +#define INIT_CONTEXT "u:r:init:s0" + +void setup_selinux(const char *, struct cred *); + +void setenforce(bool); + +bool getenforce(); + +void cache_sid(void); + +bool is_task_ksu_domain(const struct cred* cred); + +bool is_ksu_domain(); + +bool is_zygote(const struct cred* cred); + +bool is_init(const struct cred* cred); + +void apply_kernelsu_rules(); + +int handle_sepolicy(void __user *user_data, u64 data_len); + +void setup_ksu_cred(); + +extern u32 ksu_file_sid; + +#ifdef CONFIG_KSU_SUSFS +bool susfs_is_sid_equal(const struct cred *cred, u32 sid2); +u32 susfs_get_sid_from_name(const char *secctx_name); +u32 susfs_get_current_sid(void); +void susfs_set_zygote_sid(void); +bool susfs_is_current_zygote_domain(void); +void susfs_set_ksu_sid(void); +bool susfs_is_current_ksu_domain(void); +void susfs_set_init_sid(void); +bool susfs_is_current_init_domain(void); +void susfs_set_priv_app_sid(void); +#endif // #ifdef CONFIG_KSU_SUSFS + +#endif diff --git a/KernelSU-Next/kernel/selinux/sepolicy.c b/KernelSU-Next/kernel/selinux/sepolicy.c new file mode 100644 index 000000000000..09b6579e1705 --- /dev/null +++ b/KernelSU-Next/kernel/selinux/sepolicy.c @@ -0,0 +1,1499 @@ +#include "ss/avtab.h" +#include "ss/constraint.h" +#include "ss/ebitmap.h" +#include "ss/hashtab.h" +#include "ss/policydb.h" +#include "ss/services.h" +#include +#include +#include +#include + +#include "sepolicy.h" +#include "klog.h" // IWYU pragma: keep +#include "ss/symtab.h" +#include "compat/kernel_compat.h" // Add check Huawei Device + +#define KSU_SUPPORT_ADD_TYPE + +////////////////////////////////////////////////////// +// Declaration +////////////////////////////////////////////////////// + +static struct avtab_node *get_avtab_node(struct policydb *db, + struct avtab_key *key, + struct avtab_extended_perms *xperms); + +static bool add_rule(struct policydb *db, const char *s, const char *t, + const char *c, const char *p, int effect, bool invert); + +static void add_rule_raw(struct policydb *db, struct type_datum *src, + struct type_datum *tgt, struct class_datum *cls, + struct perm_datum *perm, int effect, bool invert); + +static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, + struct type_datum *tgt, struct class_datum *cls, + uint16_t low, uint16_t high, int effect, + bool invert); +static bool add_xperm_rule(struct policydb *db, const char *s, const char *t, + const char *c, const char *range, int effect, + bool invert); + +static bool add_type_rule(struct policydb *db, const char *s, const char *t, + const char *c, const char *d, int effect); + +static bool add_filename_trans(struct policydb *db, const char *s, + const char *t, const char *c, const char *d, + const char *o); + +static bool add_genfscon(struct policydb *db, const char *fs_name, + const char *path, const char *context); + +static bool add_type(struct policydb *db, const char *type_name, bool attr); + +static bool set_type_state(struct policydb *db, const char *type_name, + bool permissive); + +static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, + struct type_datum *attr); + +static bool add_typeattribute(struct policydb *db, const char *type, + const char *attr); + +////////////////////////////////////////////////////// +// Implementation +////////////////////////////////////////////////////// + +// Invert is adding rules for auditdeny; in other cases, invert is removing +// rules +#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert) + +#define ksu_hash_for_each(node_ptr, n_slot, cur) \ + int i; \ + for (i = 0; i < n_slot; ++i) \ + for (cur = node_ptr[i]; cur; cur = cur->next) + +// htable is a struct instead of pointer above 5.8.0: +// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +#define ksu_hashtab_for_each(htab, cur) \ + ksu_hash_for_each(htab.htable, htab.size, cur) +#else +#define ksu_hashtab_for_each(htab, cur) \ + ksu_hash_for_each(htab->htable, htab->size, cur) +#endif + +// symtab_search is introduced on 5.9.0: +// https://elixir.bootlin.com/linux/v5.9-rc1/source/security/selinux/ss/symtab.h +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) +#define symtab_search(s, name) hashtab_search((s)->table, name) +#define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum) +#endif + +#define avtab_for_each(avtab, cur) \ + ksu_hash_for_each(avtab.htable, avtab.nslot, cur); + +static struct avtab_node *get_avtab_node(struct policydb *db, + struct avtab_key *key, + struct avtab_extended_perms *xperms) +{ + struct avtab_node *node; + + /* AVTAB_XPERMS entries are not necessarily unique */ + if (key->specified & AVTAB_XPERMS) { + bool match = false; + node = avtab_search_node(&db->te_avtab, key); + while (node) { + if ((node->datum.u.xperms->specified == xperms->specified) && + (node->datum.u.xperms->driver == xperms->driver)) { + match = true; + break; + } + node = avtab_search_node_next(node, key->specified); + } + if (!match) + node = NULL; + } else { + node = avtab_search_node(&db->te_avtab, key); + } + + if (!node) { + struct avtab_datum avdatum = {}; + /* + * AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for + * others. Initialize the data accordingly. + */ + if (key->specified & AVTAB_XPERMS) { + avdatum.u.xperms = xperms; + } else { + avdatum.u.data = key->specified == AVTAB_AUDITDENY ? ~0U : 0U; + } + /* this is used to get the node - insertion is actually unique */ + node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum); + + int grow_size = sizeof(struct avtab_key); + grow_size += sizeof(struct avtab_datum); + if (key->specified & AVTAB_XPERMS) { + grow_size += sizeof(u8); + grow_size += sizeof(u8); + grow_size += sizeof(u32) * ARRAY_SIZE(avdatum.u.xperms->perms.p); + } + db->len += grow_size; + } + + return node; +} + +static bool add_rule(struct policydb *db, const char *s, const char *t, + const char *c, const char *p, int effect, bool invert) +{ + struct type_datum *src = NULL, *tgt = NULL; + struct class_datum *cls = NULL; + struct perm_datum *perm = NULL; + + if (s) { + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_info("source type %s does not exist\n", s); + return false; + } + } + + if (t) { + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_info("target type %s does not exist\n", t); + return false; + } + } + + if (c) { + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_info("class %s does not exist\n", c); + return false; + } + } + + if (p) { + if (c == NULL) { + pr_info("No class is specified, cannot add perm [%s] \n", p); + return false; + } + + perm = symtab_search(&cls->permissions, p); + if (perm == NULL && cls->comdatum != NULL) { + perm = symtab_search(&cls->comdatum->permissions, p); + } + if (perm == NULL) { + pr_info("perm %s does not exist in class %s\n", p, c); + return false; + } + } + add_rule_raw(db, src, tgt, cls, perm, effect, invert); + return true; +} + +static void add_rule_raw(struct policydb *db, struct type_datum *src, + struct type_datum *tgt, struct class_datum *cls, + struct perm_datum *perm, int effect, bool invert) +{ + if (src == NULL) { + struct hashtab_node *node; + if (strip_av(effect, invert)) { + ksu_hashtab_for_each(db->p_types.table, node) + { + add_rule_raw(db, (struct type_datum *)node->datum, tgt, cls, + perm, effect, invert); + }; + } else { + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = (struct type_datum *)(node->datum); + if (type->attribute) { + add_rule_raw(db, type, tgt, cls, perm, effect, invert); + } + }; + } + } else if (tgt == NULL) { + struct hashtab_node *node; + if (strip_av(effect, invert)) { + ksu_hashtab_for_each(db->p_types.table, node) + { + add_rule_raw(db, src, (struct type_datum *)node->datum, cls, + perm, effect, invert); + }; + } else { + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = (struct type_datum *)(node->datum); + if (type->attribute) { + add_rule_raw(db, src, type, cls, perm, effect, invert); + } + }; + } + } else if (cls == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_classes.table, node) + { + add_rule_raw(db, src, tgt, (struct class_datum *)node->datum, perm, + effect, invert); + } + } else { + struct avtab_key key; + key.source_type = src->value; + key.target_type = tgt->value; + key.target_class = cls->value; + key.specified = effect; + + struct avtab_node *node = get_avtab_node(db, &key, NULL); + if (invert) { + if (perm) + node->datum.u.data &= ~(1U << (perm->value - 1)); + else + node->datum.u.data = 0U; + } else { + if (perm) + node->datum.u.data |= 1U << (perm->value - 1); + else + node->datum.u.data = ~0U; + } + } +} + +#define ioctl_driver(x) (x >> 8 & 0xFF) +#define ioctl_func(x) (x & 0xFF) + +#define xperm_test(x, p) (1 & (p[x >> 5] >> (x & 0x1f))) +#define xperm_set(x, p) (p[x >> 5] |= (1 << (x & 0x1f))) +#define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f))) + +static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, + struct type_datum *tgt, struct class_datum *cls, + uint16_t low, uint16_t high, int effect, + bool invert) +{ + if (src == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = (struct type_datum *)(node->datum); + if (type->attribute) { + add_xperm_rule_raw(db, type, tgt, cls, low, high, effect, + invert); + } + }; + } else if (tgt == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = (struct type_datum *)(node->datum); + if (type->attribute) { + add_xperm_rule_raw(db, src, type, cls, low, high, effect, + invert); + } + }; + } else if (cls == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_classes.table, node) + { + add_xperm_rule_raw(db, src, tgt, + (struct class_datum *)(node->datum), low, high, + effect, invert); + }; + } else { + struct avtab_key key; + key.source_type = src->value; + key.target_type = tgt->value; + key.target_class = cls->value; + key.specified = effect; + + struct avtab_datum *datum; + struct avtab_node *node; + struct avtab_extended_perms xperms; + + memset(&xperms, 0, sizeof(xperms)); + if (ioctl_driver(low) != ioctl_driver(high)) { + xperms.specified = AVTAB_XPERMS_IOCTLDRIVER; + xperms.driver = 0; + } else { + xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION; + xperms.driver = ioctl_driver(low); + } + int i; + if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) { + for (i = ioctl_driver(low); i <= ioctl_driver(high); ++i) { + if (invert) + xperm_clear(i, xperms.perms.p); + else + xperm_set(i, xperms.perms.p); + } + } else { + for (i = ioctl_func(low); i <= ioctl_func(high); ++i) { + if (invert) + xperm_clear(i, xperms.perms.p); + else + xperm_set(i, xperms.perms.p); + } + } + + node = get_avtab_node(db, &key, &xperms); + if (!node) { + pr_warn("add_xperm_rule_raw cannot found node!\n"); + return; + } + datum = &node->datum; + + if (datum->u.xperms == NULL) { + datum->u.xperms = (struct avtab_extended_perms *)(kzalloc( + sizeof(xperms), GFP_KERNEL)); + if (!datum->u.xperms) { + pr_err("alloc xperms failed\n"); + return; + } + memcpy(datum->u.xperms, &xperms, sizeof(xperms)); + } + } +} + +static bool add_xperm_rule(struct policydb *db, const char *s, const char *t, + const char *c, const char *range, int effect, + bool invert) +{ + struct type_datum *src = NULL, *tgt = NULL; + struct class_datum *cls = NULL; + + if (s) { + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_info("source type %s does not exist\n", s); + return false; + } + } + + if (t) { + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_info("target type %s does not exist\n", t); + return false; + } + } + + if (c) { + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_info("class %s does not exist\n", c); + return false; + } + } + + u16 low, high; + + if (range) { + if (strchr(range, '-')) { + sscanf(range, "%hx-%hx", &low, &high); + } else { + sscanf(range, "%hx", &low); + high = low; + } + } else { + low = 0; + high = 0xFFFF; + } + + add_xperm_rule_raw(db, src, tgt, cls, low, high, effect, invert); + return true; +} + +static bool add_type_rule(struct policydb *db, const char *s, const char *t, + const char *c, const char *d, int effect) +{ + struct type_datum *src, *tgt, *def; + struct class_datum *cls; + + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_info("source type %s does not exist\n", s); + return false; + } + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_info("target type %s does not exist\n", t); + return false; + } + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_info("class %s does not exist\n", c); + return false; + } + def = symtab_search(&db->p_types, d); + if (def == NULL) { + pr_info("default type %s does not exist\n", d); + return false; + } + + struct avtab_key key; + key.source_type = src->value; + key.target_type = tgt->value; + key.target_class = cls->value; + key.specified = effect; + + struct avtab_node *node = get_avtab_node(db, &key, NULL); + node->datum.u.data = def->value; + + return true; +} + +// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void +// *datum, struct hashtab_key_params key_params) 5.8.0: int +// hashtab_insert(struct hashtab *h, void *k, void *d); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) +static u32 filenametr_hash(const void *k) +{ + const struct filename_trans_key *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash; +} + +static int filenametr_cmp(const void *k1, const void *k2) +{ + const struct filename_trans_key *ft1 = k1; + const struct filename_trans_key *ft2 = k2; + int v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); +} + +static const struct hashtab_key_params filenametr_key_params = { + .hash = filenametr_hash, + .cmp = filenametr_cmp, +}; +#endif + +static bool add_filename_trans(struct policydb *db, const char *s, + const char *t, const char *c, const char *d, + const char *o) +{ + struct type_datum *src, *tgt, *def; + struct class_datum *cls; + + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_warn("source type %s does not exist\n", s); + return false; + } + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_warn("target type %s does not exist\n", t); + return false; + } + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_warn("class %s does not exist\n", c); + return false; + } + def = symtab_search(&db->p_types, d); + if (def == NULL) { + pr_warn("default type %s does not exist\n", d); + return false; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) + struct filename_trans_key key; + key.ttype = tgt->value; + key.tclass = cls->value; + key.name = (char *)o; + + struct filename_trans_datum *last = NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + struct filename_trans_datum *trans = policydb_filenametr_search(db, &key); +#else + struct filename_trans_datum *trans = hashtab_search(&db->filename_trans, &key); +#endif + while (trans) { + if (ebitmap_get_bit(&trans->stypes, src->value - 1)) { + // Duplicate, overwrite existing data and return + trans->otype = def->value; + return true; + } + if (trans->otype == def->value) + break; + last = trans; + trans = trans->next; + } + + if (trans == NULL) { + trans = (struct filename_trans_datum *)kcalloc(1, sizeof(*trans), + GFP_KERNEL); + struct filename_trans_key *new_key = + (struct filename_trans_key *)kzalloc(sizeof(*new_key), GFP_KERNEL); + *new_key = key; + new_key->name = kstrdup(key.name, GFP_KERNEL); + trans->next = last; + trans->otype = def->value; + hashtab_insert(&db->filename_trans, new_key, trans, + filenametr_key_params); + } + + db->compat_filename_trans_count++; + return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0; +#else // < 5.7.0, has no filename_trans_key, but struct filename_trans + + struct filename_trans key; + key.ttype = tgt->value; + key.tclass = cls->value; + key.name = (char *)o; + + struct filename_trans_datum *trans = hashtab_search(db->filename_trans, &key); + + if (trans == NULL) { + trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), 1, + GFP_KERNEL); + if (!trans) { + pr_err("add_filename_trans: Failed to alloc datum\n"); + return false; + } + struct filename_trans *new_key = + (struct filename_trans *)kzalloc(sizeof(*new_key), GFP_KERNEL); + if (!new_key) { + pr_err("add_filename_trans: Failed to alloc new_key\n"); + return false; + } + *new_key = key; + new_key->name = kstrdup(key.name, GFP_KERNEL); + trans->otype = def->value; + hashtab_insert(db->filename_trans, new_key, trans); + } + + return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == 0; +#endif +} + +static bool add_genfscon(struct policydb *db, const char *fs_name, + const char *path, const char *context) +{ + return false; +} + +// https://github.com/torvalds/linux/commit/590b9d576caec6b4c46bba49ed36223a399c3fc5#diff-cc9aa90e094e6e0f47bd7300db4f33cf4366b98b55d8753744f31eb69c691016R844-R845 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#define ksu_kvrealloc(p, new_size, _old_size) kvrealloc(p, new_size, GFP_KERNEL) +#else +#define ksu_kvrealloc(p, new_size, old_size) \ + ksu_compat_kvrealloc(p, old_size, new_size, GFP_KERNEL) +#endif + +static bool add_type(struct policydb *db, const char *type_name, bool attr) +{ + struct type_datum *type = symtab_search(&db->p_types, type_name); + if (type) { + pr_warn("Type %s already exists\n", type_name); + return true; + } + + u32 value = ++db->p_types.nprim; + type = (struct type_datum *)kzalloc(sizeof(struct type_datum), GFP_KERNEL); + if (!type) { + pr_err("add_type: alloc type_datum failed.\n"); + return false; + } + + type->primary = 1; + type->value = value; + type->attribute = attr; + + char *key = kstrdup(type_name, GFP_KERNEL); + if (!key) { + pr_err("add_type: alloc key failed.\n"); + return false; + } + + if (symtab_insert(&db->p_types, key, type)) { + pr_err("add_type: insert symtab failed.\n"); + return false; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) + struct ebitmap *new_type_attr_map_array = + ksu_kvrealloc(db->type_attr_map_array, value * sizeof(struct ebitmap), + (value - 1) * sizeof(struct ebitmap)); + + if (!new_type_attr_map_array) { + pr_err("add_type: alloc type_attr_map_array failed\n"); + return false; + } + + struct type_datum **new_type_val_to_struct = + ksu_kvrealloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * value, + sizeof(*db->type_val_to_struct) * (value - 1)); + + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + + char **new_val_to_name_types = + ksu_kvrealloc(db->sym_val_to_name[SYM_TYPES], sizeof(char *) * value, + sizeof(char *) * (value - 1)); + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + + db->type_attr_map_array = new_type_attr_map_array; + ebitmap_init(&db->type_attr_map_array[value - 1]); + ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1); + + db->type_val_to_struct = new_type_val_to_struct; + db->type_val_to_struct[value - 1] = type; + + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + db->sym_val_to_name[SYM_TYPES][value - 1] = key; + + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } + + return true; +#elif defined(CONFIG_IS_HW_HISI) + /* + * Huawei use type_attr_map and type_val_to_struct. + * And use ebitmap not flex_array. + */ + size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim; + struct ebitmap *new_type_attr_map = + (krealloc(db->type_attr_map, new_size, GFP_KERNEL)); + + struct type_datum **new_type_val_to_struct = + krealloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * db->p_types.nprim, + GFP_KERNEL); + + if (!new_type_attr_map) { + pr_err("add_type: alloc type_attr_map failed\n"); + return false; + } + + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + + char **new_val_to_name_types = + krealloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * db->symtab[SYM_TYPES].nprim, + GFP_KERNEL); + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + + db->type_attr_map = new_type_attr_map; + ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO); + ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1); + + db->type_val_to_struct = new_type_val_to_struct; + db->type_val_to_struct[value - 1] = type; + + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + db->sym_val_to_name[SYM_TYPES][value - 1] = key; + + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } + + return true; +#else + // flex_array is not extensible, we need to create a new bigger one instead + struct flex_array *new_type_attr_map_array = + flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + + struct flex_array *new_type_val_to_struct = + flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + + struct flex_array *new_val_to_name_types = + flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim, + GFP_KERNEL | __GFP_ZERO); + + if (!new_type_attr_map_array) { + pr_err("add_type: alloc type_attr_map_array failed\n"); + return false; + } + + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + + // preallocate so we don't have to worry about the put ever failing + if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim, + GFP_KERNEL | __GFP_ZERO)) { + pr_err("add_type: prealloc type_attr_map_array failed\n"); + return false; + } + + if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim, + GFP_KERNEL | __GFP_ZERO)) { + pr_err("add_type: prealloc type_val_to_struct_array failed\n"); + return false; + } + + if (flex_array_prealloc(new_val_to_name_types, 0, + db->symtab[SYM_TYPES].nprim, + GFP_KERNEL | __GFP_ZERO)) { + pr_err("add_type: prealloc val_to_name_types failed\n"); + return false; + } + + int j; + void *old_elem; + // copy the old data or pointers to new flex arrays + for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) { + old_elem = flex_array_get(db->type_attr_map_array, j); + if (old_elem) + flex_array_put(new_type_attr_map_array, j, old_elem, + GFP_KERNEL | __GFP_ZERO); + } + + for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) { + old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j); + if (old_elem) + flex_array_put_ptr(new_type_val_to_struct, j, old_elem, + GFP_KERNEL | __GFP_ZERO); + } + + for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) { + old_elem = + flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j); + if (old_elem) + flex_array_put_ptr(new_val_to_name_types, j, old_elem, + GFP_KERNEL | __GFP_ZERO); + } + + // store the pointer of old flex arrays first, when assigning new ones we + // should free it + struct flex_array *old_fa; + + old_fa = db->type_attr_map_array; + db->type_attr_map_array = new_type_attr_map_array; + if (old_fa) { + flex_array_free(old_fa); + } + + ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1)); + ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1), + value - 1, 1); + + old_fa = db->type_val_to_struct_array; + db->type_val_to_struct_array = new_type_val_to_struct; + if (old_fa) { + flex_array_free(old_fa); + } + flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type, + GFP_KERNEL | __GFP_ZERO); + + old_fa = db->sym_val_to_name[SYM_TYPES]; + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + if (old_fa) { + flex_array_free(old_fa); + } + flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key, + GFP_KERNEL | __GFP_ZERO); + + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, 1); + } + return true; +#endif +} + +static bool set_type_state(struct policydb *db, const char *type_name, + bool permissive) +{ + struct type_datum *type; + if (type_name == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_types.table, node) + { + type = (struct type_datum *)(node->datum); + if (ebitmap_set_bit(&db->permissive_map, type->value, permissive)) + pr_info("Could not set bit in permissive map\n"); + }; + } else { + type = (struct type_datum *)symtab_search(&db->p_types, type_name); + if (type == NULL) { + pr_info("type %s does not exist\n", type_name); + return false; + } + if (ebitmap_set_bit(&db->permissive_map, type->value, permissive)) { + pr_info("Could not set bit in permissive map\n"); + return false; + } + } + return true; +} + +static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, + struct type_datum *attr) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) + struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; +#elif defined(CONFIG_IS_HW_HISI) + /* + * HISI_SELINUX_EBITMAP_RO is Huawei's unique features. + */ + struct ebitmap *sattr = &db->type_attr_map[type->value - 1], + HISI_SELINUX_EBITMAP_RO; +#else + struct ebitmap *sattr = + flex_array_get(db->type_attr_map_array, type->value - 1); +#endif + ebitmap_set_bit(sattr, attr->value - 1, 1); + + struct hashtab_node *node; + struct constraint_node *n; + struct constraint_expr *e; + ksu_hashtab_for_each(db->p_classes.table, node) + { + struct class_datum *cls = (struct class_datum *)(node->datum); + for (n = cls->constraints; n; n = n->next) { + for (e = n->expr; e; e = e->next) { + if (e->expr_type == CEXPR_NAMES && + ebitmap_get_bit(&e->type_names->types, attr->value - 1)) { + ebitmap_set_bit(&e->names, type->value - 1, 1); + } + } + } + }; +} + +static bool add_typeattribute(struct policydb *db, const char *type, + const char *attr) +{ + struct type_datum *type_d = symtab_search(&db->p_types, type); + if (type_d == NULL) { + pr_info("type %s does not exist\n", type); + return false; + } else if (type_d->attribute) { + pr_info("type %s is an attribute\n", attr); + return false; + } + + struct type_datum *attr_d = symtab_search(&db->p_types, attr); + if (attr_d == NULL) { + pr_info("attribute %s does not exist\n", type); + return false; + } else if (!attr_d->attribute) { + pr_info("type %s is not an attribute \n", attr); + return false; + } + + add_typeattribute_raw(db, type_d, attr_d); + return true; +} + +////////////////////////////////////////////////////////////////////////// + +// Operation on types +bool ksu_type(struct policydb *db, const char *name, const char *attr) +{ + return add_type(db, name, false) && add_typeattribute(db, name, attr); +} + +bool ksu_attribute(struct policydb *db, const char *name) +{ + return add_type(db, name, true); +} + +bool ksu_permissive(struct policydb *db, const char *type) +{ + return set_type_state(db, type, true); +} + +bool ksu_enforce(struct policydb *db, const char *type) +{ + return set_type_state(db, type, false); +} + +bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr) +{ + return add_typeattribute(db, type, attr); +} + +bool ksu_exists(struct policydb *db, const char *type) +{ + return symtab_search(&db->p_types, type) != NULL; +} + +// Access vector rules +bool ksu_allow(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm) +{ + return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false); +} + +bool ksu_deny(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm) +{ + return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true); +} + +bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm) +{ + return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false); +} +bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm) +{ + return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true); +} + +// Extended permissions access vector rules +bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *range) +{ + return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED, + false); +} + +bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *range) +{ + return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW, + false); +} + +bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *range) +{ + return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT, + false); +} + +// Type rules +bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *def, const char *obj) +{ + if (obj) { + return add_filename_trans(db, src, tgt, cls, def, obj); + } else { + return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION); + } +} + +bool ksu_type_change(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *def) +{ + return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE); +} + +bool ksu_type_member(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *def) +{ + return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER); +} + +// File system labeling +bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path, + const char *ctx) +{ + return add_genfscon(db, fs_name, path, ctx); +} + +#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS + +// https://github.com/torvalds/linux/commit/581646c3fb98494009671f6d347ea125bc0e663a +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) +#define CONST_IF_6_10 const +#else +#define CONST_IF_6_10 +#endif + +// ======== begin copy ======== + +static int copy_hashtab_node(struct hashtab_node *new_node, + CONST_IF_6_10 struct hashtab_node *old_node, + void *data) +{ + new_node->datum = old_node->datum; + new_node->key = old_node->key; + return 0; +} + +static int destroy_hashtab_node(void *key, void *datum, void *data) +{ + // just copied pointer, no need to free + return 0; +} + +static int shallow_copy_hashtab(struct hashtab *new_tab, + struct hashtab *old_tab) +{ + return hashtab_duplicate(new_tab, old_tab, copy_hashtab_node, + destroy_hashtab_node, NULL); +} + +// ======== class_datum ======== + +static int +copy_class_datum_partially_callback(struct hashtab_node *new_node, + CONST_IF_6_10 struct hashtab_node *old_node, + void *data) +{ + struct policydb *db = data; + struct class_datum *cls = old_node->datum, *new_cls; + struct constraint_node *oldn, *n, *nprev = NULL; + struct constraint_expr *olde, *e, *eprev; + new_node->key = old_node->key; + new_cls = kmemdup(cls, sizeof(struct class_datum), GFP_KERNEL); + if (!new_cls) + return -ENOMEM; + new_node->datum = new_cls; + new_cls->constraints = NULL; + for (oldn = cls->constraints; oldn; oldn = oldn->next) { + n = kmemdup(oldn, sizeof(struct constraint_node), GFP_KERNEL); + if (!n) + goto out_nomem; + if (nprev) { + nprev->next = n; + } else { + new_cls->constraints = n; + } + eprev = NULL; + n->expr = NULL; + for (olde = oldn->expr; olde; olde = olde->next) { + e = kmemdup(olde, sizeof(struct constraint_expr), GFP_KERNEL); + if (!e) { + goto out_nomem; + } + if (eprev) { + eprev->next = e; + } else { + n->expr = e; + } + if (olde->expr_type == CEXPR_NAMES) { + if (ebitmap_cpy(&e->names, &olde->names) < 0) { + goto out_nomem; + } + } + eprev = e; + } + nprev = n; + } + + db->class_val_to_struct[new_cls->value - 1] = new_cls; + + return 0; +out_nomem: + return -ENOMEM; +} + +static int destroy_class_datum_partially_callback(void *key, void *datum, + void *data) +{ + struct class_datum *cls = datum; + struct constraint_node *n, *nprev; + struct constraint_expr *e, *eprev; + if (cls) { + for (n = cls->constraints; n;) { + for (e = n->expr; e;) { + if (e->expr_type == CEXPR_NAMES) { + ebitmap_destroy(&e->names); + } + eprev = e; + e = e->next; + kfree(eprev); + } + nprev = n; + n = n->next; + kfree(nprev); + } + } + kfree(cls); + + return 0; +} + +static void free_class_datum_partially(struct policydb *db) +{ + if (db->class_val_to_struct) { + kfree(db->class_val_to_struct); + } + + if (db->p_classes.table.htable) { + hashtab_map(&db->p_classes.table, + destroy_class_datum_partially_callback, NULL); + hashtab_destroy(&db->p_classes.table); + } +} + +static int copy_class_datum_partially(struct policydb *new_db, + struct policydb *old_db) +{ + int ret; + u32 n = new_db->symtab[SYM_CLASSES].nprim; + struct class_datum **new_class_val_to_struct; + + new_db->class_val_to_struct = NULL; + memset(&new_db->p_classes.table, 0, sizeof(new_db->p_classes.table)); + + new_class_val_to_struct = + kcalloc(n, sizeof(struct class_datum *), GFP_KERNEL); + if (!new_class_val_to_struct) { + ret = -ENOMEM; + goto exit; + } + new_db->class_val_to_struct = new_class_val_to_struct; + + ret = hashtab_duplicate(&new_db->p_classes.table, &old_db->p_classes.table, + copy_class_datum_partially_callback, + destroy_class_datum_partially_callback, new_db); + + if (ret) { + goto exit; + } + + return 0; + +exit: + free_class_datum_partially(new_db); + return ret; +} + +// ======== avtab ======== + +static int copy_avtab(struct avtab *new_avtab, struct avtab *old_avtab) +{ + int ret, i; + struct avtab_node *n, *p; + ret = avtab_alloc_dup(new_avtab, old_avtab); + if (ret < 0) + return ret; + // avtab_alloc_dup didn't zero it + new_avtab->nel = 0; + + for (i = 0; i < old_avtab->nslot; i++) { + n = old_avtab->htable[i]; + while (n) { + p = avtab_insert_nonunique(new_avtab, &n->key, &n->datum); + if (!p) { + ret = -ENOMEM; + goto out_free; + } + n = n->next; + } + } + + return 0; + +out_free: + avtab_destroy(new_avtab); + return ret; +} + +// ======== role_datum ======== + +static int +copy_role_datum_partially_callback(struct hashtab_node *new_node, + CONST_IF_6_10 struct hashtab_node *old_node, + void *data) +{ + int ret = 0; + struct policydb *db = data; + struct role_datum *role = old_node->datum, *new_role; + new_role = kmemdup(role, sizeof(struct role_datum), GFP_KERNEL); + if (!new_role) { + ret = -ENOMEM; + goto out; + } + new_node->datum = new_role; + new_node->key = old_node->key; + + ret = ebitmap_cpy(&new_role->types, &role->types); + if (ret) { + goto out; + } + db->role_val_to_struct[role->value - 1] = new_role; + +out: + return ret; +} + +static int destroy_role_datum_partially_callback(void *key, void *datum, + void *data) +{ + struct role_datum *role = datum; + if (role) { + ebitmap_destroy(&role->types); + kfree(role); + } + return 0; +} + +static void free_role_datum_partially(struct policydb *db) +{ + if (db->role_val_to_struct) { + kfree(db->role_val_to_struct); + } + if (db->p_roles.table.htable) { + hashtab_map(&db->p_roles.table, destroy_role_datum_partially_callback, + NULL); + hashtab_destroy(&db->p_roles.table); + } +} + +static int copy_role_datum_partially(struct policydb *new_db, + struct policydb *old_db) +{ + int ret; + struct role_datum **new_role_val_to_struct; + u32 n = old_db->p_roles.nprim; + + new_db->role_val_to_struct = NULL; + memset(&new_db->p_roles.table, 0, sizeof(new_db->p_roles.table)); + + new_role_val_to_struct = + kcalloc(n, sizeof(*new_db->role_val_to_struct), GFP_KERNEL); + if (!new_role_val_to_struct) { + ret = -ENOMEM; + goto out_free; + } + new_db->role_val_to_struct = new_role_val_to_struct; + + ret = hashtab_duplicate(&new_db->p_roles.table, &old_db->p_roles.table, + copy_role_datum_partially_callback, + destroy_role_datum_partially_callback, new_db); + if (ret) + goto out_free; + return 0; + +out_free: + free_role_datum_partially(new_db); + + return ret; +} + +// ======== type_datum ======== + +static void free_type_datum_partially(struct policydb *db) +{ + u32 sz = db->p_types.nprim, i; + if (db->type_attr_map_array) { + for (i = 0; i < sz; i++) { + ebitmap_destroy(&db->type_attr_map_array[i]); + } + + kvfree(db->type_attr_map_array); + } + + if (db->type_val_to_struct) { + kvfree(db->type_val_to_struct); + } + + if (db->sym_val_to_name[SYM_TYPES]) { + kvfree(db->sym_val_to_name[SYM_TYPES]); + } + + hashtab_destroy(&db->p_types.table); +} + +static int copy_type_datum_partially(struct policydb *new_db, + struct policydb *old_db) +{ + int ret = -ENOMEM; + u32 sz = new_db->p_types.nprim, i; + struct ebitmap *new_type_attr_map_array; + struct type_datum **new_type_val_to_struct; + char **new_sym_val_to_name_types; + + new_db->type_attr_map_array = NULL; + new_db->type_val_to_struct = NULL; + new_db->sym_val_to_name[SYM_TYPES] = NULL; + memset(&new_db->p_types.table, 0, sizeof(new_db->p_types.table)); + + // ======== type_attr_map_array ======== + + new_type_attr_map_array = kvcalloc(sz, sizeof(struct ebitmap), GFP_KERNEL); + + if (!new_type_attr_map_array) { + goto out; + } + + new_db->type_attr_map_array = new_type_attr_map_array; + for (i = 0; i < sz; i++) { + ret = ebitmap_cpy(&new_db->type_attr_map_array[i], + &old_db->type_attr_map_array[i]); + if (ret < 0) + goto out; + } + + // ======== type_val_to_struct ======== + ret = -ENOMEM; + + new_type_val_to_struct = + kvcalloc(sz, sizeof(*new_db->type_val_to_struct), GFP_KERNEL); + if (!new_type_val_to_struct) { + goto out; + } + new_db->type_val_to_struct = new_type_val_to_struct; + memcpy(new_db->type_val_to_struct, old_db->type_val_to_struct, + sz * sizeof(*new_db->type_val_to_struct)); + + // ======== sym_val_to_name[SYM_TYPES] ======== + + new_sym_val_to_name_types = + kvcalloc(sz, sizeof(*new_db->sym_val_to_name[SYM_TYPES]), GFP_KERNEL); + if (!new_sym_val_to_name_types) + goto out; + new_db->sym_val_to_name[SYM_TYPES] = new_sym_val_to_name_types; + memcpy(new_db->sym_val_to_name[SYM_TYPES], + old_db->sym_val_to_name[SYM_TYPES], + sz * sizeof(*new_db->sym_val_to_name[SYM_TYPES])); + + // ======== p_types ======== + + ret = shallow_copy_hashtab(&new_db->p_types.table, &old_db->p_types.table); + if (ret < 0) + goto out; + + return 0; +out: + free_type_datum_partially(new_db); + return ret; +} + +// ======== permissive_map ======== + +static void free_permissive_map(struct policydb *db) +{ + ebitmap_destroy(&db->permissive_map); +} + +static int copy_permissive_map(struct policydb *new_db, struct policydb *old_db) +{ + // On failure, the old ebitmap is cleaned. + return ebitmap_cpy(&new_db->permissive_map, &old_db->permissive_map); +} + +// ======== filename_trans ======== + +static void free_filename_trans(struct policydb *db) +{ + hashtab_destroy(&db->filename_trans); +} + +static int copy_filename_trans(struct policydb *new_db, struct policydb *old_db) +{ + // On failure, the old hashtab is cleaned. + return shallow_copy_hashtab(&new_db->filename_trans, + &old_db->filename_trans); +} + +// ======== sepolicy ======== + +void ksu_destroy_sepolicy(struct selinux_policy *pol) +{ + if (!pol) + return; + + struct policydb *db = &pol->policydb; + + free_class_datum_partially(db); + + avtab_destroy(&db->te_avtab); + + free_role_datum_partially(db); + + free_type_datum_partially(db); + + free_permissive_map(db); + + free_filename_trans(db); + + kfree(pol); +} + +struct selinux_policy *ksu_dup_sepolicy(struct selinux_policy *old_pol) +{ + int ret; + struct selinux_policy *new_pol = + kmemdup(old_pol, sizeof(*old_pol), GFP_KERNEL); + if (!new_pol) { + return NULL; + } + struct policydb *new_db = &new_pol->policydb, *old_db = &old_pol->policydb; + + ret = copy_class_datum_partially(new_db, old_db); + if (ret < 0) { + pr_err("ksu_dup_sepolicy: copy_class_datum_partially\n"); + goto out; + } + + ret = copy_avtab(&new_db->te_avtab, &old_db->te_avtab); + if (ret < 0) { + pr_err("ksu_dup_sepolicy: copy_avtab\n"); + goto out; + } + + ret = copy_role_datum_partially(new_db, old_db); + if (ret < 0) { + pr_err("ksu_dup_sepolicy: copy_role_datum_partially\n"); + goto out; + } + + ret = copy_type_datum_partially(new_db, old_db); + if (ret < 0) { + pr_err("ksu_dup_sepolicy: copy_type_datum_partially\n"); + goto out; + } + + ret = copy_permissive_map(new_db, old_db); + if (ret < 0) { + pr_err("ksu_dup_sepolicy: copy_permissive_map\n"); + goto out; + } + + ret = copy_filename_trans(new_db, old_db); + if (ret < 0) { + pr_err("ksu_dup_sepolicy: copy_filename_trans\n"); + goto out; + } + + return new_pol; + +out: + kfree(new_pol); + return NULL; +} +#endif // SELINUX_POLICY_INSTEAD_SELINUX_SS diff --git a/KernelSU-Next/kernel/selinux/sepolicy.h b/KernelSU-Next/kernel/selinux/sepolicy.h new file mode 100644 index 000000000000..b4f0db12606a --- /dev/null +++ b/KernelSU-Next/kernel/selinux/sepolicy.h @@ -0,0 +1,46 @@ +#ifndef __KSU_H_SEPOLICY +#define __KSU_H_SEPOLICY + +#include + +#include "ss/policydb.h" + +// Operation on types +bool ksu_type(struct policydb *db, const char *name, const char *attr); +bool ksu_attribute(struct policydb *db, const char *name); +bool ksu_permissive(struct policydb *db, const char *type); +bool ksu_enforce(struct policydb *db, const char *type); +bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr); +bool ksu_exists(struct policydb *db, const char *type); + +// Access vector rules +bool ksu_allow(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm); +bool ksu_deny(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm); +bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm); +bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *perm); + +// Extended permissions access vector rules +bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *range); +bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *range); +bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *range); + +// Type rules +bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *def, const char *obj); +bool ksu_type_change(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *def); +bool ksu_type_member(struct policydb *db, const char *src, const char *tgt, + const char *cls, const char *def); + +// File system labeling +bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path, + const char *ctx); + +#endif \ No newline at end of file diff --git a/KernelSU-Next/kernel/setup.sh b/KernelSU-Next/kernel/setup.sh new file mode 100755 index 000000000000..292e2c022e5c --- /dev/null +++ b/KernelSU-Next/kernel/setup.sh @@ -0,0 +1,80 @@ +#!/bin/sh +set -eu + +GKI_ROOT=$(pwd) +OWNER="KernelSU-Next" +REPO="$OWNER" + +display_usage() { + echo "Usage: $0 [--cleanup | ]" + echo " --cleanup: Cleans up previous modifications made by the script." + echo " : Sets up or updates the KernelSU-Next to specified tag or commit." + echo " -h, --help: Displays this usage information." + echo " (no args): Sets up or updates the KernelSU-Next environment to the latest tagged version." +} + +initialize_variables() { + if test -d "$GKI_ROOT/common/drivers"; then + DRIVER_DIR="$GKI_ROOT/common/drivers" + elif test -d "$GKI_ROOT/drivers"; then + DRIVER_DIR="$GKI_ROOT/drivers" + else + echo '[ERROR] "drivers/" directory not found.' + exit 127 + fi + + DRIVER_MAKEFILE=$DRIVER_DIR/Makefile + DRIVER_KCONFIG=$DRIVER_DIR/Kconfig +} + +# Reverts modifications made by this script +perform_cleanup() { + echo "[+] Cleaning up..." + [ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed." + grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted." + grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted." + if [ -d "$GKI_ROOT/$REPO" ]; then + rm -rf "$GKI_ROOT/$REPO" && echo "[-] $REPO directory deleted." + fi +} + +# Sets up or update KernelSU-Next environment +setup_kernelsu() { + echo "[+] Setting up $REPO..." + test -d "$GKI_ROOT/$REPO" || git clone "https://github.com/$OWNER/$REPO" && echo "[+] Repository cloned." + cd "$GKI_ROOT/$REPO" + git stash && echo "[-] Stashed current changes." + + BRANCH="$(git rev-parse --abbrev-ref origin/HEAD | sed 's@^origin/@@')" + if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then + git checkout $BRANCH && echo "[-] Switched to $BRANCH branch." + fi + + git pull && echo "[+] Repository updated." + if [ -z "${1-}" ]; then + git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag." + else + git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch" + fi + cd "$DRIVER_DIR" + ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/$REPO/kernel")" "kernelsu" && echo "[+] Symlink created." + + # Add entries in Makefile and Kconfig if not already existing + grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile." + grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig." + echo '[+] Done.' +} + +# Process command-line arguments +if [ "$#" -eq 0 ]; then + initialize_variables + setup_kernelsu +elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + display_usage +elif [ "$1" = "--cleanup" ]; then + initialize_variables + perform_cleanup +else + initialize_variables + setup_kernelsu "$@" +fi diff --git a/KernelSU-Next/kernel/supercall/dispatch.c b/KernelSU-Next/kernel/supercall/dispatch.c new file mode 100644 index 000000000000..7b24c93f71f0 --- /dev/null +++ b/KernelSU-Next/kernel/supercall/dispatch.c @@ -0,0 +1,971 @@ +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KSU_SUSFS +#include +#include +#include "objsec.h" +#endif // #ifdef CONFIG_KSU_SUSFS +#include "uapi/supercall.h" +#include "supercall/internal.h" +#include "arch.h" // IWYU pragma: keep +#include "policy/allowlist.h" +#include "policy/feature.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "runtime/ksud_boot.h" +#include "feature/kernel_umount.h" +#include "manager/manager_identity.h" +#include "selinux/selinux.h" +#include "infra/file_wrapper.h" +#include "hook/hook_manager.h" +#include "policy/app_profile.h" +#include "supercall/supercall.h" + +#include "tiny_sulog.h" + +static int do_grant_root(void __user *arg) +{ + // we already check uid above on allowed_for_su() + + write_sulog('i'); // log ioctl escalation + + pr_info("allow root for: %d\n", current_uid().val); + escape_with_root_profile(); + + return 0; +} + +static int do_get_info(void __user *arg) +{ + struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0}; + + if (ksuver_override) { + cmd.version = ksuver_override; + } + +#ifdef MODULE + cmd.flags |= KSU_GET_INFO_FLAG_LKM; +#endif + + if (is_manager()) { + cmd.flags |= KSU_GET_INFO_FLAG_MANAGER; + } + if (ksu_late_loaded) { + cmd.flags |= KSU_GET_INFO_FLAG_LATE_LOAD; + } + cmd.features = KSU_FEATURE_MAX; + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_version: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_report_event(void __user *arg) +{ + struct ksu_report_event_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + switch (cmd.event) { + case EVENT_POST_FS_DATA: { + static bool post_fs_data_lock = false; + if (!post_fs_data_lock) { + post_fs_data_lock = true; + if (ksu_late_loaded) { + pr_info("post-fs-data skipped (late load)\n"); + } else { + pr_info("post-fs-data triggered\n"); + on_post_fs_data(); + } + } + break; + } + case EVENT_BOOT_COMPLETED: { + static bool boot_complete_lock = false; + if (!boot_complete_lock) { + boot_complete_lock = true; + if (ksu_late_loaded) { + pr_info("boot_complete skipped (late load)\n"); + } else { + pr_info("boot_complete triggered\n"); + on_boot_completed(); +#ifdef CONFIG_KSU_SUSFS + susfs_start_sdcard_monitor_fn(); +#endif // #ifdef CONFIG_KSU_SUSFS + } + } + break; + } + case EVENT_MODULE_MOUNTED: { + pr_info("module mounted!\n"); + on_module_mounted(); + break; + } + default: + break; + } + + return 0; +} + +static int do_set_sepolicy(void __user *arg) +{ + struct ksu_set_sepolicy_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + return handle_sepolicy((void __user *)cmd.data, cmd.data_len); +} + +static int do_check_safemode(void __user *arg) +{ + struct ksu_check_safemode_cmd cmd; + + cmd.in_safe_mode = ksu_is_safe_mode(); + + if (cmd.in_safe_mode) { + pr_warn("safemode enabled!\n"); + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("check_safemode: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_new_get_allow_list_common(void __user *arg, bool allow) +{ + struct ksu_new_get_allow_list_cmd cmd; + int *arr = NULL; + int err = 0; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + if (cmd.count) { + // kmalloc_array safely checks for mathematical overflows before allocating + arr = kmalloc_array(cmd.count, sizeof(int), GFP_KERNEL); + if (!arr) { + return -ENOMEM; + } + } + + bool success = + ksu_get_allow_list(arr, cmd.count, &cmd.count, &cmd.total_count, allow); + + if (!success) { + err = -EFAULT; + goto out; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("new_get_allow_list: copy_to_user count failed\n"); + err = -EFAULT; + goto out; + } + + if (cmd.count && + copy_to_user(&((struct ksu_new_get_allow_list_cmd *)arg)->uids, arr, + sizeof(int) * cmd.count)) { + pr_err("new_get_allow_list: copy_to_user uids failed\n"); + err = -EFAULT; + } + +out: + if (arr) { + kfree(arr); + } + return err; +} + +static int do_new_get_deny_list(void __user *arg) +{ + return do_new_get_allow_list_common(arg, false); +} + +static int do_new_get_allow_list(void __user *arg) +{ + return do_new_get_allow_list_common(arg, true); +} + +static int do_get_allow_list_common(void __user *arg, bool allow) +{ + int *arr = NULL; + int err = 0; + u16 count; + u32 out_count; + static const u16 kSize = 128; + + arr = kmalloc(sizeof(int) * kSize, GFP_KERNEL); + if (!arr) { + return -ENOMEM; + } + + bool success = ksu_get_allow_list(arr, kSize, &count, NULL, allow); + + if (!success) { + err = -EFAULT; + goto out; + } + + out_count = count; + + if (copy_to_user(arg + offsetof(struct ksu_get_allow_list_cmd, count), + &out_count, sizeof(u32))) { + pr_err("get_allow_list: copy_to_user count failed\n"); + err = -EFAULT; + goto out; + } + + if (copy_to_user(arg, arr, sizeof(u32) * count)) { + pr_err("get_allow_list: copy_to_user uids failed\n"); + err = -EFAULT; + } + +out: + if (arr) { + kfree(arr); + } + return err; +} + +static int do_get_deny_list(void __user *arg) +{ + return do_get_allow_list_common(arg, false); +} + +static int do_get_allow_list(void __user *arg) +{ + return do_get_allow_list_common(arg, true); +} + +static int do_uid_granted_root(void __user *arg) +{ + struct ksu_uid_granted_root_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + cmd.granted = ksu_is_allow_uid_for_current(cmd.uid); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("uid_granted_root: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_uid_should_umount(void __user *arg) +{ + struct ksu_uid_should_umount_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + cmd.should_umount = ksu_uid_should_umount(cmd.uid); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("uid_should_umount: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_manager_appid(void __user *arg) +{ + struct ksu_get_manager_appid_cmd cmd; + + cmd.appid = ksu_get_manager_appid(); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_manager_appid: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_app_profile(void __user *arg) +{ +#ifdef CONFIG_KSU_DISABLE_POLICY + return -EOPNOTSUPP; +#endif + + struct ksu_get_app_profile_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_app_profile: copy_from_user failed\n"); + return -EFAULT; + } + + if (!ksu_get_app_profile(&cmd.profile)) { + return -ENOENT; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_app_profile: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_set_app_profile(void __user *arg) +{ +#ifdef CONFIG_KSU_DISABLE_POLICY + return -EOPNOTSUPP; +#endif + + struct ksu_set_app_profile_cmd cmd; + int ret; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("set_app_profile: copy_from_user failed\n"); + return -EFAULT; + } + + ret = ksu_set_app_profile(&cmd.profile); + if (!ret) { + ksu_persistent_allow_list(); +#ifdef KSU_KPROBES_HOOK + ksu_mark_running_process(); +#endif + } + return ret; +} + +static int do_get_feature(void __user *arg) +{ + struct ksu_get_feature_cmd cmd; + bool supported; + int ret; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_feature: copy_from_user failed\n"); + return -EFAULT; + } + + + ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported); + cmd.supported = supported ? 1 : 0; + + if (ret && supported) { + pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_feature: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_set_feature(void __user *arg) +{ + struct ksu_set_feature_cmd cmd; + int ret; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("set_feature: copy_from_user failed\n"); + return -EFAULT; + } + + + ret = ksu_set_feature(cmd.feature_id, cmd.value); + if (ret) { + pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } + + return 0; +} + +static int do_get_wrapper_fd(void __user *arg) { + if (!ksu_file_sid) { + return -EINVAL; + } + + struct ksu_get_wrapper_fd_cmd cmd; + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_wrapper_fd: copy_from_user failed\n"); + return -EFAULT; + } + + return ksu_install_file_wrapper(cmd.fd); +} + +static int do_manage_mark(void __user *arg) +{ +#ifdef KSU_KPROBES_HOOK + struct ksu_manage_mark_cmd cmd; + int ret = 0; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("manage_mark: copy_from_user failed\n"); + return -EFAULT; + } + + switch (cmd.operation) { + case KSU_MARK_GET: { +#ifndef CONFIG_KSU_SUSFS + // Get task mark status + ret = ksu_get_task_mark(cmd.pid); + if (ret < 0) { + pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret); + return ret; + } + cmd.result = (u32)ret; + break; +#else + if (susfs_is_current_proc_umounted()) { + ret = 0; // SYSCALL_TRACEPOINT is NOT flagged + } else { + ret = 1; // SYSCALL_TRACEPOINT is flagged + } + pr_info("manage_mark: ret for pid %d: %d\n", cmd.pid, ret); + cmd.result = (u32)ret; + break; +#endif // #ifndef CONFIG_KSU_SUSFS + } + case KSU_MARK_MARK: { +#ifndef CONFIG_KSU_SUSFS + if (cmd.pid == 0) { + ksu_mark_all_process(); + } else { + ret = ksu_set_task_mark(cmd.pid, true); + if (ret < 0) { + pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid, + ret); + return ret; + } + } +#else + if (cmd.pid != 0) { + return ret; + } +#endif // #ifndef CONFIG_KSU_SUSFS + break; + } + case KSU_MARK_UNMARK: { +#ifndef CONFIG_KSU_SUSFS + if (cmd.pid == 0) { + ksu_unmark_all_process(); + } else { + ret = ksu_set_task_mark(cmd.pid, false); + if (ret < 0) { + pr_err("manage_mark: set_unmark failed for pid %d: %d\n", + cmd.pid, ret); + return ret; + } + } +#else + if (cmd.pid != 0) { + return ret; + } +#endif // #ifndef CONFIG_KSU_SUSFS + break; + } + case KSU_MARK_REFRESH: { +#ifndef CONFIG_KSU_SUSFS + pr_info("manage_mark: refreshed running processes\n"); +#else + pr_info("susfs: cmd: KSU_MARK_REFRESH: do nothing\n"); +#endif // #ifndef CONFIG_KSU_SUSFS + break; + } + default: { + pr_err("manage_mark: invalid operation %u\n", cmd.operation); + return -EINVAL; + } + } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("manage_mark: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +#else + // We don't care, just return -ENOTSUPP + pr_warn("manage_mark: this supercalls is not implemented for manual hook.\n"); + return -ENOTSUPP; +#endif +} + +static int do_get_hook_mode(void __user *arg) +{ + struct ksu_get_hook_mode_cmd cmd = {0}; + const char *type = "Kprobes"; + +#ifndef KSU_KPROBES_HOOK + type = "Manual"; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(cmd.mode, type, sizeof(cmd.mode)); +#else + strlcpy(cmd.mode, type, sizeof(cmd.mode)); +#endif + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_hook_mode: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_version_tag(void __user *arg) +{ + struct ksu_get_version_tag_cmd cmd = {0}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(cmd.tag, KERNEL_SU_VERSION_TAG, sizeof(cmd.tag)); +#else + strlcpy(cmd.tag, KERNEL_SU_VERSION_TAG, sizeof(cmd.tag)); +#endif + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_version_tag: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_nuke_ext4_sysfs(void __user *arg) +{ + struct ksu_nuke_ext4_sysfs_cmd cmd; + char mnt[256]; + long ret; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) + return -EFAULT; + + if (!cmd.arg) + return -EINVAL; + + memset(mnt, 0, sizeof(mnt)); + + ret = strncpy_from_user(mnt, cmd.arg, sizeof(mnt)); + if (ret < 0) { + pr_err("nuke ext4 copy mnt failed: %ld\\n", ret); + return -EFAULT; // 或者 return ret; + } + + if (ret == sizeof(mnt)) { + pr_err("nuke ext4 mnt path too long\\n"); + return -ENAMETOOLONG; + } + + pr_info("do_nuke_ext4_sysfs: %s\n", mnt); + + return nuke_ext4_sysfs(mnt); +} + +struct list_head mount_list = LIST_HEAD_INIT(mount_list); +DECLARE_RWSEM(mount_list_lock); + +static int add_try_umount(void __user *arg) +{ + struct mount_entry *new_entry, *entry, *tmp; + struct ksu_add_try_umount_cmd cmd; + char buf[256] = {0}; + + if (copy_from_user(&cmd, arg, sizeof cmd)) + return -EFAULT; + + switch (cmd.mode) { + case KSU_UMOUNT_WIPE: { + struct mount_entry *entry, *tmp; + down_write(&mount_list_lock); + list_for_each_entry_safe(entry, tmp, &mount_list, list) { + pr_info("wipe_umount_list: removing entry: %s\n", entry->umountable); + list_del(&entry->list); + kfree(entry->umountable); + kfree(entry); + } + up_write(&mount_list_lock); + + return 0; + } + + case KSU_UMOUNT_ADD: { + long len = strncpy_from_user(buf, (const char __user *)cmd.arg, 256); + if (len <= 0) + return -EFAULT; + + buf[sizeof(buf) - 1] = '\0'; + + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + + new_entry->umountable = kstrdup(buf, GFP_KERNEL); + if (!new_entry->umountable) { + kfree(new_entry); + return -ENOMEM; + } + + down_write(&mount_list_lock); + + // disallow dupes + // if this gets too many, we can consider moving this whole task to a kthread + list_for_each_entry(entry, &mount_list, list) { + if (!strcmp(entry->umountable, buf)) { + pr_info("cmd_add_try_umount: %s is already here!\n", buf); + up_write(&mount_list_lock); + kfree(new_entry->umountable); + kfree(new_entry); + return -EEXIST; + } + } + + // now check flags and add + // this also serves as a null check + if (cmd.flags) + new_entry->flags = cmd.flags; + else + new_entry->flags = 0; + + // debug + list_add(&new_entry->list, &mount_list); + up_write(&mount_list_lock); + pr_info("cmd_add_try_umount: %s added!\n", buf); + + return 0; + } + + // this is just strcmp'd wipe anyway + case KSU_UMOUNT_DEL: { + long len = strncpy_from_user(buf, (const char __user *)cmd.arg, sizeof(buf) - 1); + if (len <= 0) + return -EFAULT; + + buf[sizeof(buf) - 1] = '\0'; + + down_write(&mount_list_lock); + list_for_each_entry_safe(entry, tmp, &mount_list, list) { + if (!strcmp(entry->umountable, buf)) { + pr_info("cmd_add_try_umount: entry removed: %s\n", entry->umountable); + list_del(&entry->list); + kfree(entry->umountable); + kfree(entry); + } + } + up_write(&mount_list_lock); + + return 0; + } + + // this way userspace can deduce the memory it has to prepare. + case KSU_UMOUNT_GETSIZE: { + // check for pointer first + if (!cmd.arg) + return -EFAULT; + + size_t total_size = 0; // size of list in bytes + + down_read(&mount_list_lock); + list_for_each_entry(entry, &mount_list, list) { + total_size = total_size + strlen(entry->umountable) + 1; // + 1 for \0 + } + up_read(&mount_list_lock); + + pr_info("cmd_add_try_umount: total_size: %zu\n", total_size); + + if (copy_to_user((size_t __user *)cmd.arg, &total_size, sizeof(total_size))) + return -EFAULT; + + return 0; + } + + // WARNING! this is straight up pointerwalking. + // this way we dont need to redefine the ioctl defs. + // this also avoids us needing to kmalloc + // userspace have to send pointer to memory (malloc/alloca) or pointer to a VLA. + case KSU_UMOUNT_GETLIST: { + if (!cmd.arg) + return -EFAULT; + + void *user_buf = (void *)cmd.arg; + + down_read(&mount_list_lock); + list_for_each_entry(entry, &mount_list, list) { + pr_info("cmd_add_try_umount: entry: %s\n", entry->umountable); + + if (copy_to_user(user_buf, entry->umountable, strlen(entry->umountable) + 1 )) { + up_read(&mount_list_lock); + return -EFAULT; + } + + // walk it! +1 for null terminator + user_buf = (char *)user_buf + strlen(entry->umountable) + 1; + } + up_read(&mount_list_lock); + + return 0; + } + + default: { + pr_err("cmd_add_try_umount: invalid operation %u\n", cmd.mode); + return -EINVAL; + } + + } // switch(cmd.mode) + + return 0; +} + +static int do_set_init_pgrp(void __user *arg) +{ + int err = -EPERM; + struct task_struct *p; + struct pid *init_group; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0) + struct pid *pids[PIDTYPE_MAX] = { 0 }; +#endif + + write_lock_irq(&tasklist_lock); + + p = current->group_leader; + init_group = task_pgrp(&init_task); + + if (task_session(p) != task_session(&init_task)) + goto out; + + err = 0; + if (task_pgrp(p) != init_group) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0) + change_pid(pids, p, PIDTYPE_PGID, init_group); +#else + change_pid(p, PIDTYPE_PGID, init_group); +#endif + } + +out: + write_unlock_irq(&tasklist_lock); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0) + free_pids(pids); +#endif + + return err; +} + +// IOCTL handlers mapping table +// clang-format off +static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { + { + .cmd = KSU_IOCTL_GRANT_ROOT, + .name = "GRANT_ROOT", + .handler = do_grant_root, + .perm_check = allowed_for_su + }, + { + .cmd = KSU_IOCTL_GET_INFO, + .name = "GET_INFO", + .handler = do_get_info, + .perm_check = always_allow + }, + { + .cmd = KSU_IOCTL_REPORT_EVENT, + .name = "REPORT_EVENT", + .handler = do_report_event, + .perm_check = only_root + }, + { + .cmd = KSU_IOCTL_SET_SEPOLICY, + .name = "SET_SEPOLICY", + .handler = do_set_sepolicy, + .perm_check = only_root + }, + { + .cmd = KSU_IOCTL_CHECK_SAFEMODE, + .name = "CHECK_SAFEMODE", + .handler = do_check_safemode, + .perm_check = always_allow + }, + { + .cmd = KSU_IOCTL_GET_ALLOW_LIST, + .name = "GET_ALLOW_LIST", + .handler = do_get_allow_list, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_GET_DENY_LIST, + .name = "GET_DENY_LIST", + .handler = do_get_deny_list, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_NEW_GET_ALLOW_LIST, + .name = "NEW_GET_ALLOW_LIST", + .handler = do_new_get_allow_list, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_NEW_GET_DENY_LIST, + .name = "NEW_GET_DENY_LIST", + .handler = do_new_get_deny_list, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_UID_GRANTED_ROOT, + .name = "UID_GRANTED_ROOT", + .handler = do_uid_granted_root, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, + .name = "UID_SHOULD_UMOUNT", + .handler = do_uid_should_umount, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_GET_MANAGER_APPID, + .name = "GET_MANAGER_APPID", + .handler = do_get_manager_appid, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_GET_APP_PROFILE, + .name = "GET_APP_PROFILE", + .handler = do_get_app_profile, + .perm_check = only_manager + }, + { + .cmd = KSU_IOCTL_SET_APP_PROFILE, + .name = "SET_APP_PROFILE", + .handler = do_set_app_profile, + .perm_check = only_manager + }, + { + .cmd = KSU_IOCTL_GET_FEATURE, + .name = "GET_FEATURE", + .handler = do_get_feature, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_SET_FEATURE, + .name = "SET_FEATURE", + .handler = do_set_feature, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_GET_WRAPPER_FD, + .name = "GET_WRAPPER_FD", + .handler = do_get_wrapper_fd, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_MANAGE_MARK, + .name = "MANAGE_MARK", + .handler = do_manage_mark, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_NUKE_EXT4_SYSFS, + .name = "NUKE_EXT4_SYSFS", + .handler = do_nuke_ext4_sysfs, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_ADD_TRY_UMOUNT, + .name = "ADD_TRY_UMOUNT", + .handler = add_try_umount, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_SET_INIT_PGRP, + .name = "SET_INIT_PGRP", + .handler = do_set_init_pgrp, + .perm_check = only_root + }, + { + .cmd = KSU_IOCTL_GET_HOOK_MODE, + .name = "GET_HOOK_MODE", + .handler = do_get_hook_mode, + .perm_check = manager_or_root + }, + { + .cmd = KSU_IOCTL_GET_VERSION_TAG, + .name = "GET_VERSION_TAG", + .handler = do_get_version_tag, + .perm_check = manager_or_root + }, + { + .cmd = 0, + .name = NULL, + .handler = NULL, + .perm_check = NULL + } // Sentinel +}; +// clang-format on + +long ksu_supercall_handle_ioctl(unsigned int cmd, void __user *argp) +{ + int i; + +#ifdef CONFIG_KSU_DEBUG + pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val); +#endif + + for (i = 0; ksu_ioctl_handlers[i].handler; i++) { + if (cmd == ksu_ioctl_handlers[i].cmd) { + // Check permission first + if (ksu_ioctl_handlers[i].perm_check && + !ksu_ioctl_handlers[i].perm_check()) { + pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n", + cmd, current_uid().val); + return -EPERM; + } + // Execute handler + return ksu_ioctl_handlers[i].handler(argp); + } + } + + pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd); + return -ENOTTY; +} + +void __init ksu_supercall_dump_commands(void) +{ + int i; + + pr_info("KernelSU IOCTL Commands:\n"); + for (i = 0; ksu_ioctl_handlers[i].handler; i++) { + pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd); + } +} + +void ksu_supercall_cleanup_state(void) +{ + struct mount_entry *entry, *tmp; + + down_write(&mount_list_lock); + list_for_each_entry_safe (entry, tmp, &mount_list, list) { + list_del(&entry->list); + kfree(entry->umountable); + kfree(entry); + } + up_write(&mount_list_lock); +} \ No newline at end of file diff --git a/KernelSU-Next/kernel/supercall/internal.h b/KernelSU-Next/kernel/supercall/internal.h new file mode 100644 index 000000000000..89a9735963f8 --- /dev/null +++ b/KernelSU-Next/kernel/supercall/internal.h @@ -0,0 +1,19 @@ +#ifndef __KSU_H_SUPERCALL_INTERNAL +#define __KSU_H_SUPERCALL_INTERNAL + +#include +#include + +bool only_manager(void); +bool only_root(void); +bool manager_or_root(void); +bool always_allow(void); +bool allowed_for_su(void); + +long ksu_supercall_handle_ioctl(unsigned int cmd, void __user *argp); +void ksu_supercall_dump_commands(void); +void ksu_supercall_cleanup_state(void); + +extern uint32_t ksuver_override; + +#endif // __KSU_H_SUPERCALL_INTERNAL diff --git a/KernelSU-Next/kernel/supercall/perm.c b/KernelSU-Next/kernel/supercall/perm.c new file mode 100644 index 000000000000..fe61dc78d943 --- /dev/null +++ b/KernelSU-Next/kernel/supercall/perm.c @@ -0,0 +1,32 @@ +#include + +#include "supercall/internal.h" +#include "manager/manager_identity.h" +#include "policy/allowlist.h" + +// Permission check functions +bool only_manager(void) +{ + return is_manager(); +} + +bool only_root(void) +{ + return current_uid().val == 0; +} + +bool manager_or_root(void) +{ + return current_uid().val == 0 || is_manager(); +} + +bool always_allow(void) +{ + return true; // No permission check +} + +bool allowed_for_su(void) +{ + bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val); + return is_allowed; +} diff --git a/KernelSU-Next/kernel/supercall/supercall.c b/KernelSU-Next/kernel/supercall/supercall.c new file mode 100644 index 000000000000..7c8457530a1a --- /dev/null +++ b/KernelSU-Next/kernel/supercall/supercall.c @@ -0,0 +1,392 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // utsname() and uts_sem +#ifdef CONFIG_KSU_SUSFS +#include +#include +#include "objsec.h" +#endif // #ifdef CONFIG_KSU_SUSFS + +#include "uapi/supercall.h" +#include "supercall/internal.h" +#include "arch.h" +#include "klog.h" // IWYU pragma: keep +#include "manager/manager_identity.h" + +#include "tiny_sulog.h" + +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +__weak void ksu_handle_umount(uid_t old_uid, uid_t new_uid) +{ + (void)old_uid; + (void)new_uid; +} + +void susfs_try_umount(uid_t new_uid) +{ + uid_t old_uid = current_uid().val; + ksu_handle_umount(old_uid, new_uid); +} + +int susfs_add_try_umount(void __user *arg) +{ + return 0; +} + +#endif + +uint32_t ksuver_override = 0; + +static int anon_ksu_release(struct inode *inode, struct file *filp) +{ + pr_info("ksu fd released\n"); + return 0; +} + +static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return ksu_supercall_handle_ioctl(cmd, (void __user *)arg); +} + +static const struct file_operations anon_ksu_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = anon_ksu_ioctl, + .compat_ioctl = anon_ksu_ioctl, + .release = anon_ksu_release, +}; + +int ksu_install_fd(void) +{ + struct file *filp; + int fd; + + // Get unused fd + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + pr_err("ksu_install_fd: failed to get unused fd\n"); + return fd; + } + + // Create anonymous inode file + filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, O_RDWR | O_CLOEXEC); + if (IS_ERR(filp)) { + pr_err("ksu_install_fd: failed to create anon inode file\n"); + put_unused_fd(fd); + return PTR_ERR(filp); + } + + // Install fd + fd_install(fd, filp); + + pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid); + + return fd; +} + +int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, + void __user **arg) +{ + if (magic1 != KSU_INSTALL_MAGIC1) + return 0; + +#ifdef CONFIG_KSU_DEBUG + pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1, + magic2); +#endif + +#ifdef CONFIG_KSU_SUSFS + // If magic2 is susfs and current process is root + if (magic2 == SUSFS_MAGIC && current_uid().val == 0) { +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (cmd == CMD_SUSFS_ADD_SUS_PATH) { + susfs_add_sus_path(arg); + return 0; + } + if (cmd == CMD_SUSFS_ADD_SUS_PATH_LOOP) { + susfs_add_sus_path_loop(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (cmd == CMD_SUSFS_HIDE_SUS_MNTS_FOR_NON_SU_PROCS) { + susfs_set_hide_sus_mnts_for_non_su_procs(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (cmd == CMD_SUSFS_ADD_SUS_KSTAT) { + susfs_add_sus_kstat(arg); + return 0; + } + if (cmd == CMD_SUSFS_UPDATE_SUS_KSTAT) { + susfs_update_sus_kstat(arg); + return 0; + } + if (cmd == CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY) { + susfs_add_sus_kstat(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + if (cmd == CMD_SUSFS_ADD_TRY_UMOUNT) { + susfs_add_try_umount(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + if (cmd == CMD_SUSFS_SET_UNAME) { + susfs_set_uname(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + if (cmd == CMD_SUSFS_ENABLE_LOG) { + susfs_enable_log(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + if (cmd == CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG) { + susfs_set_cmdline_or_bootconfig(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (cmd == CMD_SUSFS_ADD_OPEN_REDIRECT) { + susfs_add_open_redirect(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (cmd == CMD_SUSFS_ADD_SUS_MAP) { + susfs_add_sus_map(arg); + return 0; + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (cmd == CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING) { + susfs_set_avc_log_spoofing(arg); + return 0; + } + if (cmd == CMD_SUSFS_SHOW_ENABLED_FEATURES) { + susfs_get_enabled_features(arg); + return 0; + } + if (cmd == CMD_SUSFS_SHOW_VARIANT) { + susfs_show_variant(arg); + return 0; + } + if (cmd == CMD_SUSFS_SHOW_VERSION) { + susfs_show_version(arg); + return 0; + } + return 0; + } +#endif // #ifdef CONFIG_KSU_SUSFS + + // Check if this is a request to install KSU fd + if (magic2 == KSU_INSTALL_MAGIC2) { + int fd = ksu_install_fd(); + // downstream: dereference all arg usage! + if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) { + pr_err("install ksu fd reply err\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + close_fd(fd); +#else + __close_fd(current->files, fd); +#endif + } + return 0; + } + + // extensions + u64 reply = (u64)*arg; + + if (magic2 == CHANGE_MANAGER_UID) { + // only root is allowed for this command + if (current_uid().val != 0) + return 0; + + pr_info("sys_reboot: ksu_set_manager_appid to: %d\n", cmd); + ksu_set_manager_appid(cmd); + + if (cmd == ksu_get_manager_appid()) { + if (copy_to_user((void __user *)*arg, &reply, sizeof(reply))) + pr_info("sys_reboot: reply fail\n"); + } + + return 0; + } + + if (magic2 == GET_SULOG_DUMP_V2) { + // only root is allowed for this command + if (current_uid().val != 0) + return 0; + + int ret = send_sulog_dump(*arg); + if (ret) + return 0; + + if (copy_to_user((void __user *)*arg, &reply, sizeof(reply) )) + return 0; + } + + if (magic2 == CHANGE_KSUVER) { + // only root is allowed for this command + if (current_uid().val != 0) + return 0; + + pr_info("sys_reboot: ksu_change_ksuver to: %d\n", cmd); + ksuver_override = cmd; + + if (copy_to_user((void __user *)*arg, &reply, sizeof(reply) )) + return 0; + } + + // WARNING!!! triple ptr zone! *** + // https://wiki.c2.com/?ThreeStarProgrammer + if (magic2 == CHANGE_SPOOF_UNAME) { + // only root is allowed for this command + if (current_uid().val != 0) + return 0; + + char release_buf[65]; + char version_buf[65]; + static char original_release_buf[65] = {0}; + static char original_version_buf[65] = {0}; + + // basically void * void __user * void __user *arg + void ***ppptr = (uintptr_t)arg; + + // user pointer storage + // init this as zero so this works on 32-on-64 compat (LE) + uint64_t u_pptr = 0; + uint64_t u_ptr = 0; + + pr_info("sys_reboot: ppptr: 0x%lx \n", ppptr); + + // arg here is ***, dereference to pull out ** + if (copy_from_user(&u_pptr, (void __user *)*ppptr, sizeof(u_pptr))) + return 0; + + pr_info("sys_reboot: u_pptr: 0x%lx \n", u_pptr); + + // now we got the __user ** + // we cannot dereference this as this is __user + // we just do another copy_from_user to get it + if (copy_from_user(&u_ptr, (void __user *)u_pptr, sizeof(u_ptr))) + return 0; + + pr_info("sys_reboot: u_ptr: 0x%lx \n", u_ptr); + + // for release + if (strncpy_from_user(release_buf, (char __user *)u_ptr, sizeof(release_buf)) < 0) + return 0; + release_buf[sizeof(release_buf) - 1] = '\0'; + + // for version + if (strncpy_from_user(version_buf, (char __user *)(u_ptr + strlen(release_buf) + 1), sizeof(version_buf)) < 0) + return 0; + version_buf[sizeof(version_buf) - 1] = '\0'; + + if (original_release_buf[0] == '\0') { + struct new_utsname *u_curr = utsname(); + // we save current version as the original before modifying +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(original_release_buf, u_curr->release, sizeof(original_release_buf)); + strscpy(original_version_buf, u_curr->version, sizeof(original_version_buf)); +#else + strlcpy(original_release_buf, u_curr->release, sizeof(original_release_buf)); + strlcpy(original_version_buf, u_curr->version, sizeof(original_version_buf)); +#endif + pr_info("sys_reboot: original uname saved: %s %s\n", original_release_buf, original_version_buf); + } + + // so user can reset + if (!strcmp(release_buf, "default") || !strcmp(version_buf, "default") ) { + memcpy(release_buf, original_release_buf, sizeof(release_buf)); + memcpy(version_buf, original_version_buf, sizeof(version_buf)); + } + + pr_info("sys_reboot: spoofing kernel to: %s - %s\n", release_buf, version_buf); + + struct new_utsname *u = utsname(); + + down_write(&uts_sem); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(u->release, release_buf, sizeof(u->release)); + strscpy(u->version, version_buf, sizeof(u->version)); +#else + strlcpy(u->release, release_buf, sizeof(u->release)); + strlcpy(u->version, version_buf, sizeof(u->version)); +#endif + up_write(&uts_sem); + + // we write our confirmation on ** + if (copy_to_user((void __user *)*arg, &reply, sizeof(reply))) + return 0; + } + + return 0; +} + +#ifdef KSU_KPROBES_HOOK +static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int magic1 = (int)PT_REGS_PARM1(real_regs); + int magic2 = (int)PT_REGS_PARM2(real_regs); + unsigned int cmd = (unsigned int)PT_REGS_PARM3(real_regs); + unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); + unsigned long reply = (unsigned long)arg4; + + return ksu_handle_sys_reboot(magic1, magic2, cmd, (void __user **)&arg4); +} + +static struct kprobe reboot_kp = { + .symbol_name = REBOOT_SYMBOL, + .pre_handler = reboot_handler_pre, +}; +#endif + +void __init ksu_supercalls_init(void) +{ + int i; + + ksu_supercall_dump_commands(); + +#ifdef KSU_KPROBES_HOOK + int rc = register_kprobe(&reboot_kp); + if (rc) { + pr_err("reboot kprobe failed: %d\n", rc); + } else { + pr_info("reboot kprobe registered successfully\n"); + } +#endif + + sulog_init_heap(); // grab heap memory +} + +void __exit ksu_supercalls_exit(void){ + struct mount_entry *entry, *tmp; + +#ifdef KSU_KPROBES_HOOK + unregister_kprobe(&reboot_kp); +#endif + + ksu_supercall_cleanup_state(); +} diff --git a/KernelSU-Next/kernel/supercall/supercall.h b/KernelSU-Next/kernel/supercall/supercall.h new file mode 100644 index 000000000000..fbedd11f8542 --- /dev/null +++ b/KernelSU-Next/kernel/supercall/supercall.h @@ -0,0 +1,24 @@ +#ifndef __KSU_H_SUPERCALL +#define __KSU_H_SUPERCALL + +#include +#include + +// IOCTL handler types +typedef int (*ksu_ioctl_handler_t)(void __user *arg); +typedef bool (*ksu_perm_check_t)(void); + +// IOCTL command mapping +struct ksu_ioctl_cmd_map { + unsigned int cmd; + const char *name; + ksu_ioctl_handler_t handler; + ksu_perm_check_t perm_check; // Permission check function +}; + +// Install KSU fd to current process +int ksu_install_fd(void); + +void ksu_supercalls_init(void); +void ksu_supercalls_exit(void); +#endif // __KSU_H_SUPERCALL diff --git a/KernelSU-Next/kernel/tiny_sulog.c b/KernelSU-Next/kernel/tiny_sulog.c new file mode 100644 index 000000000000..81ab5bcda40b --- /dev/null +++ b/KernelSU-Next/kernel/tiny_sulog.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tiny_sulog.h" + +// half assed ringbuffer +// 8 bytes +struct sulog_entry { + uint32_t s_time; // uptime in seconds + uint32_t data; // uint8_t[0,1,2] = uid, basically uint24_t, uint8_t[3] = symbol +} __attribute__((packed)); + +#define SULOG_ENTRY_MAX 250 +#define SULOG_BUFSIZ SULOG_ENTRY_MAX * (sizeof (struct sulog_entry)) + +static void *sulog_buf_ptr = NULL; +static uint8_t sulog_index_next = 0; + +static DEFINE_SPINLOCK(sulog_lock); + +void sulog_init_heap(void) +{ + sulog_buf_ptr = kzalloc(SULOG_BUFSIZ, GFP_KERNEL); + if (!sulog_buf_ptr) + return; + + pr_info("sulog_init: allocated %lu bytes on 0x%p \n", SULOG_BUFSIZ, sulog_buf_ptr); +} + +/* + * + * boottime_s_get, get kernel uptime in seconds + * + * - handles sub 4.10 compat + * - we do this forced pointer cast to cut down on compat, pre 4.10, ktime is a union + * + * - bs handling 64-bit division on 32-bit (do_div) + * - remainder = do_div(dividend, divisor); dividend will hold the quotient + * - for 64-bit we can straight up just use divide + * + */ +static inline uint32_t boottime_s_get() +{ + ktime_t boottime_kt = ktime_get_boottime(); + +#ifdef CONFIG_64BIT + uint64_t boottime_s = *(uint64_t *)&boottime_kt / 1000000000; +#else + uint64_t boottime_s = *(uint64_t *)&boottime_kt; + do_div(boottime_s, 1000000000); +#endif + + return (uint32_t)boottime_s; +} + +void write_sulog(uint8_t sym) +{ + if (!sulog_buf_ptr) + return; + + unsigned int offset = sulog_index_next * sizeof(struct sulog_entry); + struct sulog_entry entry = {0}; + + // WARNING!!! this is LE only! + entry.s_time = boottime_s_get(); + entry.data = (uint32_t)current_uid().val; + *((char *)&entry.data + 3) = sym; + + // we can perform this write atomic on 64-bit + // however this still has to be locked for exclusion as theres a reader + + spin_lock(&sulog_lock); + +#ifdef CONFIG_64BIT + *(volatile uint64_t *)(sulog_buf_ptr + offset) = *(volatile uint64_t *)&entry; +#else + __builtin_memcpy(sulog_buf_ptr + offset, &entry, sizeof(entry)); +#endif + spin_unlock(&sulog_lock); + + // move ptr for next iteration + sulog_index_next = sulog_index_next + 1; + + if (sulog_index_next >= SULOG_ENTRY_MAX) + sulog_index_next = 0; +} + +struct sulog_entry_rcv_ptr { + uint64_t index_ptr; // send index here + uint64_t buf_ptr; // send buf here + uint64_t uptime_ptr; // uptime +}; + +int send_sulog_dump(void __user *uptr) +{ + if (!sulog_buf_ptr) + return 1; + + struct sulog_entry_rcv_ptr sbuf = {0}; + + if (copy_from_user(&sbuf, uptr, sizeof(sbuf) )) + return 1; + + if (!sbuf.index_ptr || !sbuf.buf_ptr || !sbuf.uptime_ptr ) + return 1; + + // send uptime + + uint32_t uptime = boottime_s_get(); + + if (copy_to_user((void __user *)sbuf.uptime_ptr, &uptime, sizeof(uptime) )) + return 1; + + // send index + if (copy_to_user((void __user *)sbuf.index_ptr, &sulog_index_next, sizeof(sulog_index_next) )) + return 1; + + // send buffer data + spin_lock(&sulog_lock); + if (copy_to_user((void __user *)sbuf.buf_ptr, sulog_buf_ptr, SULOG_BUFSIZ )) { + spin_unlock(&sulog_lock); + return 1; + } + spin_unlock(&sulog_lock); + + return 0; +} \ No newline at end of file diff --git a/KernelSU-Next/kernel/tiny_sulog.h b/KernelSU-Next/kernel/tiny_sulog.h new file mode 100644 index 000000000000..54ae0776daa9 --- /dev/null +++ b/KernelSU-Next/kernel/tiny_sulog.h @@ -0,0 +1,11 @@ +#ifndef _TINY_SULOG_H +#define _TINY_SULOG_H + +#include +#include /* void __user * */ + +void sulog_init_heap(void); +void write_sulog(uint8_t sym); +int send_sulog_dump(void __user *uptr); + +#endif /* _TINY_SULOG_H */ diff --git a/KernelSU-Next/kernel/tools/check_symbol.c b/KernelSU-Next/kernel/tools/check_symbol.c new file mode 100644 index 000000000000..303094e6ec45 --- /dev/null +++ b/KernelSU-Next/kernel/tools/check_symbol.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + void *data; + size_t size; + Elf64_Ehdr *ehdr; + Elf64_Shdr *shdr; + char *shstrtab; +} ElfFile; + +int open_elf(const char *path, ElfFile *elf) +{ + int fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error: Cannot open file %s\n", path); + return -1; + } + + struct stat st; + if (fstat(fd, &st) < 0) { + fprintf(stderr, "Error: Cannot stat file %s\n", path); + close(fd); + return -1; + } + + elf->size = st.st_size; + elf->data = mmap(NULL, elf->size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + + if (elf->data == MAP_FAILED) { + fprintf(stderr, "Error: Cannot mmap file %s\n", path); + return -1; + } + + elf->ehdr = (Elf64_Ehdr *)elf->data; + + if (memcmp(elf->ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + fprintf(stderr, "Error: %s is not a valid ELF file\n", path); + munmap(elf->data, elf->size); + return -1; + } + + if (elf->ehdr->e_ident[EI_CLASS] != ELFCLASS64) { + fprintf(stderr, "Error: %s is not a 64-bit ELF file\n", path); + munmap(elf->data, elf->size); + return -1; + } + + elf->shdr = (Elf64_Shdr *)((char *)elf->data + elf->ehdr->e_shoff); + + elf->shstrtab = + (char *)elf->data + elf->shdr[elf->ehdr->e_shstrndx].sh_offset; + + return 0; +} + +void close_elf(ElfFile *elf) +{ + munmap(elf->data, elf->size); +} + +Elf64_Shdr *find_section(ElfFile *elf, const char *name) +{ + for (int i = 0; i < elf->ehdr->e_shnum; i++) { + const char *section_name = elf->shstrtab + elf->shdr[i].sh_name; + if (strcmp(section_name, name) == 0) { + return &elf->shdr[i]; + } + } + return NULL; +} + +Elf64_Shdr *find_symtab(ElfFile *elf) +{ + for (int i = 0; i < elf->ehdr->e_shnum; i++) { + if (elf->shdr[i].sh_type == SHT_SYMTAB) { + return &elf->shdr[i]; + } + } + return NULL; +} + +Elf64_Sym *find_symbol(ElfFile *elf, const char *name, Elf64_Shdr *symtab, + char *strtab) +{ + Elf64_Sym *syms = (Elf64_Sym *)((char *)elf->data + symtab->sh_offset); + int sym_count = symtab->sh_size / sizeof(Elf64_Sym); + + for (int i = 0; i < sym_count; i++) { + const char *sym_name = strtab + syms[i].st_name; + if (strcmp(sym_name, name) == 0) { + return &syms[i]; + } + } + return NULL; +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *ko_path = argv[1]; + const char *vmlinux_path = argv[2]; + + ElfFile ko_elf, vmlinux; + + if (open_elf(ko_path, &ko_elf) < 0) { + return 1; + } + + if (open_elf(vmlinux_path, &vmlinux) < 0) { + close_elf(&ko_elf); + return 1; + } + + Elf64_Shdr *ko_symtab = find_symtab(&ko_elf); + Elf64_Shdr *vmlinux_symtab = find_symtab(&vmlinux); + Elf64_Shdr *ko_version_sec = find_section(&ko_elf, "__versions"); + + if (!ko_symtab) { + fprintf(stderr, "Error: No symbol table found in %s\n", ko_path); + close_elf(&ko_elf); + close_elf(&vmlinux); + return 1; + } + + if (!vmlinux_symtab) { + fprintf(stderr, "Error: No symbol table found in %s\n", vmlinux_path); + close_elf(&ko_elf); + close_elf(&vmlinux); + return 1; + } + + if (!ko_version_sec) { + fprintf(stderr, "Error: No __versions section found in %s\n", ko_path); + close_elf(&ko_elf); + close_elf(&vmlinux); + return 1; + } + + if (ko_version_sec->sh_size != 0) { + fprintf( + stderr, + "Error: __versions section in %s must have size 0 (actual=%llu)\n", + ko_path, (unsigned long long)ko_version_sec->sh_size); + close_elf(&ko_elf); + close_elf(&vmlinux); + return 1; + } + + char *ko_strtab = + (char *)ko_elf.data + ko_elf.shdr[ko_symtab->sh_link].sh_offset; + char *vmlinux_strtab = + (char *)vmlinux.data + vmlinux.shdr[vmlinux_symtab->sh_link].sh_offset; + + Elf64_Sym *ko_syms = + (Elf64_Sym *)((char *)ko_elf.data + ko_symtab->sh_offset); + int ko_sym_count = ko_symtab->sh_size / sizeof(Elf64_Sym); + + int has_error = 0; + + for (int i = 0; i < ko_sym_count; i++) { + if (ko_syms[i].st_shndx == SHN_UNDEF && ko_syms[i].st_name != 0) { + const char *sym_name = ko_strtab + ko_syms[i].st_name; + + Elf64_Sym *vmlinux_sym = + find_symbol(&vmlinux, sym_name, vmlinux_symtab, vmlinux_strtab); + + if (!vmlinux_sym || vmlinux_sym->st_shndx == SHN_UNDEF) { + fprintf(stderr, + "Error: Symbol '%s' not found or undefined in %s\n", + sym_name, vmlinux_path); + has_error = 1; + } else { + int binding = ELF64_ST_BIND(vmlinux_sym->st_info); + if (binding != STB_GLOBAL && binding != STB_WEAK) { + fprintf( + stderr, + "Warning: Symbol '%s' is defined in %s but not global (binding=%d)\n", + sym_name, vmlinux_path, binding); + } + } + } + } + + close_elf(&ko_elf); + close_elf(&vmlinux); + + return has_error ? 1 : 0; +} diff --git a/KernelSU-Next/uapi/app_profile.h b/KernelSU-Next/uapi/app_profile.h new file mode 100644 index 000000000000..660dbcf09858 --- /dev/null +++ b/KernelSU-Next/uapi/app_profile.h @@ -0,0 +1,63 @@ +#ifndef __KSU_UAPI_APP_PROFILE_H +#define __KSU_UAPI_APP_PROFILE_H + +#include + +#define KSU_APP_PROFILE_VER 3 +#define KSU_MAX_PACKAGE_NAME 256 +/* NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. */ +#define KSU_MAX_GROUPS 32 +#define KSU_SELINUX_DOMAIN 64 + +struct root_profile { + __s32 uid; + __s32 gid; + + __u32 groups_count; + __s32 groups[KSU_MAX_GROUPS]; + + /* kernel_cap_t is u32[2] for capabilities v3 */ + struct { + __u64 effective; + __u64 permitted; + __u64 inheritable; + } capabilities; + + char selinux_domain[KSU_SELINUX_DOMAIN]; + + __s32 namespaces; +}; + +struct non_root_profile { + bool umount_modules; +}; + +struct app_profile { + /* + * It may be utilized for backward compatibility, although we have never + * explicitly made any promises regarding this. + */ + __u32 version; + + /* this is usually the package of the app, but can be other value for special apps */ + char key[KSU_MAX_PACKAGE_NAME]; + __s32 current_uid; + bool allow_su; + + union { + struct { + bool use_default; + char template_name[KSU_MAX_PACKAGE_NAME]; + + struct root_profile profile; + } rp_config; + + struct { + bool use_default; + + struct non_root_profile profile; + } nrp_config; + }; +}; + +#endif diff --git a/KernelSU-Next/uapi/feature.h b/KernelSU-Next/uapi/feature.h new file mode 100644 index 000000000000..c1f75c24de6f --- /dev/null +++ b/KernelSU-Next/uapi/feature.h @@ -0,0 +1,14 @@ +#ifndef __KSU_UAPI_FEATURE_H +#define __KSU_UAPI_FEATURE_H + +enum ksu_feature_id { + KSU_FEATURE_SU_COMPAT = 0, + KSU_FEATURE_KERNEL_UMOUNT = 1, + + // custom extensions + KSU_FEATURE_AVC_SPOOF = 10003, + + KSU_FEATURE_MAX +}; + +#endif diff --git a/KernelSU-Next/uapi/selinux.h b/KernelSU-Next/uapi/selinux.h new file mode 100644 index 000000000000..a33915ed712e --- /dev/null +++ b/KernelSU-Next/uapi/selinux.h @@ -0,0 +1,31 @@ +#ifndef __KSU_UAPI_SELINUX_H +#define __KSU_UAPI_SELINUX_H + +#include + +static const __u32 KSU_SEPOLICY_CMD_NORMAL_PERM = 1; +static const __u32 KSU_SEPOLICY_CMD_XPERM = 2; +static const __u32 KSU_SEPOLICY_CMD_TYPE_STATE = 3; +static const __u32 KSU_SEPOLICY_CMD_TYPE = 4; +static const __u32 KSU_SEPOLICY_CMD_TYPE_ATTR = 5; +static const __u32 KSU_SEPOLICY_CMD_ATTR = 6; +static const __u32 KSU_SEPOLICY_CMD_TYPE_TRANSITION = 7; +static const __u32 KSU_SEPOLICY_CMD_TYPE_CHANGE = 8; +static const __u32 KSU_SEPOLICY_CMD_GENFSCON = 9; + +static const __u32 KSU_SEPOLICY_SUBCMD_NORMAL_PERM_ALLOW = 1; +static const __u32 KSU_SEPOLICY_SUBCMD_NORMAL_PERM_DENY = 2; +static const __u32 KSU_SEPOLICY_SUBCMD_NORMAL_PERM_AUDITALLOW = 3; +static const __u32 KSU_SEPOLICY_SUBCMD_NORMAL_PERM_DONTAUDIT = 4; + +static const __u32 KSU_SEPOLICY_SUBCMD_XPERM_ALLOW = 1; +static const __u32 KSU_SEPOLICY_SUBCMD_XPERM_AUDITALLOW = 2; +static const __u32 KSU_SEPOLICY_SUBCMD_XPERM_DONTAUDIT = 3; + +static const __u32 KSU_SEPOLICY_SUBCMD_TYPE_STATE_PERMISSIVE = 1; +static const __u32 KSU_SEPOLICY_SUBCMD_TYPE_STATE_ENFORCE = 2; + +static const __u32 KSU_SEPOLICY_SUBCMD_TYPE_CHANGE_CHANGE = 1; +static const __u32 KSU_SEPOLICY_SUBCMD_TYPE_CHANGE_MEMBER = 2; + +#endif diff --git a/KernelSU-Next/uapi/supercall.h b/KernelSU-Next/uapi/supercall.h new file mode 100644 index 000000000000..4eb62a7b0234 --- /dev/null +++ b/KernelSU-Next/uapi/supercall.h @@ -0,0 +1,180 @@ +#ifndef __KSU_UAPI_SUPERCALL_H +#define __KSU_UAPI_SUPERCALL_H + +#include +#include + +#include "uapi/app_profile.h" + +/* Magic numbers for reboot hook to install fd */ +static const __u32 KSU_INSTALL_MAGIC1 = 0xDEADBEEF; +static const __u32 KSU_INSTALL_MAGIC2 = 0xCAFEBABE; + +// Toolkit extensions +static const __u32 CHANGE_MANAGER_UID = 10006; +static const __u8 KSU_UMOUNT_GETSIZE = 107; +static const __u8 KSU_UMOUNT_GETLIST = 108; +static const __u32 GET_SULOG_DUMP_V2 = 10010; +static const __u32 CHANGE_KSUVER = 10011; +static const __u32 CHANGE_SPOOF_UNAME = 10012; + +struct ksu_become_daemon_cmd { + __u8 token[65]; /* Input: daemon token (null-terminated) */ +}; + +static const __u32 EVENT_POST_FS_DATA = 1; +static const __u32 EVENT_BOOT_COMPLETED = 2; +static const __u32 EVENT_MODULE_MOUNTED = 3; + +static const __u32 KSU_GET_INFO_FLAG_LKM = (1U << 0); +static const __u32 KSU_GET_INFO_FLAG_MANAGER = (1U << 1); +static const __u32 KSU_GET_INFO_FLAG_LATE_LOAD = (1U << 2); +static const __u32 KSU_GET_INFO_FLAG_PR_BUILD = (1U << 3); + +struct ksu_get_info_cmd { + __u32 version; /* Output: KERNEL_SU_VERSION */ + __u32 flags; /* Output: KSU_GET_INFO_FLAG_* bits */ + __u32 features; /* Output: max feature ID supported */ +}; + +struct ksu_report_event_cmd { + __u32 event; /* Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc. */ +}; + +struct ksu_set_sepolicy_cmd { + __u64 data_len; /* Input: bytes of serialized command payload */ + __aligned_u64 data; /* Input: pointer to serialized payload */ +}; + +struct ksu_sepolicy_cmd_hdr { + __u32 cmd; /* Input: command type, CMD_* */ + __u32 subcmd; /* Input: command subtype */ +}; +/* + * After each ksu_sepolicy_cmd_hdr, command arguments are encoded sequentially as: + * [u32 len][len bytes][\0], where len excludes the trailing '\0'. + * len == 0 represents ALL. + * Argument count is derived from cmd: + * KSU_SEPOLICY_CMD_NORMAL_PERM=4, KSU_SEPOLICY_CMD_XPERM=5, + * KSU_SEPOLICY_CMD_TYPE_STATE=1, KSU_SEPOLICY_CMD_TYPE=2, + * KSU_SEPOLICY_CMD_TYPE_ATTR=2, KSU_SEPOLICY_CMD_ATTR=1, + * KSU_SEPOLICY_CMD_TYPE_TRANSITION=5, KSU_SEPOLICY_CMD_TYPE_CHANGE=4, + * KSU_SEPOLICY_CMD_GENFSCON=3. + */ + +struct ksu_check_safemode_cmd { + __u8 in_safe_mode; /* Output: true if in safe mode, false otherwise */ +}; + +/* deprecated */ +struct ksu_get_allow_list_cmd { + __u32 uids[128]; /* Output: array of allowed/denied UIDs */ + __u32 count; /* Output: number of UIDs in array */ + __u8 allow; /* Input: true for allow list, false for deny list */ +}; + +struct ksu_new_get_allow_list_cmd { + __u16 count; /* Input / Output: number of UIDs in array */ + __u16 total_count; /* Output: total number of UIDs in requested list */ + __u32 uids[0]; /* Output: array of allowed/denied UIDs */ +}; + +struct ksu_uid_granted_root_cmd { + __u32 uid; /* Input: target UID to check */ + __u8 granted; /* Output: true if granted, false otherwise */ +}; + +struct ksu_uid_should_umount_cmd { + __u32 uid; /* Input: target UID to check */ + __u8 should_umount; /* Output: true if should umount, false otherwise */ +}; + +struct ksu_get_manager_appid_cmd { + __u32 appid; /* Output: manager app id */ +}; + +struct ksu_get_app_profile_cmd { + struct app_profile profile; /* Input/Output: app profile structure */ +}; + +struct ksu_set_app_profile_cmd { + struct app_profile profile; /* Input: app profile structure */ +}; + +struct ksu_get_feature_cmd { + __u32 feature_id; /* Input: feature ID (enum ksu_feature_id) */ + __u64 value; /* Output: feature value/state */ + __u8 supported; /* Output: true if feature is supported, false otherwise */ +}; + +struct ksu_set_feature_cmd { + __u32 feature_id; /* Input: feature ID (enum ksu_feature_id) */ + __u64 value; /* Input: feature value/state to set */ +}; + +struct ksu_get_wrapper_fd_cmd { + __u32 fd; /* Input: userspace fd */ + __u32 flags; /* Input: flags of userspace fd */ +}; + +struct ksu_manage_mark_cmd { + __u32 operation; /* Input: KSU_MARK_* */ + __s32 pid; /* Input: target pid (0 for all processes) */ + __u32 result; /* Output: for get operation - mark status or reg_count */ +}; + +struct ksu_get_hook_mode_cmd { + char mode[16]; +}; + +struct ksu_get_version_tag_cmd { + char tag[32]; +}; + +static const __u32 KSU_MARK_GET = 1; +static const __u32 KSU_MARK_MARK = 2; +static const __u32 KSU_MARK_UNMARK = 3; +static const __u32 KSU_MARK_REFRESH = 4; + +struct ksu_nuke_ext4_sysfs_cmd { + __aligned_u64 arg; /* Input: mnt pointer */ +}; + +struct ksu_add_try_umount_cmd { + __aligned_u64 arg; /* char ptr, this is the mountpoint */ + __u32 flags; /* this is the flag we use for it */ + __u8 mode; /* denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry */ +}; + +static const __u8 KSU_UMOUNT_WIPE = 0; /* ignore everything and wipe list */ +static const __u8 KSU_UMOUNT_ADD = 1; /* add entry (path + flags) */ +static const __u8 KSU_UMOUNT_DEL = 2; /* delete entry, strcmp */ + +/* IOCTL command definitions */ +static const __u32 KSU_IOCTL_GRANT_ROOT = _IOC(_IOC_NONE, 'K', 1, 0); +static const __u32 KSU_IOCTL_GET_INFO = _IOC(_IOC_READ, 'K', 2, 0); +static const __u32 KSU_IOCTL_REPORT_EVENT = _IOC(_IOC_WRITE, 'K', 3, 0); +static const __u32 KSU_IOCTL_SET_SEPOLICY = _IOC(_IOC_READ | _IOC_WRITE, 'K', 4, 0); +static const __u32 KSU_IOCTL_CHECK_SAFEMODE = _IOC(_IOC_READ, 'K', 5, 0); +/* deprecated */ +static const __u32 KSU_IOCTL_GET_ALLOW_LIST = _IOC(_IOC_READ | _IOC_WRITE, 'K', 6, 0); +/* deprecated */ +static const __u32 KSU_IOCTL_GET_DENY_LIST = _IOC(_IOC_READ | _IOC_WRITE, 'K', 7, 0); +static const __u32 KSU_IOCTL_NEW_GET_ALLOW_LIST = _IOWR('K', 6, struct ksu_new_get_allow_list_cmd); +static const __u32 KSU_IOCTL_NEW_GET_DENY_LIST = _IOWR('K', 7, struct ksu_new_get_allow_list_cmd); +static const __u32 KSU_IOCTL_UID_GRANTED_ROOT = _IOC(_IOC_READ | _IOC_WRITE, 'K', 8, 0); +static const __u32 KSU_IOCTL_UID_SHOULD_UMOUNT = _IOC(_IOC_READ | _IOC_WRITE, 'K', 9, 0); +static const __u32 KSU_IOCTL_GET_MANAGER_APPID = _IOC(_IOC_READ, 'K', 10, 0); +static const __u32 KSU_IOCTL_GET_APP_PROFILE = _IOC(_IOC_READ | _IOC_WRITE, 'K', 11, 0); +static const __u32 KSU_IOCTL_SET_APP_PROFILE = _IOC(_IOC_WRITE, 'K', 12, 0); +static const __u32 KSU_IOCTL_GET_FEATURE = _IOC(_IOC_READ | _IOC_WRITE, 'K', 13, 0); +static const __u32 KSU_IOCTL_SET_FEATURE = _IOC(_IOC_WRITE, 'K', 14, 0); +static const __u32 KSU_IOCTL_GET_WRAPPER_FD = _IOC(_IOC_WRITE, 'K', 15, 0); +static const __u32 KSU_IOCTL_MANAGE_MARK = _IOC(_IOC_READ | _IOC_WRITE, 'K', 16, 0); +static const __u32 KSU_IOCTL_NUKE_EXT4_SYSFS = _IOC(_IOC_WRITE, 'K', 17, 0); +static const __u32 KSU_IOCTL_ADD_TRY_UMOUNT = _IOC(_IOC_WRITE, 'K', 18, 0); +static const __u32 KSU_IOCTL_SET_INIT_PGRP = _IO('K', 19); +static const __u32 KSU_IOCTL_GET_HOOK_MODE = _IOC(_IOC_READ, 'K', 98, 0); +static const __u32 KSU_IOCTL_GET_VERSION_TAG = _IOC(_IOC_READ, 'K', 99, 0); + +#endif diff --git a/arch/arm64/boot/dts/mediatek/custom_common.dtsi b/arch/arm64/boot/dts/mediatek/custom_common.dtsi index 2b8ece9d13ac..1045a82f7ca2 100755 --- a/arch/arm64/boot/dts/mediatek/custom_common.dtsi +++ b/arch/arm64/boot/dts/mediatek/custom_common.dtsi @@ -585,17 +585,17 @@ charger1@1A { compatible = "sgm,sgm41513_charger"; reg = <0x1A>; - sgm,sgm41513,usb-vlim = <4600>; - sgm,sgm41513,usb-ilim = <500>; - sgm,sgm41513,usb-vreg = <0x1130>; - sgm,sgm41513,usb-ichg = <500>; - sgm,sgm41513,precharge-current = <180>; - sgm,sgm41513,termination-current = <240>; - sgm,sgm41513,boost-voltage = <5150>; - sgm,sgm41513,boost-current = <1200>; - sgm,sgm41513,enable_term; - sgm,sgm41513,stat-pin-ctrl = <3>; - sgm,sgm41513,vac-ovp-threshold = <6500>; + sgm41513,usb-vlim = <4600>; + sgm41513,usb-ilim = <2000>; + sgm41513,usb-vreg = <0x1130>; + sgm41513,usb-ichg = <2000>; + sgm41513,precharge-current = <180>; + sgm41513,termination-current = <240>; + sgm41513,boost-voltage = <5150>; + sgm41513,boost-current = <1200>; + sgm41513,enable_term; + sgm41513,stat-pin-ctrl = <3>; + sgm41513,vac-ovp-threshold = <6500>; }; charger@6B { diff --git a/arch/arm64/boot/dts/mediatek/dandelion/cust.dtsi b/arch/arm64/boot/dts/mediatek/dandelion/cust.dtsi index 48b8f113d0ff..0522efa1668e 100644 --- a/arch/arm64/boot/dts/mediatek/dandelion/cust.dtsi +++ b/arch/arm64/boot/dts/mediatek/dandelion/cust.dtsi @@ -189,7 +189,6 @@ }; }; - &i2c5 { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/configs/blossom_defconfig b/arch/arm64/configs/blossom_defconfig index a10caabc20b6..1c64257565b6 100644 --- a/arch/arm64/configs/blossom_defconfig +++ b/arch/arm64/configs/blossom_defconfig @@ -1,4 +1,4 @@ -CONFIG_LOCALVERSION="-niigo-enanan-yoka" +CONFIG_LOCALVERSION="-ShadowKernel" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_PREEMPT=y # CONFIG_CPU_ISOLATION is not set @@ -17,7 +17,6 @@ CONFIG_SCHED_TUNE=y CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y # CONFIG_SYSFS_SYSCALL is not set # CONFIG_FHANDLE is not set -# CONFIG_KALLSYMS_BASE_RELATIVE is not set CONFIG_USERFAULTFD=y # CONFIG_RSEQ is not set CONFIG_MTK_SCHED_EXTENSION=y @@ -293,6 +292,9 @@ CONFIG_CHARGER_TYPE_MT6357=y CONFIG_MTK_CHARGER=y CONFIG_MTK_KERNEL_POWER_OFF_CHARGING=y CONFIG_CHARGER_BQ2560X=y +CONFIG_CHARGER_ETA6963=y +CONFIG_CHARGER_CX7601=y +CONFIG_CHARGER_SGM41513=y # CONFIG_HWMON is not set CONFIG_THERMAL=y CONFIG_THERMAL_DEFAULT_GOV_BACKWARD_COMPATIBLE=y @@ -501,3 +503,29 @@ CONFIG_PANIC_TIMEOUT=1 CONFIG_FTRACE=y # CONFIG_TRACING_EVENTS_GPIO is not set # CONFIG_RUNTIME_TESTING_MENU is not set +# KernelSU +CONFIG_KSU=y +# CONFIG_KPROBES is not set +# CONFIG_KPROBE_EVENTS is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_HAVE_KPROBES is not set +# CONFIG_KALLSYMS_BASE_RELATIVE is not set +CONFIG_KSM=y +# KSU SuSFS +CONFIG_KSU_SUSFS=y +CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT=y +CONFIG_KSU_SUSFS_SUS_PATH=y +CONFIG_KSU_SUSFS_SUS_MOUNT=y +CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT=y +CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT=y +CONFIG_KSU_SUSFS_SUS_KSTAT=y +CONFIG_KSU_SUSFS_SUS_OVERLAYFS=y +CONFIG_KSU_SUSFS_TRY_UMOUNT=y +CONFIG_KSU_SUSFS_SPOOF_UNAME=y +CONFIG_KSU_SUSFS_ENABLE_LOG=y +CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS=y +CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG=y +CONFIG_KSU_SUSFS_OPEN_REDIRECT=y +CONFIG_KSU_SUSFS_SUS_SU=y +CONFIG_KSU_MANUAL_HOOK=y diff --git a/arch/arm64/configs/stock_defconfig b/arch/arm64/configs/stock_defconfig index d52974ef479c..9918a7ea2d66 100644 --- a/arch/arm64/configs/stock_defconfig +++ b/arch/arm64/configs/stock_defconfig @@ -2885,7 +2885,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_CHARGER_BQ24735 is not set CONFIG_CHARGER_BQ2560X=y CONFIG_CHARGER_SGM41513=y -CONFIG_CHARGER_ETA6963=y +# CONFIG_CHARGER_ETA6963 is not set # CONFIG_CHARGER_CX7601 is not set # CONFIG_CHARGER_BQ25890 is not set # CONFIG_CHARGER_SMB347 is not set diff --git a/build.log b/build.log new file mode 100644 index 000000000000..e793494213b9 --- /dev/null +++ b/build.log @@ -0,0 +1,68 @@ +make[1]: вход в каталог «/home/sorvigolova/VSCodeProjects/niigo_kernel_xiaomi_blossom/out» + GEN ./Makefile +for i in ../arch/arm64/boot/dts/mediatek/dandelion.dts; do \ + base_prj=`grep -m 1 '#include [<\"].*\/cust\.dtsi[>\"]' $i | sed 's/#include [<"]//g'\ + | sed 's/\/cust\.dtsi[>"]//g' | sed 's/\/\*//g' | sed 's/\*\///g' | sed 's/ //g'`\ + prj_path=./arch/arm64/boot/dts/$base_prj ;\ + dws_path=../drivers/misc/mediatek/dws/mt6765/$base_prj.dws ;\ + if [ -f $dws_path ] ; then \ + mkdir -p $prj_path ;\ + ../tools/dct/DrvGen.py $dws_path $prj_path $prj_path cust_dtsi;\ + fi \ +done +rm -f dtboimg.cfg.tmp +echo /home/sorvigolova/VSCodeProjects/niigo_kernel_xiaomi_blossom/out/arch/arm64/boot/dts/mediatek/dandelion.dtb >>dtboimg.cfg.tmp; echo " id=0" >>dtboimg.cfg.tmp; +rm -f dtbimg.cfg.tmp +touch dtboimg.cfg.tmp +if ! cmp -s dtboimg.cfg.tmp dtboimg.cfg; then \ + mv dtboimg.cfg.tmp dtboimg.cfg; \ +else \ + rm dtboimg.cfg.tmp; \ +fi +echo /home/sorvigolova/VSCodeProjects/niigo_kernel_xiaomi_blossom/out/arch/arm64/boot/mtk.dtb >>dtbimg.cfg.tmp; echo " id=0" >>dtbimg.cfg.tmp; +touch dtbimg.cfg.tmp +if ! cmp -s dtbimg.cfg.tmp dtbimg.cfg; then \ + mv dtbimg.cfg.tmp dtbimg.cfg; \ +else \ + rm dtbimg.cfg.tmp; \ +fi +for i in ./arch/arm64/boot/dts/mediatek/dandelion.dtb; do \ + ../scripts/dtc/ufdt_apply_overlay ./arch/arm64/boot/dts/mediatek/mt6765.dtb $i $i.merge;\ +done + CALL ../scripts/checksyscalls.sh +ufdt_apply_overlay: took 0.000000000 secs + CHK include/generated/compile.h + CC fs/namespace.o +../fs/namespace.c:1128:2: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] + 1128 | if (type->alloc_mnt_data) { + | ^ +../fs/namespace.c:1125:5: note: previous statement is here + 1125 | if (!mnt) + | ^ +1 warning generated. +-- KernelSU-Next Git repo detected at: /home/sorvigolova/VSCodeProjects/niigo_kernel_xiaomi_blossom/KernelSU-Next +-- KernelSU-Next version: 33133 +-- KernelSU-Next tag: v3.2.0-legacy +-- KernelSU-Next Manager signature size: 0x3e6 +-- KernelSU-Next Manager signature hash: 79e590113c4c4c0c222978e413a5faa801666957b1212a328e46c00c69821bf7 +-- KernelSU-Next: Hook mode: Manual + +-- SUSFS_VERSION: v2.1.0 + AR fs/built-in.a + GEN .version + CHK include/generated/compile.h + UPD include/generated/compile.h + CC init/version.o + AR init/built-in.a + AR built-in.a + LTO vmlinux.o +ld.lld: warning: :0:0: stack frame size (4224) exceeds limit (2800) in function '__arm64_sys_getcwd' + MODPOST vmlinux.o + KSYM .tmp_kallsyms1.o + KSYM .tmp_kallsyms2.o + LD vmlinux + SORTEX vmlinux + SYSMAP System.map + OBJCOPY arch/arm64/boot/Image + GZIP arch/arm64/boot/Image.gz +make[1]: выход из каталога «/home/sorvigolova/VSCodeProjects/niigo_kernel_xiaomi_blossom/out» diff --git a/drivers/Kconfig b/drivers/Kconfig index c8b6e5bd0068..ea588b0cca7b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -224,4 +224,5 @@ source "drivers/slimbus/Kconfig" source "drivers/interconnect/Kconfig" source "drivers/energy_model/Kconfig" +source "drivers/kernelsu/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 8421bcbfd69e..a0039f4201b1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -191,3 +191,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ obj-$(CONFIG_INTERCONNECT) += interconnect/ +obj-$(CONFIG_KSU) += kernelsu/ diff --git a/drivers/kernelsu b/drivers/kernelsu new file mode 120000 index 000000000000..b32a3654a683 --- /dev/null +++ b/drivers/kernelsu @@ -0,0 +1 @@ +../KernelSU-Next/kernel \ No newline at end of file diff --git a/drivers/misc/mediatek/include/mt-plat/v1/charger_class.h b/drivers/misc/mediatek/include/mt-plat/v1/charger_class.h new file mode 100644 index 000000000000..68522675fc70 --- /dev/null +++ b/drivers/misc/mediatek/include/mt-plat/v1/charger_class.h @@ -0,0 +1,406 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef LINUX_POWER_CHARGER_CLASS_H +#define LINUX_POWER_CHARGER_CLASS_H + +#include +#include +#include +/* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 start */ +#include +/* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 end */ + +enum adc_channel { + ADC_CHANNEL_VBUS, + ADC_CHANNEL_VSYS, + ADC_CHANNEL_VBAT, + ADC_CHANNEL_IBUS, + ADC_CHANNEL_IBAT, + ADC_CHANNEL_TEMP_JC, + ADC_CHANNEL_USBID, + ADC_CHANNEL_TS, + ADC_CHANNEL_TBAT, + ADC_CHANNEL_VOUT, + ADC_CHANNEL_MAX, +}; + +struct charger_properties { + const char *alias_name; +}; + +/* Data of notifier from charger device */ +struct chgdev_notify { + bool vbusov_stat; +}; + +struct charger_device { + struct charger_properties props; + struct chgdev_notify noti; + const struct charger_ops *ops; + struct mutex ops_lock; + struct device dev; + struct srcu_notifier_head evt_nh; + void *driver_data; + bool is_polling_mode; +}; + +struct charger_ops { + int (*suspend)(struct charger_device *dev, pm_message_t state); + int (*resume)(struct charger_device *dev); + + /* cable plug in/out */ + int (*plug_in)(struct charger_device *dev); + int (*plug_out)(struct charger_device *dev); + + /* enable/disable charger */ + int (*enable)(struct charger_device *dev, bool en); + + int (*is_enabled)(struct charger_device *dev, bool *en); + + /* enable/disable chip */ + int (*enable_chip)(struct charger_device *dev, bool en); + int (*is_chip_enabled)(struct charger_device *dev, bool *en); + + /* get/set charging current*/ + int (*get_charging_current)(struct charger_device *dev, u32 *uA); + int (*set_charging_current)(struct charger_device *dev, u32 uA); + int (*get_min_charging_current)(struct charger_device *dev, u32 *uA); + + /* set cv */ + int (*set_constant_voltage)(struct charger_device *dev, u32 uV); + int (*get_constant_voltage)(struct charger_device *dev, u32 *uV); + + /* set input_current */ + int (*get_input_current)(struct charger_device *dev, u32 *uA); + int (*set_input_current)(struct charger_device *dev, u32 uA); + int (*get_min_input_current)(struct charger_device *dev, u32 *uA); + + /* set termination current */ + int (*get_eoc_current)(struct charger_device *dev, u32 *uA); + int (*set_eoc_current)(struct charger_device *dev, u32 uA); + + /* kick wdt */ + int (*kick_wdt)(struct charger_device *dev); + + int (*event)(struct charger_device *dev, u32 event, u32 args); + + /* PE+/PE+2.0 */ + int (*send_ta_current_pattern)(struct charger_device *dev, bool is_inc); + int (*send_ta20_current_pattern)(struct charger_device *dev, u32 uV); + int (*reset_ta)(struct charger_device *dev); + int (*enable_cable_drop_comp)(struct charger_device *dev, bool en); + + int (*set_mivr)(struct charger_device *dev, u32 uV); + int (*get_mivr)(struct charger_device *dev, u32 *uV); + int (*get_mivr_state)(struct charger_device *dev, bool *in_loop); + + /* enable/disable powerpath */ + int (*is_powerpath_enabled)(struct charger_device *dev, bool *en); + int (*enable_powerpath)(struct charger_device *dev, bool en); + + /* enable/disable vbus ovp */ + int (*enable_vbus_ovp)(struct charger_device *dev, bool en); + + /* enable/disable charging safety timer */ + int (*is_safety_timer_enabled)(struct charger_device *dev, bool *en); + int (*enable_safety_timer)(struct charger_device *dev, bool en); + + /* enable term */ + int (*enable_termination)(struct charger_device *dev, bool en); + + /* direct charging */ + int (*enable_direct_charging)(struct charger_device *dev, bool en); + int (*kick_direct_charging_wdt)(struct charger_device *dev); + int (*set_direct_charging_ibusoc)(struct charger_device *dev, u32 uA); + int (*set_direct_charging_vbusov)(struct charger_device *dev, u32 uV); + + int (*set_ibusocp)(struct charger_device *dev, u32 uA); + int (*set_vbusovp)(struct charger_device *dev, u32 uV); + int (*set_ibatocp)(struct charger_device *dev, u32 uA); + int (*set_vbatovp)(struct charger_device *dev, u32 uV); + int (*set_vbatovp_alarm)(struct charger_device *dev, u32 uV); + int (*reset_vbatovp_alarm)(struct charger_device *dev); + int (*set_vbusovp_alarm)(struct charger_device *dev, u32 uV); + int (*reset_vbusovp_alarm)(struct charger_device *dev); + int (*is_vbuslowerr)(struct charger_device *dev, bool *err); + int (*init_chip)(struct charger_device *dev); + int (*enable_auto_trans)(struct charger_device *dev, bool en); + int (*set_auto_trans)(struct charger_device *dev, u32 uV, bool en); + + /* OTG */ + int (*enable_otg)(struct charger_device *dev, bool en); + int (*enable_discharge)(struct charger_device *dev, bool en); + int (*set_boost_current_limit)(struct charger_device *dev, u32 uA); + + /* charger type detection */ + int (*enable_chg_type_det)(struct charger_device *dev, bool en); + + /* run AICL */ + int (*run_aicl)(struct charger_device *dev, u32 *uA); + + /* reset EOC state */ + int (*reset_eoc_state)(struct charger_device *dev); + + int (*safety_check)(struct charger_device *dev, u32 polling_ieoc); + + int (*is_charging_done)(struct charger_device *dev, bool *done); + int (*set_pe20_efficiency_table)(struct charger_device *dev); + int (*dump_registers)(struct charger_device *dev); + + int (*get_adc)(struct charger_device *dev, enum adc_channel chan, + int *min, int *max); + int (*get_adc_accuracy)(struct charger_device *dev, + enum adc_channel chan, int *min, int *max); + int (*get_vbus_adc)(struct charger_device *dev, u32 *vbus); + int (*get_ibus_adc)(struct charger_device *dev, u32 *ibus); + int (*get_ibat_adc)(struct charger_device *dev, u32 *ibat); + int (*get_tchg_adc)(struct charger_device *dev, int *tchg_min, + int *tchg_max); + int (*get_zcv)(struct charger_device *dev, u32 *uV); + + /* TypeC */ + int (*enable_usbid)(struct charger_device *dev, bool en); + int (*set_usbid_rup)(struct charger_device *dev, u32 rup); + int (*set_usbid_src_ton)(struct charger_device *dev, u32 src_ton); + int (*enable_usbid_floating)(struct charger_device *dev, bool en); + int (*enable_force_typec_otp)(struct charger_device *dev, bool en); + int (*enable_hidden_mode)(struct charger_device *dev, bool en); + int (*get_ctd_dischg_status)(struct charger_device *dev, u8 *status); + int (*enable_hz)(struct charger_device *dev, bool en); + + int (*enable_bleed_discharge)(struct charger_device *dev, bool en); + + /* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 start */ + int (*set_hiz_mode)(struct charger_device *dev, bool en); + int (*get_hiz_mode)(struct charger_device *dev); + /* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 end */ + + /* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 start*/ + int (*get_ship_mode)(struct charger_device *chg_dev); + int (*set_ship_mode)(struct charger_device *chg_dev, bool enable); + /* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 end*/ + + /* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 start*/ + int (*get_chr_status)(struct charger_device *chg_dev, int *status); + /* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 end*/ + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + int (*get_vbus_status)(struct charger_device *dev); + int (*dynamic_set_hwovp_threshold)(struct charger_device *dev, int adapter_type); + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ + int (*bypass_chgdet)(struct charger_device *chg_dev, bool bypass_chgdet_en); + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ +}; + +static inline void *charger_dev_get_drvdata( + const struct charger_device *charger_dev) +{ + return charger_dev->driver_data; +} + +static inline void charger_dev_set_drvdata( + struct charger_device *charger_dev, void *data) +{ + charger_dev->driver_data = data; +} + +extern struct charger_device *charger_device_register( + const char *name, + struct device *parent, void *devdata, const struct charger_ops *ops, + const struct charger_properties *props); +extern void charger_device_unregister( + struct charger_device *charger_dev); +extern struct charger_device *get_charger_by_name( + const char *name); + +#define to_charger_device(obj) container_of(obj, struct charger_device, dev) + +static inline void *charger_get_data( + struct charger_device *charger_dev) +{ + return dev_get_drvdata(&charger_dev->dev); +} + +extern int charger_dev_enable(struct charger_device *charger_dev, bool en); +extern int charger_dev_is_enabled(struct charger_device *charger_dev, bool *en); +extern int charger_dev_plug_in(struct charger_device *charger_dev); +extern int charger_dev_plug_out(struct charger_device *charger_dev); +extern int charger_dev_set_charging_current( + struct charger_device *charger_dev, u32 uA); +extern int charger_dev_get_charging_current( + struct charger_device *charger_dev, u32 *uA); +extern int charger_dev_get_min_charging_current( + struct charger_device *charger_dev, u32 *uA); +extern int charger_dev_set_input_current( + struct charger_device *charger_dev, u32 uA); +extern int charger_dev_get_input_current( + struct charger_device *charger_dev, u32 *uA); +extern int charger_dev_get_min_input_current( + struct charger_device *charger_dev, u32 *uA); +extern int charger_dev_set_eoc_current( + struct charger_device *charger_dev, u32 uA); +extern int charger_dev_get_eoc_current( + struct charger_device *charger_dev, u32 *uA); +extern int charger_dev_kick_wdt( + struct charger_device *charger_dev); +extern int charger_dev_set_constant_voltage( + struct charger_device *charger_dev, u32 uV); +extern int charger_dev_get_constant_voltage( + struct charger_device *charger_dev, u32 *uV); +extern int charger_dev_dump_registers( + struct charger_device *charger_dev); +extern int charger_dev_enable_vbus_ovp( + struct charger_device *charger_dev, bool en); +extern int charger_dev_set_mivr( + struct charger_device *charger_dev, u32 uV); +extern int charger_dev_get_mivr( + struct charger_device *charger_dev, u32 *uV); +extern int charger_dev_get_mivr_state( + struct charger_device *charger_dev, bool *in_loop); +extern int charger_dev_do_event( + struct charger_device *charger_dev, u32 event, u32 args); +extern int charger_dev_is_powerpath_enabled( + struct charger_device *charger_dev, bool *en); +extern int charger_dev_is_safety_timer_enabled( + struct charger_device *charger_dev, bool *en); +extern int charger_dev_enable_termination( + struct charger_device *charger_dev, bool en); +extern int charger_dev_is_charging_done( + struct charger_device *charger_dev, bool *done); +extern int charger_dev_enable_powerpath( + struct charger_device *charger_dev, bool en); +extern int charger_dev_enable_safety_timer( + struct charger_device *charger_dev, bool en); +extern int charger_dev_enable_chg_type_det( + struct charger_device *charger_dev, bool en); +extern int charger_dev_enable_otg( + struct charger_device *charger_dev, bool en); +extern int charger_dev_enable_discharge( + struct charger_device *charger_dev, bool en); +extern int charger_dev_set_boost_current_limit( + struct charger_device *charger_dev, u32 uA); +extern int charger_dev_get_zcv( + struct charger_device *charger_dev, u32 *uV); +extern int charger_dev_run_aicl( + struct charger_device *charger_dev, u32 *uA); +extern int charger_dev_reset_eoc_state( + struct charger_device *charger_dev); +extern int charger_dev_safety_check( + struct charger_device *charger_dev, u32 polling_ieoc); +extern int charger_dev_enable_hz( + struct charger_device *charger_dev, bool en); + +/* PE+/PE+2.0 */ +extern int charger_dev_send_ta_current_pattern( + struct charger_device *charger_dev, bool is_increase); +extern int charger_dev_send_ta20_current_pattern( + struct charger_device *charger_dev, u32 uV); +extern int charger_dev_reset_ta( + struct charger_device *charger_dev); +extern int charger_dev_set_pe20_efficiency_table( + struct charger_device *charger_dev); +extern int charger_dev_enable_cable_drop_comp( + struct charger_device *charger_dev, bool en); + +/* PE 3.0 */ +extern int charger_dev_enable_chip( + struct charger_device *charger_dev, bool en); +extern int charger_dev_is_chip_enabled( + struct charger_device *charger_dev, bool *en); +extern int charger_dev_enable_direct_charging( + struct charger_device *charger_dev, bool en); +extern int charger_dev_kick_direct_charging_wdt( + struct charger_device *charger_dev); +extern int charger_dev_get_adc(struct charger_device *charger_dev, + enum adc_channel chan, int *min, int *max); +extern int charger_dev_get_adc_accuracy(struct charger_device *charger_dev, + enum adc_channel chan, int *min, int *max); +/* Prefer use charger_dev_get_adc api */ +extern int charger_dev_get_vbus( + struct charger_device *charger_dev, u32 *vbus); +extern int charger_dev_get_ibus( + struct charger_device *charger_dev, u32 *ibus); +extern int charger_dev_get_ibat( + struct charger_device *charger_dev, u32 *ibat); +extern int charger_dev_get_temperature( + struct charger_device *charger_dev, int *tchg_min, + int *tchg_max); +extern int charger_dev_set_direct_charging_ibusoc( + struct charger_device *charger_dev, u32 ua); +extern int charger_dev_set_direct_charging_vbusov( + struct charger_device *charger_dev, u32 uv); + +extern int charger_dev_set_ibusocp(struct charger_device *chg_dev, u32 uA); +extern int charger_dev_set_vbusovp(struct charger_device *chg_dev, u32 uV); +extern int charger_dev_set_ibatocp(struct charger_device *chg_dev, u32 uA); +extern int charger_dev_set_vbatovp(struct charger_device *chg_dev, u32 uV); +extern int charger_dev_set_vbatovp_alarm(struct charger_device *chg_dev, + u32 uV); +extern int charger_dev_reset_vbatovp_alarm(struct charger_device *chg_dev); +extern int charger_dev_set_vbusovp_alarm(struct charger_device *chg_dev, + u32 uV); +extern int charger_dev_reset_vbusovp_alarm(struct charger_device *chg_dev); +extern int charger_dev_is_vbuslowerr(struct charger_device *chg_dev, bool *err); +extern int charger_dev_init_chip(struct charger_device *chg_dev); +extern int charger_dev_enable_auto_trans(struct charger_device *chg_dev, + bool en); +extern int charger_dev_set_auto_trans(struct charger_device *chg_dev, u32 uV, + bool en); + +/* TypeC */ +extern int charger_dev_enable_usbid(struct charger_device *dev, bool en); +extern int charger_dev_set_usbid_rup(struct charger_device *dev, u32 rup); +extern int charger_dev_set_usbid_src_ton(struct charger_device *dev, + u32 src_ton); +extern int charger_dev_enable_usbid_floating(struct charger_device *dev, + bool en); +extern int charger_dev_enable_force_typec_otp(struct charger_device *dev, + bool en); +extern int charger_dev_get_ctd_dischg_status(struct charger_device *dev, + u8 *status); + +extern int charger_dev_enable_bleed_discharge(struct charger_device *dev, + bool en); + +/* For buck1 FPWM */ +extern int charger_dev_enable_hidden_mode(struct charger_device *dev, bool en); + +extern int register_charger_device_notifier( + struct charger_device *charger_dev, + struct notifier_block *nb); +extern int unregister_charger_device_notifier( + struct charger_device *charger_dev, + struct notifier_block *nb); +extern int charger_dev_notify( + struct charger_device *charger_dev, int event); + +/* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 start */ +extern int charger_dev_set_hiz_mode(struct charger_device *chg_dev, bool en); +extern int charger_dev_get_hiz_mode(struct charger_device *chg_dev); +/* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 end */ + +/* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 start */ +extern enum chg_ic_supplier chg_info; +/* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 end */ + +/* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 start*/ +extern int charger_dev_set_shipmode(struct charger_device *chg_dev, bool enable); +extern int charger_dev_get_shipmode(struct charger_device *chg_dev); +/* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 end*/ + +/* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 start*/ +extern int charger_dev_get_chr_status(struct charger_device *chg_dev, int *status); +/* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 end*/ + +/* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ +extern int charger_dev_get_vbus_status(struct charger_device *charger_dev); +extern int charger_dev_dynamic_set_hwovp_threshold(struct charger_device *charger_dev, + int adapter_type); +/* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ +/* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ +extern int charger_dev_bypass_chgdet(struct charger_device *chg_dev,bool bypass_chgdet_en); +/* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ +#endif /*LINUX_POWER_CHARGER_CLASS_H*/ \ No newline at end of file diff --git a/drivers/misc/mediatek/include/mt-plat/v1/charger_type.h b/drivers/misc/mediatek/include/mt-plat/v1/charger_type.h new file mode 100644 index 000000000000..ac4cc1eb5370 --- /dev/null +++ b/drivers/misc/mediatek/include/mt-plat/v1/charger_type.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_CHARGER_TYPE_H__ +#define __MTK_CHARGER_TYPE_H__ + +enum charger_type { + CHARGER_UNKNOWN = 0, + STANDARD_HOST, /* USB : 450mA */ + CHARGING_HOST, + NONSTANDARD_CHARGER, /* AC : 450mA~1A */ + STANDARD_CHARGER, /* AC : ~1A */ + APPLE_2_4A_CHARGER, /* 2.4A apple charger */ + APPLE_2_1A_CHARGER, /* 2.1A apple charger */ + APPLE_1_0A_CHARGER, /* 1A apple charger */ + APPLE_0_5A_CHARGER, /* 0.5A apple charger */ + SAMSUNG_CHARGER, + WIRELESS_CHARGER, +}; +#if defined(CONFIG_USB_MTK_HDRC) || defined(CONFIG_USB_MU3D_DRV) \ + || defined(CONFIG_EXTCON_MTK_USB) +extern void mt_usb_connect_v1(void); +extern void mt_usb_disconnect_v1(void); +#else +#define mt_usb_connect() do { } while (0) +#define mt_usb_disconnect() do { } while (0) +#endif + +extern enum charger_type mt_get_charger_type(void); +extern void mtk_charger_int_handler(void); + +#if defined(CONFIG_MTK_PUMP_EXPRESS_PLUS_30_SUPPORT) +extern int register_charger_det_callback(int (*func)(int)); +#endif /*CONFIG_MTK_PUMP_EXPRESS_PLUS_30_SUPPORT*/ + +#if defined(CONFIG_MACH_MT6877) || defined(CONFIG_MACH_MT6893) \ + || defined(CONFIG_MACH_MT6885) || defined(CONFIG_MACH_MT6785) +extern bool is_usb_rdy(struct device *dev); +#else +extern bool is_usb_rdy(void); +#endif +extern bool mt_usb_is_device(void); +extern int is_otg_en(void); + +#ifndef CONFIG_MTK_EXTERNAL_CHARGER_TYPE_DETECT +extern void mtk_pmic_enable_chr_type_det(bool en); +#endif + +#endif /* __MTK_CHARGER_TYPE_H__ */ \ No newline at end of file diff --git a/drivers/misc/mediatek/include/mt-plat/v1/mtk_battery.h b/drivers/misc/mediatek/include/mt-plat/v1/mtk_battery.h new file mode 100644 index 000000000000..8302e144c2c2 --- /dev/null +++ b/drivers/misc/mediatek/include/mt-plat/v1/mtk_battery.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef _MTK_BATTERY_H +#define _MTK_BATTERY_H + +#ifndef _DEA_MODIFY_ +#include +#include +#else +#include "simulator_kernel.h" +#endif + + +/* ============================================================ */ +/* typedef */ +/* ============================================================ */ + +/* coulomb service */ +struct gauge_consumer { + char *name; + struct device *dev; + long start; + long end; + int variable; + + int (*callback)(struct gauge_consumer *gc); + struct list_head list; +}; + +extern void gauge_coulomb_service_init(void); +extern void gauge_coulomb_consumer_init(struct gauge_consumer *coulomb, + struct device *dev, char *name); +extern void gauge_coulomb_start(struct gauge_consumer *coulomb, int car); +extern void gauge_coulomb_stop(struct gauge_consumer *coulomb); +extern void gauge_coulomb_dump_list(void); +extern void gauge_coulomb_before_reset(void); +extern void gauge_coulomb_after_reset(void); +extern void gauge_coulomb_set_log_level(int x); +/* coulomb sub system end */ + + +/* battery notify charger_consumer */ +enum { + EVENT_BATTERY_PLUG_OUT, +}; + +extern int register_battery_notifier(struct notifier_block *nb); +extern int unregister_battery_notifier(struct notifier_block *nb); +/* battery notify charger_consumer end*/ + + +/* battery common interface */ +extern signed int battery_get_bat_voltage(void); +extern signed int battery_get_bat_current(void); +extern signed int battery_get_bat_current_mA(void); +extern signed int battery_get_soc(void); +extern signed int battery_get_precise_soc(void); +extern signed int battery_get_uisoc(void); +extern signed int battery_get_precise_uisoc(void); +extern signed int battery_get_bat_temperature(void); +extern signed int battery_get_ibus(void); +extern signed int battery_get_vbus(void); +extern signed int battery_get_bat_avg_current(void); + + +#endif /* End of _FUEL_GAUGE_GM_30_H */ \ No newline at end of file diff --git a/drivers/misc/mediatek/include/mt-plat/v1/mtk_charger.h b/drivers/misc/mediatek/include/mt-plat/v1/mtk_charger.h new file mode 100644 index 000000000000..33a6acc52c2c --- /dev/null +++ b/drivers/misc/mediatek/include/mt-plat/v1/mtk_charger.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_CHARGER_H__ +#define __MTK_CHARGER_H__ + +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include +/* hs14 code for AL6528ADEU-342 by wenyaqi at 2022/10/11 start */ +#if defined(CONFIG_HQ_PROJECT_O22) +#include + +extern enum tcpc_cc_supplier tcpc_info; +#endif +/* hs14 code for AL6528ADEU-342 by wenyaqi at 2022/10/11 end */ + +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#ifdef CONFIG_AFC_CHARGER +extern struct charger_manager *ssinfo; +#endif +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ + +/* charger_manager notify charger_consumer */ +enum { + CHARGER_NOTIFY_EOC, + CHARGER_NOTIFY_START_CHARGING, + CHARGER_NOTIFY_STOP_CHARGING, + CHARGER_NOTIFY_ERROR, + CHARGER_NOTIFY_NORMAL, +}; + +enum { + MAIN_CHARGER = 0, + SLAVE_CHARGER = 1, + TOTAL_CHARGER = 2, + DIRECT_CHARGER = 10, + MAIN_DIVIDER_CHARGER = 20, + SLAVE_DIVIDER_CHARGER = 21, +}; + +struct charger_consumer { + struct device *dev; + void *cm; + struct notifier_block *pnb; + struct list_head list; + bool hv_charging_disabled; +}; + +/* ============================================= */ +/* The following are charger consumer interfaces */ +/* ============================================= */ + +/* @supply_name: name of charging port + * use charger_port1, charger_port2, ... + * for most cases, use charging_port1 + */ +extern struct charger_consumer *charger_manager_get_by_name( + struct device *dev, + const char *supply_name); +extern int charger_manager_set_input_current_limit( + struct charger_consumer *consumer, + int idx, + int input_current_uA); +extern int charger_manager_set_charging_current_limit( + struct charger_consumer *consumer, + int idx, + int charging_current_uA); +extern int charger_manager_set_pe30_input_current_limit( + struct charger_consumer *consumer, + int idx, + int input_current_uA); +extern int charger_manager_get_pe30_input_current_limit( + struct charger_consumer *consumer, + int idx, + int *input_current_uA, + int *min_current_uA, + int *max_current_uA); +extern int charger_manager_get_current_charging_type( + struct charger_consumer *consumer); +extern int register_charger_manager_notifier( + struct charger_consumer *consumer, + struct notifier_block *nb); +extern int charger_manager_get_charger_temperature( + struct charger_consumer *consumer, + int idx, + int *tchg_min, + int *tchg_max); +extern int unregister_charger_manager_notifier( + struct charger_consumer *consumer, + struct notifier_block *nb); +extern int charger_manager_enable_high_voltage_charging( + struct charger_consumer *consumer, + bool en); +extern int charger_manager_enable_power_path( + struct charger_consumer *consumer, + int idx, + bool en); +extern int charger_manager_force_disable_power_path( + struct charger_consumer *consumer, + int idx, + bool disable); +extern int charger_manager_enable_charging( + struct charger_consumer *consumer, + int idx, + bool en); +extern int charger_manager_get_zcv( + struct charger_consumer *consumer, + int idx, + u32 *uV); +extern int charger_manager_set_constant_voltage( + struct charger_consumer *consumer, + int idx, + u32 uV); +extern int charger_manager_enable_chg_type_det( + struct charger_consumer *consumer, + bool en); +extern int mtk_chr_is_charger_exist(unsigned char *exist); +extern bool is_power_path_supported(void); +extern int charger_get_vbus(void); +extern bool mt_charger_plugin(void); + +/* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 start */ +extern int charger_manager_input_suspend(struct charger_consumer *consumer, + int idx, + bool en); +/* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 end */ + +/* hs14 code for SR-AL6528A-01-259 by qiaodan at 2022/10/17 start*/ +extern int charger_manager_get_shipmode(struct charger_consumer *consumer, + int idx); +extern int charger_manager_set_shipmode(struct charger_consumer *consumer, + int idx); +/* hs14 code for SR-AL6528A-01-259 by qiaodan at 2022/10/17 end*/ + +/* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 start*/ +extern int charger_manager_get_chr_type(struct charger_consumer *consumer, + int idx, int *chr_type); +/* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 end*/ +/* hs14 code for SR-AL6528A-445 by shanxinkai at 2022/10/28 start */ +extern int charger_manager_dump_charger_ic(struct charger_consumer *consumer, + int idx); +/* hs14 code for SR-AL6528A-445 by shanxinkai at 2022/10/28 end */ +/* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ +extern int charger_manager_bypass_chgdet(struct charger_consumer *consumer, + int idx, bool bypass_chgdet); +/* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#ifdef CONFIG_AFC_CHARGER +extern bool ss_fast_charger_status(struct charger_manager *info); +#endif +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ +#endif /* __MTK_CHARGER_H__ */ +/*hs14 code for SR-AL6528A-01-380 by lichang at 2022/09/26 start*/ +#ifdef CONFIG_HQ_PROJECT_O22 +extern int register_usb_check_notifier(struct notifier_block *nb); +extern int unregister_usb_check_notifier(struct notifier_block *nb); +#endif +/*hs14 code for SR-AL6528A-01-380 by lichang at 2022/09/26 end*/ \ No newline at end of file diff --git a/drivers/misc/mediatek/include/mt-plat/v1/prop_chgalgo_class.h b/drivers/misc/mediatek/include/mt-plat/v1/prop_chgalgo_class.h new file mode 100644 index 000000000000..677ce71fb36e --- /dev/null +++ b/drivers/misc/mediatek/include/mt-plat/v1/prop_chgalgo_class.h @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __LINUX_PROP_CHGALGO_CLASS_H +#define __LINUX_PROP_CHGALGO_CLASS_H + +#include +#include + +#define PCA_DBG_EN 1 +#define PCA_INFO_EN 1 +#define PCA_ERR_EN 1 + +#define PCA_DBG(fmt, ...) \ + do { \ + if (PCA_DBG_EN) \ + pr_info("[PCA]%s " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define PCA_INFO(fmt, ...) \ + do { \ + if (PCA_INFO_EN) \ + pr_info("[PCA]%s " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define PCA_ERR(fmt, ...) \ + do { \ + if (PCA_ERR_EN) \ + pr_info("[PCA]%s " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define SIMPLE_PCA_TA_DESC(_name, ops) \ +const struct prop_chgalgo_desc _name##_desc = { \ + .name = #_name, \ + .type = PCA_DEVTYPE_TA, \ + .ta_ops = &ops, \ + .chg_ops = NULL, \ + .algo_ops = NULL, \ +} + +#define SIMPLE_PCA_CHG_DESC(_name, ops) \ +const struct prop_chgalgo_desc _name##_desc = { \ + .name = #_name, \ + .type = PCA_DEVTYPE_CHARGER, \ + .ta_ops = NULL, \ + .chg_ops = &ops, \ + .algo_ops = NULL, \ +} + +#define SIMPLE_PCA_ALGO_DESC(_name, ops) \ +const struct prop_chgalgo_desc _name##_desc = { \ + .name = #_name, \ + .type = PCA_DEVTYPE_ALGO, \ + .ta_ops = NULL, \ + .chg_ops = NULL, \ + .algo_ops = &ops, \ +} + +struct prop_chgalgo_ta_status { + int temp1; + int temp2; + u8 temp_level; + u8 present_input; + u8 present_battery_input; + bool ocp; + bool otp; + bool ovp; +}; + +struct prop_chgalgo_ta_auth_data { + int vcap_min; + int vcap_max; + int icap_min; + int vta_min; + int vta_max; + int ita_max; + int ita_min; + bool pwr_lmt; + u8 pdp; + bool support_meas_cap; + bool support_status; + bool support_cc; + u32 vta_step; + u32 ita_step; + u32 ita_gap_per_vstep; +}; + +enum prop_chgalgo_notify_source { + PCA_NOTISRC_TCP, + PCA_NOTISRC_CHG, + PCA_NOTISRC_ALGO, + PCA_NOTISRC_MAX, +}; + +enum prop_chgalgo_notify_evt { + PCA_NOTIEVT_DETACH, + PCA_NOTIEVT_HARDRESET, + PCA_NOTIEVT_VBUSOVP, + PCA_NOTIEVT_IBUSOCP, + PCA_NOTIEVT_IBUSUCP_FALL, + PCA_NOTIEVT_VBATOVP, + PCA_NOTIEVT_IBATOCP, + PCA_NOTIEVT_VOUTOVP, + PCA_NOTIEVT_VDROVP, + PCA_NOTIEVT_VBATOVP_ALARM, + PCA_NOTIEVT_VBUSOVP_ALARM, + PCA_NOTIEVT_ALGO_STOP, + PCA_NOTIEVT_MAX, +}; + +struct prop_chgalgo_notify { + enum prop_chgalgo_notify_source src; + enum prop_chgalgo_notify_evt evt; +}; + +enum prop_chgalgo_adc_channel { + PCA_ADCCHAN_VBUS = 0, + PCA_ADCCHAN_IBUS, + PCA_ADCCHAN_VBAT, + PCA_ADCCHAN_IBAT, + PCA_ADCCHAN_TBAT, + PCA_ADCCHAN_TCHG, + PCA_ADCCHAN_VOUT, + PCA_ADCCHAN_VSYS, + PCA_ADCCHAN_MAX, +}; + +struct prop_chgalgo_device; + +struct prop_chgalgo_ta_ops { + int (*enable_charging)(struct prop_chgalgo_device *pca, bool en, u32 mV, + u32 mA); + int (*set_cap)(struct prop_chgalgo_device *pca, u32 mV, u32 mA); + int (*get_measure_cap)(struct prop_chgalgo_device *pca, u32 *mV, + u32 *mA); + int (*get_temperature)(struct prop_chgalgo_device *pca, int *degree); + int (*get_status)(struct prop_chgalgo_device *pca, + struct prop_chgalgo_ta_status *status); + int (*is_cc)(struct prop_chgalgo_device *pca, bool *cc); + int (*send_hardreset)(struct prop_chgalgo_device *pca); + int (*authenticate_ta)(struct prop_chgalgo_device *pca, + struct prop_chgalgo_ta_auth_data *data); + int (*enable_wdt)(struct prop_chgalgo_device *pca, bool en); + int (*set_wdt)(struct prop_chgalgo_device *pca, u32 ms); + int (*sync_vta)(struct prop_chgalgo_device *pca, u32 vta); +}; + +struct prop_chgalgo_chg_ops { + int (*enable_power_path)(struct prop_chgalgo_device *pca, bool en); + int (*enable_charging)(struct prop_chgalgo_device *pca, bool en); + int (*enable_chip)(struct prop_chgalgo_device *pca, bool en); + int (*enable_hz)(struct prop_chgalgo_device *pca, bool en); + int (*set_vbusovp)(struct prop_chgalgo_device *pca, u32 mV); + int (*set_ibusocp)(struct prop_chgalgo_device *pca, u32 mA); + int (*set_vbatovp)(struct prop_chgalgo_device *pca, u32 mV); + int (*set_ibatocp)(struct prop_chgalgo_device *pca, u32 mA); + int (*set_vbatovp_alarm)(struct prop_chgalgo_device *pca, u32 mV); + int (*reset_vbatovp_alarm)(struct prop_chgalgo_device *pca); + int (*set_vbusovp_alarm)(struct prop_chgalgo_device *pca, u32 mV); + int (*reset_vbusovp_alarm)(struct prop_chgalgo_device *pca); + int (*set_aicr)(struct prop_chgalgo_device *pca, u32 mA); + int (*set_ichg)(struct prop_chgalgo_device *pca, u32 mA); + int (*get_adc)(struct prop_chgalgo_device *pca, + enum prop_chgalgo_adc_channel chan, int *min, int *max); + int (*get_soc)(struct prop_chgalgo_device *pca, u32 *soc); + int (*is_vbuslowerr)(struct prop_chgalgo_device *pca, bool *err); + int (*is_charging_enabled)(struct prop_chgalgo_device *pca, bool *en); + int (*get_adc_accuracy)(struct prop_chgalgo_device *pca, + enum prop_chgalgo_adc_channel chan, int *min, + int *max); + int (*init_chip)(struct prop_chgalgo_device *pca); + int (*enable_auto_trans)(struct prop_chgalgo_device *pca, bool en); + int (*set_auto_trans)(struct prop_chgalgo_device *pca, u32 mV, bool en); + int (*dump_registers)(struct prop_chgalgo_device *pca); +}; + +struct prop_chgalgo_algo_ops { + int (*init_algo)(struct prop_chgalgo_device *pca); + bool (*is_algo_ready)(struct prop_chgalgo_device *pca); + int (*start_algo)(struct prop_chgalgo_device *pca); + bool (*is_algo_running)(struct prop_chgalgo_device *pca); + int (*plugout_reset)(struct prop_chgalgo_device *pca); + int (*stop_algo)(struct prop_chgalgo_device *pca, bool rerun); + int (*thermal_throttling)(struct prop_chgalgo_device *pca, int mA); + int (*set_jeita_vbat_cv)(struct prop_chgalgo_device *pca, int mV); + int (*notifier_call)(struct prop_chgalgo_device *pca, + struct prop_chgalgo_notify *notify); +}; + +enum prop_chgalgo_dev_type { + PCA_DEVTYPE_TA = 0, + PCA_DEVTYPE_CHARGER, + PCA_DEVTYPE_ALGO, + PCA_DEVTYPE_MAX, +}; + +struct prop_chgalgo_desc { + const char *name; + enum prop_chgalgo_dev_type type; + const struct prop_chgalgo_ta_ops *ta_ops; + const struct prop_chgalgo_chg_ops *chg_ops; + const struct prop_chgalgo_algo_ops *algo_ops; +}; + +struct prop_chgalgo_device { + struct device dev; + const struct prop_chgalgo_desc *desc; + struct srcu_notifier_head nh; + void *drv_data; + int (*suspend)(struct prop_chgalgo_device *pca); + int (*resume)(struct prop_chgalgo_device *pca); +}; + +extern struct prop_chgalgo_device * +prop_chgalgo_device_register(struct device *parent, + const struct prop_chgalgo_desc *desc, + void *drv_data); +extern void prop_chgalgo_device_unregister(struct prop_chgalgo_device *pca); +extern struct prop_chgalgo_device * +prop_chgalgo_dev_get_by_name(const char *name); +extern const char * +prop_chgalgo_notify_evt_tostring(enum prop_chgalgo_notify_evt evt); + +static inline int prop_chgalgo_get_devtype(struct prop_chgalgo_device *pca) +{ + return pca->desc->type; +} + +static inline void *prop_chgalgo_get_drvdata(struct prop_chgalgo_device *pca) +{ + return pca->drv_data; +} + +static inline int +prop_chgalgo_notifier_register(struct prop_chgalgo_device *pca, + struct notifier_block *nb) +{ + return srcu_notifier_chain_register(&pca->nh, nb); +} + +static inline int +prop_chgalgo_notifier_unregister(struct prop_chgalgo_device *pca, + struct notifier_block *nb) +{ + return srcu_notifier_chain_unregister(&pca->nh, nb); +} + +/* Richtek pca TA interface */ +extern int prop_chgalgo_enable_ta_charging(struct prop_chgalgo_device *pca, + bool en, u32 mV, u32 mA); +extern int prop_chgalgo_set_ta_cap(struct prop_chgalgo_device *pca, u32 mV, + u32 mA); +extern int prop_chgalgo_get_ta_measure_cap(struct prop_chgalgo_device *pca, + u32 *mV, u32 *mA); +extern int prop_chgalgo_get_ta_temperature(struct prop_chgalgo_device *pca, + int *degree); +extern int prop_chgalgo_get_ta_status(struct prop_chgalgo_device *pca, + struct prop_chgalgo_ta_status *status); +extern int prop_chgalgo_is_ta_cc(struct prop_chgalgo_device *pca, bool *cc); +extern int prop_chgalgo_send_ta_hardreset(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_authenticate_ta(struct prop_chgalgo_device *pca, + struct prop_chgalgo_ta_auth_data *data); +extern int prop_chgalgo_enable_ta_wdt(struct prop_chgalgo_device *pca, bool en); +extern int prop_chgalgo_set_ta_wdt(struct prop_chgalgo_device *pca, u32 ms); +extern int prop_chgalgo_sync_ta_volt(struct prop_chgalgo_device *pca, u32 vta); + +/* Richtek pca charger interface */ +extern int prop_chgalgo_enable_power_path(struct prop_chgalgo_device *pca, + bool en); +extern int prop_chgalgo_enable_charging(struct prop_chgalgo_device *pca, + bool en); +extern int prop_chgalgo_enable_chip(struct prop_chgalgo_device *pca, bool en); +extern int prop_chgalgo_enable_hz(struct prop_chgalgo_device *pca, bool en); +extern int prop_chgalgo_set_vbusovp(struct prop_chgalgo_device *pca, u32 mV); +extern int prop_chgalgo_set_ibusocp(struct prop_chgalgo_device *pca, u32 mA); +extern int prop_chgalgo_set_vbatovp(struct prop_chgalgo_device *pca, u32 mV); +extern int prop_chgalgo_set_ibatocp(struct prop_chgalgo_device *pca, u32 mA); +extern int prop_chgalgo_set_vbatovp_alarm(struct prop_chgalgo_device *pca, + u32 mV); +extern int prop_chgalgo_reset_vbatovp_alarm(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_set_vbusovp_alarm(struct prop_chgalgo_device *pca, + u32 mV); +extern int prop_chgalgo_reset_vbusovp_alarm(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_get_adc(struct prop_chgalgo_device *pca, + enum prop_chgalgo_adc_channel chan, int *min, + int *max); +extern int prop_chgalgo_get_soc(struct prop_chgalgo_device *pca, u32 *soc); +extern int prop_chgalgo_set_ichg(struct prop_chgalgo_device *pca, u32 mA); +extern int prop_chgalgo_set_aicr(struct prop_chgalgo_device *pca, u32 mA); +extern int prop_chgalgo_is_vbuslowerr(struct prop_chgalgo_device *pca, + bool *err); +extern int prop_chgalgo_is_charging_enabled(struct prop_chgalgo_device *pca, + bool *en); +extern int prop_chgalgo_get_adc_accuracy(struct prop_chgalgo_device *pca, + enum prop_chgalgo_adc_channel chan, + int *min, int *max); +extern int prop_chgalgo_init_chip(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_enable_auto_trans(struct prop_chgalgo_device *pca, + bool en); +extern int prop_chgalgo_set_auto_trans(struct prop_chgalgo_device *pca, u32 mV, bool en); +extern int prop_chgalgo_dump_registers(struct prop_chgalgo_device *pca); + +/* Richtek pca algorithm interface */ +#ifdef CONFIG_RT_PROP_CHGALGO +extern int prop_chgalgo_init_algo(struct prop_chgalgo_device *pca); +extern bool prop_chgalgo_is_algo_ready(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_start_algo(struct prop_chgalgo_device *pca); +extern bool prop_chgalgo_is_algo_running(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_plugout_reset(struct prop_chgalgo_device *pca); +extern int prop_chgalgo_stop_algo(struct prop_chgalgo_device *pca, bool rerun); +extern int prop_chgalgo_notifier_call(struct prop_chgalgo_device *pca, + struct prop_chgalgo_notify *notify); +extern int prop_chgalgo_thermal_throttling(struct prop_chgalgo_device *pca, + int mA); +extern int prop_chgalgo_set_jeita_vbat_cv(struct prop_chgalgo_device *pca, + int mV); +#else +static inline int prop_chgalgo_init_algo(struct prop_chgalgo_device *pca) +{ + return -ENOTSUPP; +} + +static inline bool prop_chgalgo_is_algo_ready(struct prop_chgalgo_device *pca) +{ + return false; +} + +static inline int prop_chgalgo_start_algo(struct prop_chgalgo_device *pca) +{ + return -ENOTSUPP; +} + +static inline bool prop_chgalgo_is_algo_running(struct prop_chgalgo_device *pca) +{ + return false; +} + +static inline int prop_chgalgo_plugout_reset(struct prop_chgalgo_device *pca) +{ + return -ENOTSUPP; +} + +static inline int prop_chgalgo_stop_algo(struct prop_chgalgo_device *pca, + bool rerun) +{ + return -ENOTSUPP; +} + +static inline int prop_chgalgo_notifier_call(struct prop_chgalgo_device *pca, + struct prop_chgalgo_notify *notify) +{ + return -ENOTSUPP; +} + +static inline int +prop_chgalgo_thermal_throttling(struct prop_chgalgo_device *pca, int mA) +{ + return -ENOTSUPP; +} + +static inline int +prop_chgalgo_set_jeita_vbat_cv(struct prop_chgalgo_device *pca, int mV) +{ + return -ENOTSUPP; +} +#endif /* CONFIG_RT_PROP_CHGALGO */ +#endif /* __LINUX_PROP_CHGALGO_CLASS_H */ \ No newline at end of file diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index b13df7b3f8d6..62c998cb8959 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -817,6 +817,16 @@ config CHARGER_BQ2560X It depends on the config MTK_CHARGER. +config CHARGER_SGM41513 + bool "CHARGER_SGM41513" + depends on MTK_CHARGER + help + Say Yes to enable + SGM SGM41513 battery charger driver. + It depends on + the config MTK_CHARGER. + + config CHARGER_CX7601 bool "CHARGER_CX7601" depends on MTK_CHARGER diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index f3116bccac40..a0750b738a6b 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -3,6 +3,7 @@ subdir-ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/usb20 subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/typec/tcpc/inc subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/power_throttling +ccflags-y += -I$(src)/mediatek/charger -I$(src)/mediatek/misc power_supply-y := power_supply_core.o power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o @@ -90,6 +91,7 @@ obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o obj-$(CONFIG_CHARGER_BQ25601) += bq25601.o obj-$(CONFIG_CHARGER_BQ2560X) += bq2560x_charger.o +obj-$(CONFIG_CHARGER_SGM41513) += mediatek/charger/sgm41513_charger.o obj-$(CONFIG_CHARGER_CX7601) += cx7601_charger.o obj-$(CONFIG_CHARGER_RT9458) += rt9458.o obj-$(CONFIG_CHARGER_RT9465) += rt9465.o diff --git a/drivers/power/supply/mediatek/charger/adapter_class.h b/drivers/power/supply/mediatek/charger/adapter_class.h new file mode 100644 index 000000000000..2e7d93fcc9b8 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/adapter_class.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 MediaTek Inc. + */ + +#ifndef LINUX_POWER_ADAPTER_CLASS_H +#define LINUX_POWER_ADAPTER_CLASS_H + +#include +#include +#include + + +#define ADAPTER_CAP_MAX_NR 10 + +struct adapter_power_cap { + uint8_t selected_cap_idx; + uint8_t nr; + uint8_t pdp; + uint8_t pwr_limit[ADAPTER_CAP_MAX_NR]; + int max_mv[ADAPTER_CAP_MAX_NR]; + int min_mv[ADAPTER_CAP_MAX_NR]; + int ma[ADAPTER_CAP_MAX_NR]; + int maxwatt[ADAPTER_CAP_MAX_NR]; + int minwatt[ADAPTER_CAP_MAX_NR]; + uint8_t type[ADAPTER_CAP_MAX_NR]; + int info[ADAPTER_CAP_MAX_NR]; +}; + +enum adapter_type { + MTK_PD_ADAPTER, +}; + +enum adapter_event { + MTK_PD_CONNECT_NONE, + MTK_PD_CONNECT_HARD_RESET, + MTK_PD_CONNECT_PE_READY_SNK, + MTK_PD_CONNECT_PE_READY_SNK_PD30, + MTK_PD_CONNECT_PE_READY_SNK_APDO, + MTK_PD_CONNECT_TYPEC_ONLY_SNK, + MTK_TYPEC_WD_STATUS, + MTK_TYPEC_HRESET_STATUS, +}; + +enum adapter_property { + TYPEC_RP_LEVEL, + PD_TYPE, +}; + +enum adapter_cap_type { + MTK_PD_APDO_START, + MTK_PD_APDO_END, + MTK_PD, + MTK_PD_APDO, + MTK_CAP_TYPE_UNKNOWN, +}; + +enum adapter_return_value { + MTK_ADAPTER_OK = 0, + MTK_ADAPTER_NOT_SUPPORT, + MTK_ADAPTER_TIMEOUT, + MTK_ADAPTER_REJECT, + MTK_ADAPTER_ERROR, + MTK_ADAPTER_ADJUST, +}; + + +struct adapter_status { + int temperature; + bool ocp; + bool otp; + bool ovp; +}; + +struct adapter_properties { + const char *alias_name; +}; + +struct adapter_device { + struct adapter_properties props; + const struct adapter_ops *ops; + struct mutex ops_lock; + struct device dev; + struct srcu_notifier_head evt_nh; + void *driver_data; + +}; + +struct adapter_ops { + int (*suspend)(struct adapter_device *dev, pm_message_t state); + int (*resume)(struct adapter_device *dev); + int (*get_property)(struct adapter_device *dev, + enum adapter_property pro); + int (*get_status)(struct adapter_device *dev, + struct adapter_status *sta); + int (*set_cap)(struct adapter_device *dev, enum adapter_cap_type type, + int mV, int mA); + int (*get_cap)(struct adapter_device *dev, enum adapter_cap_type type, + struct adapter_power_cap *cap); + int (*get_output)(struct adapter_device *dev, int *mV, int *mA); + +}; + +static inline void *adapter_dev_get_drvdata( + const struct adapter_device *adapter_dev) +{ + return adapter_dev->driver_data; +} + +static inline void adapter_dev_set_drvdata( + struct adapter_device *adapter_dev, void *data) +{ + adapter_dev->driver_data = data; +} + +extern struct adapter_device *adapter_device_register( + const char *name, + struct device *parent, void *devdata, const struct adapter_ops *ops, + const struct adapter_properties *props); +extern void adapter_device_unregister( + struct adapter_device *adapter_dev); +extern int register_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb); +extern int unregister_adapter_device_notifier( + struct adapter_device *adapter_dev, + struct notifier_block *nb); +extern struct adapter_device *get_adapter_by_name( + const char *name); + +#define to_adapter_device(obj) container_of(obj, struct adapter_device, dev) + +extern int adapter_dev_get_property(struct adapter_device *adapter_dev, + enum adapter_property sta); +extern int adapter_dev_get_status(struct adapter_device *adapter_dev, + struct adapter_status *sta); +extern int adapter_dev_get_output(struct adapter_device *adapter_dev, + int *mV, int *mA); +extern int adapter_dev_set_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + int mV, int mA); +extern int adapter_dev_get_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + struct adapter_power_cap *cap); + + +#endif /*LINUX_POWER_ADAPTER_CLASS_H*/ + diff --git a/drivers/power/supply/mediatek/charger/mtk_charger_init.h b/drivers/power/supply/mediatek/charger/mtk_charger_init.h new file mode 100644 index 000000000000..e4830b5584de --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_charger_init.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_CHARGER_INIT_H__ +#define __MTK_CHARGER_INIT_H__ + +/* hs14 code for SR-AL6528A-01-323|AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ +#define BATTERY_CV 4400000 +#define CV_HIGH_THRESHOLD 4500000 +#define V_CHARGER_MAX 6300000 /* 6.3 V */ +#define HV_CHARGER_MAX 10400000 /* 10.4 V */ +#define V_CHARGER_DROP 700000 /* 0.7 V */ +/* hs14 code for SR-AL6528A-01-323|AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ +#define V_CHARGER_MIN 4600000 /* 4.6 V */ +/* hs14 code for AL6528A-604 by gaozhengwei at 2022/11/07 start */ +#define HV_CHARGER_MIN 8000000 /* 8.0 V */ +/* hs14 code for AL6528A-604 by gaozhengwei at 2022/11/07 end */ + +#define USB_CHARGER_CURRENT_SUSPEND 0 /* def CONFIG_USB_IF */ +#define USB_CHARGER_CURRENT_UNCONFIGURED 70000 /* 70mA */ +#define USB_CHARGER_CURRENT_CONFIGURED 500000 /* 500mA */ +#define USB_CHARGER_CURRENT 500000 /* 500mA */ +/* hs14 code for SR-AL6528A-01-323 by gaozhengwei at 2022/09/22 start */ +#define AC_CHARGER_CURRENT 2000000 +#define AC_CHARGER_INPUT_CURRENT 1550000 +/* hs14 code for SR-AL6528A-01-323 by gaozhengwei at 2022/09/22 end */ +#define NON_STD_AC_CHARGER_CURRENT 500000 +#define CHARGING_HOST_CHARGER_CURRENT 650000 +#define APPLE_1_0A_CHARGER_CURRENT 650000 +#define APPLE_2_1A_CHARGER_CURRENT 800000 +#define TA_AC_CHARGING_CURRENT 3000000 +#define USB_UNLIMITED_CURRENT 2000000 +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/20 start */ +#define PD_CHARGER_CURRENT 2700000 +#define PD_INPUT_CURRENT 1650000 +#define PD_VOLTAGE_THR 8000 +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/20 end */ + +/* dynamic mivr */ +#define V_CHARGER_MIN_1 4400000 /* 4.4 V */ +#define V_CHARGER_MIN_2 4200000 /* 4.2 V */ +#define MAX_DMIVR_CHARGER_CURRENT 1400000 /* 1.4 A */ + +/* hs14 code for SR-AL6528A-01-323 by gaozhengwei at 2022/09/22 start */ +/* sw jeita */ +#define JEITA_TEMP_ABOVE_T4_CV 4200000 +#define JEITA_TEMP_T3_TO_T4_CV 4200000 +#define JEITA_TEMP_T2_TO_T3_CV 4400000 +#define JEITA_TEMP_T1_TO_T2_CV 4400000 +#define JEITA_TEMP_T0_TO_T1_CV 4400000 +#define JEITA_TEMP_BELOW_T0_CV 4400000 +#define JEITA_TEMP_ABOVE_T4_CUR 0 +#define JEITA_TEMP_T3_TO_T4_CUR 1750000 +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/23 start */ +#define JEITA_TEMP_T2_TO_T3_CUR 2700000 +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/23 end */ +#define JEITA_TEMP_T1_TO_T2_CUR 1500000 +#define JEITA_TEMP_T0_TO_T1_CUR 500000 +#define JEITA_TEMP_BELOW_T0_CUR 0 +#define TEMP_T4_THRES 50 +#define TEMP_T4_THRES_MINUS_X_DEGREE 48 +#define TEMP_T3_THRES 45 +#define TEMP_T3_THRES_MINUS_X_DEGREE 43 +#define TEMP_T2_THRES 12 +#define TEMP_T2_THRES_PLUS_X_DEGREE 14 +#define TEMP_T1_THRES 5 +#define TEMP_T1_THRES_PLUS_X_DEGREE 7 +#define TEMP_T0_THRES 0 +#define TEMP_T0_THRES_PLUS_X_DEGREE 2 +#define TEMP_NEG_10_THRES 0 +/* hs14 code for SR-AL6528A-01-323 by gaozhengwei at 2022/09/22 end */ + +/* Battery Temperature Protection */ +#define MIN_CHARGE_TEMP 0 +#define MIN_CHARGE_TEMP_PLUS_X_DEGREE 6 +#define MAX_CHARGE_TEMP 50 +#define MAX_CHARGE_TEMP_MINUS_X_DEGREE 47 + +/* pe */ +#define PE_ICHG_LEAVE_THRESHOLD 1000000 /* uA */ +#define TA_AC_12V_INPUT_CURRENT 3200000 +#define TA_AC_9V_INPUT_CURRENT 3200000 +#define TA_AC_7V_INPUT_CURRENT 3200000 +#define TA_9V_SUPPORT +#define TA_12V_SUPPORT + +/* pe2.0 */ +#define PE20_ICHG_LEAVE_THRESHOLD 1000000 /* uA */ +#define TA_START_BATTERY_SOC 0 +#define TA_STOP_BATTERY_SOC 85 + +/* dual charger */ +#define TA_AC_MASTER_CHARGING_CURRENT 1500000 +#define TA_AC_SLAVE_CHARGING_CURRENT 1500000 +#define SLAVE_MIVR_DIFF 100000 + +/* slave charger */ +#define CHG2_EFF 90 + +/* cable measurement impedance */ +#define CABLE_IMP_THRESHOLD 699 +#define VBAT_CABLE_IMP_THRESHOLD 3900000 /* uV */ + +/* bif */ +#define BIF_THRESHOLD1 4250000 /* UV */ +#define BIF_THRESHOLD2 4300000 /* UV */ +#define BIF_CV_UNDER_THRESHOLD2 4450000 /* UV */ +#define BIF_CV BATTERY_CV /* UV */ + +#define R_SENSE 56 /* mohm */ + +#define MAX_CHARGING_TIME (12 * 60 * 60) /* 12 hours */ + +#define DEFAULT_BC12_CHARGER 0 /* MAIN_CHARGER */ + +/* battery warning */ +#define BATTERY_NOTIFY_CASE_0001_VCHARGER +#define BATTERY_NOTIFY_CASE_0002_VBATTEMP + +/* pe4 */ +#define PE40_MAX_VBUS 11000 +#define PE40_MAX_IBUS 3000 +#define HIGH_TEMP_TO_LEAVE_PE40 46 +#define HIGH_TEMP_TO_ENTER_PE40 39 +#define LOW_TEMP_TO_LEAVE_PE40 10 +#define LOW_TEMP_TO_ENTER_PE40 16 + +/* pd */ +#define PD_VBUS_UPPER_BOUND 10000000 /* uv */ +#define PD_VBUS_LOW_BOUND 5000000 /* uv */ +#define PD_ICHG_LEAVE_THRESHOLD 1000000 /* uA */ +#define PD_STOP_BATTERY_SOC 80 + +#define VSYS_WATT 5000000 +#define IBUS_ERR 14 + +#define SC_BATTERY_SIZE 3000 +#define SC_CV_TIME 3600 +#define SC_CURRENT_LIMIT 2000 + +#endif /*__MTK_CHARGER_INIT_H__*/ + +/* hs14 code for SR-AL6528A-01-336 by shanxinkai at 2022/09/15 start */ +/*D85 setting */ +#ifdef HQ_D85_BUILD +#define D85_BATTERY_CV 4000000 +#define D85_JEITA_TEMP_CV 4000000 +#endif +/* hs14 code for SR-AL6528A-01-336 by shanxinkai at 2022/09/15 end */ \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/mtk_charger_intf.h b/drivers/power/supply/mediatek/charger/mtk_charger_intf.h new file mode 100644 index 000000000000..b47b459060f6 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_charger_intf.h @@ -0,0 +1,610 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ +#ifndef __MTK_CHARGER_INTF_H__ +#define __MTK_CHARGER_INTF_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct charger_manager; +struct charger_data; +#include "mtk_pe_intf.h" +#include "mtk_pe20_intf.h" +#include "mtk_pe40_intf.h" +#include "mtk_pe50_intf.h" +#include "mtk_pdc_intf.h" +#include "adapter_class.h" +#include "mtk_smartcharging.h" + +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#ifdef CONFIG_AFC_CHARGER +#include "afc_charger_intf.h" +#endif +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ + +#define CHARGING_INTERVAL 10 +#define CHARGING_FULL_INTERVAL 20 + +#define CHRLOG_ERROR_LEVEL 1 +#define CHRLOG_DEBUG_LEVEL 2 + +extern int chr_get_debug_level(void); + +#define chr_err(fmt, args...) \ +do { \ + if (chr_get_debug_level() >= CHRLOG_ERROR_LEVEL) { \ + pr_notice(fmt, ##args); \ + } \ +} while (0) + +#define chr_info(fmt, args...) \ +do { \ + if (chr_get_debug_level() >= CHRLOG_ERROR_LEVEL) { \ + pr_notice_ratelimited(fmt, ##args); \ + } \ +} while (0) + +#define chr_debug(fmt, args...) \ +do { \ + if (chr_get_debug_level() >= CHRLOG_DEBUG_LEVEL) { \ + pr_notice(fmt, ##args); \ + } \ +} while (0) + +#define CHR_CC (0x0001) +#define CHR_TOPOFF (0x0002) +#define CHR_TUNING (0x0003) +#define CHR_POSTCC (0x0004) +#define CHR_BATFULL (0x0005) +#define CHR_ERROR (0x0006) +#define CHR_PE40_INIT (0x0007) +#define CHR_PE40_CC (0x0008) +#define CHR_PE40_TUNING (0x0009) +#define CHR_PE40_POSTCC (0x000A) +#define CHR_PE30 (0x000B) +#define CHR_PE40 (0x000C) +#define CHR_PDC (0x000D) +#define CHR_PE50_READY (0x000E) +#define CHR_PE50_RUNNING (0x000F) +#define CHR_PE50 (0x0010) + +/* charging abnormal status */ +#define CHG_VBUS_OV_STATUS (1 << 0) +#define CHG_BAT_OT_STATUS (1 << 1) +#define CHG_OC_STATUS (1 << 2) +#define CHG_BAT_OV_STATUS (1 << 3) +#define CHG_ST_TMO_STATUS (1 << 4) +#define CHG_BAT_LT_STATUS (1 << 5) +#define CHG_TYPEC_WD_STATUS (1 << 6) + +/* charger_algorithm notify charger_dev */ +enum { + EVENT_EOC, + EVENT_RECHARGE, +}; + +/* charger_dev notify charger_manager */ +enum { + CHARGER_DEV_NOTIFY_VBUS_OVP, + CHARGER_DEV_NOTIFY_BAT_OVP, + CHARGER_DEV_NOTIFY_EOC, + CHARGER_DEV_NOTIFY_RECHG, + CHARGER_DEV_NOTIFY_SAFETY_TIMEOUT, + CHARGER_DEV_NOTIFY_VBATOVP_ALARM, + CHARGER_DEV_NOTIFY_VBUSOVP_ALARM, + CHARGER_DEV_NOTIFY_IBATOCP, + CHARGER_DEV_NOTIFY_IBUSOCP, + CHARGER_DEV_NOTIFY_IBUSUCP_FALL, + CHARGER_DEV_NOTIFY_VOUTOVP, + CHARGER_DEV_NOTIFY_VDROVP, +}; + +/* + * Software JEITA + * T0: -10 degree Celsius + * T1: 0 degree Celsius + * T2: 10 degree Celsius + * T3: 45 degree Celsius + * T4: 50 degree Celsius + */ +enum sw_jeita_state_enum { + TEMP_BELOW_T0 = 0, + TEMP_T0_TO_T1, + TEMP_T1_TO_T2, + TEMP_T2_TO_T3, + TEMP_T3_TO_T4, + TEMP_ABOVE_T4 +}; + +struct sw_jeita_data { + int sm; + int pre_sm; + int cv; + bool charging; + bool error_recovery_flag; +}; + +/* battery thermal protection */ +enum bat_temp_state_enum { + BAT_TEMP_LOW = 0, + BAT_TEMP_NORMAL, + BAT_TEMP_HIGH +}; + +struct battery_thermal_protection_data { + int sm; + bool enable_min_charge_temp; + int min_charge_temp; + int min_charge_temp_plus_x_degree; + int max_charge_temp; + int max_charge_temp_minus_x_degree; +}; + +/* hs14 code for SR-AL6528A-01-338 by chengyuanhang at 2022/10/03 start */ +#ifndef HQ_FACTORY_BUILD +#define is_between(left, right, value) \ + (((left) >= (right) && (left) >= (value) \ + && (value) >= (right)) \ + || ((left) <= (right) && (left) <= (value) \ + && (value) <= (right))) + +struct range_data { + u32 low_threshold; + u32 high_threshold; + u32 value; +}; +#define MAX_CV_ENTRIES 8 + +#define MAX_CYCLE_COUNT 0xFFFF +#endif +/* hs14 code for SR-AL6528A-01-338 by chengyuanhang at 2022/10/03 end */ + +struct charger_custom_data { + int battery_cv; /* uv */ + int max_charger_voltage; + int max_charger_voltage_setting; + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + int hv_max_charger_voltage; + int hv_max_charger_voltage_setting; + int charger_voltage_drop; + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + int min_charger_voltage; + + int usb_charger_current_suspend; + int usb_charger_current_unconfigured; + int usb_charger_current_configured; + int usb_charger_current; + int ac_charger_current; + int ac_charger_input_current; + int non_std_ac_charger_current; + int charging_host_charger_current; + int apple_1_0a_charger_current; + int apple_2_1a_charger_current; + int usb_unlimited_current; + int ta_ac_charger_current; + int pd_charger_current; + /* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 start */ + int pd_input_current; + int pd_voltage_thr; + /* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 end */ + + /* dynamic mivr */ + int min_charger_voltage_1; + int min_charger_voltage_2; + int max_dmivr_charger_current; + + /* sw jeita */ + int jeita_temp_above_t4_cv; + int jeita_temp_t3_to_t4_cv; + int jeita_temp_t2_to_t3_cv; + int jeita_temp_t1_to_t2_cv; + int jeita_temp_t0_to_t1_cv; + int jeita_temp_below_t0_cv; + /* hs14 code for SR-AL6528A-01-323 by gaozhengwei at 2022/09/22 start */ + int jeita_temp_above_t4_cur; + int jeita_temp_t3_to_t4_cur; + int jeita_temp_t2_to_t3_cur; + int jeita_temp_t1_to_t2_cur; + int jeita_temp_t0_to_t1_cur; + int jeita_temp_below_t0_cur; + /* hs14 code for SR-AL6528A-01-323 by gaozhengwei at 2022/09/22 end */ + int temp_t4_thres; + int temp_t4_thres_minus_x_degree; + int temp_t3_thres; + int temp_t3_thres_minus_x_degree; + int temp_t2_thres; + int temp_t2_thres_plus_x_degree; + int temp_t1_thres; + int temp_t1_thres_plus_x_degree; + int temp_t0_thres; + int temp_t0_thres_plus_x_degree; + int temp_neg_10_thres; + + /* battery temperature protection */ + int mtk_temperature_recharge_support; + int max_charge_temp; + int max_charge_temp_minus_x_degree; + int min_charge_temp; + int min_charge_temp_plus_x_degree; + + /* pe */ + int pe_ichg_level_threshold; /* ma */ + int ta_ac_12v_input_current; + int ta_ac_9v_input_current; + int ta_ac_7v_input_current; + bool ta_12v_support; + bool ta_9v_support; + + /* pe2.0 */ + int pe20_ichg_level_threshold; /* ma */ + int ta_start_battery_soc; + int ta_stop_battery_soc; + + /* pe4.0 */ + int pe40_single_charger_input_current; /* ma */ + int pe40_single_charger_current; + int pe40_dual_charger_input_current; + int pe40_dual_charger_chg1_current; + int pe40_dual_charger_chg2_current; + int pe40_stop_battery_soc; + int pe40_max_vbus; + int pe40_max_ibus; + int high_temp_to_leave_pe40; + int high_temp_to_enter_pe40; + int low_temp_to_leave_pe40; + int low_temp_to_enter_pe40; + + /* pe4.0 cable impedance threshold (mohm) */ + u32 pe40_r_cable_1a_lower; + u32 pe40_r_cable_2a_lower; + u32 pe40_r_cable_3a_lower; + + /* dual charger */ + u32 chg1_ta_ac_charger_current; + u32 chg2_ta_ac_charger_current; + int slave_mivr_diff; + u32 dual_polling_ieoc; + + /* slave charger */ + int chg2_eff; + bool parallel_vbus; + + /* cable measurement impedance */ + int cable_imp_threshold; + int vbat_cable_imp_threshold; + + /* bif */ + int bif_threshold1; /* uv */ + int bif_threshold2; /* uv */ + int bif_cv_under_threshold2; /* uv */ + + /* power path */ + bool power_path_support; + + int max_charging_time; /* second */ + + int bc12_charger; + + /* pd */ + int pd_vbus_upper_bound; + int pd_vbus_low_bound; + int pd_ichg_level_threshold; + int pd_stop_battery_soc; + + int vsys_watt; + int ibus_err; + +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#ifdef CONFIG_AFC_CHARGER + /* afc */ + int afc_start_battery_soc; + int afc_stop_battery_soc; + int afc_pre_input_current; + int afc_charger_input_current; + int afc_charger_current; + int afc_ichg_level_threshold; + int afc_min_charger_voltage; + int afc_max_charger_voltage; +#endif +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ +/* hs14 code for SR-AL6528A-01-338 by chengyuanhang at 2022/10/03 start */ +#ifndef HQ_FACTORY_BUILD + bool ss_batt_aging_enable; + int ss_batt_cycle; + struct range_data batt_cv_data[MAX_CV_ENTRIES]; + int setting_cv; /* uv */ +#endif +/* hs14 code for SR-AL6528A-01-338 by chengyuanhang at 2022/10/03 end */ +}; + +struct charger_data { + int force_charging_current; + int thermal_input_current_limit; + int thermal_charging_current_limit; + int input_current_limit; + int charging_current_limit; + int disable_charging_count; + int input_current_limit_by_aicl; + int junction_temp_min; + int junction_temp_max; +}; + +struct charger_manager { + bool init_done; + const char *algorithm_name; + struct platform_device *pdev; + void *algorithm_data; + int usb_state; + bool usb_unlimited; + bool disable_charger; + + struct charger_device *chg1_dev; + struct notifier_block chg1_nb; + struct charger_data chg1_data; + struct charger_consumer *chg1_consumer; + + struct charger_device *chg2_dev; + struct notifier_block chg2_nb; + struct charger_data chg2_data; + + struct charger_device *dvchg1_dev; + struct notifier_block dvchg1_nb; + struct charger_data dvchg1_data; + + struct charger_device *dvchg2_dev; + struct notifier_block dvchg2_nb; + struct charger_data dvchg2_data; + + struct adapter_device *pd_adapter; + + + enum charger_type chr_type; + bool can_charging; + int cable_out_cnt; + + int (*do_algorithm)(struct charger_manager *cm); + int (*plug_in)(struct charger_manager *cm); + int (*plug_out)(struct charger_manager *cm); + int (*do_charging)(struct charger_manager *cm, bool en); + int (*do_event)(struct notifier_block *nb, unsigned long ev, void *v); + int (*change_current_setting)(struct charger_manager *cm); + + /* notify charger user */ + struct srcu_notifier_head evt_nh; + /* receive from battery */ + struct notifier_block psy_nb; + + /* common info */ + int battery_temp; + + /* sw jeita */ + bool enable_sw_jeita; + struct sw_jeita_data sw_jeita; + + /* dynamic_cv */ + bool enable_dynamic_cv; + + bool cmd_discharging; + bool safety_timeout; + bool vbusov_stat; + + /* battery warning */ + unsigned int notify_code; + unsigned int notify_test_mode; + + /* battery thermal protection */ + struct battery_thermal_protection_data thermal; + + /* dtsi custom data */ + struct charger_custom_data data; + + bool enable_sw_safety_timer; + bool sw_safety_timer_setting; + + /* High voltage charging */ + bool enable_hv_charging; + + /* pe */ + bool enable_pe_plus; + struct mtk_pe pe; + + /* pe 2.0 */ + bool enable_pe_2; + struct mtk_pe20 pe2; + + /* pe 4.0 */ + bool enable_pe_4; + bool leave_pe4; + struct mtk_pe40 pe4; + + /* pe 5.0 */ + bool enable_pe_5; + bool leave_pe5; + struct mtk_pe50 pe5; + + /* type-C*/ + bool enable_type_c; + + /* water detection */ + bool water_detected; + + /* pd */ + bool leave_pdc; + struct mtk_pdc pdc; + bool disable_pd_dual; + bool is_pdc_run; + + int pd_type; + bool pd_reset; + +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#ifdef CONFIG_AFC_CHARGER + /* afc */ + struct afc_dev afc; + bool enable_afc; + int hv_disable; + int afc_sts; +/* hs14 code for AL6528ADEU-2119 by qiaodan at 2022/11/18 start */ +#ifndef HQ_FACTORY_BUILD //ss version + bool boot_with_dcp; +#endif +/* hs14 code for AL6528ADEU-2119 by qiaodan at 2022/11/18 end */ +#endif +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ + + /* thread related */ + struct hrtimer charger_kthread_timer; + + /* alarm timer */ + struct alarm charger_timer; + struct timespec endtime; + bool is_suspend; + + struct wakeup_source *charger_wakelock; + struct mutex charger_lock; + struct mutex charger_pd_lock; + struct mutex cable_out_lock; + spinlock_t slock; + unsigned int polling_interval; + bool charger_thread_timeout; + wait_queue_head_t wait_que; + bool charger_thread_polling; + + /* kpoc */ + atomic_t enable_kpoc_shdn; + + /* ATM */ + bool atm_enabled; + + /* dynamic mivr */ + bool enable_dynamic_mivr; + + struct smartcharging sc; + + + /*daemon related*/ + struct sock *daemo_nl_sk; + u_int g_scd_pid; + struct scd_cmd_param_t_1 sc_data; + + bool force_disable_pp[TOTAL_CHARGER]; + bool enable_pp[TOTAL_CHARGER]; + struct mutex pp_lock[TOTAL_CHARGER]; +#ifndef HQ_FACTORY_BUILD +/* hs14 code for SR-AL6528A-01-324 by chengyuanhang at 2022/10/10 start */ + u64 charging_dur_time; + u64 charging_count_start; + bool batt_protect_flag; + bool en_batt_protect; + struct delayed_work charging_count_work; +/* hs14 code for SR-AL6528A-01-324 by chengyuanhang at 2022/10/10 end */ +/* hs14 code for SR-AL6528A-01-261 | SR-AL6528A-01-343 by chengyuanhang at 2022/10/11 start */ + int cust_batt_cap; + int batt_full_flag; + int batt_status; + int capacity; +/* hs14 code for SR-AL6528A-01-261 | SR-AL6528A-01-343 by chengyuanhang at 2022/10/11 end */ +#endif + + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + bool swovp_disable; + int g_ovp_trigger; + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ +/* hs14 code for SR-AL6528A-01-244 by shanxinkai at 2022/11/04 start */ + bool input_suspend_flag; + bool hiz_flag; +#ifdef HQ_FACTORY_BUILD //factory version + bool batt_cap_control; +#endif +#ifndef HQ_FACTORY_BUILD //ss version + int store_mode; + bool batt_store_mode; + struct wakeup_source *charger_wakelock_app; + struct delayed_work retail_app_status_change_work; +#endif +/* hs14 code for SR-AL6528A-01-244 by shanxinkai at 2022/11/04 end */ +}; + +/* charger related module interface */ +extern int charger_manager_notifier(struct charger_manager *info, int event); +extern int mtk_switch_charging_init(struct charger_manager *info); +extern int mtk_switch_charging_init2(struct charger_manager *info); +extern int mtk_dual_switch_charging_init(struct charger_manager *info); +extern int mtk_linear_charging_init(struct charger_manager *info); +extern void _wake_up_charger(struct charger_manager *info); +extern int mtk_get_dynamic_cv(struct charger_manager *info, unsigned int *cv); +extern bool is_dual_charger_supported(struct charger_manager *info); +extern int charger_enable_vbus_ovp(struct charger_manager *pinfo, bool enable); +extern bool is_typec_adapter(struct charger_manager *info); + +/* pmic API */ +extern unsigned int upmu_get_rgs_chrdet(void); +extern int pmic_get_vbus(void); +extern int pmic_get_charging_current(void); +extern int pmic_get_battery_voltage(void); +extern int pmic_get_bif_battery_voltage(int *vbat); +extern int pmic_is_bif_exist(void); +extern int pmic_enable_hw_vbus_ovp(bool enable); +extern bool pmic_is_battery_exist(void); +/* hs14 code for SR-AL6528A-01-261 | SR-AL6528A-01-343 by chengyuanhang at 2022/10/11 start */ +#ifndef HQ_FACTORY_BUILD +extern void ss_batt_full_flag_get(int *val); +#endif +/* hs14 code for SR-AL6528A-01-261 | SR-AL6528A-01-343 by chengyuanhang at 2022/10/11 end */ +/* hs14 code for P221216-05713 by shanxinkai at 2022/12/19 start */ +extern void ss_charger_check_status(struct charger_manager *info); +/* hs14 code for P221216-05713 by shanxinkai at 2022/12/19 end */ +extern void notify_adapter_event(enum adapter_type type, enum adapter_event evt, + void *val); + + +/* FIXME */ +enum usb_state_enum { + USB_SUSPEND = 0, + USB_UNCONFIGURED, + USB_CONFIGURED +}; + +#if defined(CONFIG_MACH_MT6877) || defined(CONFIG_MACH_MT6893) \ + || defined(CONFIG_MACH_MT6885) || defined(CONFIG_MACH_MT6785) +bool is_usb_rdy(struct device *dev); +#else +bool __attribute__((weak)) is_usb_rdy(void) +{ + pr_info("%s is not defined\n", __func__); + return false; +} +#endif + +/* procfs */ +#define PROC_FOPS_RW(name) \ +static int mtk_chg_##name##_open(struct inode *node, struct file *file) \ +{ \ + return single_open(file, mtk_chg_##name##_show, PDE_DATA(node));\ +} \ +static const struct file_operations mtk_chg_##name##_fops = { \ + .owner = THIS_MODULE, \ + .open = mtk_chg_##name##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + .write = mtk_chg_##name##_write, \ +} + +#endif /* __MTK_CHARGER_INTF_H__ */ \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/mtk_pdc_intf.h b/drivers/power/supply/mediatek/charger/mtk_pdc_intf.h new file mode 100644 index 000000000000..a277962257e7 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_pdc_intf.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_PD_INTF_H +#define __MTK_PD_INTF_H + +#include "adapter_class.h" + +/* PD charging */ +struct mtk_pdc { + struct tcpc_device *tcpc; + struct adapter_power_cap cap; + int pdc_max_watt; + int pdc_max_watt_setting; + + bool check_impedance; + int pd_cap_max_watt; + int pd_idx; + int pd_reset_idx; + int pd_boost_idx; + int pd_buck_idx; + int vbus_l; + int vbus_h; + + struct mutex access_lock; + struct mutex pmic_sync_lock; + struct wakeup_source suspend_lock; + int ta_vchr_org; + bool to_check_chr_type; + bool to_tune_ta_vchr; + bool is_cable_out_occur; + bool is_connect; + bool is_enabled; +}; + +extern bool mtk_pdc_check_charger(struct charger_manager *info); +extern void mtk_pdc_plugout_reset(struct charger_manager *info); +extern void mtk_pdc_set_max_watt(struct charger_manager *info, int watt); +extern int mtk_pdc_get_max_watt(struct charger_manager *info); +extern int mtk_pdc_get_setting(struct charger_manager *info, int *vbus, + int *cur, int *idx); +extern void mtk_pdc_init_table(struct charger_manager *info); +extern bool mtk_pdc_init(struct charger_manager *info); +extern int mtk_pdc_setup(struct charger_manager *info, int idx); +extern void mtk_pdc_plugout(struct charger_manager *info); +extern void mtk_pdc_check_cable_impedance(struct charger_manager *info); +extern void mtk_pdc_reset(struct charger_manager *info); +extern bool mtk_pdc_check_leave(struct charger_manager *info); + +#ifdef CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT + + + +#else /* NOT CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT */ + + +#endif /* CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT */ + + +#endif /* __MTK_PD_INTF_H */ \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/mtk_pe20_intf.h b/drivers/power/supply/mediatek/charger/mtk_pe20_intf.h new file mode 100644 index 000000000000..31a7b2ba0fdb --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_pe20_intf.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_PE20_INTF_H__ +#define __MTK_PE20_INTF_H__ + +/* pe 2.0*/ +struct pe20_profile { + unsigned int vbat; + unsigned int vchr; +}; + +struct mtk_pe20 { + struct mutex access_lock; + struct mutex pmic_sync_lock; + struct wakeup_source *suspend_lock; + int ta_vchr_org; + int idx; + int vbus; + bool to_check_chr_type; + bool is_cable_out_occur; /* Plug out happened while detect PE+20 */ + bool is_connect; + bool is_enabled; + struct pe20_profile profile[10]; + + int vbat_orig; /* Measured VBAT before cable impedance measurement */ + int aicr_cable_imp; /* AICR to set after cable impedance measurement */ +}; + +#ifdef CONFIG_MTK_PUMP_EXPRESS_PLUS_20_SUPPORT + +extern int mtk_pe20_init(struct charger_manager *pinfo); +extern int mtk_pe20_reset_ta_vchr(struct charger_manager *pinfo); +extern int mtk_pe20_check_charger(struct charger_manager *pinfo); +extern int mtk_pe20_start_algorithm(struct charger_manager *pinfo); +extern int mtk_pe20_set_charging_current(struct charger_manager *pinfo, + unsigned int *ichg, + unsigned int *aicr); + +extern void mtk_pe20_set_to_check_chr_type(struct charger_manager *pinfo, + bool check); +extern void mtk_pe20_set_is_enable(struct charger_manager *pinfo, bool enable); +extern void mtk_pe20_set_is_cable_out_occur(struct charger_manager *pinfo, + bool out); + +extern bool mtk_pe20_get_to_check_chr_type(struct charger_manager *pinfo); +extern bool mtk_pe20_get_is_connect(struct charger_manager *pinfo); +extern bool mtk_pe20_get_is_enable(struct charger_manager *pinfo); + +#else /* NOT CONFIG_MTK_PUMP_EXPRESS_PLUS_20_SUPPORT */ + +static inline int mtk_pe20_init(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe20_reset_ta_vchr(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe20_check_charger(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe20_start_algorithm(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe20_set_charging_current(struct charger_manager *pinfo, + unsigned int *ichg, + unsigned int *aicr) +{ + return -ENOTSUPP; +} + +static inline void mtk_pe20_set_to_check_chr_type(struct charger_manager *pinfo, + bool check) +{ +} + +static inline void mtk_pe20_set_is_enable(struct charger_manager *pinfo, + bool enable) +{ +} + +static inline +void mtk_pe20_set_is_cable_out_occur(struct charger_manager *pinfo, bool out) +{ +} + +static inline bool mtk_pe20_get_to_check_chr_type(struct charger_manager *pinfo) +{ + return false; +} + +static inline bool mtk_pe20_get_is_connect(struct charger_manager *pinfo) +{ + return false; +} + +static inline bool mtk_pe20_get_is_enable(struct charger_manager *pinfo) +{ + return false; +} + +#endif /* CONFIG_MTK_PUMP_EXPRESS_PLUS_20_SUPPORT */ + +#endif /* __MTK_PE20_INTF_H__ */ \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/mtk_pe40_intf.h b/drivers/power/supply/mediatek/charger/mtk_pe40_intf.h new file mode 100644 index 000000000000..35ad8c5ec660 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_pe40_intf.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_PE_40_INTF_H +#define __MTK_PE_40_INTF_H + +#define CONFIG_MTK_PUMP_EXPRESS_PLUS_40_SUPPORT +#include "adapter_class.h" + +struct pe4_pps_status { + int output_mv; /* 0xffff means no support */ + int output_ma; /* 0xff means no support */ + uint8_t real_time_flags; +}; + + +struct mtk_pe40 { + bool is_connect; + bool is_enabled; + bool can_query; + struct adapter_power_cap cap; + + int avbus; + int vbus; + int ibus; + int watt; + + int r_sw; + int r_cable; + int r_cable_1; + int r_cable_2; + + int pmic_vbus; + int TA_vbus; + int vbus_cali; + + int max_charger_ibus; + int max_vbus; + int max_ibus; + + int pe4_input_current_limit; + int pe4_input_current_limit_setting; + int input_current_limit; + +}; + +#ifdef CONFIG_MTK_PUMP_EXPRESS_PLUS_40_SUPPORT +extern bool mtk_pe40_init(struct charger_manager *pinfo); +extern bool mtk_is_TA_support_pd_pps(struct charger_manager *info); +extern bool mtk_pe40_is_ready(struct charger_manager *pinfo); +extern bool mtk_pe40_get_is_connect(struct charger_manager *pinfo); +extern void mtk_pe40_set_is_enable(struct charger_manager *pinfo, bool enable); +extern bool mtk_pe40_get_is_enable(struct charger_manager *pinfo); +extern int mtk_pe40_init_state(struct charger_manager *pinfo); +extern int mtk_pe40_tune1_state(struct charger_manager *pinfo); +extern int mtk_pe40_tune2_state(struct charger_manager *pinfo); +extern int mtk_pe40_cc_state(struct charger_manager *pinfo); +extern void mtk_pe40_plugout_reset(struct charger_manager *pinfo); +extern void mtk_pe40_end(struct charger_manager *pinfo, int type, bool retry); +#else + +#endif + +#endif /* __MTK_PE_40_INTF_H */ diff --git a/drivers/power/supply/mediatek/charger/mtk_pe50_intf.h b/drivers/power/supply/mediatek/charger/mtk_pe50_intf.h new file mode 100644 index 000000000000..35e460e025ca --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_pe50_intf.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_PE_50_INTF_H +#define __MTK_PE_50_INTF_H + +#include + +struct mtk_pe50 { + struct prop_chgalgo_device *pca_algo; + struct notifier_block nb; + bool online; + bool is_enabled; +}; + +enum mtk_pe50_notify_src { + MTK_PE50_NOTISRC_TCP, + MTK_PE50_NOTISRC_CHG, + MTK_PE50_NOTISRC_MAX, +}; + +#ifdef CONFIG_MTK_PUMP_EXPRESS_50_SUPPORT +extern int mtk_pe50_init(struct charger_manager *chgmgr); +extern bool mtk_pe50_is_ready(struct charger_manager *chgmgr); +extern int mtk_pe50_start(struct charger_manager *chgmgr); +extern bool mtk_pe50_is_running(struct charger_manager *chgmgr); +extern int mtk_pe50_plugout_reset(struct charger_manager *chgmgr); +extern bool mtk_pe50_get_is_connect(struct charger_manager *chgmgr); +extern bool mtk_pe50_get_is_enable(struct charger_manager *chgmgr); +extern void mtk_pe50_set_is_enable(struct charger_manager *chgmgr, bool enable); +extern int mtk_pe50_notifier_call(struct charger_manager *chgmgr, + enum mtk_pe50_notify_src src, + unsigned long event, void *data); +extern int mtk_pe50_deinit(struct charger_manager *chgmgr); +extern int mtk_pe50_thermal_throttling(struct charger_manager *chgmgr, int uA); +extern int mtk_pe50_set_jeita_vbat_cv(struct charger_manager *chgmgr, int uV); +extern int mtk_pe50_stop_algo(struct charger_manager *chgmgr, bool rerun); +#else +static inline int mtk_pe50_init(struct charger_manager *chgmgr) +{ + return -ENOTSUPP; +} + +static inline bool mtk_pe50_is_ready(struct charger_manager *chgmgr) +{ + return false; +} + +static inline int mtk_pe50_start(struct charger_manager *chgmgr) +{ + return -ENOTSUPP; +} + +static inline bool mtk_pe50_is_running(struct charger_manager *chgmgr) +{ + return false; +} + +static inline int mtk_pe50_plugout_reset(struct charger_manager *chgmgr) +{ + return -ENOTSUPP; +} + +static inline bool mtk_pe50_get_is_connect(struct charger_manager *chgmgr) +{ + return false; +} + +static inline bool mtk_pe50_get_is_enable(struct charger_manager *chgmgr) +{ + return false; +} + +static inline void mtk_pe50_set_is_enable(struct charger_manager *chgmgr, + bool enable) +{ +} + +static inline int mtk_pe50_notifier_call(struct charger_manager *chgmgr, + enum mtk_pe50_notify_src src, + unsigned long event, void *data) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe50_deinit(struct charger_manager *chgmgr) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe50_thermal_throttling(struct charger_manager *chgmgr, + int uA) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe50_set_jeita_vbat_cv(struct charger_manager *chgmgr, + int uV) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe50_stop_algo(struct charger_manager *chgmgr, bool rerun) +{ + return -ENOTSUPP; +} +#endif /* CONFIG_MTK_PUMP_EXPRESS_50_SUPPORT */ +#endif /* __MTK_PE_50_INTF_H */ \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/mtk_pe_intf.h b/drivers/power/supply/mediatek/charger/mtk_pe_intf.h new file mode 100644 index 000000000000..520c45f693b7 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_pe_intf.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_PE_INTF_H +#define __MTK_PE_INTF_H + + +struct mtk_pe { + struct mutex access_lock; + struct mutex pmic_sync_lock; + struct wakeup_source *suspend_lock; + int ta_vchr_org; /* uA */ + bool to_check_chr_type; + bool to_tune_ta_vchr; + bool is_cable_out_occur; /* Plug out happened while detecting PE+ */ + bool is_connect; + bool is_enabled; +}; + +#ifdef CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT + +extern int mtk_pe_init(struct charger_manager *pinfo); +extern int mtk_pe_reset_ta_vchr(struct charger_manager *pinfo); +extern int mtk_pe_check_charger(struct charger_manager *pinfo); +extern int mtk_pe_start_algorithm(struct charger_manager *pinfo); +extern int mtk_pe_set_charging_current(struct charger_manager *pinfo, + unsigned int *ichg, unsigned int *aicr); + +extern void mtk_pe_set_to_check_chr_type(struct charger_manager *pinfo, + bool check); +extern void mtk_pe_set_is_enable(struct charger_manager *pinfo, bool enable); +extern void mtk_pe_set_is_cable_out_occur(struct charger_manager *pinfo, + bool out); + +extern bool mtk_pe_get_to_check_chr_type(struct charger_manager *pinfo); +extern bool mtk_pe_get_is_connect(struct charger_manager *pinfo); +extern bool mtk_pe_get_is_enable(struct charger_manager *pinfo); + +#else /* NOT CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT */ + +static inline int mtk_pe_init(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} +static inline int mtk_pe_reset_ta_vchr(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} +static inline int mtk_pe_check_charger(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} +static inline int mtk_pe_start_algorithm(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe_plugout_reset(struct charger_manager *pinfo) +{ + return -ENOTSUPP; +} + +static inline int mtk_pe_set_charging_current(struct charger_manager *pinfo, + unsigned int *ichg, unsigned int *aicr) +{ + return -ENOTSUPP; +} + +static inline void mtk_pe_set_to_check_chr_type(struct charger_manager *pinfo, + bool check) +{ +} +static inline void mtk_pe_set_is_cable_out_occur(struct charger_manager *pinfo, + bool out) +{ +} +static inline void mtk_pe_set_is_enable(struct charger_manager *pinfo, + bool enable) +{ +} + +static inline bool mtk_pe_get_to_check_chr_type(struct charger_manager *pinfo) +{ + return false; +} +static inline bool mtk_pe_get_is_connect(struct charger_manager *pinfo) +{ + return false; +} +static inline bool mtk_pe_get_is_enable(struct charger_manager *pinfo) +{ + return false; +} +#endif /* CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT */ + + +#endif /* __MTK_PE_INTF_H */ \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/mtk_smartcharging.h b/drivers/power/supply/mediatek/charger/mtk_smartcharging.h new file mode 100644 index 000000000000..a64edb61a183 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/mtk_smartcharging.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ +#ifndef _MTK_SMARTCHARGING_H +#define _MTK_SMARTCHARGING_H + +#include /* netlink */ +#include /* netlink */ +#include /* netlink */ +#include /* netlink */ + +#define NETLINK_CHG 28 + +#define SCD_NL_MSG_T_HDR_LEN 28 +#define MAX_NL_MSG_LEN_SND 4096 +#define MAX_NL_MSG_LEN_RCV 9200 +#define SCD_NL_MSG_MAX_LEN 9200 + +#define SCD_NL_MAGIC 19800212 + +enum sc_daemon_cmds { + SC_DAEMON_CMD_PRINT_LOG, + SC_DAEMON_CMD_SET_DAEMON_PID, + SC_DAEMON_CMD_NOTIFY_DAEMON, + SC_DAEMON_CMD_SETTING, + + SC_DAEMON_CMD_FROM_USER_NUMBER +}; + + +enum sc_kernel_events { + SC_EVENT_PLUG_IN, + SC_EVENT_PLUG_OUT, + SC_EVENT_CHARGING, + SC_EVENT_STOP_CHARGING, +}; + +/* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ +enum sc_adapter_type { + SC_ADAPTER_NORMAL, + SC_ADAPTER_HV, +}; +/* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + +struct sc_nl_msg_t { + unsigned int sc_cmd; + unsigned int sc_subcmd; + unsigned int sc_subcmd_para1; + unsigned int sc_subcmd_para2; + unsigned int sc_data_len; + unsigned int sc_ret_data_len; + unsigned int identity; + char sc_data[SCD_NL_MSG_MAX_LEN]; +}; + +struct scd_cmd_param_t_1 { + int size; + int data[50]; +}; + +enum sc_info { + SC_VBAT, + SC_BAT_TMP, + SC_UISOC, + SC_SOC, + SC_ENABLE, + SC_BAT_SIZE, + SC_START_TIME, + SC_END_TIME, + SC_IBAT_LIMIT, + SC_TARGET_PERCENTAGE, + SC_LEFT_TIME_FOR_CV, + SC_IBAT_SETTING, + SC_IBAT, + SC_IBAT_ALG, + SC_IBUS, + SC_DBGLV, + SC_SOLUTION, + + SC_INFO_MAX +}; + +enum sc_current_direction { + SC_IGNORE, + SC_KEEP, + SC_DISABLE, + SC_REDUCE, +}; + +struct smartcharging { + /*daemon related*/ + struct sock *daemo_nl_sk; + u_int g_scd_pid; + struct scd_cmd_param_t_1 data; + bool enable; + int battery_size; + int current_limit; + int target_percentage; + int left_time_for_cv; + int start_time; + int end_time; + + bool disable_charger; + enum sc_current_direction solution; + int sc_ibat; + int pre_ibat; + int bh; + + bool disable_in_this_plug; +}; + +extern int wakeup_sc_algo_cmd(struct scd_cmd_param_t_1 *data, int subcmd, int para1); +extern void sc_update(struct charger_manager *pinfo); +extern void sc_select_charging_current(struct charger_manager *info, struct charger_data *pdata); + +#endif /* End of _MTK_SMARTCHARGING_H */ diff --git a/drivers/power/supply/mediatek/charger/sgm41513.h b/drivers/power/supply/mediatek/charger/sgm41513.h new file mode 100644 index 000000000000..930741a9a5c1 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/sgm41513.h @@ -0,0 +1,67 @@ +/* + * sgm41513 battery charging driver + * + * Copyright (C) 2013 SGM + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _LINUX_SGM41513_I2C_H +#define _LINUX_SGM41513_I2C_H + +#include + + +struct sgm41513_charge_param { + int vlim; + int ilim; + int ichg; + int vreg; +}; + +enum stat_ctrl { + STAT_CTRL_STAT, + STAT_CTRL_ICHG, + STAT_CTRL_INDPM, + STAT_CTRL_DISABLE, +}; + +enum vboost { + BOOSTV_4850 = 4850, + BOOSTV_5000 = 5000, + BOOSTV_5150 = 5150, + BOOSTV_5300 = 5300, +}; + +enum iboost { + BOOSTI_500 = 500, + BOOSTI_1200 = 1200, +}; + +enum vac_ovp { + VAC_OVP_5500 = 5500, + VAC_OVP_6500 = 6500, + VAC_OVP_10500 = 10500, + VAC_OVP_14000 = 14000, +}; + + +struct sgm41513_platform_data { + struct sgm41513_charge_param usb; + int iprechg; + int iterm; + + enum stat_ctrl statctrl; + enum vboost boostv; // options are 4850, + enum iboost boosti; // options are 500mA, 1200mA + enum vac_ovp vac_ovp; + +}; + +#endif \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/sgm41513_charger.c b/drivers/power/supply/mediatek/charger/sgm41513_charger.c new file mode 100644 index 000000000000..8e4fe9206af0 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/sgm41513_charger.c @@ -0,0 +1,1873 @@ +/* + * SGM41513 battery charging driver + * + * Copyright (C) 2022 SGM + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define pr_fmt(fmt) "[sgm41513]:%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ +#include +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ +#include +#include +/* AL6528A code for SR-AL6528A-01-303 by wenyaqi at 2022/08/31 start */ +#include +#include "mtk_charger_intf.h" +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 start */ +#include "mtk_charger_init.h" +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 end */ + +#include "sgm41513_reg.h" +#include "sgm41513.h" + +/* hs14 code for AL6528A-600|AL6528A-1033 by gaozhengwei at 2022/12/09 start */ +#define BC12_DONE_TIMEOUT_CHECK_MAX_RETRY 10 +/* hs14 code for AL6528A-600|AL6528A-1033 by gaozhengwei at 2022/12/09 end */ + +/* hs14 code for SR-AL6528A-01-787 by gaozhengwei at 2022/12/06 start */ +#define I2C_RETRY_CNT 3 +/* hs14 code for SR-AL6528A-01-787 by gaozhengwei at 2022/12/06 end */ + +struct sgm41513 { + struct device *dev; + struct i2c_client *client; + + int part_no; + + const char *chg_dev_name; + const char *eint_name; + + bool chg_det_enable; + + // enum charger_type chg_type; + struct power_supply_desc psy_desc; + int psy_usb_type; + + int status; + u32 intr_gpio; + int irq; + + struct mutex i2c_rw_lock; + + bool charge_enabled; /* Register bit status */ + bool power_good; + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + bool vbus_gd; + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + bool vbus_stat; + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ + bool bypass_chgdet_en; + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ + + struct sgm41513_platform_data *platform_data; + struct charger_device *chg_dev; + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + struct delayed_work psy_dwork; + struct delayed_work prob_dwork; + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ + struct delayed_work charge_detect_delayed_work; + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ + struct power_supply *psy; +}; + +static const unsigned int IPRECHG_CURRENT_STABLE[] = { + 5000, 10000, 15000, 20000, 30000, 40000, 50000, 60000, + 80000, 100000, 120000, 140000, 160000, 180000, 200000, 240000 +}; + +static const unsigned int ITERM_CURRENT_STABLE[] = { + 5000, 10000, 15000, 20000, 30000, 40000, 50000, 60000, + 80000, 100000, 120000, 140000, 160000, 180000, 200000, 240000 +}; + +static const struct charger_properties sgm41513_chg_props = { + .alias_name = "sgm41513", +}; + +static int __sgm41513_read_reg(struct sgm41513 *sgm, u8 reg, u8 *data) +{ + s32 ret; + /* hs14 code for SR-AL6528A-01-787 by gaozhengwei at 2022/12/06 start */ + int i; + + for (i = 0; i < I2C_RETRY_CNT; ++i) { + + ret = i2c_smbus_read_byte_data(sgm->client, reg); + + if (ret >= 0) + break; + + pr_info("%s reg(0x%x), ret(%d), i2c_retry_cnt(%d/%d)\n", + __func__, reg, ret, i + 1, I2C_RETRY_CNT); + } + /* hs14 code for SR-AL6528A-01-787 by gaozhengwei at 2022/12/06 end */ + + if (ret < 0) { + pr_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *data = (u8) ret; + + return 0; +} + +static int __sgm41513_write_reg(struct sgm41513 *sgm, int reg, u8 val) +{ + s32 ret; + /* hs14 code for SR-AL6528A-01-787 by gaozhengwei at 2022/12/06 start */ + int i; + + for (i = 0; i < I2C_RETRY_CNT; ++i) { + + ret = i2c_smbus_write_byte_data(sgm->client, reg, val); + + if (ret >= 0) + break; + + pr_info("%s reg(0x%x), ret(%d), i2c_retry_cnt(%d/%d)\n", + __func__, reg, ret, i + 1, I2C_RETRY_CNT); + } + /* hs14 code for SR-AL6528A-01-787 by gaozhengwei at 2022/12/06 end */ + + if (ret < 0) { + pr_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n", + val, reg, ret); + return ret; + } + return 0; +} + +static int sgm41513_read_byte(struct sgm41513 *sgm, u8 reg, u8 *data) +{ + int ret; + + mutex_lock(&sgm->i2c_rw_lock); + ret = __sgm41513_read_reg(sgm, reg, data); + mutex_unlock(&sgm->i2c_rw_lock); + + return ret; +} + +static int sgm41513_write_byte(struct sgm41513 *sgm, u8 reg, u8 data) +{ + int ret; + + mutex_lock(&sgm->i2c_rw_lock); + ret = __sgm41513_write_reg(sgm, reg, data); + mutex_unlock(&sgm->i2c_rw_lock); + + if (ret) + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + + return ret; +} + +static int sgm41513_update_bits(struct sgm41513 *sgm, u8 reg, u8 mask, u8 data) +{ + int ret; + u8 tmp; + + mutex_lock(&sgm->i2c_rw_lock); + ret = __sgm41513_read_reg(sgm, reg, &tmp); + if (ret) { + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + goto out; + } + + tmp &= ~mask; + tmp |= data & mask; + + ret = __sgm41513_write_reg(sgm, reg, tmp); + if (ret) + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + +out: + mutex_unlock(&sgm->i2c_rw_lock); + return ret; +} + +static int sgm41513_enable_otg(struct sgm41513 *sgm) +{ + u8 val = REG01_OTG_ENABLE << REG01_OTG_CONFIG_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_01, REG01_OTG_CONFIG_MASK, + val); + +} + +static int sgm41513_disable_otg(struct sgm41513 *sgm) +{ + u8 val = REG01_OTG_DISABLE << REG01_OTG_CONFIG_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_01, REG01_OTG_CONFIG_MASK, + val); + +} + +static int sgm41513_enable_charger(struct sgm41513 *sgm) +{ + int ret; + u8 val = REG01_CHG_ENABLE << REG01_CHG_CONFIG_SHIFT; + + ret = + sgm41513_update_bits(sgm, SGM41513_REG_01, REG01_CHG_CONFIG_MASK, val); + + return ret; +} + +static int sgm41513_disable_charger(struct sgm41513 *sgm) +{ + int ret; + u8 val = REG01_CHG_DISABLE << REG01_CHG_CONFIG_SHIFT; + + ret = + sgm41513_update_bits(sgm, SGM41513_REG_01, REG01_CHG_CONFIG_MASK, val); + return ret; +} + +int sgm41513_set_chargecurrent(struct sgm41513 *sgm, int curr) +{ + u8 ichg; + + if (curr < 0) { + curr = 0; + } else if (curr > 3000) { + curr = 3000; + } + + if (curr < 40) { + ichg = curr / 5; + } else if (curr < 130) { + ichg = 8 + (curr - 40) / 10; + } else if (curr < 300) { + ichg = 16 + (curr - 110) / 20; + } else if (curr < 540) { + ichg = 24 + (curr - 300) / 30; + } else if (curr < 1500) { + ichg = 32 + (curr - 540) / 60; + } else { + ichg = 48 + (curr - 1500) / 120; + } + + return sgm41513_update_bits(sgm, SGM41513_REG_02, REG02_ICHG_MASK, + ichg << REG02_ICHG_SHIFT); + +} + +int sgm41513_set_term_current(struct sgm41513 *sgm, int curr) +{ + u8 reg_val; + + curr = curr*1000; + if (curr > ITERM_CURRENT_STABLE[15]) + curr = ITERM_CURRENT_STABLE[15]; + + for(reg_val = 1; reg_val < 16 && curr >= ITERM_CURRENT_STABLE[reg_val]; reg_val++) + ; + reg_val--; + + return sgm41513_update_bits(sgm, SGM41513_REG_03, + REG03_ITERM_MASK, reg_val); +} + +EXPORT_SYMBOL_GPL(sgm41513_set_term_current); + +int sgm41513_set_prechg_current(struct sgm41513 *sgm, int curr) +{ + u8 reg_val; + + curr = curr*1000; + if (curr > IPRECHG_CURRENT_STABLE[15]) + curr = IPRECHG_CURRENT_STABLE[15]; + + for(reg_val = 1; reg_val < 16 && curr >= IPRECHG_CURRENT_STABLE[reg_val]; reg_val++) + ; + reg_val--; + + return sgm41513_update_bits(sgm, SGM41513_REG_03, REG03_IPRECHG_MASK, + reg_val << REG03_IPRECHG_SHIFT); +} +EXPORT_SYMBOL_GPL(sgm41513_set_prechg_current); + +int sgm41513_set_chargevolt(struct sgm41513 *sgm, int volt) +{ + u8 val; + + if (volt < REG04_VREG_BASE) + volt = REG04_VREG_BASE; + + if (volt == 4352) { + val = 15; + /* hs03s_NM code added for DEVAL5626-680 by shixuanxuan at 20220426 start */ + } else if (volt == 4200) { + val = (volt - REG04_VREG_BASE) / REG04_VREG_LSB + 1; + /* hs03s_NM code added for DEVAL5626-680 by shixuanxuan at 20220426 end */ + } else { + val = (volt - REG04_VREG_BASE) / REG04_VREG_LSB; + } + + return sgm41513_update_bits(sgm, SGM41513_REG_04, REG04_VREG_MASK, + val << REG04_VREG_SHIFT); +} + +int sgm41513_set_input_volt_limit(struct sgm41513 *sgm, int volt) +{ + u8 val; + + if (volt < REG06_VINDPM_BASE) + volt = REG06_VINDPM_BASE; + + val = (volt - REG06_VINDPM_BASE) / REG06_VINDPM_LSB; + return sgm41513_update_bits(sgm, SGM41513_REG_06, REG06_VINDPM_MASK, + val << REG06_VINDPM_SHIFT); +} + +int sgm41513_set_input_current_limit(struct sgm41513 *sgm, int curr) +{ + u8 val; + + if (curr < REG00_IINLIM_BASE) + curr = REG00_IINLIM_BASE; + + val = (curr - REG00_IINLIM_BASE) / REG00_IINLIM_LSB; + return sgm41513_update_bits(sgm, SGM41513_REG_00, REG00_IINLIM_MASK, + val << REG00_IINLIM_SHIFT); +} + +int sgm41513_set_watchdog_timer(struct sgm41513 *sgm, u8 timeout) +{ + u8 temp; + + temp = (u8) (((timeout - + REG05_WDT_BASE) / REG05_WDT_LSB) << REG05_WDT_SHIFT); + + return sgm41513_update_bits(sgm, SGM41513_REG_05, REG05_WDT_MASK, temp); +} +EXPORT_SYMBOL_GPL(sgm41513_set_watchdog_timer); + +int sgm41513_disable_watchdog_timer(struct sgm41513 *sgm) +{ + u8 val = REG05_WDT_DISABLE << REG05_WDT_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_05, REG05_WDT_MASK, val); +} +EXPORT_SYMBOL_GPL(sgm41513_disable_watchdog_timer); + +int sgm41513_reset_watchdog_timer(struct sgm41513 *sgm) +{ + u8 val = REG01_WDT_RESET << REG01_WDT_RESET_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_01, REG01_WDT_RESET_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_reset_watchdog_timer); + +int sgm41513_reset_chip(struct sgm41513 *sgm) +{ + int ret; + u8 val = REG0B_REG_RESET << REG0B_REG_RESET_SHIFT; + + ret = + sgm41513_update_bits(sgm, SGM41513_REG_0B, REG0B_REG_RESET_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(sgm41513_reset_chip); + +int sgm41513_enter_hiz_mode(struct sgm41513 *sgm) +{ + u8 val = REG00_HIZ_ENABLE << REG00_ENHIZ_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_00, REG00_ENHIZ_MASK, val); + +} +EXPORT_SYMBOL_GPL(sgm41513_enter_hiz_mode); + +int sgm41513_exit_hiz_mode(struct sgm41513 *sgm) +{ + + u8 val = REG00_HIZ_DISABLE << REG00_ENHIZ_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_00, REG00_ENHIZ_MASK, val); + +} +EXPORT_SYMBOL_GPL(sgm41513_exit_hiz_mode); + +static int sgm41513_enable_term(struct sgm41513 *sgm, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = REG05_TERM_ENABLE << REG05_EN_TERM_SHIFT; + else + val = REG05_TERM_DISABLE << REG05_EN_TERM_SHIFT; + + ret = sgm41513_update_bits(sgm, SGM41513_REG_05, REG05_EN_TERM_MASK, val); + + return ret; +} +EXPORT_SYMBOL_GPL(sgm41513_enable_term); + +/* hs14 code for AL6528A-341 by wenyaqi at 2022/10/20 start */ +int sgm41513_set_boost_current(struct sgm41513 *sgm, int curr) +{ + u8 val; + + if (curr >= BOOSTI_1200) + val = REG02_BOOST_LIM_1P2A; + else + val = REG02_BOOST_LIM_0P5A; + + return sgm41513_update_bits(sgm, SGM41513_REG_02, REG02_BOOST_LIM_MASK, + val << REG02_BOOST_LIM_SHIFT); +} + +int sgm41513_set_boost_voltage(struct sgm41513 *sgm, int volt) +{ + u8 val; + + if (volt >= BOOSTV_5300) + val = REG06_BOOSTV_5P3V; + else if (volt >= BOOSTV_5150 && volt < BOOSTV_5300) + val = REG06_BOOSTV_5P15V; + else if (volt >= BOOSTV_4850 && volt < BOOSTV_5000) + val = REG06_BOOSTV_4P85V; + else + val = REG06_BOOSTV_5V; + + return sgm41513_update_bits(sgm, SGM41513_REG_06, REG06_BOOSTV_MASK, + val << REG06_BOOSTV_SHIFT); +} +EXPORT_SYMBOL_GPL(sgm41513_set_boost_voltage); + +static int sgm41513_set_acovp_threshold(struct sgm41513 *sgm, int volt) +{ + u8 val; + + if (volt >= VAC_OVP_14000) + val = REG06_OVP_14P0V; + else if (volt >= VAC_OVP_10500 && volt < VAC_OVP_14000) + val = REG06_OVP_10P5V; + else if (volt >= VAC_OVP_6500 && volt < REG06_OVP_10P5V) + val = REG06_OVP_6P5V; + else + val = REG06_OVP_5P5V; + + return sgm41513_update_bits(sgm, SGM41513_REG_06, REG06_OVP_MASK, + val << REG06_OVP_SHIFT); +} +EXPORT_SYMBOL_GPL(sgm41513_set_acovp_threshold); +/* hs14 code for AL6528A-341 by wenyaqi at 2022/10/20 end */ + +static int sgm41513_set_stat_ctrl(struct sgm41513 *sgm, int ctrl) +{ + u8 val; + + val = ctrl; + + return sgm41513_update_bits(sgm, SGM41513_REG_00, REG00_STAT_CTRL_MASK, + val << REG00_STAT_CTRL_SHIFT); +} + +static int sgm41513_set_int_mask(struct sgm41513 *sgm, int mask) +{ + u8 val; + + val = mask; + + return sgm41513_update_bits(sgm, SGM41513_REG_0A, REG0A_INT_MASK_MASK, + val << REG0A_INT_MASK_SHIFT); +} + +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ +static int sgm41513_force_dpdm(struct sgm41513 *sgm) +{ + const u8 val = REG07_FORCE_DPDM << REG07_FORCE_DPDM_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_FORCE_DPDM_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_force_dpdm); +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + +static int sgm41513_enable_batfet(struct sgm41513 *sgm) +{ + const u8 val = REG07_BATFET_ON << REG07_BATFET_DIS_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_BATFET_DIS_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_enable_batfet); + +static int sgm41513_disable_batfet(struct sgm41513 *sgm) +{ + const u8 val = REG07_BATFET_OFF << REG07_BATFET_DIS_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_BATFET_DIS_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_disable_batfet); + +static int sgm41513_set_batfet_delay(struct sgm41513 *sgm, uint8_t delay) +{ + u8 val; + + if (delay == 0) + val = REG07_BATFET_DLY_0S; + else + val = REG07_BATFET_DLY_10S; + + val <<= REG07_BATFET_DLY_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_BATFET_DLY_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_set_batfet_delay); + +static int sgm41513_enable_safety_timer(struct sgm41513 *sgm) +{ + const u8 val = REG05_CHG_TIMER_ENABLE << REG05_EN_TIMER_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_05, REG05_EN_TIMER_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_enable_safety_timer); + +static int sgm41513_disable_safety_timer(struct sgm41513 *sgm) +{ + const u8 val = REG05_CHG_TIMER_DISABLE << REG05_EN_TIMER_SHIFT; + + return sgm41513_update_bits(sgm, SGM41513_REG_05, REG05_EN_TIMER_MASK, + val); +} +EXPORT_SYMBOL_GPL(sgm41513_disable_safety_timer); + +static struct sgm41513_platform_data *sgm41513_parse_dt(struct device_node *np, + struct sgm41513 *sgm) +{ + int ret; + struct sgm41513_platform_data *pdata; + + pdata = devm_kzalloc(sgm->dev, sizeof(struct sgm41513_platform_data), + GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_string(np, "charger_name", &sgm->chg_dev_name) < 0) { + sgm->chg_dev_name = "primary_chg"; + pr_warn("no charger name\n"); + } + + if (of_property_read_string(np, "eint_name", &sgm->eint_name) < 0) { + sgm->eint_name = "chr_stat"; + pr_warn("no eint name\n"); + } + + ret = of_get_named_gpio(np, "sgm,intr_gpio", 0); + if (ret < 0) { + pr_err("%s no sgm,intr_gpio(%d)\n", __func__, ret); + } else { + sgm->intr_gpio = ret; + } + + sgm->chg_det_enable = + of_property_read_bool(np, "sgm41513,charge-detect-enable"); + + ret = of_property_read_u32(np, "sgm41513,usb-vlim", &pdata->usb.vlim); + if (ret) { + pdata->usb.vlim = 4500; + pr_err("Failed to read node of sgm41513,usb-vlim\n"); + } + + ret = of_property_read_u32(np, "sgm41513,usb-ilim", &pdata->usb.ilim); + if (ret) { + pdata->usb.ilim = 2000; + pr_err("Failed to read node of sgm41513,usb-ilim\n"); + } + + ret = of_property_read_u32(np, "sgm41513,usb-vreg", &pdata->usb.vreg); + if (ret) { + pdata->usb.vreg = 4200; + pr_err("Failed to read node of sgm41513,usb-vreg\n"); + } + + ret = of_property_read_u32(np, "sgm41513,usb-ichg", &pdata->usb.ichg); + if (ret) { + pdata->usb.ichg = 2000; + pr_err("Failed to read node of sgm41513,usb-ichg\n"); + } + + ret = of_property_read_u32(np, "sgm41513,stat-pin-ctrl", + &pdata->statctrl); + if (ret) { + pdata->statctrl = 0; + pr_err("Failed to read node of sgm41513,stat-pin-ctrl\n"); + } + + ret = of_property_read_u32(np, "sgm41513,precharge-current", + &pdata->iprechg); + if (ret) { + pdata->iprechg = 180; + pr_err("Failed to read node of sgm41513,precharge-current\n"); + } + + ret = of_property_read_u32(np, "sgm41513,termination-current", + &pdata->iterm); + if (ret) { + pdata->iterm = 180; + pr_err + ("Failed to read node of sgm41513,termination-current\n"); + } + + ret = + of_property_read_u32(np, "sgm41513,boost-voltage", + &pdata->boostv); + if (ret) { + pdata->boostv = 5000; + pr_err("Failed to read node of sgm41513,boost-voltage\n"); + } + + ret = + of_property_read_u32(np, "sgm41513,boost-current", + &pdata->boosti); + if (ret) { + pdata->boosti = 1200; + pr_err("Failed to read node of sgm41513,boost-current\n"); + } + + ret = of_property_read_u32(np, "sgm41513,vac-ovp-threshold", + &pdata->vac_ovp); + if (ret) { + pdata->vac_ovp = 6500; + pr_err("Failed to read node of sgm41513,vac-ovp-threshold\n"); + } + + return pdata; +} + +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +static int sgm41513_dpdm_detect_is_done(struct sgm41513 * sgm) +{ + u8 chrg_stat,iindet_stat; + int ret; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_07, &iindet_stat); + if (ret) { + dev_err(sgm->dev, "Check iindet_stat failed\n"); + return false; + } + + ret = sgm41513_read_byte(sgm, SGM41513_INPUT_DET, &chrg_stat); + if (ret) { + dev_err(sgm->dev, "Check DPDM detecte error\n"); + } + + if ((iindet_stat & REG07_FORCE_DPDM_MASK) != 0) { + pr_err("[%s] Reg07 = 0x%.2x\n", __func__, iindet_stat); + return false; + } + + if ((chrg_stat & SGM41513_DPDM_ONGOING) != SGM41513_DPDM_ONGOING) { + pr_err("[%s] Reg0E = 0x%.2x\n", __func__, chrg_stat); + return false; + } + + return true; +} + +static int sgm41513_enable_hvdcp(struct sgm41513 * sgm) +{ + int ret; + int dp_val, dm_val; + + /*dp and dm connected,dp 0.6V dm Hiz*/ + dp_val = SGM41513_REG_02 << REG02_EN_HVDCP_SHIFT; + ret = sgm41513_update_bits(sgm, SGM41513_CHRG_CTRL_d, + SGM41513_DP_VSEL_MASK, dp_val); //dp 0.6V + if (ret) { + return ret; + } + + dm_val = 0; + ret = sgm41513_update_bits(sgm, SGM41513_CHRG_CTRL_d, + SGM41513_DM_VSEL_MASK, dm_val); //dm Hiz + + return ret; +} +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ + +/* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ +static void sgm41513_charger_detect_work_func(struct work_struct *work) +{ + int ret = 0; + u8 reg_val = 0; + int vbus_stat = 0; + int chg_type = CHARGER_UNKNOWN; + int retry_cnt = 0; + + struct sgm41513 *sgm = container_of(work, struct sgm41513, + charge_detect_delayed_work.work); + + do { + if (!sgm41513_dpdm_detect_is_done(sgm)) { + pr_err("[%s] DPDM detecte not done, retry_cnt:%d\n", __func__, retry_cnt); + } else { + pr_err("[%s] BC1.2 done\n", __func__); + break; + } + mdelay(60); + } while(retry_cnt++ < BC12_DONE_TIMEOUT_CHECK_MAX_RETRY); + + ret = sgm41513_read_byte(sgm, SGM41513_REG_08, ®_val); + + if (ret) + return; + + vbus_stat = (reg_val & REG08_VBUS_STAT_MASK); + vbus_stat >>= REG08_VBUS_STAT_SHIFT; + + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + pr_info("[%s] reg08: 0x%02x :vbus state %d\n", __func__, reg_val, vbus_stat); + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + + switch (vbus_stat) { + case REG08_VBUS_TYPE_NONE: + chg_type = CHARGER_UNKNOWN; + break; + case REG08_VBUS_TYPE_SDP: + chg_type = STANDARD_HOST; + break; + case REG08_VBUS_TYPE_CDP: + chg_type = CHARGING_HOST; + break; + case REG08_VBUS_TYPE_DCP: + chg_type = STANDARD_CHARGER; + break; + case REG08_VBUS_TYPE_UNKNOWN: + /* hs14 code for SR-AL6528A-01-252 by chengyuanhang at 2022/09/27 start */ + chg_type = NONSTANDARD_CHARGER; + /* hs14 code for SR-AL6528A-01-252 by chengyuanhang at 2022/09/27 end */ + break; + case REG08_VBUS_TYPE_NON_STD: + chg_type = NONSTANDARD_CHARGER; + break; + default: + chg_type = NONSTANDARD_CHARGER; + break; + } + + /* hs14 code for SR-AL6528A-01-306|SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ + if (chg_type != STANDARD_CHARGER) { + Charger_Detect_Release(); + } else { + ret = sgm41513_enable_hvdcp(sgm); + if (ret) + pr_err("Failed to en HVDCP, ret = %d\n", ret); + } + /* hs14 code for SR-AL6528A-01-306|SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ + + sgm->psy_usb_type = chg_type; + + schedule_delayed_work(&sgm->psy_dwork, 0); + + return; +} +/* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ + +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ +static void sgm41513_inform_psy_dwork_handler(struct work_struct *work) +{ + int ret = 0; + union power_supply_propval propval; + struct sgm41513 *sgm = container_of(work, struct sgm41513, + psy_dwork.work); + if (!sgm->psy) { + sgm->psy = power_supply_get_by_name("charger"); + if (!sgm->psy) { + pr_err("%s Couldn't get psy\n", __func__); + mod_delayed_work(system_wq, &sgm->psy_dwork, + msecs_to_jiffies(2000)); + return; + } + } +#ifdef BEFORE_MTK_ANDROID_T // which is used before Android T + if (sgm->psy_usb_type != POWER_SUPPLY_TYPE_UNKNOWN) +#else + if (sgm->psy_usb_type != CHARGER_UNKNOWN) +#endif + propval.intval = 1; + else + propval.intval = 0; + + ret = power_supply_set_property(sgm->psy, POWER_SUPPLY_PROP_ONLINE, + &propval); + + if (ret < 0) + pr_notice("inform power supply online failed:%d\n", ret); + + propval.intval = sgm->psy_usb_type; + + ret = power_supply_set_property(sgm->psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, + &propval); + + if (ret < 0) + pr_notice("inform power supply charge type failed:%d\n", ret); + + return; +} + +static irqreturn_t sgm41513_irq_handler(int irq, void *data) +{ + int ret; + u8 reg_val; + bool prev_pg; + bool prev_vbus_gd; + + struct sgm41513 *sgm = (struct sgm41513 *)data; + + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ + if (sgm->bypass_chgdet_en == true) { + pr_err("%s:bypass_chgdet_en=%d, skip bc12\n", __func__, sgm->bypass_chgdet_en); + return IRQ_HANDLED; + } + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ + + ret = sgm41513_read_byte(sgm, SGM41513_REG_0A, ®_val); + if (ret) + return IRQ_HANDLED; + + prev_vbus_gd = sgm->vbus_gd; + + sgm->vbus_gd = !!(reg_val & REG0A_VBUS_GD_MASK); + + ret = sgm41513_read_byte(sgm, SGM41513_REG_08, ®_val); + if (ret) + return IRQ_HANDLED; + + prev_pg = sgm->power_good; + + sgm->power_good = !!(reg_val & REG08_PG_STAT_MASK); + + /* hs14 code for AL6528ADEU-28 by gaozhengwei at 2022/09/29 start */ + if (!prev_vbus_gd && sgm->vbus_gd) { + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + sgm->vbus_stat = true; + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + pr_notice("adapter/usb inserted\n"); + Charger_Detect_Init(); + } else if (prev_vbus_gd && !sgm->vbus_gd) { + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + sgm->vbus_stat = false; + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + sgm->psy_usb_type = CHARGER_UNKNOWN; + schedule_delayed_work(&sgm->psy_dwork, 0); + pr_notice("adapter/usb removed\n"); + Charger_Detect_Release(); + return IRQ_HANDLED; + } + /* hs14 code for AL6528ADEU-28 by gaozhengwei at 2022/09/29 end */ + + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ + if (!prev_pg && sgm->power_good) + schedule_delayed_work(&sgm->charge_detect_delayed_work, 0); + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ + + return IRQ_HANDLED; + +} +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + +/* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 start*/ +static int sgm41513_set_shipmode(struct charger_device *chg_dev, bool en) +{ + int ret; + u8 val; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + if (en) { + val = REG07_BATFET_OFF << REG07_BATFET_DIS_SHIFT; + ret = sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_BATFET_DIS_MASK, val); + } else { + val = REG07_BATFET_ON << REG07_BATFET_DIS_SHIFT; + ret = sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_BATFET_DIS_MASK, val); + } + + pr_err("%s shipmode %s\n", en ? "enable" : "disable", + !ret ? "successfully" : "failed"); + + return ret; +} + +static int sgm41513_get_shipmode(struct charger_device *chg_dev) +{ + int ret; + u8 val; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + msleep(100); + ret = sgm41513_read_byte(sgm, SGM41513_REG_07, &val); + if (ret == 0){ + pr_err("Reg[%.2x] = 0x%.2x\n", SGM41513_REG_07, val); + } else { + pr_err("%s: get shipmode reg fail! \n",__func__); + return ret; + } + ret = (val & REG07_BATFET_DIS_MASK) >> REG07_BATFET_DIS_SHIFT; + pr_err("%s:shipmode %s\n",__func__, ret ? "enabled" : "disabled"); + + return ret; +} +static int sgm41513_disable_battfet_rst(struct sgm41513 *sgm) +{ + int ret; + u8 val; + + val = REG07_BATFET_RST_DISABLE << REG07_BATFET_RST_EN_SHIFT; + ret = sgm41513_update_bits(sgm, SGM41513_REG_07, REG07_BATFET_RST_EN_MASK, val); + + pr_err("disable BATTFET_RST_EN %s\n", !ret ? "successfully" : "failed"); + + return ret; +} +/* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 end*/ + +/* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 start*/ +static int sgm41513_get_charging_status(struct charger_device *chg_dev, + int *chg_stat) +{ + int ret; + u8 val; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + ret = sgm41513_read_byte(sgm, SGM41513_REG_08, &val); + if (!ret) { + val = val & REG08_CHRG_STAT_MASK; + val = val >> REG08_CHRG_STAT_SHIFT; + *chg_stat = val; + } + + return ret; +} +/* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 end*/ + +static int sgm41513_register_interrupt(struct sgm41513 *sgm) +{ + int ret = 0; + + ret = devm_gpio_request_one(sgm->dev, sgm->intr_gpio, GPIOF_DIR_IN, + "sgm41511_intr_gpio"); + if (ret < 0) { + dev_info(sgm->dev, "request gpio fail\n"); + return ret; + } else { + sgm->client->irq = gpio_to_irq(sgm->intr_gpio); + } + + if (! sgm->client->irq) { + pr_info("sgm->client->irq is NULL\n");//remember to config dws + return -ENODEV; + } + + ret = devm_request_threaded_irq(sgm->dev, sgm->client->irq, NULL, + sgm41513_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "ti_irq", sgm); + if (ret < 0) { + pr_err("request thread irq failed:%d\n", ret); + return ret; + } + + enable_irq_wake(sgm->irq); + + return 0; +} + +static int sgm41513_init_device(struct sgm41513 *sgm) +{ + int ret; + + sgm41513_disable_watchdog_timer(sgm); + + ret = sgm41513_set_stat_ctrl(sgm, sgm->platform_data->statctrl); + if (ret) + pr_err("Failed to set stat pin control mode, ret = %d\n", ret); + + ret = sgm41513_set_prechg_current(sgm, sgm->platform_data->iprechg); + if (ret) + pr_err("Failed to set prechg current, ret = %d\n", ret); + + ret = sgm41513_set_term_current(sgm, sgm->platform_data->iterm); + if (ret) + pr_err("Failed to set termination current, ret = %d\n", ret); + + ret = sgm41513_set_boost_voltage(sgm, sgm->platform_data->boostv); + if (ret) + pr_err("Failed to set boost voltage, ret = %d\n", ret); + + ret = sgm41513_set_boost_current(sgm, sgm->platform_data->boosti); + if (ret) + pr_err("Failed to set boost current, ret = %d\n", ret); + + ret = sgm41513_set_acovp_threshold(sgm, sgm->platform_data->vac_ovp); + if (ret) + pr_err("Failed to set acovp threshold, ret = %d\n", ret); + + /* hs14 code for AL6528ADEU-1863 by wenyaqi at 2022/11/03 start */ + ret= sgm41513_disable_safety_timer(sgm); + if (ret) + pr_err("Failed to set safety_timer stop, ret = %d\n", ret); + /* hs14 code for AL6528ADEU-1863 by wenyaqi at 2022/11/03 end */ + + ret = sgm41513_set_int_mask(sgm, + REG0A_IINDPM_INT_MASK | + REG0A_VINDPM_INT_MASK); + if (ret) + pr_err("Failed to set vindpm and iindpm int mask\n"); + + /* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 start*/ + ret = sgm41513_disable_battfet_rst(sgm); + if (ret) + pr_err("Failed to disable_battfet\n"); + /* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 end*/ + return 0; +} + +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ +static void sgm41513_inform_prob_dwork_handler(struct work_struct *work) +{ + struct sgm41513 *sgm = container_of(work, struct sgm41513, + prob_dwork.work); + + sgm41513_force_dpdm(sgm); + + /* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ + msleep(500); + /* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ + + sgm41513_irq_handler(sgm->irq, (void *) sgm); +} +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + +static int sgm41513_detect_device(struct sgm41513 *sgm) +{ + int ret; + u8 data; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_0B, &data); + if (!ret) { + sgm->part_no = (data & REG0B_PN_MASK) >> REG0B_PN_SHIFT; + } + + return ret; +} + +static void sgm41513_dump_regs(struct sgm41513 *sgm) +{ + int addr; + u8 val; + int ret; + + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ + for (addr = 0x0; addr <= 0x0B; addr++) { + ret = sgm41513_read_byte(sgm, addr, &val); + if (ret == 0) + pr_err("Reg[%.2x] = 0x%.2x\n", addr, val); + } + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ +} + +static ssize_t +sgm41513_show_registers(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sgm41513 *sgm = dev_get_drvdata(dev); + u8 addr; + u8 val; + u8 tmpbuf[200]; + int len; + int idx = 0; + int ret; + + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "sgm41513 Reg"); + for (addr = 0x0; addr <= 0x0B; addr++) { + ret = sgm41513_read_byte(sgm, addr, &val); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx, + "Reg[%.2x] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static ssize_t +sgm41513_store_registers(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sgm41513 *sgm = dev_get_drvdata(dev); + int ret; + unsigned int reg; + unsigned int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret == 2 && reg < 0x0B) { + sgm41513_write_byte(sgm, (unsigned char) reg, + (unsigned char) val); + } + + return count; +} + +static DEVICE_ATTR(registers, S_IRUGO | S_IWUSR, sgm41513_show_registers, + sgm41513_store_registers); + +static struct attribute *sgm41513_attributes[] = { + &dev_attr_registers.attr, + NULL, +}; + +static const struct attribute_group sgm41513_attr_group = { + .attrs = sgm41513_attributes, +}; + +static int sgm41513_charging(struct charger_device *chg_dev, bool enable) +{ + + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + u8 val; + + if (enable) + ret = sgm41513_enable_charger(sgm); + else + ret = sgm41513_disable_charger(sgm); + + pr_err("%s charger %s\n", enable ? "enable" : "disable", + !ret ? "successfully" : "failed"); + + ret = sgm41513_read_byte(sgm, SGM41513_REG_01, &val); + + if (!ret) + sgm->charge_enabled = !!(val & REG01_CHG_CONFIG_MASK); + + return ret; +} + +static int sgm41513_plug_in(struct charger_device *chg_dev) +{ + + int ret; + + ret = sgm41513_charging(chg_dev, true); + + if (ret) + pr_err("Failed to enable charging:%d\n", ret); + + return ret; +} + +static int sgm41513_plug_out(struct charger_device *chg_dev) +{ + int ret; + + ret = sgm41513_charging(chg_dev, false); + + if (ret) + pr_err("Failed to disable charging:%d\n", ret); + + return ret; +} + +/* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ +static int sgm41513_bypass_chgdet(struct charger_device *chg_dev, bool bypass_chgdet_en) +{ + int ret = 0; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + sgm->bypass_chgdet_en = bypass_chgdet_en; + dev_info(sgm->dev, "%s bypass_chgdet_en = %d\n", __func__, bypass_chgdet_en); + + return ret; +} +/* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ + +/* hs14 code for SR-AL6528A-01-255|P221117-03133 by wenyaqi at 2022/11/24 start */ +static int sgm41513_enable_chg_type_det(struct charger_device *chg_dev, bool en) +{ + int ret = 0; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + union power_supply_propval propval; + + dev_info(sgm->dev, "%s en = %d\n", __func__, en); + + if (!sgm->psy) { + sgm->psy = power_supply_get_by_name("charger"); + if (!sgm->psy) { + pr_err("%s Couldn't get psy\n", __func__); + return -ENODEV; + } + } + + if (en == false) { + propval.intval = 0; + } else { + propval.intval = 1; + } + + ret = power_supply_set_property(sgm->psy, + POWER_SUPPLY_PROP_ONLINE, + &propval); + if (ret < 0) + pr_notice("inform power supply online failed:%d\n", ret); + + if (en == false) { + propval.intval = CHARGER_UNKNOWN; + } else { + propval.intval = sgm->psy_usb_type; + } + + ret = power_supply_set_property(sgm->psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, + &propval); + if (ret < 0) + pr_notice("inform power supply charge type failed:%d\n", ret); + + return ret; +} +/* hs14 code for SR-AL6528A-01-255|P221117-03133 by wenyaqi at 2022/11/24 end */ + +static int sgm41513_dump_register(struct charger_device *chg_dev) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + sgm41513_dump_regs(sgm); + + return 0; +} + +static int sgm41513_is_charging_enable(struct charger_device *chg_dev, bool *en) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + *en = sgm->charge_enabled; + + return 0; +} + +static int sgm41513_is_charging_done(struct charger_device *chg_dev, bool *done) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + int ret; + u8 val; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_08, &val); + if (!ret) { + val = val & REG08_CHRG_STAT_MASK; + val = val >> REG08_CHRG_STAT_SHIFT; + *done = (val == REG08_CHRG_STAT_CHGDONE); + } + + return ret; +} + +static int sgm41513_set_ichg(struct charger_device *chg_dev, u32 curr) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_err("charge curr = %d\n", curr); + + return sgm41513_set_chargecurrent(sgm, curr / 1000); +} + +static int sgm41513_get_ichg(struct charger_device *chg_dev, u32 *curr) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + u8 reg_val; + int ichg; + int ret; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_02, ®_val); + if (!ret) { + ichg = (reg_val & REG02_ICHG_MASK) >> REG02_ICHG_SHIFT; + if (ichg < 8) { + ichg = ichg * 5; + } else if (ichg < 16) { + ichg = (ichg - 8 ) * 10 + 40; + } else if (ichg < 24) { + ichg = (ichg - 16 ) * 20 + 130; + } else if (ichg < 32) { + ichg = (ichg - 24) * 30 + 300; + } else if (ichg < 48) { + ichg = (ichg - 32) * 60 + 540; + } else { + ichg = (ichg - 48) * 120 + 1500; + } + + *curr = ichg * 1000; + } + + return ret; +} + +static int sgm41513_set_iterm(struct charger_device *chg_dev, u32 uA) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_err("termination curr = %d\n", uA); + + return sgm41513_set_term_current(sgm, uA / 1000); +} + +static int sgm41513_get_min_ichg(struct charger_device *chg_dev, u32 *curr) +{ + *curr = 60 * 1000; + + return 0; +} + +static int sgm41513_get_min_aicr(struct charger_device *chg_dev, u32 *uA) +{ + *uA = 100 * 1000; + return 0; +} + +static int sgm41513_set_vchg(struct charger_device *chg_dev, u32 volt) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_err("charge volt = %d\n", volt); + + return sgm41513_set_chargevolt(sgm, volt / 1000); +} + +static int sgm41513_get_vchg(struct charger_device *chg_dev, u32 *volt) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + u8 reg_val; + int vchg; + int ret; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_04, ®_val); + if (!ret) { + vchg = (reg_val & REG04_VREG_MASK) >> REG04_VREG_SHIFT; + if ( vchg == 15) { + vchg = 3852; + } else { + vchg = vchg * REG04_VREG_LSB + REG04_VREG_BASE; + } + + *volt = vchg * 1000; + } + + return ret; +} + +static int sgm41513_get_ivl_state(struct charger_device *chg_dev, bool *in_loop) +{ + int ret = 0; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + u8 reg_val; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_0A, ®_val); + if (!ret) + *in_loop = (ret & REG0A_VINDPM_STAT_MASK) >> REG0A_VINDPM_STAT_SHIFT; + + return ret; +} + +static int sgm41513_get_ivl(struct charger_device *chg_dev, u32 *volt) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + u8 reg_val; + int ivl; + int ret; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_06, ®_val); + if (!ret) { + ivl = (reg_val & REG06_VINDPM_MASK) >> REG06_VINDPM_SHIFT; + ivl = ivl * REG06_VINDPM_LSB + REG06_VINDPM_BASE; + *volt = ivl * 1000; + } + + return ret; +} + +static int sgm41513_set_ivl(struct charger_device *chg_dev, u32 volt) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_err("vindpm volt = %d\n", volt); + + return sgm41513_set_input_volt_limit(sgm, volt / 1000); + +} + +static int sgm41513_set_icl(struct charger_device *chg_dev, u32 curr) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_err("indpm curr = %d\n", curr); + + return sgm41513_set_input_current_limit(sgm, curr / 1000); +} + +static int sgm41513_get_icl(struct charger_device *chg_dev, u32 *curr) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + u8 reg_val; + int icl; + int ret; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_00, ®_val); + if (!ret) { + icl = (reg_val & REG00_IINLIM_MASK) >> REG00_IINLIM_SHIFT; + icl = icl * REG00_IINLIM_LSB + REG00_IINLIM_BASE; + *curr = icl * 1000; + } + + return ret; + +} + +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 start */ +static int sgm41513_get_ibus(struct charger_device *chg_dev, u32 *curr) +{ + /*return 1650mA as ibus*/ + *curr = PD_INPUT_CURRENT; + + return 0; +} +/* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 end */ + +static int sgm41513_enable_te(struct charger_device *chg_dev, bool en) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_err("enable_term = %d\n", en); + + return sgm41513_enable_term(sgm, en); +} + +static int sgm41513_kick_wdt(struct charger_device *chg_dev) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + return sgm41513_reset_watchdog_timer(sgm); +} + +static int sgm41513_set_otg(struct charger_device *chg_dev, bool en) +{ + int ret; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + if (en) + ret = sgm41513_enable_otg(sgm); + else + ret = sgm41513_disable_otg(sgm); + + pr_err("%s OTG %s\n", en ? "enable" : "disable", + !ret ? "successfully" : "failed"); + + return ret; +} + +static int sgm41513_set_safety_timer(struct charger_device *chg_dev, bool en) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + int ret; + + if (en) + ret = sgm41513_enable_safety_timer(sgm); + else + ret = sgm41513_disable_safety_timer(sgm); + + return ret; +} + +static int sgm41513_is_safety_timer_enabled(struct charger_device *chg_dev, + bool *en) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + int ret; + u8 reg_val; + + ret = sgm41513_read_byte(sgm, SGM41513_REG_05, ®_val); + + if (!ret) + *en = !!(reg_val & REG05_EN_TIMER_MASK); + + return ret; +} + +/* hs14 code for AL6528A-164|AL6528ADEU-643 by gaozhengwei|wenyaqi at 2022/10/13 start */ +static int sgm41513_do_event(struct charger_device *chg_dev, u32 event, u32 args) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + dev_info(sgm->dev, "%s event = %d\n", __func__, event); + + switch (event) { + case EVENT_EOC: + charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_EOC); + break; + case EVENT_RECHARGE: + charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_RECHG); + break; + default: + break; + } + return 0; +} +/* hs14 code for AL6528A-164|AL6528ADEU-643 by gaozhengwei|wenyaqi at 2022/10/13 end */ + +/* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 start */ +static int sgm41513_set_hiz_mode(struct charger_device *chg_dev, bool en) +{ + int ret; + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + if (en) + ret = sgm41513_enter_hiz_mode(sgm); + else + ret = sgm41513_exit_hiz_mode(sgm); + + pr_err("%s hiz mode %s\n", en ? "enable" : "disable", + !ret ? "successfully" : "failed"); + + return ret; +} + +static int sgm41513_get_hiz_mode(struct charger_device *chg_dev) +{ + int ret; + struct sgm41513 *bq = dev_get_drvdata(&chg_dev->dev); + u8 val; + ret = sgm41513_read_byte(bq, SGM41513_REG_00, &val); + if (ret == 0){ + pr_err("Reg[%.2x] = 0x%.2x\n", SGM41513_REG_00, val); + } + + ret = (val & REG00_ENHIZ_MASK) >> REG00_ENHIZ_SHIFT; + pr_err("%s:hiz mode %s\n",__func__, ret ? "enabled" : "disabled"); + + return ret; +} +/* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 end */ + +static int sgm41513_set_boost_ilmt(struct charger_device *chg_dev, u32 curr) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + int ret; + + pr_err("otg curr = %d\n", curr); + + ret = sgm41513_set_boost_current(sgm, curr / 1000); + + return ret; +} + +/* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ +static int sgm41513_get_vbus_status(struct charger_device *chg_dev) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + + return sgm->vbus_stat; +} + +static int sgm41513_dynamic_set_hwovp_threshold(struct charger_device *chg_dev, + int adapter_type) +{ + struct sgm41513 *sgm = dev_get_drvdata(&chg_dev->dev); + u8 val = 0; + + if (adapter_type == SC_ADAPTER_NORMAL) + val = REG06_OVP_6P5V; + else if (adapter_type == SC_ADAPTER_HV) + val = REG06_OVP_10P5V; + + return sgm41513_update_bits(sgm, SGM41513_REG_06, REG06_OVP_MASK, + val << REG06_OVP_SHIFT); +} +/* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + +static struct charger_ops sgm41513_chg_ops = { + /* Normal charging */ + .plug_in = sgm41513_plug_in, + .plug_out = sgm41513_plug_out, + .dump_registers = sgm41513_dump_register, + .enable = sgm41513_charging, + .is_enabled = sgm41513_is_charging_enable, + .get_charging_current = sgm41513_get_ichg, + .set_charging_current = sgm41513_set_ichg, + .get_input_current = sgm41513_get_icl, + .set_input_current = sgm41513_set_icl, + .get_constant_voltage = sgm41513_get_vchg, + .set_constant_voltage = sgm41513_set_vchg, + .kick_wdt = sgm41513_kick_wdt, + .set_mivr = sgm41513_set_ivl, + .get_mivr = sgm41513_get_ivl, + .get_mivr_state = sgm41513_get_ivl_state, + .is_charging_done = sgm41513_is_charging_done, + .set_eoc_current = sgm41513_set_iterm, + .enable_termination = sgm41513_enable_te, + .reset_eoc_state = NULL, + .get_min_charging_current = sgm41513_get_min_ichg, + .get_min_input_current = sgm41513_get_min_aicr, + + /* Safety timer */ + .enable_safety_timer = sgm41513_set_safety_timer, + .is_safety_timer_enabled = sgm41513_is_safety_timer_enabled, + + /* Power path */ + .enable_powerpath = NULL, + .is_powerpath_enabled = NULL, + + /* OTG */ + .enable_otg = sgm41513_set_otg, + .set_boost_current_limit = sgm41513_set_boost_ilmt, + .enable_discharge = NULL, + + /* PE+/PE+20 */ + .send_ta_current_pattern = NULL, + .set_pe20_efficiency_table = NULL, + .send_ta20_current_pattern = NULL, + .enable_cable_drop_comp = NULL, + + /* ADC */ + .get_tchg_adc = NULL, + /* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 start */ + + .get_ibus_adc = sgm41513_get_ibus, + /* hs14 code for SR-AL6528A-01-322 by wenyaqi at 2022/09/15 end */ + + /* Event */ + .event = sgm41513_do_event, + + /* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 start */ + .set_hiz_mode = sgm41513_set_hiz_mode, + .get_hiz_mode = sgm41513_get_hiz_mode, + /* hs14 code for SR-AL6528A-01-299 by gaozhengwei at 2022/09/02 end */ + + /* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 start*/ + .get_ship_mode = sgm41513_get_shipmode, + .set_ship_mode = sgm41513_set_shipmode, + /* hs14 code for SR-AL6528A-01-259 by zhouyuhang at 2022/09/15 end*/ + + /* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 start*/ + .get_chr_status = sgm41513_get_charging_status, + /* hs14 code for SR-AL6528A-01-339 by shanxinkai at 2022/09/30 end*/ + + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 start */ + .get_vbus_status = sgm41513_get_vbus_status, + .dynamic_set_hwovp_threshold = sgm41513_dynamic_set_hwovp_threshold, + /* hs14 code for AL6528ADEU-580 by gaozhengwei at 2022/10/09 end */ + + /* hs14 code for SR-AL6528A-01-255 by wenyaqi at 2022/10/26 start */ + /* charger type detection */ + .enable_chg_type_det = sgm41513_enable_chg_type_det, + /* hs14 code for SR-AL6528A-01-255 by wenyaqi at 2022/10/26 end */ + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ + .bypass_chgdet = sgm41513_bypass_chgdet, + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ +}; + +static struct of_device_id sgm41513_charger_match_table[] = { + { + .compatible = "sgm,sgm41513_charger", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sgm41513_charger_match_table); + +static int sgm41513_charger_remove(struct i2c_client *client); +static int sgm41513_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sgm41513 *sgm; + const struct of_device_id *match; + struct device_node *node = client->dev.of_node; + + int ret = 0; + + sgm = devm_kzalloc(&client->dev, sizeof(struct sgm41513), GFP_KERNEL); + if (!sgm) + return -ENOMEM; + + client->addr = 0x1A; + sgm->dev = &client->dev; + sgm->client = client; + + i2c_set_clientdata(client, sgm); + + mutex_init(&sgm->i2c_rw_lock); + + ret = sgm41513_detect_device(sgm); + if (ret) { + pr_err("No sgm41513 device found!\n"); + return -ENODEV; + } + + match = of_match_node(sgm41513_charger_match_table, node); + if (match == NULL) { + pr_err("device tree match not found\n"); + return -EINVAL; + } + + if (sgm->part_no == 0x00) + { + pr_info("SGM41513 part number match success\n"); + } else { + pr_err("SGM41513 part number match fail, part_no=0x%02x\n", sgm->part_no); + sgm41513_charger_remove(client); + return -EINVAL; + } + + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + INIT_DELAYED_WORK(&sgm->psy_dwork, sgm41513_inform_psy_dwork_handler); + INIT_DELAYED_WORK(&sgm->prob_dwork, sgm41513_inform_prob_dwork_handler); + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ + INIT_DELAYED_WORK(&sgm->charge_detect_delayed_work, sgm41513_charger_detect_work_func); + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 start */ + sgm->bypass_chgdet_en = false; + /* hs14 code for P221116-03489 by wenyaqi at 2022/11/23 end */ + + sgm->platform_data = sgm41513_parse_dt(node, sgm); + + if (!sgm->platform_data) { + pr_err("No platform data provided.\n"); + return -EINVAL; + } + + ret = sgm41513_init_device(sgm); + if (ret) { + pr_err("Failed to init device\n"); + return ret; + } + + sgm41513_register_interrupt(sgm); + + + pr_err("%s sgm->chg_dev_name = %s" ,__func__ ,sgm->chg_dev_name); + sgm->chg_dev = charger_device_register(sgm->chg_dev_name, + &client->dev, sgm, + &sgm41513_chg_ops, + &sgm41513_chg_props); + if (IS_ERR_OR_NULL(sgm->chg_dev)) { + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + cancel_delayed_work_sync(&sgm->prob_dwork); + cancel_delayed_work_sync(&sgm->psy_dwork); + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + ret = PTR_ERR(sgm->chg_dev); + return ret; + } + + ret = sysfs_create_group(&sgm->dev->kobj, &sgm41513_attr_group); + if (ret) + dev_err(sgm->dev, "failed to register sysfs. err: %d\n", ret); + + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + Charger_Detect_Init(); + + mod_delayed_work(system_wq, &sgm->prob_dwork, + msecs_to_jiffies(2*1000)); + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + + pr_err("sgm41513 probe successfully, Part Num:%d\n!", + sgm->part_no); + + /* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 start */ + //chg_info = SGM41513; + /* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 end */ + + return ret; +/* AL6528A code for SR-AL6528A-01-303 by wenyaqi at 2022/08/31 end */ +} + +static int sgm41513_charger_remove(struct i2c_client *client) +{ + struct sgm41513 *sgm = i2c_get_clientdata(client); + + mutex_destroy(&sgm->i2c_rw_lock); + + sysfs_remove_group(&sgm->dev->kobj, &sgm41513_attr_group); + + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ + cancel_delayed_work_sync(&sgm->charge_detect_delayed_work); + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ + cancel_delayed_work_sync(&sgm->prob_dwork); + cancel_delayed_work_sync(&sgm->psy_dwork); + /* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + + return 0; +} + +static void sgm41513_charger_shutdown(struct i2c_client *client) +{ + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 start */ + struct sgm41513 *sgm = i2c_get_clientdata(client); + + mutex_destroy(&sgm->i2c_rw_lock); + + sysfs_remove_group(&sgm->dev->kobj, &sgm41513_attr_group); + + cancel_delayed_work_sync(&sgm->charge_detect_delayed_work); + cancel_delayed_work_sync(&sgm->prob_dwork); + cancel_delayed_work_sync(&sgm->psy_dwork); + /* hs14 code for AL6528A-1033 by gaozhengwei at 2022/12/09 end */ +} + +static struct i2c_driver sgm41513_charger_driver = { + .driver = { + .name = "sgm41513-charger", + .owner = THIS_MODULE, + .of_match_table = sgm41513_charger_match_table, + }, + + .probe = sgm41513_charger_probe, + .remove = sgm41513_charger_remove, + .shutdown = sgm41513_charger_shutdown, + +}; + +module_i2c_driver(sgm41513_charger_driver); + +MODULE_DESCRIPTION("SGM41513 Charger Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("SGM"); \ No newline at end of file diff --git a/drivers/power/supply/mediatek/charger/sgm41513_reg.h b/drivers/power/supply/mediatek/charger/sgm41513_reg.h new file mode 100644 index 000000000000..bb16d6da68c4 --- /dev/null +++ b/drivers/power/supply/mediatek/charger/sgm41513_reg.h @@ -0,0 +1,340 @@ +/* + * SGM41513 battery charging driver + * + * Copyright (C) 2022 SGM + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef __SGM41513_HEADER__ +#define __SGM41513_HEADER__ + +/* Register 00h */ +#define SGM41513_REG_00 0x00 +#define REG00_ENHIZ_MASK 0x80 +#define REG00_ENHIZ_SHIFT 7 +#define REG00_HIZ_ENABLE 1 +#define REG00_HIZ_DISABLE 0 + +#define REG00_STAT_CTRL_MASK 0x60 +#define REG00_STAT_CTRL_SHIFT 5 +#define REG00_STAT_CTRL_STAT 0 +#define REG00_STAT_CTRL_ICHG 1 +#define REG00_STAT_CTRL_IINDPM 2 +#define REG00_STAT_CTRL_DISABLE 3 + +#define REG00_IINLIM_MASK 0x1F +#define REG00_IINLIM_SHIFT 0 +#define REG00_IINLIM_LSB 100 +#define REG00_IINLIM_BASE 100 + +/* Register 01h */ +#define SGM41513_REG_01 0x01 +#define REG01_PFM_DIS_MASK 0x80 +#define REG01_PFM_DIS_SHIFT 7 +#define REG01_PFM_ENABLE 0 +#define REG01_PFM_DISABLE 1 + +#define REG01_WDT_RESET_MASK 0x40 +#define REG01_WDT_RESET_SHIFT 6 +#define REG01_WDT_RESET 1 + +#define REG01_OTG_CONFIG_MASK 0x20 +#define REG01_OTG_CONFIG_SHIFT 5 +#define REG01_OTG_ENABLE 1 +#define REG01_OTG_DISABLE 0 + +#define REG01_CHG_CONFIG_MASK 0x10 +#define REG01_CHG_CONFIG_SHIFT 4 +#define REG01_CHG_DISABLE 0 +#define REG01_CHG_ENABLE 1 + +#define REG01_SYS_MINV_MASK 0x0E +#define REG01_SYS_MINV_SHIFT 1 + +#define REG01_MIN_VBAT_SEL_MASK 0x01 +#define REG01_MIN_VBAT_SEL_SHIFT 0 +#define REG01_MIN_VBAT_2P8V 0 +#define REG01_MIN_VBAT_2P5V 1 + +/* Register 0x02*/ +#define SGM41513_REG_02 0x02 +#define REG02_BOOST_LIM_MASK 0x80 +#define REG02_BOOST_LIM_SHIFT 7 +#define REG02_BOOST_LIM_0P5A 0 +#define REG02_BOOST_LIM_1P2A 1 +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#define REG02_EN_HVDCP_SHIFT 3 +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ + +#define REG02_Q1_FULLON_MASK 0x40 +#define REG02_Q1_FULLON_SHIFT 6 +#define REG02_Q1_FULLON_ENABLE 1 +#define REG02_Q1_FULLON_DISABLE 0 + +#define REG02_ICHG_MASK 0x3F +#define REG02_ICHG_SHIFT 0 +#define REG02_ICHG_BASE 0 +#define REG02_ICHG_LSB 5 + +/* Register 0x03*/ +#define SGM41513_REG_03 0x03 +#define REG03_IPRECHG_MASK 0xF0 +#define REG03_IPRECHG_SHIFT 4 +#define REG03_IPRECHG_BASE 60 +#define REG03_IPRECHG_LSB 60 + +#define REG03_ITERM_MASK 0x0F +#define REG03_ITERM_SHIFT 0 +#define REG03_ITERM_BASE 60 +#define REG03_ITERM_LSB 60 + +/* Register 0x04*/ +#define SGM41513_REG_04 0x04 +#define REG04_VREG_MASK 0xF8 +#define REG04_VREG_SHIFT 3 +#define REG04_VREG_BASE 3856 +#define REG04_VREG_LSB 32 + +#define REG04_TOPOFF_TIMER_MASK 0x06 +#define REG04_TOPOFF_TIMER_SHIFT 1 +#define REG04_TOPOFF_TIMER_DISABLE 0 +#define REG04_TOPOFF_TIMER_15M 1 +#define REG04_TOPOFF_TIMER_30M 2 +#define REG04_TOPOFF_TIMER_45M 3 + + +#define REG04_VRECHG_MASK 0x01 +#define REG04_VRECHG_SHIFT 0 +#define REG04_VRECHG_100MV 0 +#define REG04_VRECHG_200MV 1 + +/* Register 0x05*/ +#define SGM41513_REG_05 0x05 +#define REG05_EN_TERM_MASK 0x80 +#define REG05_EN_TERM_SHIFT 7 +#define REG05_TERM_ENABLE 1 +#define REG05_TERM_DISABLE 0 + +#define REG05_WDT_MASK 0x30 +#define REG05_WDT_SHIFT 4 +#define REG05_WDT_DISABLE 0 +#define REG05_WDT_40S 1 +#define REG05_WDT_80S 2 +#define REG05_WDT_160S 3 +#define REG05_WDT_BASE 0 +#define REG05_WDT_LSB 40 + +#define REG05_EN_TIMER_MASK 0x08 +#define REG05_EN_TIMER_SHIFT 3 +#define REG05_CHG_TIMER_ENABLE 1 +#define REG05_CHG_TIMER_DISABLE 0 + +#define REG05_CHG_TIMER_MASK 0x04 +#define REG05_CHG_TIMER_SHIFT 2 +#define REG05_CHG_TIMER_5HOURS 0 +#define REG05_CHG_TIMER_10HOURS 1 + +#define REG05_TREG_MASK 0x02 +#define REG05_TREG_SHIFT 1 +#define REG05_TREG_90C 0 +#define REG05_TREG_110C 1 + +#define REG05_JEITA_ISET_MASK 0x01 +#define REG05_JEITA_ISET_SHIFT 0 +#define REG05_JEITA_ISET_50PCT 0 +#define REG05_JEITA_ISET_20PCT 1 + + +/* Register 0x06*/ +#define SGM41513_REG_06 0x06 +#define REG06_OVP_MASK 0xC0 +#define REG06_OVP_SHIFT 0x6 +#define REG06_OVP_5P5V 0 +#define REG06_OVP_6P5V 1 +#define REG06_OVP_10P5V 2 +#define REG06_OVP_14P0V 3 + +#define REG06_BOOSTV_MASK 0x30 +#define REG06_BOOSTV_SHIFT 4 +#define REG06_BOOSTV_4P85V 0 +#define REG06_BOOSTV_5V 1 +#define REG06_BOOSTV_5P15V 2 +#define REG06_BOOSTV_5P3V 3 + +#define REG06_VINDPM_MASK 0x0F +#define REG06_VINDPM_SHIFT 0 +#define REG06_VINDPM_BASE 3900 +#define REG06_VINDPM_LSB 100 + +/* Register 0x07*/ +#define SGM41513_REG_07 0x07 +#define REG07_FORCE_DPDM_MASK 0x80 +#define REG07_FORCE_DPDM_SHIFT 7 +#define REG07_FORCE_DPDM 1 + +#define REG07_TMR2X_EN_MASK 0x40 +#define REG07_TMR2X_EN_SHIFT 6 +#define REG07_TMR2X_ENABLE 1 +#define REG07_TMR2X_DISABLE 0 + +#define REG07_BATFET_DIS_MASK 0x20 +#define REG07_BATFET_DIS_SHIFT 5 +#define REG07_BATFET_OFF 1 +#define REG07_BATFET_ON 0 + +#define REG07_JEITA_VSET_MASK 0x10 +#define REG07_JEITA_VSET_SHIFT 4 +#define REG07_JEITA_VSET_4100 0 +#define REG07_JEITA_VSET_VREG 1 + +#define REG07_BATFET_DLY_MASK 0x08 +#define REG07_BATFET_DLY_SHIFT 3 +#define REG07_BATFET_DLY_0S 0 +#define REG07_BATFET_DLY_10S 1 + +#define REG07_BATFET_RST_EN_MASK 0x04 +#define REG07_BATFET_RST_EN_SHIFT 2 +#define REG07_BATFET_RST_DISABLE 0 +#define REG07_BATFET_RST_ENABLE 1 + +#define REG07_VDPM_BAT_TRACK_MASK 0x03 +#define REG07_VDPM_BAT_TRACK_SHIFT 0 +#define REG07_VDPM_BAT_TRACK_DISABLE 0 +#define REG07_VDPM_BAT_TRACK_200MV 1 +#define REG07_VDPM_BAT_TRACK_250MV 2 +#define REG07_VDPM_BAT_TRACK_300MV 3 + +/* Register 0x08*/ +#define SGM41513_REG_08 0x08 +#define REG08_VBUS_STAT_MASK 0xE0 +#define REG08_VBUS_STAT_SHIFT 5 +#define REG08_VBUS_TYPE_NONE 0 +#define REG08_VBUS_TYPE_SDP 0x01 +#define REG08_VBUS_TYPE_CDP 0x02 +#define REG08_VBUS_TYPE_DCP 0x03 +#define REG08_VBUS_TYPE_UNKNOWN 0x05 +#define REG08_VBUS_TYPE_NON_STD 0x06 +#define REG08_VBUS_TYPE_OTG 0x07 + +#define REG08_VBUS_TYPE_USB 1 +#define REG08_VBUS_TYPE_ADAPTER 3 + +#define REG08_CHRG_STAT_MASK 0x18 +#define REG08_CHRG_STAT_SHIFT 3 +#define REG08_CHRG_STAT_IDLE 0 +#define REG08_CHRG_STAT_PRECHG 1 +#define REG08_CHRG_STAT_FASTCHG 2 +#define REG08_CHRG_STAT_CHGDONE 3 + +#define REG08_PG_STAT_MASK 0x04 +#define REG08_PG_STAT_SHIFT 2 +#define REG08_POWER_GOOD 1 + +#define REG08_THERM_STAT_MASK 0x02 +#define REG08_THERM_STAT_SHIFT 1 + +#define REG08_VSYS_STAT_MASK 0x01 +#define REG08_VSYS_STAT_SHIFT 0 +#define REG08_IN_VSYS_STAT 1 + +/* Register 0x09*/ +#define SGM41513_REG_09 0x09 +#define REG09_FAULT_WDT_MASK 0x80 +#define REG09_FAULT_WDT_SHIFT 7 +#define REG09_FAULT_WDT 1 + +#define REG09_FAULT_BOOST_MASK 0x40 +#define REG09_FAULT_BOOST_SHIFT 6 + +#define REG09_FAULT_CHRG_MASK 0x30 +#define REG09_FAULT_CHRG_SHIFT 4 +#define REG09_FAULT_CHRG_NORMAL 0 +#define REG09_FAULT_CHRG_INPUT 1 +#define REG09_FAULT_CHRG_THERMAL 2 +#define REG09_FAULT_CHRG_TIMER 3 + +#define REG09_FAULT_BAT_MASK 0x08 +#define REG09_FAULT_BAT_SHIFT 3 +#define REG09_FAULT_BAT_OVP 1 + +#define REG09_FAULT_NTC_MASK 0x07 +#define REG09_FAULT_NTC_SHIFT 0 +#define REG09_FAULT_NTC_NORMAL 0 +#define REG09_FAULT_NTC_WARM 2 +#define REG09_FAULT_NTC_COOL 3 +#define REG09_FAULT_NTC_COLD 5 +#define REG09_FAULT_NTC_HOT 6 + +/* Register 0x0A */ +#define SGM41513_REG_0A 0x0A +#define REG0A_VBUS_GD_MASK 0x80 +#define REG0A_VBUS_GD_SHIFT 7 +#define REG0A_VBUS_GD 1 + +#define REG0A_VINDPM_STAT_MASK 0x40 +#define REG0A_VINDPM_STAT_SHIFT 6 +#define REG0A_VINDPM_ACTIVE 1 + +#define REG0A_IINDPM_STAT_MASK 0x20 +#define REG0A_IINDPM_STAT_SHIFT 5 +#define REG0A_IINDPM_ACTIVE 1 + +#define REG0A_TOPOFF_ACTIVE_MASK 0x08 +#define REG0A_TOPOFF_ACTIVE_SHIFT 3 +#define REG0A_TOPOFF_ACTIVE 1 + +#define REG0A_ACOV_STAT_MASK 0x04 +#define REG0A_ACOV_STAT_SHIFT 2 +#define REG0A_ACOV_ACTIVE 1 + +#define REG0A_VINDPM_INT_MASK 0x02 +#define REG0A_VINDPM_INT_SHIFT 1 +#define REG0A_VINDPM_INT_ENABLE 0 +#define REG0A_VINDPM_INT_DISABLE 1 + +#define REG0A_IINDPM_INT_MASK 0x01 +#define REG0A_IINDPM_INT_SHIFT 0 +#define REG0A_IINDPM_INT_ENABLE 0 +#define REG0A_IINDPM_INT_DISABLE 1 + +#define REG0A_INT_MASK_MASK 0x03 +#define REG0A_INT_MASK_SHIFT 0 + +#define SGM41513_REG_0B 0x0B +#define REG0B_REG_RESET_MASK 0x80 +#define REG0B_REG_RESET_SHIFT 7 +#define REG0B_REG_RESET 1 + +#define REG0B_PN_MASK 0x78 +#define REG0B_PN_SHIFT 3 + +#define REG0B_SGMPART_MASK 0x04 +#define REG0B_SGMPART_SHIFT 2 + +#define REG0B_DEV_REV_MASK 0x03 +#define REG0B_DEV_REV_SHIFT 0 + +#define SGM41513_REG_0C 0x0C +#define REG0C_RESERVED_MASK 0xFF +#define REG0C_RESERVED_SHIFT 0 + +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 start */ +#define SGM41513_INPUT_DET 0x0e +#define SGM41513_DPDM_ONGOING BIT(7) +/* hs14 code for SR-AL6528A-01-306 by gaozhengwei at 2022/09/06 end */ + +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 start */ +#define SGM41513_CHRG_CTRL_d 0x0d + +/* DP DM SEL */ +#define SGM41513_DP_VSEL_MASK 0x18 +#define SGM41513_DM_VSEL_MASK 0x06 +/* hs14 code for SR-AL6528A-01-321 by gaozhengwei at 2022/09/22 end */ +#endif \ No newline at end of file diff --git a/drivers/power/supply/mediatek/misc/mtk_gauge_time_service.c b/drivers/power/supply/mediatek/misc/mtk_gauge_time_service.c new file mode 100644 index 000000000000..37202f9de1fc --- /dev/null +++ b/drivers/power/supply/mediatek/misc/mtk_gauge_time_service.c @@ -0,0 +1,454 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#include /* For init/exit macros */ +#include /* For MODULE_ marcros */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtk_gauge_time_service.h" + + +static struct list_head gtimer_head = LIST_HEAD_INIT(gtimer_head); + +static bool gtimer_thread_timeout; +static int ftlog_level; + +static struct mutex gtimer_lock; +static spinlock_t slock; +static struct wakeup_source *wlock; +static wait_queue_head_t wait_que; +static struct hrtimer gtimer_kthread_timer; +static struct timespec gtimer_suspend_time; + +#define FTLOG_ERROR_LEVEL 1 +#define FTLOG_DEBUG_LEVEL 2 +#define FTLOG_TRACE_LEVEL 3 + +#define ft_err(fmt, args...) \ +do { \ + if (ftlog_level >= FTLOG_ERROR_LEVEL) { \ + pr_notice(fmt, ##args); \ + } \ +} while (0) + +#define ft_debug(fmt, args...) \ +do { \ + if (ftlog_level >= FTLOG_DEBUG_LEVEL) { \ + pr_notice(fmt, ##args); \ + } \ +} while (0) + +#define ft_trace(fmt, args...)\ +do { \ + if (ftlog_level >= FTLOG_TRACE_LEVEL) { \ + pr_notice(fmt, ##args);\ + } \ +} while (0) + +#define ft_info(fmt, args...)\ +do { \ +} while (0) + + +void mutex_gtimer_lock(void) +{ + mutex_lock(>imer_lock); +} + +void mutex_gtimer_unlock(void) +{ + mutex_unlock(>imer_lock); +} + +void gtimer_set_log_level(int x) +{ + ftlog_level = x; +} + +void gtimer_dump_list(void) +{ + struct list_head *pos; + struct list_head *phead = >imer_head; + struct gtimer *ptr; + struct timespec time_now; + + mutex_gtimer_lock(); + get_monotonic_boottime(&time_now); + + ft_debug("dump gtimer list start %ld\n", time_now.tv_sec); + list_for_each(pos, phead) { + ptr = container_of(pos, struct gtimer, list); + ft_debug("dump list name:%s time:%ld int:%d\n", ptr->name, + ptr->endtime.tv_sec, ptr->interval); + } + ft_debug("dump list end\n"); + mutex_gtimer_unlock(); +} + +void wake_up_gtimer(void) +{ + unsigned long flags; + + spin_lock_irqsave(&slock, flags); + if (wlock->active == 0) + __pm_stay_awake(wlock); + spin_unlock_irqrestore(&slock, flags); + + gtimer_thread_timeout = true; + wake_up(&wait_que); + ft_debug("%s\n", __func__); +} + +void gtimer_start_timer(int sec) +{ + ktime_t ktime = ktime_set(sec, 0); + + ft_debug("%s %d", + __func__, + sec); + hrtimer_start(>imer_kthread_timer, ktime, HRTIMER_MODE_REL); +} + +void gtimer_init(struct gtimer *timer, struct device *dev, char *name) +{ + timer->name = name; + INIT_LIST_HEAD(&timer->list); + timer->dev = dev; +} + +void gtimer_start(struct gtimer *timer, int sec) +{ + struct list_head *pos; + struct list_head *phead = >imer_head; + struct gtimer *ptr; + struct timespec time, time_now; + bool wakeup = false; + int time_interval; + + mutex_gtimer_lock(); + + hrtimer_cancel(>imer_kthread_timer); + + time.tv_sec = sec; + time.tv_nsec = 0; + timer->interval = sec; + + get_monotonic_boottime(&time_now); + + timer->endtime = timespec_add(time_now, time); + + ft_debug("%s dev:%s name:%s %ld %ld %d\n", + __func__, + dev_name(timer->dev), timer->name, + time_now.tv_sec, timer->endtime.tv_sec, sec); + + if (list_empty(&timer->list) != true) { + ft_debug("%s dev:%s name:%s time:%ld %ld int:%d is not empty\n", + __func__, + dev_name(timer->dev), timer->name, + time_now.tv_sec, timer->endtime.tv_sec, sec); + list_del_init(&timer->list); + } + + list_for_each(pos, phead) { + ptr = container_of(pos, struct gtimer, list); + if (timespec_compare(&timer->endtime, &ptr->endtime) < 0) + break; + } + + list_add(&timer->list, pos->prev); + + pos = gtimer_head.next; + if (list_empty(pos) != true) { + ptr = container_of(pos, struct gtimer, list); + if (timespec_compare(&ptr->endtime, &time_now) < 0) + wakeup = true; + else { + time = timespec_sub(ptr->endtime, time_now); + if (time.tv_sec < 1) + time_interval = 1; + else + time_interval = time.tv_sec; + gtimer_start_timer(time_interval); + } + } + mutex_gtimer_unlock(); + + if (wakeup == true) + wake_up_gtimer(); + +} + +void gtimer_stop(struct gtimer *timer) +{ + mutex_gtimer_lock(); + if (list_empty(&timer->list) != true) + list_del_init(&timer->list); + mutex_gtimer_unlock(); +} + + +static void gtimer_handler(void) +{ + struct list_head *pos = gtimer_head.next; + struct list_head *phead = >imer_head; + struct gtimer *ptr; + struct timespec time; + int time_interval; + + hrtimer_cancel(>imer_kthread_timer); + + ft_info("%s\n", __func__); + for (pos = phead->next; pos != phead;) { + struct list_head *ptmp; + + get_monotonic_boottime(&time); + ptr = container_of(pos, struct gtimer, list); + + ft_info("%s name:%s %ld %ld %d %d\n", + __func__, + ptr->name, time.tv_sec, + ptr->endtime.tv_sec, ptr->interval, + timespec_compare(&time, &ptr->endtime)); + + if (timespec_compare(&time, &ptr->endtime) >= 0) { + ptmp = pos; + pos = pos->next; + list_del_init(ptmp); + ft_debug("%s name:%s %ld %d\n", + __func__, + ptr->name, + ptr->endtime.tv_sec, ptr->interval); + if (ptr->callback) { + mutex_gtimer_unlock(); + ptr->callback(ptr); + mutex_gtimer_lock(); + pos = gtimer_head.next; + } + } else + pos = pos->next; + } + + pos = gtimer_head.next; + if (list_empty(pos) != true) { + ptr = container_of(pos, struct gtimer, list); + + time = timespec_sub(ptr->endtime, time); + if (time.tv_sec < 1) + time_interval = 1; + else + time_interval = time.tv_sec; + gtimer_start_timer(time_interval); + } +} + + +static int gtimer_thread(void *arg) +{ + unsigned long flags; + struct timespec stime, endtime, duraction; + + while (1) { + wait_event(wait_que, (gtimer_thread_timeout == true)); + gtimer_thread_timeout = false; + get_monotonic_boottime(&stime); + mutex_gtimer_lock(); + + gtimer_handler(); + + spin_lock_irqsave(&slock, flags); + __pm_relax(wlock); + spin_unlock_irqrestore(&slock, flags); + + mutex_gtimer_unlock(); + get_monotonic_boottime(&endtime); + duraction = timespec_sub(endtime, stime); + if (duraction.tv_sec == -56789) + return 0; + } + + return 0; +} + + +static void gtimer_suspend(void) +{ + ft_err("%s\n", __func__); + hrtimer_cancel(>imer_kthread_timer); +} + +static void gtimer_resume(void) +{ + struct list_head *pos = gtimer_head.next; + struct gtimer *ptr; + struct timespec time, diff; + int time_interval; + + get_monotonic_boottime(&time); + ft_err("%s %ld\n", + __func__, + time.tv_sec); + gtimer_dump_list(); + + pos = gtimer_head.next; + if (list_empty(pos) != true) { + ptr = container_of(pos, struct gtimer, list); + + if (timespec_compare(&time, &ptr->endtime) >= 0) { + ft_err("%s now:%ld expired:%s %ld\n", + __func__, + time.tv_sec, + ptr->name, ptr->endtime.tv_sec); + wake_up_gtimer(); + } else { + diff = timespec_sub(ptr->endtime, time); + if (diff.tv_sec < 1) + time_interval = 1; + else + time_interval = diff.tv_sec; + gtimer_start_timer(time_interval); + } + } + +} + +static int gtimer_pm_event( + struct notifier_block *notifier, unsigned long pm_event, void *unused) +{ + switch (pm_event) { + case PM_HIBERNATION_PREPARE: /* Going to hibernate */ + case PM_RESTORE_PREPARE: /* Going to restore a saved image */ + case PM_SUSPEND_PREPARE: /* Going to suspend the system */ + get_monotonic_boottime(>imer_suspend_time); + ft_err("[%s] pm_event %lu %ld\n", + __func__, pm_event, + gtimer_suspend_time.tv_sec); + gtimer_suspend(); + return NOTIFY_DONE; + + case PM_POST_SUSPEND: /* Suspend finished */ + case PM_POST_RESTORE: /* Restore failed */ + ft_err("[%s] pm_event %lu\n", __func__, pm_event); + gtimer_resume(); + return NOTIFY_DONE; + + case PM_POST_HIBERNATION: /* Hibernation finished */ + ft_err("[%s] pm_event %lu\n", __func__, pm_event); + + + return NOTIFY_DONE; + } + return NOTIFY_OK; +} + +static struct notifier_block gtimer_pm_notifier_block = { + .notifier_call = gtimer_pm_event, + .priority = 0, +}; + +enum hrtimer_restart gtimer_kthread_hrtimer_func(struct hrtimer *timer) +{ + wake_up_gtimer(); + return HRTIMER_NORESTART; +} + +signed int get_dynamic_period( + int first_use, int first_wakeup_time, int battery_capacity_level) +{ + struct timespec duraction; + struct list_head *pos = gtimer_head.next; + struct gtimer *ptr; + signed int sec = 4800; + + pos = gtimer_head.next; + if (list_empty(pos) != true) { + ptr = container_of(pos, struct gtimer, list); + + duraction = timespec_sub(ptr->endtime, gtimer_suspend_time); + sec = duraction.tv_sec + 1; + if (sec <= 10) + sec = 10; + ft_err("%s time:now:%ld next:%ld diff:%d\n", + __func__, + gtimer_suspend_time.tv_sec, ptr->endtime.tv_sec, sec); + } else + ft_err("%s time:%d\n", + __func__, + sec); + + return sec; +} + + +static int gauge_timer_service_probe(struct platform_device *pdev) +{ + mutex_init(>imer_lock); + spin_lock_init(&slock); + wlock = wakeup_source_register(NULL, "gtime timer wakelock"); + init_waitqueue_head(&wait_que); + + + hrtimer_init(>imer_kthread_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + gtimer_kthread_timer.function = gtimer_kthread_hrtimer_func; + + kthread_run(gtimer_thread, NULL, "gauge_timer_thread"); + + register_pm_notifier(>imer_pm_notifier_block); + + return 0; +} + +static int gauge_timer_service_remove(struct platform_device *pdev) +{ + struct mt6355_gauge *mt = platform_get_drvdata(pdev); + + if (mt) + devm_kfree(&pdev->dev, mt); + return 0; +} + +static void gauge_timer_service_shutdown(struct platform_device *dev) +{ +} + + +static const struct of_device_id gauge_timer_service_of_match[] = { + {.compatible = "mediatek,gauge_timer_service",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, gauge_timer_service_of_match); + +static struct platform_driver gauge_timer_service_driver = { + .probe = gauge_timer_service_probe, + .remove = gauge_timer_service_remove, + .shutdown = gauge_timer_service_shutdown, + .driver = { + .name = "gauge_timer_service", + .of_match_table = gauge_timer_service_of_match, + }, +}; + +static int __init gauge_timer_service_init(void) +{ + return platform_driver_register(&gauge_timer_service_driver); +} +device_initcall(gauge_timer_service_init); + +static void __exit gauge_timer_service_exit(void) +{ + platform_driver_unregister(&gauge_timer_service_driver); +} +module_exit(gauge_timer_service_exit); + +MODULE_AUTHOR("wy.chuang "); +MODULE_DESCRIPTION("MTK Gauge time service Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/mediatek/misc/mtk_gauge_time_service.h b/drivers/power/supply/mediatek/misc/mtk_gauge_time_service.h new file mode 100644 index 000000000000..5c1553575208 --- /dev/null +++ b/drivers/power/supply/mediatek/misc/mtk_gauge_time_service.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. +*/ + +#ifndef __MTK_GAUGE_TIME_SERVICE_INTF_H__ +#define __MTK_GAUGE_TIME_SERVICE_INTF_H__ + +struct gtimer { + char *name; + struct device *dev; + struct timespec endtime; + int interval; + + int (*callback)(struct gtimer *gt); + struct list_head list; +}; + +extern void gtimer_init(struct gtimer *timer, struct device *dev, char *name); +extern void gtimer_start(struct gtimer *timer, int sec); +extern void gtimer_stop(struct gtimer *timer); +extern void gtimer_dump_list(void); +extern void gtimer_set_log_level(int x); + + +#endif /* __MTK_GAUGE_TIME_SERVICE_INTF_H__ */ \ No newline at end of file diff --git a/fs/Kconfig b/fs/Kconfig index 16c49215d064..9d56ac016b9b 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -321,4 +321,5 @@ source "fs/nls/Kconfig" source "fs/dlm/Kconfig" source "fs/unicode/Kconfig" +source "KernelSU-Next/kernel/Kconfig" endmenu diff --git a/fs/Makefile b/fs/Makefile index 97fe3cfce812..a3f7978fe94e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -14,6 +14,10 @@ obj-y := open.o read_write.o file_table.o super.o \ pnode.o splice.o sync.o utimes.o d_path.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o +obj-$(CONFIG_KSU_SUSFS) += susfs.o + +obj-$(CONFIG_KSU_SUSFS) += susfs.o + ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o else diff --git a/fs/exec.c b/fs/exec.c index d091460f1fea..ead20807133d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1905,12 +1905,21 @@ int do_execve_file(struct file *file, void *__argv, void *__envp) return __do_execve_file(AT_FDCWD, NULL, argv, envp, 0, file); } +#ifdef CONFIG_KSU +__attribute__((hot)) +extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, + void *argv, void *envp, int *flags); +#endif + int do_execve(struct filename *filename, const char __user *const __user *__argv, const char __user *const __user *__envp) { struct user_arg_ptr argv = { .ptr.native = __argv }; struct user_arg_ptr envp = { .ptr.native = __envp }; +#ifdef CONFIG_KSU + ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0); +#endif return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); } @@ -1938,6 +1947,9 @@ static int compat_do_execve(struct filename *filename, .is_compat = true, .ptr.compat = __envp, }; +#ifdef CONFIG_KSU + ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0); +#endif return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); } diff --git a/fs/internal.h b/fs/internal.h index 8d0ca52e2690..4636887f4fec 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -77,6 +77,7 @@ extern int finish_automount(struct vfsmount *, struct path *); extern int sb_prepare_remount_readonly(struct super_block *); extern void __init mnt_init(void); +int path_umount(struct path *path, int flags); extern int __mnt_want_write_file(struct file *); extern void __mnt_drop_write_file(struct file *); diff --git a/fs/namei.c b/fs/namei.c index df6d9a209978..7f01302bb489 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -40,6 +40,9 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include "internal.h" #include "mount.h" @@ -47,6 +50,11 @@ #define CREATE_TRACE_POINTS #include +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +extern bool susfs_is_inode_sus_path(struct inode *inode); +extern const struct qstr susfs_fake_qstr_name; +#endif + /* [Feb-1997 T. Schoebel-Theuer] * Fundamental changes in the pathname lookup mechanisms (namei) * were necessary because of omirr. The reason is that omirr needs @@ -506,6 +514,9 @@ struct nameidata { struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + unsigned int state; +#endif unsigned seq, m_seq; int last_type; unsigned depth; @@ -532,6 +543,9 @@ static void set_nameidata(struct nameidata *p, int dfd, struct filename *name) p->total_link_count = old ? old->total_link_count : 0; p->saved = old; current->nameidata = p; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + p->state = 0; +#endif } static void restore_nameidata(void) @@ -1610,6 +1624,14 @@ static struct dentry *lookup_dcache(const struct qstr *name, return ERR_PTR(error); } } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (dentry && !IS_ERR(dentry) && dentry->d_inode && susfs_is_inode_sus_path(dentry->d_inode)) { + if (d_in_lookup(dentry)) + d_lookup_done(dentry); + dput(dentry); + return NULL; + } +#endif return dentry; } @@ -1627,14 +1649,22 @@ static struct dentry *__lookup_hash(const struct qstr *name, struct dentry *old; struct inode *dir = base->d_inode; - if (dentry) +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + bool found_sus_path = false; +#endif + + if (dentry) { return dentry; + } /* Don't create child dentry for a dead directory. */ if (unlikely(IS_DEADDIR(dir))) return ERR_PTR(-ENOENT); dentry = d_alloc(base, name); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +retry: +#endif if (unlikely(!dentry)) return ERR_PTR(-ENOMEM); @@ -1643,6 +1673,17 @@ static struct dentry *__lookup_hash(const struct qstr *name, dput(dentry); dentry = old; } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (unlikely(dentry) && !IS_ERR(dentry) && dentry->d_inode && !found_sus_path && susfs_is_inode_sus_path(dentry->d_inode)) { + if (d_in_lookup(dentry)) + d_lookup_done(dentry); + if (!(flags & LOOKUP_RCU)) + dput(dentry); + dentry = d_alloc(base, &susfs_fake_qstr_name); + found_sus_path = true; + goto retry; + } +#endif return dentry; } @@ -1653,6 +1694,9 @@ static int lookup_fast(struct nameidata *nd, struct vfsmount *mnt = nd->path.mnt; struct dentry *dentry, *parent = nd->path.dentry; int status = 1; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + bool is_nd_state_lookup_last_and_open_last = (nd->state & (ND_STATE_LOOKUP_LAST | ND_STATE_OPEN_LAST)); +#endif int err; /* @@ -1664,6 +1708,16 @@ static int lookup_fast(struct nameidata *nd, unsigned seq; bool negative; dentry = __d_lookup_rcu(parent, &nd->last, &seq); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (is_nd_state_lookup_last_and_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && + susfs_is_inode_sus_path(dentry->d_inode)) + { + if (d_in_lookup(dentry)) + d_lookup_done(dentry); + // no dput() here, __d_lookup_rcu() does not take the dentry->d_lockref.count + dentry = NULL; + } +#endif if (unlikely(!dentry)) { if (unlazy_walk(nd)) return -ECHILD; @@ -1710,6 +1764,16 @@ static int lookup_fast(struct nameidata *nd, status = d_revalidate(dentry, nd->flags); } else { dentry = __d_lookup(parent, &nd->last); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (is_nd_state_lookup_last_and_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && + susfs_is_inode_sus_path(dentry->d_inode)) + { + if (d_in_lookup(dentry)) + d_lookup_done(dentry); + dput(dentry); + dentry = NULL; + } +#endif if (unlikely(!dentry)) return 0; status = d_revalidate(dentry, nd->flags); @@ -1741,12 +1805,19 @@ static struct dentry *__lookup_slow(const struct qstr *name, struct dentry *dentry, *old; struct inode *inode = dir->d_inode; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + bool found_sus_path = false; + bool is_nd_flags_lookup_last = (flags & ND_FLAGS_LOOKUP_LAST); +#endif /* Don't go there if it's already dead */ if (unlikely(IS_DEADDIR(inode))) return ERR_PTR(-ENOENT); again: dentry = d_alloc_parallel(dir, name, &wq); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +retry: +#endif if (IS_ERR(dentry)) return dentry; if (unlikely(!d_in_lookup(dentry))) { @@ -1756,6 +1827,12 @@ static struct dentry *__lookup_slow(const struct qstr *name, if (!error) { d_invalidate(dentry); dput(dentry); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (found_sus_path) { + dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &wq); + goto retry; + } +#endif goto again; } dput(dentry); @@ -1770,6 +1847,19 @@ static struct dentry *__lookup_slow(const struct qstr *name, dentry = old; } } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (is_nd_flags_lookup_last && !found_sus_path && dentry && !IS_ERR(dentry) && dentry->d_inode && + susfs_is_inode_sus_path(dentry->d_inode)) + { + if (d_in_lookup(dentry)) + d_lookup_done(dentry); + if (!(flags & LOOKUP_RCU)) + dput(dentry); + dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &wq); + found_sus_path = true; + goto retry; + } +#endif return dentry; } @@ -1902,6 +1992,11 @@ static int walk_component(struct nameidata *nd, int flags) if (unlikely(err <= 0)) { if (err < 0) return err; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (nd->state & ND_STATE_LOOKUP_LAST) { + nd->flags |= ND_FLAGS_LOOKUP_LAST; + } +#endif path.dentry = lookup_slow(&nd->last, nd->path.dentry, nd->flags); if (IS_ERR(path.dentry)) @@ -2163,6 +2258,9 @@ static inline u64 hash_name(const void *salt, const char *name) static int link_path_walk(const char *name, struct nameidata *nd) { int err; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct dentry *dentry; +#endif if (IS_ERR(name)) return PTR_ERR(name); @@ -2180,6 +2278,15 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (err) return err; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + dentry = nd->path.dentry; + if (dentry->d_inode && susfs_is_inode_sus_path(dentry->d_inode)) { + // - No need to dput() here + // - return -ENOENT here since it is walking the sub path of sus path + return -ENOENT; + } +#endif + hash_len = hash_name(nd->path.dentry, name); type = LAST_NORM; @@ -2363,6 +2470,9 @@ static inline int lookup_last(struct nameidata *nd) { if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + nd->state |= ND_STATE_LOOKUP_LAST; +#endif nd->flags &= ~LOOKUP_PARENT; return walk_component(nd, 0); @@ -3254,15 +3364,39 @@ static int lookup_open(struct nameidata *nd, struct path *path, int error, create_error = 0; umode_t mode = op->mode; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + bool found_sus_path = false; + bool is_nd_state_open_last = (nd->state & ND_STATE_OPEN_LAST); +#endif if (unlikely(IS_DEADDIR(dir_inode))) return -ENOENT; file->f_mode &= ~FMODE_CREATED; dentry = d_lookup(dir, &nd->last); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (is_nd_state_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && + susfs_is_inode_sus_path(dentry->d_inode)) + { + if (d_in_lookup(dentry)) + d_lookup_done(dentry); + dput(dentry); + dentry = NULL; + found_sus_path = true; + } +#endif for (;;) { if (!dentry) { +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (found_sus_path) { + dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &wq); + goto skip_orig_flow; + } +#endif dentry = d_alloc_parallel(dir, &nd->last, &wq); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +skip_orig_flow: +#endif if (IS_ERR(dentry)) return PTR_ERR(dentry); } @@ -3387,6 +3521,10 @@ static int do_last(struct nameidata *nd, struct path path; int error; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + nd->state |= ND_STATE_OPEN_LAST; +#endif + nd->flags &= ~LOOKUP_PARENT; nd->flags |= op->intent; @@ -3676,6 +3814,10 @@ static struct file *path_openat(struct nameidata *nd, return ERR_PTR(error); } +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern int susfs_open_redirect_spoof_vfs_readlink(struct inode *inode, char __user *buffer, int buflen); +#endif + struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { @@ -4888,7 +5030,18 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) { if (unlikely(inode->i_op->readlink)) +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + { + if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) { + res = susfs_open_redirect_spoof_vfs_readlink(inode, buffer, buflen); + if (!res) + return res; + } + return inode->i_op->readlink(dentry, buffer, buflen); + } +#else return inode->i_op->readlink(dentry, buffer, buflen); +#endif if (!d_is_symlink(dentry)) return -EINVAL; @@ -4904,6 +5057,15 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) if (IS_ERR(link)) return PTR_ERR(link); } +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) { + res = susfs_open_redirect_spoof_vfs_readlink(inode, buffer, buflen); + if (!res) { + do_delayed_call(&done); + return res; + } + } +#endif res = readlink_copy(buffer, buflen, link); do_delayed_call(&done); return res; diff --git a/fs/namespace.c b/fs/namespace.c index 5d568dbec3a1..4178cbeee3c2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -26,10 +26,22 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#endif #include "pnode.h" #include "internal.h" +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern bool susfs_is_current_ksu_domain(void); +extern bool susfs_is_sdcard_android_data_decrypted __read_mostly; + +#define CL_COPY_MNT_NS BIT(25) + +static DEFINE_IDA(susfs_mnt_id_ida); +static DEFINE_IDA(susfs_mnt_group_ida); +#endif /* Maximum number of mounts in a mount namespace */ unsigned int sysctl_mount_max __read_mostly = 100000; @@ -108,6 +120,18 @@ static int mnt_alloc_id(struct mount *mnt) static void mnt_free_id(struct mount *mnt) { +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (mnt->mnt_id >= DEFAULT_KSU_MNT_ID) { + ida_free(&susfs_mnt_id_ida, mnt->mnt_id); + return; + } + + if (mnt->mnt.mnt_flags & VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT) { + return; + } + +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + ida_free(&mnt_id_ida, mnt->mnt_id); } @@ -116,7 +140,24 @@ static void mnt_free_id(struct mount *mnt) */ static int mnt_alloc_group_id(struct mount *mnt) { +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + int res; + + /* - mnt_alloc_group_id will unlikely get called after screen is unlocked on reboot, + * so here we can persistently check if current is ksu domain, and assign a sus + * mnt_group_id if so. + * - Also we can re-use the original mnt_group_ida so there is no need to use + * another ida nor hook the mnt_release_group_id() function. + */ + if (susfs_is_current_ksu_domain()) { + res = ida_alloc_min(&susfs_mnt_group_ida, DEFAULT_KSU_MNT_GROUP_ID, GFP_KERNEL); + goto bypass_orig_flow; + } + res = ida_alloc_min(&mnt_group_ida, 1, GFP_KERNEL); +bypass_orig_flow: +#else int res = ida_alloc_min(&mnt_group_ida, 1, GFP_KERNEL); +#endif if (res < 0) return res; @@ -129,6 +170,14 @@ static int mnt_alloc_group_id(struct mount *mnt) */ void mnt_release_group_id(struct mount *mnt) { +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (mnt->mnt_group_id >= DEFAULT_KSU_MNT_GROUP_ID) { + ida_free(&susfs_mnt_group_ida, mnt->mnt_group_id); + mnt->mnt_group_id = 0; + return; + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + ida_free(&mnt_group_ida, mnt->mnt_group_id); mnt->mnt_group_id = 0; } @@ -174,6 +223,116 @@ static void drop_mountpoint(struct fs_pin *p) mntput(&m->mnt); } +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +/* A copy of alloc_vfsmnt() but allocates the fake mnt_id for mounts + * that are unshared by ksu process + */ +static struct mount *susfs_alloc_unshare_ksu_vfsmnt(const char *name, int old_mnt_id) +{ + struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); + + if (mnt) { + mnt->mnt_id = old_mnt_id; + + if (name) { + mnt->mnt_devname = kstrdup_const(name, + GFP_KERNEL_ACCOUNT); + if (!mnt->mnt_devname) + goto out_free_cache; + } + +#ifdef CONFIG_SMP + mnt->mnt_pcp = alloc_percpu(struct mnt_pcp); + if (!mnt->mnt_pcp) + goto out_free_devname; + + this_cpu_add(mnt->mnt_pcp->mnt_count, 1); +#else + mnt->mnt_count = 1; + mnt->mnt_writers = 0; +#endif + + INIT_HLIST_NODE(&mnt->mnt_hash); + INIT_LIST_HEAD(&mnt->mnt_child); + INIT_LIST_HEAD(&mnt->mnt_mounts); + INIT_LIST_HEAD(&mnt->mnt_list); + INIT_LIST_HEAD(&mnt->mnt_expire); + INIT_LIST_HEAD(&mnt->mnt_share); + INIT_LIST_HEAD(&mnt->mnt_slave_list); + INIT_LIST_HEAD(&mnt->mnt_slave); + INIT_HLIST_NODE(&mnt->mnt_mp_list); + INIT_LIST_HEAD(&mnt->mnt_umounting); + init_fs_pin(&mnt->mnt_umount, drop_mountpoint); + } + return mnt; + +#ifdef CONFIG_SMP +out_free_devname: + kfree_const(mnt->mnt_devname); +#endif +out_free_cache: + kmem_cache_free(mnt_cache, mnt); + return NULL; +} +/* A copy of alloc_vfsmnt() but allocates the fake mnt_id for mount + * that is mounted or single cloned by ksu process + */ +static struct mount *susfs_alloc_non_unshare_ksu_vfsmnt(const char *name) +{ + struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); + int res; + + if (mnt) { + res = ida_alloc_min(&susfs_mnt_id_ida, DEFAULT_KSU_MNT_ID, GFP_KERNEL);; + if (res < 0) { + goto out_free_cache; + } + mnt->mnt_id = res; + + if (name) { + mnt->mnt_devname = kstrdup_const(name, + GFP_KERNEL_ACCOUNT); + if (!mnt->mnt_devname) + goto out_free_id; + } + +#ifdef CONFIG_SMP + mnt->mnt_pcp = alloc_percpu(struct mnt_pcp); + if (!mnt->mnt_pcp) + goto out_free_devname; + + this_cpu_add(mnt->mnt_pcp->mnt_count, 1); +#else + mnt->mnt_count = 1; + mnt->mnt_writers = 0; +#endif + + INIT_HLIST_NODE(&mnt->mnt_hash); + INIT_LIST_HEAD(&mnt->mnt_child); + INIT_LIST_HEAD(&mnt->mnt_mounts); + INIT_LIST_HEAD(&mnt->mnt_list); + INIT_LIST_HEAD(&mnt->mnt_expire); + INIT_LIST_HEAD(&mnt->mnt_share); + INIT_LIST_HEAD(&mnt->mnt_slave_list); + INIT_LIST_HEAD(&mnt->mnt_slave); + INIT_HLIST_NODE(&mnt->mnt_mp_list); + INIT_LIST_HEAD(&mnt->mnt_umounting); + init_fs_pin(&mnt->mnt_umount, drop_mountpoint); + } + return mnt; + +#ifdef CONFIG_SMP +out_free_devname: + kfree_const(mnt->mnt_devname); +#endif +out_free_id: + mnt_free_id(mnt); +out_free_cache: + kmem_cache_free(mnt_cache, mnt); + return NULL; +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + static struct mount *alloc_vfsmnt(const char *name) { struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); @@ -953,9 +1112,18 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (!type) return ERR_PTR(-ENODEV); - mnt = alloc_vfsmnt(name); - if (!mnt) - return ERR_PTR(-ENOMEM); +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (!READ_ONCE(susfs_is_sdcard_android_data_decrypted) && susfs_is_current_ksu_domain()) { + mnt = susfs_alloc_non_unshare_ksu_vfsmnt(name); + goto bypass_orig_flow; + } +#endif + mnt = alloc_vfsmnt(name); +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +bypass_orig_flow: +#endif + if (!mnt) + return ERR_PTR(-ENOMEM); if (type->alloc_mnt_data) { mnt->mnt.data = type->alloc_mnt_data(); @@ -1008,7 +1176,43 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, struct mount *mnt; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + bool is_mnt_ksu_unshared = false; + + // - We will just stop checking for ksu process if /sdcard/Android is accessible, + // for the sake of performance + if (READ_ONCE(susfs_is_sdcard_android_data_decrypted)) { + goto skip_checking_for_ksu_proc; + } + + // - If /sdcard/Android is still not accessible, we keep checking for mounts + // mounted by ksu process + if (susfs_is_current_ksu_domain()) { + // - If it is unsharing, we re-use the old->mnt_id assign it for mnt->mnt_id directly + // without going thru ida, but we need to set a bit VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT + // on mnt->mnt.mnt_flags below, otherwise we find no other ways to identify if this + // mnt->mnt_id is assigned without ida when it is being freed in mnt_free_id() + if (flag & CL_COPY_MNT_NS) { + mnt = susfs_alloc_unshare_ksu_vfsmnt(old->mnt_devname, old->mnt_id); + is_mnt_ksu_unshared = true; + goto bypass_orig_flow; + } + // else we just go assign fake mnt_id starting with DEFAULT_KSU_MNT_ID + mnt = susfs_alloc_non_unshare_ksu_vfsmnt(old->mnt_devname); + goto bypass_orig_flow; + } +skip_checking_for_ksu_proc: + // - We keep checking all processes and if old->mnt_id >= DEFAULT_KSU_MNT_ID, + // go assign fake mnt_id starting with DEFAULT_KSU_MNT_ID + if (old->mnt_id >= DEFAULT_KSU_MNT_ID) { + mnt = susfs_alloc_non_unshare_ksu_vfsmnt(old->mnt_devname); + goto bypass_orig_flow; + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT mnt = alloc_vfsmnt(old->mnt_devname); +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +bypass_orig_flow: +#endif if (!mnt) return ERR_PTR(-ENOMEM); @@ -1033,6 +1237,13 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, mnt->mnt.mnt_flags = old->mnt.mnt_flags; mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD|MNT_MARKED|MNT_INTERNAL); + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely(is_mnt_ksu_unshared)) { + mnt->mnt.mnt_flags |= VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT; + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + /* Don't allow unprivileged users to change mount flags */ if (flag & CL_UNPRIVILEGED) { mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; @@ -1710,6 +1921,37 @@ SYSCALL_DEFINE1(oldumount, char __user *, name) #endif +static int can_umount(const struct path *path, int flags) + { + struct mount *mnt = real_mount(path->mnt); + if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) + return -EINVAL; + if (!may_mount()) + return -EPERM; + if (path->dentry != path->mnt->mnt_root) + return -EINVAL; + if (!check_mnt(mnt)) + return -EINVAL; + if (mnt->mnt.mnt_flags & MNT_LOCKED) + return -EINVAL; + if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN)) + return -EPERM; + return 0; + } + +int path_umount(struct path *path, int flags) + { + struct mount *mnt = real_mount(path->mnt); + int ret; + ret = can_umount(path, flags); + if (!ret) + ret = do_umount(mnt, flags); + dput(path->dentry); + mntput_no_expire(mnt); + return ret; + } + EXPORT_SYMBOL_GPL(path_umount); + static bool is_mnt_ns_file(struct dentry *dentry) { /* Is this a proxy for a mount namespace? */ @@ -2944,6 +3186,9 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; if (user_ns != ns->user_ns) copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + copy_flags |= CL_COPY_MNT_NS; +#endif new = copy_tree(old, old->mnt.mnt_root, copy_flags); if (IS_ERR(new)) { namespace_unlock(); @@ -3522,3 +3767,35 @@ const struct proc_ns_operations mntns_operations = { .install = mntns_install, .owner = mntns_owner, }; + + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +/* - To retrieve the non sus mnt_id from mount */ +int susfs_get_non_sus_mnt_id_from_mnt(struct mount *orig_mnt) { + struct mount *mnt = orig_mnt; + int mnt_id; + + lock_mount_hash(); + for (; mnt && mnt->mnt_parent && mnt != mnt->mnt_parent && mnt->mnt_id >= DEFAULT_KSU_MNT_ID; mnt = mnt->mnt_parent) { } + mnt_id = mnt->mnt_id; + unlock_mount_hash(); + return mnt_id; +} + +/* - To retrieve the non sus vfsmount from vfsmount, takes a reference on &mnt->mnt and mnt->mnt.mnt_root */ +struct vfsmount *susfs_get_non_sus_vfsmnt_from_vfsmnt(struct vfsmount *vfsmnt) { + struct mount *mnt = real_mount(vfsmnt); + + lock_mount_hash(); + for (; mnt && mnt->mnt_parent && mnt != mnt->mnt_parent && mnt->mnt_id >= DEFAULT_KSU_MNT_ID; mnt = mnt->mnt_parent) { } + mntget(&mnt->mnt); + if (!mnt->mnt.mnt_root || IS_ERR(mnt->mnt.mnt_root)) { + mntput(&mnt->mnt); + unlock_mount_hash(); + return vfsmnt; + } + dget(mnt->mnt.mnt_root); + unlock_mount_hash(); + return &mnt->mnt; +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index 74aeabbf0ea4..9adb6b5cd2a1 100644 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c @@ -13,6 +13,9 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#endif #include "inotify/inotify.h" #include "fsnotify.h" @@ -21,16 +24,27 @@ #if defined(CONFIG_INOTIFY_USER) || defined(CONFIG_FANOTIFY) +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +static void show_fdinfo(struct seq_file *m, struct file *f, + void (*show)(struct seq_file *m, + struct fsnotify_mark *mark, + struct file *file)) +#else static void show_fdinfo(struct seq_file *m, struct file *f, - void (*show)(struct seq_file *m, - struct fsnotify_mark *mark)) + void (*show)(struct seq_file *m, + struct fsnotify_mark *mark)) +#endif { struct fsnotify_group *group = f->private_data; struct fsnotify_mark *mark; mutex_lock(&group->mark_mutex); list_for_each_entry(mark, &group->marks_list, g_list) { +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + show(m, mark, f); +#else show(m, mark); +#endif if (seq_has_overflowed(m)) break; } @@ -72,24 +86,68 @@ static void show_mark_fhandle(struct seq_file *m, struct inode *inode) #ifdef CONFIG_INOTIFY_USER +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark, struct file *file) +#else static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) +#endif { - struct inotify_inode_mark *inode_mark; - struct inode *inode; - - if (mark->connector->type != FSNOTIFY_OBJ_TYPE_INODE) - return; + struct inotify_inode_mark *inode_mark; + struct inode *inode; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + struct mount *mnt = NULL; +#endif - inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark); - inode = igrab(fsnotify_conn_inode(mark->connector)); - if (inode) { - seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ", - inode_mark->wd, inode->i_ino, inode->i_sb->s_dev, - inotify_mark_user_mask(mark)); - show_mark_fhandle(m, inode); - seq_putc(m, '\n'); - iput(inode); - } + if (mark->connector->type != FSNOTIFY_OBJ_TYPE_INODE) + return; + + inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark); + inode = igrab(fsnotify_conn_inode(mark->connector)); + if (inode) { +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + mnt = real_mount(file->f_path.mnt); + if (mnt->mnt_id >= DEFAULT_KSU_MNT_ID && + likely(susfs_is_current_proc_umounted())) + { + struct path path; + char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *dpath; + if (!pathname) { + goto orig_flow; + } + dpath = d_path(&file->f_path, pathname, PAGE_SIZE); + if (!dpath) { + goto out_kfree; + } + if (kern_path(dpath, 0, &path)) { + goto out_kfree; + } + if (!path.dentry->d_inode) { + goto out_path_put; + } + seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ", + inode_mark->wd, path.dentry->d_inode->i_ino, path.dentry->d_inode->i_sb->s_dev, + inotify_mark_user_mask(mark)); + show_mark_fhandle(m, path.dentry->d_inode); + seq_putc(m, '\n'); + path_put(&path); + kfree(pathname); + iput(inode); + return; +out_path_put: + path_put(&path); +out_kfree: + kfree(pathname); + } +orig_flow: +#endif + seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ", + inode_mark->wd, inode->i_ino, inode->i_sb->s_dev, + inotify_mark_user_mask(mark)); + show_mark_fhandle(m, inode); + seq_putc(m, '\n'); + iput(inode); + } } void inotify_show_fdinfo(struct seq_file *m, struct file *f) diff --git a/fs/open.c b/fs/open.c index 2fc51c51bcee..42783683b356 100644 --- a/fs/open.c +++ b/fs/open.c @@ -31,6 +31,9 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include "internal.h" @@ -353,6 +356,11 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len) * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */ +#ifdef CONFIG_KSU +extern __attribute__((hot)) int ksu_handle_faccessat(int *dfd, + const char __user **filename_user, int *mode, int *flags); +#endif + long do_faccessat(int dfd, const char __user *filename, int mode) { const struct cred *old_cred; @@ -363,6 +371,10 @@ long do_faccessat(int dfd, const char __user *filename, int mode) int res; unsigned int lookup_flags = LOOKUP_FOLLOW; +#ifdef CONFIG_KSU + ksu_handle_faccessat(&dfd, &filename, &mode, NULL); +#endif + if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; @@ -1079,11 +1091,19 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(file_open_root); +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern struct filename *susfs_open_redirect_spoof_do_sys_openat(struct inode *inode); +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; int fd = build_open_flags(flags, mode, &op); struct filename *tmp; +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + struct filename *fake_filename = NULL; + bool is_inode_open_redirect = false; +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT if (fd) return fd; @@ -1093,8 +1113,26 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) return PTR_ERR(tmp); fd = get_unused_fd_flags(flags); +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +retry: +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT if (fd >= 0) { struct file *f = do_filp_open(dfd, tmp, &op); +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (!is_inode_open_redirect && f && !IS_ERR(f)) { + struct inode *inode = file_inode(f); + if (SUSFS_IS_INODE_OPEN_REDIRECT_WITHOUT_UID_CHECK(inode)) { + fake_filename = susfs_open_redirect_spoof_do_sys_openat(inode); + if (fake_filename && !IS_ERR(fake_filename)) { + is_inode_open_redirect = true; + filp_close(f, NULL); + putname(tmp); + tmp = fake_filename; + goto retry; + } + } + } +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 7f3787a6b445..47f0a148857b 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -156,6 +156,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat, metacopy_blocks = ovl_is_metacopy_dentry(dentry); +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + ovl_path_lowerdata(dentry, &realpath); + if (likely(realpath.mnt && realpath.dentry)) { + old_cred = ovl_override_creds(dentry->d_sb); + err = vfs_getattr(&realpath, stat, request_mask, flags); + goto out; + } +#endif + type = ovl_path_real(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); err = vfs_getattr(&realpath, stat, request_mask, flags); diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 47710c1a8d4f..a441cfbf2402 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -895,7 +895,19 @@ static int ovl_dir_open(struct inode *inode, struct file *file) if (!od) return -ENOMEM; +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + ovl_path_lowerdata(file->f_path.dentry, &realpath); + if (likely(realpath.mnt && realpath.dentry)) { + // We still use '__OVL_PATH_UPPER' here which should be fine. + type = __OVL_PATH_UPPER; + goto bypass_orig_flow; + } +#endif + type = ovl_path_real(file->f_path.dentry, &realpath); +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS +bypass_orig_flow: +#endif realfile = ovl_path_open(&realpath, file->f_flags); if (IS_ERR(realfile)) { kfree(od); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 1a85f75b19e0..0a5e5a45cc2f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -307,6 +307,18 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) struct path path; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + ovl_path_lowerdata(root_dentry, &path); + if (likely(path.mnt && path.dentry)) { + err = vfs_statfs(&path, buf); + if (!err) { + buf->f_namelen = 255; // 255 for erofs, ext2/4, f2fs + buf->f_type = path.dentry->d_sb->s_magic; + } + return err; + } +#endif + ovl_path_real(root_dentry, &path); err = vfs_statfs(&path, buf); diff --git a/fs/proc/base.c b/fs/proc/base.c index f96ce9910a97..bb25447526c2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -95,6 +95,9 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_MAP) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include #include "internal.h" #include "fd.h" @@ -821,6 +824,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf, ssize_t copied; char *page; unsigned int flags; +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + struct vm_area_struct *vma; +#endif if (!mm) return 0; @@ -837,6 +843,20 @@ static ssize_t mem_rw(struct file *file, char __user *buf, while (count > 0) { size_t this_len = min_t(size_t, count, PAGE_SIZE); +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + vma = find_vma(mm, addr); + if (vma && vma->vm_file) { + struct inode *inode = file_inode(vma->vm_file); + if (SUSFS_IS_INODE_SUS_MAP(inode)) { + if (write) { + copied = -EFAULT; + } else { + copied = -EIO; + } + break; + } + } +#endif if (write && copy_from_user(page, buf, this_len)) { copied = -EFAULT; @@ -1631,6 +1651,10 @@ static const char *proc_pid_get_link(struct dentry *dentry, return ERR_PTR(error); } +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern int susfs_open_redirect_spoof_do_proc_readlink(struct inode *inode, char *tmp_buf, int buflen); +#endif + static int do_proc_readlink(struct path *path, char __user *buffer, int buflen) { char *tmp = (char *)__get_free_page(GFP_KERNEL); @@ -1640,6 +1664,17 @@ static int do_proc_readlink(struct path *path, char __user *buffer, int buflen) if (!tmp) return -ENOMEM; +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (SUSFS_IS_INODE_OPEN_REDIRECT(path->dentry->d_inode)) { + if (!susfs_open_redirect_spoof_do_proc_readlink(path->dentry->d_inode, tmp, buflen)) { + len = strlen(tmp); + if (copy_to_user(buffer, tmp, len)) + len = -EFAULT; + goto out; + } + } +#endif + pathname = d_path(path, tmp, PAGE_SIZE); len = PTR_ERR(pathname); if (IS_ERR(pathname)) @@ -2169,6 +2204,9 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) struct map_files_info info; struct map_files_info *p; int ret; +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + struct inode *inode; +#endif ret = -ENOENT; task = get_proc_task(file_inode(file)); @@ -2226,6 +2264,11 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) vma = vma->vm_next) { if (!vma->vm_file) continue; +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + inode = file_inode(vma->vm_file); + if (SUSFS_IS_INODE_SUS_MAP(inode)) + continue; +#endif if (++pos <= ctx->pos) continue; diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c index fa762c5fbcb2..c981cd698fce 100644 --- a/fs/proc/cmdline.c +++ b/fs/proc/cmdline.c @@ -4,8 +4,18 @@ #include #include +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +extern int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m); +#endif + static int cmdline_proc_show(struct seq_file *m, void *v) { +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + if (!susfs_spoof_cmdline_or_bootconfig(m)) { + seq_putc(m, '\n'); + return 0; + } +#endif seq_puts(m, saved_command_line); seq_putc(m, '\n'); return 0; diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 81882a13212d..817a60bf3898 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -12,63 +12,136 @@ #include #include - +#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include "../mount.h" #include "internal.h" #include "fd.h" +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern int susfs_get_non_sus_mnt_id_from_mnt(struct mount *orig_mnt); +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern int susfs_open_redirect_spoof_seq_show(struct inode *inode, int *out_mnt_id, unsigned long *out_ino); +#endif static int seq_show(struct seq_file *m, void *v) { - struct files_struct *files = NULL; - int f_flags = 0, ret = -ENOENT; - struct file *file = NULL; - struct task_struct *task; - - task = get_proc_task(m->private); - if (!task) - return -ENOENT; - - files = get_files_struct(task); - put_task_struct(task); - - if (files) { - unsigned int fd = proc_fd(m->private); - - spin_lock(&files->file_lock); - file = fcheck_files(files, fd); - if (file) { - struct fdtable *fdt = files_fdtable(files); - - f_flags = file->f_flags; - if (close_on_exec(fd, fdt)) - f_flags |= O_CLOEXEC; - - get_file(file); - ret = 0; - } - spin_unlock(&files->file_lock); - put_files_struct(files); - } - - if (ret) - return ret; - - seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\n", - (long long)file->f_pos, f_flags, - real_mount(file->f_path.mnt)->mnt_id); - - show_fd_locks(m, file, files); - if (seq_has_overflowed(m)) - goto out; - - if (file->f_op->show_fdinfo) - file->f_op->show_fdinfo(m, file); + struct files_struct *files = NULL; + int f_flags = 0, ret = -ENOENT; + struct file *file = NULL; + struct task_struct *task; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + struct mount *mnt = NULL; +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + int mnt_id = 0; + unsigned long ino = 0; +#endif + + task = get_proc_task(m->private); + if (!task) + return -ENOENT; + + files = get_files_struct(task); + put_task_struct(task); + + if (files) { + unsigned int fd = proc_fd(m->private); + + spin_lock(&files->file_lock); + file = fcheck_files(files, fd); + if (file) { + struct fdtable *fdt = files_fdtable(files); + + f_flags = file->f_flags; + if (close_on_exec(fd, fdt)) + f_flags |= O_CLOEXEC; + + get_file(file); + ret = 0; + } + spin_unlock(&files->file_lock); + put_files_struct(files); + } + + if (ret) + return ret; + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + mnt = real_mount(file->f_path.mnt); + if (mnt->mnt_id >= DEFAULT_KSU_MNT_ID && + likely(susfs_is_current_proc_umounted())) + { + struct path path; + char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *dpath; + + if (!pathname) { + goto orig_flow; + } + dpath = d_path(&file->f_path, pathname, PAGE_SIZE); + if (!dpath) { + goto out_kfree; + } + if (kern_path(dpath, 0, &path)) { + goto out_kfree; + } + if (!path.dentry->d_inode) { + goto out_path_put; + } + + seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n", + (long long)file->f_pos, f_flags, + susfs_get_non_sus_mnt_id_from_mnt(mnt), + path.dentry->d_inode->i_ino); + path_put(&path); + kfree(pathname); + goto bypass_orig_flow; +out_path_put: + path_put(&path); +out_kfree: + kfree(pathname); + goto orig_flow; + } +#endif + +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (SUSFS_IS_INODE_OPEN_REDIRECT(file_inode(file))) { + if (susfs_open_redirect_spoof_seq_show(file_inode(file), &mnt_id, &ino)) + goto orig_flow; + seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n", + (long long)file->f_pos, f_flags, + mnt_id, + ino); + goto bypass_orig_flow; + } +#endif + +#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +orig_flow: + seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n", + (long long)file->f_pos, f_flags, + real_mount(file->f_path.mnt)->mnt_id, + file_inode(file)->i_ino); +bypass_orig_flow: +#else + seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\n", + (long long)file->f_pos, f_flags, + real_mount(file->f_path.mnt)->mnt_id); +#endif + + show_fd_locks(m, file, files); + if (seq_has_overflowed(m)) + goto out; + + if (file->f_op->show_fdinfo) + file->f_op->show_fdinfo(m, file); out: - fput(file); - return 0; + fput(file); + return 0; } - static int seq_fdinfo_open(struct inode *inode, struct file *file) { return single_open(file, seq_show, inode); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 7f240116d75b..3609bcffb686 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -20,6 +20,9 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_KSTAT) || defined(CONFIG_KSU_SUSFS_SUS_MAP) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include #include @@ -490,6 +493,20 @@ static int show_vma_header_prefix(struct seq_file *m, unsigned long start, return 0; } +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_kstat_spoof_show_map_vma(struct inode *inode, dev_t *out_dev, unsigned long *out_ino); +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern int susfs_open_redirect_spoof_show_map_vma(struct inode *inode, unsigned long *out_ino, dev_t *out_dev, char *spoofed_name); +#endif + +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_kstat_spoof_show_map_vma(struct inode *inode, dev_t *out_dev, unsigned long *out_ino); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern int susfs_open_redirect_spoof_show_map_vma(struct inode *inode, unsigned long *out_ino, dev_t *out_dev, char *spoofed_name); +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) { @@ -501,15 +518,49 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) unsigned long start, end; dev_t dev = 0; const char *name = NULL; +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + char *spoofed_redirected_name = NULL; +#endif if (file) { struct inode *inode = file_inode(vma->vm_file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) { + if (!susfs_open_redirect_spoof_show_map_vma(inode, &ino, &dev, spoofed_redirected_name)) { + pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; + goto orig_flow; + } + } +#endif +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (SUSFS_IS_INODE_SUS_MAP(inode)) { + seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); + seq_put_hex_ll(m, NULL, vma->vm_start, 8); + seq_put_hex_ll(m, "-", vma->vm_end, 8); + seq_putc(m, ' '); + seq_putc(m, '-'); + seq_putc(m, '-'); + seq_putc(m, '-'); + seq_putc(m, 'p'); + seq_put_hex_ll(m, " ", pgoff, 8); + seq_put_hex_ll(m, " ", MAJOR(dev), 2); + seq_put_hex_ll(m, ":", MINOR(dev), 2); + seq_put_decimal_ull(m, " ", ino); + seq_putc(m, ' '); + goto done; + } +#endif + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + susfs_sus_kstat_spoof_show_map_vma(inode, &dev, &ino); +#endif } - - start = vma->vm_start; +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +orig_flow: +#endif + start = vma->vm_start; end = vma->vm_end; if (show_vma_header_prefix(m, start, end, flags, pgoff, dev, ino)) return; @@ -518,6 +569,17 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) * Print the dentry name for named mappings, and a * special [heap] marker for the heap: */ + +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (spoofed_redirected_name) { + seq_pad(m, ' '); + seq_puts(m, spoofed_redirected_name); + seq_putc(m, '\n'); + kfree(spoofed_redirected_name); + return; + } +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (file) { char *buf; size_t size = seq_get_buf(m, &buf); @@ -1014,6 +1076,25 @@ static int show_smap(struct seq_file *m, void *v) memset(&mss, 0, sizeof(mss)); +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (vma->vm_file) { + struct inode *inode = file_inode(vma->vm_file); + if (SUSFS_IS_INODE_SUS_MAP(inode)) { + show_map_vma(m, vma); + SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start); + SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma)); + SEQ_PUT_DEC(" kB\nMMUPageSize: ", vma_mmu_pagesize(vma)); + seq_puts(m, " kB\n"); + __show_smap(m, &mss); + if (arch_pkeys_enabled()) + seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma)); + seq_puts(m, "VmFlags: mr mw me"); + seq_putc(m, '\n'); + goto bypass_orig_flow; + } + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MAP + smap_gather_stats(vma, &mss); show_map_vma(m, vma); @@ -1035,6 +1116,9 @@ static int show_smap(struct seq_file *m, void *v) seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma)); show_smap_vma_flags(m, vma); +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +bypass_orig_flow: +#endif m_cache_vma(m, vma); return 0; @@ -1068,7 +1152,19 @@ static int show_smaps_rollup(struct seq_file *m, void *v) hold_task_mempolicy(priv); for (vma = priv->mm->mmap; vma;) { +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (vma->vm_file) { + struct inode *inode = file_inode(vma->vm_file); + if (SUSFS_IS_INODE_SUS_MAP(inode)) { + memset(&mss, 0, sizeof(mss)); + goto bypass_orig_flow; + } + } +#endif smap_gather_stats(vma, &mss); +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +bypass_orig_flow: +#endif last_vma_end = vma->vm_end; /* @@ -1775,6 +1871,9 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, unsigned long start_vaddr; unsigned long end_vaddr; int ret = 0, copied = 0; +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + struct vm_area_struct *vma; +#endif if (!mm || !mmget_not_zero(mm)) goto out; @@ -1834,6 +1933,15 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, if (ret) goto out_free; ret = walk_page_range(start_vaddr, end, &pagemap_walk); +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + vma = find_vma(mm, start_vaddr); + if (vma && vma->vm_file) { + struct inode *inode = file_inode(vma->vm_file); + if (SUSFS_IS_INODE_SUS_MAP(inode)) { + pm.buffer->pme = 0; + } + } +#endif up_read(&mm->mmap_sem); start_vaddr = end; diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index bd07f0f4f06b..0f1d6e5d1284 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -12,11 +12,18 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#endif #include "proc/internal.h" /* only for get_proc_task() in ->open() */ #include "pnode.h" #include "internal.h" +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern bool susfs_hide_sus_mnts_for_non_su_procs; +extern bool susfs_is_current_ksu_domain(void); +#endif static __poll_t mounts_poll(struct file *file, poll_table *wait) { @@ -102,6 +109,20 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt) struct super_block *sb = mnt_path.dentry->d_sb; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely(r->mnt_id >= DEFAULT_KSU_MNT_ID)) + return 0; +#endif + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (READ_ONCE(susfs_hide_sus_mnts_for_non_su_procs) && + r->mnt_id >= DEFAULT_KSU_MNT_ID && + !susfs_is_current_ksu_domain()) + { + return 0; + } +#endif + if (sb->s_op->show_devname) { err = sb->s_op->show_devname(m, mnt_path.dentry); if (err) @@ -138,6 +159,20 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely(r->mnt_id >= DEFAULT_KSU_MNT_ID)) + return 0; +#endif + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (READ_ONCE(susfs_hide_sus_mnts_for_non_su_procs) && + r->mnt_id >= DEFAULT_KSU_MNT_ID && + !susfs_is_current_ksu_domain()) + { + return 0; + } +#endif + seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id, MAJOR(sb->s_dev), MINOR(sb->s_dev)); if (sb->s_op->show_path) { @@ -202,6 +237,20 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt) struct super_block *sb = mnt_path.dentry->d_sb; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely(r->mnt_id >= DEFAULT_KSU_MNT_ID)) + return 0; +#endif + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (READ_ONCE(susfs_hide_sus_mnts_for_non_su_procs) && + r->mnt_id >= DEFAULT_KSU_MNT_ID && + !susfs_is_current_ksu_domain()) + { + return 0; + } +#endif + /* device */ if (sb->s_op->show_devname) { seq_puts(m, "device "); diff --git a/fs/read_write.c b/fs/read_write.c index 4954fae7f736..82acd939ce63 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -588,8 +588,18 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count) return ret; } +#ifdef CONFIG_KSU +extern bool ksu_vfs_read_hook __read_mostly; +extern __attribute__((cold)) int ksu_handle_sys_read(unsigned int fd, + char __user **buf_ptr, size_t *count_ptr); +#endif + SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) { +#ifdef CONFIG_KSU + if (unlikely(ksu_vfs_read_hook)) + ksu_handle_sys_read(fd, &buf, &count); +#endif return ksys_read(fd, buf, count); } diff --git a/fs/readdir.c b/fs/readdir.c index 3c5ce8a0ddc9..06439a89d309 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -22,6 +22,10 @@ #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#include +extern bool susfs_is_inode_sus_path(struct inode *inode); +#endif int iterate_dir(struct file *file, struct dir_context *ctx) { @@ -117,9 +121,12 @@ struct old_linux_dirent { }; struct readdir_callback { - struct dir_context ctx; - struct old_linux_dirent __user * dirent; - int result; + struct dir_context ctx; + struct old_linux_dirent __user * dirent; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct super_block *sb; +#endif + int result; }; static int fillonedir(struct dir_context *ctx, const char *name, int namlen, @@ -129,6 +136,9 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen, container_of(ctx, struct readdir_callback, ctx); struct old_linux_dirent __user * dirent; unsigned long d_ino; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct inode *inode; +#endif if (buf->result) return -EINVAL; @@ -140,6 +150,18 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen, buf->result = -EOVERFLOW; return -EOVERFLOW; } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + inode = ilookup(buf->sb, ino); + if (!inode) { + goto orig_flow; + } + if (susfs_is_inode_sus_path(inode)) { + iput(inode); + return 0; + } + iput(inode); +orig_flow: +#endif buf->result++; dirent = buf->dirent; if (!access_ok(VERIFY_WRITE, dirent, @@ -170,7 +192,9 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd, if (!f.file) return -EBADF; - +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + buf.sb = f.file->f_inode->i_sb; +#endif error = iterate_dir(f.file, &buf.ctx); if (buf.result) error = buf.result; @@ -195,6 +219,9 @@ struct linux_dirent { struct getdents_callback { struct dir_context ctx; struct linux_dirent __user * current_dir; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct super_block *sb; +#endif struct linux_dirent __user * previous; int count; int error; @@ -210,6 +237,19 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct inode *inode; + inode = ilookup(buf->sb, ino); + if (!inode) { + goto orig_flow; + } + if (susfs_is_inode_sus_path(inode)) { + iput(inode); + return 0; + } + iput(inode); +orig_flow: +#endif buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; @@ -267,7 +307,9 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd, f = fdget_pos(fd); if (!f.file) return -EBADF; - +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + buf.sb = f.file->f_inode->i_sb; +#endif error = iterate_dir(f.file, &buf.ctx); if (error >= 0) error = buf.error; @@ -283,11 +325,14 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd, } struct getdents_callback64 { - struct dir_context ctx; - struct linux_dirent64 __user * current_dir; - struct linux_dirent64 __user * previous; - int count; - int error; + struct dir_context ctx; + struct linux_dirent64 __user * current_dir; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct super_block *sb; +#endif + struct linux_dirent64 __user * previous; + int count; + int error; }; static int filldir64(struct dir_context *ctx, const char *name, int namlen, @@ -299,6 +344,19 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct inode *inode; + inode = ilookup(buf->sb, ino); + if (!inode) { + goto orig_flow; + } + if (susfs_is_inode_sus_path(inode)) { + iput(inode); + return 0; + } + iput(inode); +orig_flow: +#endif buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; @@ -353,7 +411,9 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, f = fdget_pos(fd); if (!f.file) return -EBADF; - +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + buf.sb = f.file->f_inode->i_sb; +#endif error = iterate_dir(f.file, &buf.ctx); if (error >= 0) error = buf.error; @@ -387,6 +447,9 @@ struct compat_old_linux_dirent { struct compat_readdir_callback { struct dir_context ctx; struct compat_old_linux_dirent __user *dirent; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct super_block *sb; +#endif int result; }; @@ -401,6 +464,19 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name, if (buf->result) return -EINVAL; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct inode *inode; + inode = ilookup(buf->sb, ino); + if (!inode) { + goto orig_flow; + } + if (susfs_is_inode_sus_path(inode)) { + iput(inode); + return 0; + } + iput(inode); +orig_flow: +#endif buf->result = verify_dirent_name(name, namlen); if (buf->result < 0) return buf->result; @@ -439,7 +515,9 @@ COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd, if (!f.file) return -EBADF; - +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + buf.sb = f.file->f_inode->i_sb; +#endif error = iterate_dir(f.file, &buf.ctx); if (buf.result) error = buf.result; @@ -458,6 +536,9 @@ struct compat_linux_dirent { struct compat_getdents_callback { struct dir_context ctx; struct compat_linux_dirent __user *current_dir; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct super_block *sb; +#endif struct compat_linux_dirent __user *previous; int count; int error; @@ -476,6 +557,19 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + struct inode *inode; + inode = ilookup(buf->sb, ino); + if (!inode) { + goto orig_flow; + } + if (susfs_is_inode_sus_path(inode)) { + iput(inode); + return 0; + } + iput(inode); +orig_flow: +#endif d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; @@ -527,7 +621,9 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, f = fdget_pos(fd); if (!f.file) return -EBADF; - +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + buf.sb = f.file->f_inode->i_sb; +#endif error = iterate_dir(f.file, &buf.ctx); if (error >= 0) error = buf.error; diff --git a/fs/stat.c b/fs/stat.c index 376543199b5a..cc01e9229ac4 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -17,10 +17,25 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_KSTAT) || defined(CONFIG_KSU_SUSFS_SUS_MOUNT) +#include +#endif #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_kstat_spoof_generic_fillattr(struct inode *inode, struct kstat *stat); +#endif + +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat); +#endif + +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_kstat_spoof_generic_fillattr(struct inode *inode, struct kstat *stat); +#endif + /** * generic_fillattr - Fill in the basic attributes from the inode struct * @inode: Inode to use as the source @@ -32,6 +47,7 @@ */ void generic_fillattr(struct inode *inode, struct kstat *stat) { + stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; stat->mode = inode->i_mode; @@ -45,6 +61,9 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) stat->ctime = inode->i_ctime; stat->blksize = i_blocksize(inode); stat->blocks = inode->i_blocks; +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + susfs_sus_kstat_spoof_generic_fillattr(inode, stat); +#endif if (IS_NOATIME(inode)) stat->result_mask &= ~STATX_ATIME; @@ -67,23 +86,32 @@ EXPORT_SYMBOL(generic_fillattr); * attributes to any user. Any other code probably wants vfs_getattr. */ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, - u32 request_mask, unsigned int query_flags) -{ - struct inode *inode = d_backing_inode(path->dentry); - - memset(stat, 0, sizeof(*stat)); - stat->result_mask |= STATX_BASIC_STATS; - request_mask &= STATX_ALL; - query_flags &= KSTAT_QUERY_FLAGS; - if (inode->i_op->getattr) - return inode->i_op->getattr(path, stat, request_mask, - query_flags); + u32 request_mask, unsigned int query_flags) +{ + struct inode *inode = d_backing_inode(path->dentry); + + memset(stat, 0, sizeof(*stat)); + stat->result_mask |= STATX_BASIC_STATS; + request_mask &= STATX_ALL; + query_flags &= KSTAT_QUERY_FLAGS; + if (inode->i_op->getattr) +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + { + int err = inode->i_op->getattr(path, stat, request_mask, + query_flags); + if (!err) + susfs_sus_kstat_spoof_generic_fillattr(inode, stat); + return err; + } +#else + return inode->i_op->getattr(path, stat, request_mask, + query_flags); +#endif - generic_fillattr(inode, stat); - return 0; + generic_fillattr(inode, stat); + return 0; } EXPORT_SYMBOL(vfs_getattr_nosec); - /* * vfs_getattr - Get the enhanced basic attributes of a file * @path: The file of interest @@ -354,12 +382,21 @@ SYSCALL_DEFINE2(newlstat, const char __user *, filename, } #if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT) +#ifdef CONFIG_KSU +extern __attribute__((hot)) int ksu_handle_stat(int *dfd, + const char __user **filename_user, int *flags); +#endif + SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename, struct stat __user *, statbuf, int, flag) { struct kstat stat; int error; +#ifdef CONFIG_KSU + ksu_handle_stat(&dfd, &filename, &flag); +#endif + error = vfs_fstatat(dfd, filename, &stat, flag); if (error) return error; diff --git a/fs/statfs.c b/fs/statfs.c index 56f655f757ff..4c4e29cdeaf8 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -9,7 +9,16 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include "internal.h" +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern struct vfsmount *susfs_get_non_sus_vfsmnt_from_vfsmnt(struct vfsmount *vfsmnt); +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern int susfs_open_redirect_spoof_vfs_statfs(struct inode *inode, struct kstatfs *buf); +#endif static int flags_by_mnt(int mnt_flags) { @@ -69,12 +78,44 @@ static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) int vfs_statfs(const struct path *path, struct kstatfs *buf) { - int error; + int error; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + struct vfsmount *no_sus_vfsmnt = NULL; +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + struct inode *inode = path->dentry->d_inode; + + if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) { + if (susfs_open_redirect_spoof_vfs_statfs(inode, buf)) + goto orig_flow; + return 0; + } +#endif - error = statfs_by_dentry(path->dentry, buf); - if (!error) - buf->f_flags = calculate_f_flags(path->mnt); - return error; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (likely(susfs_is_current_proc_umounted() && path->mnt)) { + no_sus_vfsmnt = susfs_get_non_sus_vfsmnt_from_vfsmnt(path->mnt); + if (path->mnt == no_sus_vfsmnt) { + dput(no_sus_vfsmnt->mnt_root); + mntput(no_sus_vfsmnt); + goto orig_flow; + } + error = statfs_by_dentry(no_sus_vfsmnt->mnt_root, buf); + if (!error) + buf->f_flags = calculate_f_flags(no_sus_vfsmnt); + dput(no_sus_vfsmnt->mnt_root); + mntput(no_sus_vfsmnt); + return error; + } +#endif + +#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +orig_flow: +#endif + error = statfs_by_dentry(path->dentry, buf); + if (!error) + buf->f_flags = calculate_f_flags(path->mnt); + return error; } EXPORT_SYMBOL(vfs_statfs); @@ -93,6 +134,7 @@ int user_statfs(const char __user *pathname, struct kstatfs *st) goto retry; } } + return error; } diff --git a/fs/susfs.c b/fs/susfs.c new file mode 100644 index 000000000000..b65d53b7dc28 --- /dev/null +++ b/fs/susfs.c @@ -0,0 +1,1446 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // We need check kernel version. +#include +#include "fuse/fuse_i.h" +#include "mount.h" + +extern bool susfs_is_current_ksu_domain(void); + +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +bool susfs_is_log_enabled __read_mostly = true; +#define SUSFS_LOGI(fmt, ...) if (READ_ONCE(susfs_is_log_enabled)) pr_info("susfs:[%u][%d][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) +#define SUSFS_LOGE(fmt, ...) if (READ_ONCE(susfs_is_log_enabled)) pr_err("susfs:[%u][%d][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) +#else +#define SUSFS_LOGI(fmt, ...) +#define SUSFS_LOGE(fmt, ...) +#endif + +bool susfs_starts_with(const char *str, const char *prefix) { + while (*prefix) { + if (*str++ != *prefix++) + return false; + } + return true; +} + +#ifndef FUSE_SUPER_MAGIC +#define FUSE_SUPER_MAGIC 0x65735546 +#endif + +/* sus_path */ +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +DEFINE_STATIC_SRCU(susfs_srcu_sus_path_loop); +static DEFINE_SPINLOCK(susfs_spin_lock_sus_path); +static LIST_HEAD(LH_SUS_PATH_LOOP); + +const struct qstr susfs_fake_qstr_name = QSTR_INIT("..5.u.S", 7); // used to re-test the dcache lookup, make sure you don't have file named like this!! + +void susfs_add_sus_path(void __user **user_info) { + struct st_susfs_sus_path info = {0}; + struct path path; + struct inode *inode = NULL; + struct fuse_inode *fi = NULL; + + if (copy_from_user(&info, (struct st_susfs_sus_path __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + info.err = kern_path(info.target_pathname, LOOKUP_FOLLOW, &path); + if (info.err) { + SUSFS_LOGE("failed opening file '%s'\n", info.target_pathname); + goto out_copy_to_user; + } + + inode = d_backing_inode(path.dentry); + if (!inode || !inode->i_mapping) { + SUSFS_LOGE("inode || inode->i_mapping is NULL\n"); + info.err = -ENOENT; + goto out_path_put_path; + } + + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi || !fi->inode.i_mapping) { + SUSFS_LOGE("fi || fi->inode.i_mapping is NULL\n"); + info.err = -ENOENT; + goto out_path_put_path; + } + set_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_mapping->flags); + SUSFS_LOGI("flagged AS_FLAGS_SUS_PATH on pathname: '%s', fi->nodeid: %llu, fi->inode.i_ino: %lu, fi->inode.i_mapping->flags: 0x%lx\n", + info.target_pathname, fi->nodeid, fi->inode.i_ino, fi->inode.i_mapping->flags); + info.err = 0; + goto out_path_put_path; + } + + set_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags); + SUSFS_LOGI("flagged AS_FLAGS_SUS_PATH on pathname: '%s', ino: '%lu', inode->i_mapping->flags: 0x%lx\n", + info.target_pathname, inode->i_ino, inode->i_mapping->flags); + info.err = 0; +out_path_put_path: + path_put(&path); +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_path __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_PATH -> ret: %d\n", info.err); +} + +void susfs_add_sus_path_loop(void __user **user_info) { + struct st_susfs_sus_path_list *new_list = NULL; + struct st_susfs_sus_path info = {0}; + + if (copy_from_user(&info, (struct st_susfs_sus_path __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + if (*info.target_pathname == '\0') { + SUSFS_LOGE("target_pathname cannot be empty\n"); + info.err = -EINVAL; + goto out_copy_to_user; + } + + new_list = kzalloc(sizeof(struct st_susfs_sus_path_list), GFP_KERNEL); + if (!new_list) { + info.err = -ENOMEM; + goto out_copy_to_user; + } + strncpy(new_list->info.target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + strncpy(new_list->target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + INIT_LIST_HEAD(&new_list->list); + spin_lock(&susfs_spin_lock_sus_path); + list_add_tail_rcu(&new_list->list, &LH_SUS_PATH_LOOP); + spin_unlock(&susfs_spin_lock_sus_path); + SUSFS_LOGI("target_pathname: '%s', is successfully added to LH_SUS_PATH_LOOP\n", new_list->target_pathname); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_path __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_PATH_LOOP -> ret: %d\n", info.err); +} + +void susfs_run_sus_path_loop(void) { + struct st_susfs_sus_path_list *cursor = NULL; + struct path path; + struct inode *inode; + struct fuse_inode *fi = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_sus_path_loop); + + list_for_each_entry_rcu(cursor, &LH_SUS_PATH_LOOP, list) { + if (!kern_path(cursor->target_pathname, 0, &path)) + { + inode = d_backing_inode(path.dentry); + if (!inode || !inode->i_mapping) { + SUSFS_LOGE("inode || inode->i_mapping is NULL\n"); + path_put(&path); + continue; + } + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi || !fi->inode.i_mapping) { + SUSFS_LOGE("fi || fi->inode.i_mapping is NULL\n"); + path_put(&path); + continue; + } + set_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_mapping->flags); + SUSFS_LOGI("re-flag AS_FLAGS_SUS_PATH on path '%s', fi->inode.i_ino: '%lu', fi->inode.i_mapping->flags: 0x%lx\n", + cursor->target_pathname, fi->inode.i_ino, fi->inode.i_mapping->flags); + } else { + set_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags); + SUSFS_LOGI("re-flag AS_FLAGS_SUS_PATH on path '%s', inode->i_ino: '%lu', inode->i_mapping->flags: 0x%lx\n", + cursor->target_pathname, inode->i_ino, inode->i_mapping->flags); + } + path_put(&path); + } + } + srcu_read_unlock(&susfs_srcu_sus_path_loop, srcu_idx); +} + +static inline bool is_i_uid_not_allowed(uid_t i_uid) { + return likely(current_uid().val != i_uid); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) +bool susfs_is_inode_sus_path(struct mnt_idmap* idmap, struct inode *inode) +#else +bool susfs_is_inode_sus_path(struct inode *inode) +#endif +{ + struct fuse_inode *fi = NULL; + if (!susfs_is_current_proc_umounted_app()) { + return false; + } + if (!inode->i_mapping) { + SUSFS_LOGE("inode->i_mapping is NULL\n"); + return false; + } + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi || !fi->inode.i_mapping) { + SUSFS_LOGE("fi || fi->inode.i_mapping is NULL\n"); + return false; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_mapping->flags) && + is_i_uid_not_allowed(i_uid_into_vfsuid(idmap, &fi->inode).val))) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_mapping->flags) && + is_i_uid_not_allowed(i_uid_into_mnt(i_user_ns(&fi->inode), &fi->inode).val))) +#else + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_mapping->flags) && + is_i_uid_not_allowed(fi->inode.i_uid.val))) +#endif + { + SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); + return true; + } + return false; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags) && + is_i_uid_not_allowed(i_uid_into_vfsuid(idmap, inode).val))) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags) && + is_i_uid_not_allowed(i_uid_into_mnt(i_user_ns(inode), inode).val))) +#else + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags) && + is_i_uid_not_allowed(inode->i_uid.val))) +#endif + { + SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); + return true; + } + return false; +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH + +/* sus_mount */ +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +// - Default to false now so zygisk can pick up the sus mounts without the need to turn it off manually in post-fs-data stage +// otherwise user needs to turn it on in post-fs-data stage and turn it off in boot-completed stage +bool susfs_hide_sus_mnts_for_non_su_procs = false; + +void susfs_set_hide_sus_mnts_for_non_su_procs(void __user **user_info) { + struct st_susfs_hide_sus_mnts_for_non_su_procs info = {0}; + + if (copy_from_user(&info, (struct st_susfs_hide_sus_mnts_for_non_su_procs __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + WRITE_ONCE(susfs_hide_sus_mnts_for_non_su_procs, info.enabled); + SUSFS_LOGI("susfs_hide_sus_mnts_for_non_su_procs: %d\n", info.enabled); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_hide_sus_mnts_for_non_su_procs __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_HIDE_SUS_MNTS_FOR_NON_SU_PROCS -> ret: %d\n", info.err); +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + +/* sus_kstat */ +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +static DEFINE_SPINLOCK(susfs_spin_lock_sus_kstat); +static DEFINE_HASHTABLE(SUS_KSTAT_HLIST, 10); + +static int susfs_mark_inode_sus_kstat(char *target_pathname, struct st_susfs_sus_kstat_hlist *new_entry) { + struct path path; + struct inode *inode = NULL; + struct fuse_inode *fi = NULL; + int err = 0; + + err = kern_path(target_pathname, 0, &path); + if (err) { + SUSFS_LOGE("failed opening file '%s'\n", target_pathname); + return err; + } + + inode = d_backing_inode(path.dentry); + if (!inode || !inode->i_mapping) { + SUSFS_LOGE("inode || inode->i_mapping is NULL\n"); + err = -ENOENT; + goto out_path_put_path; + } + + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi || !fi->inode.i_mapping) { + SUSFS_LOGE("fi || fi->inode.i_mapping is NULL\n"); + err = -ENOENT; + goto out_path_put_path; + } + set_bit(AS_FLAGS_SUS_KSTAT, &fi->inode.i_mapping->flags); + new_entry->is_fuse = true; + new_entry->target_dev = fi->inode.i_sb->s_dev; + SUSFS_LOGI("flagged AS_FLAGS_SUS_KSTAT on pathname: '%s', is_fuse: %d, fi->inode.i_sb->s_dev: %u, fi->nodeid: %llu, fi->inode.i_ino: %lu, fi->inode.i_mapping->flags: 0x%lx\n", + target_pathname, new_entry->is_fuse, fi->inode.i_sb->s_dev, fi->nodeid, fi->inode.i_ino, fi->inode.i_mapping->flags); + err = 0; + goto out_path_put_path; + } + + set_bit(AS_FLAGS_SUS_KSTAT, &inode->i_mapping->flags); + new_entry->is_fuse = false; + new_entry->target_dev = inode->i_sb->s_dev; + SUSFS_LOGI("flagged AS_FLAGS_SUS_KSTAT on pathname: '%s', is_fuse: %d, inode->i_sb->s_dev: %u, inode->i_ino: %lu, inode->i_mapping->flags: 0x%lx\n", + target_pathname, new_entry->is_fuse, inode->i_sb->s_dev, inode->i_ino, inode->i_mapping->flags); + +out_path_put_path: + path_put(&path); + return 0; +} + +void susfs_add_sus_kstat(void __user **user_info) { + struct st_susfs_sus_kstat info = {0}; + struct st_susfs_sus_kstat_hlist *new_entry, *tmp_entry; + + if (copy_from_user(&info, (struct st_susfs_sus_kstat __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + if (*info.target_pathname == '\0') { + info.err = -EINVAL; + goto out_copy_to_user; + } + + new_entry = kzalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL); + if (!new_entry) { + info.err = -ENOMEM; + goto out_copy_to_user; + } + + // If it is added statically, check for duplicated entry, and remove it first if so + if (info.is_statically) { + spin_lock(&susfs_spin_lock_sus_kstat); + hash_for_each_possible(SUS_KSTAT_HLIST, tmp_entry, node, info.target_ino) { + if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) { + memcpy(&new_entry->info, &tmp_entry->info, sizeof(tmp_entry->info)); + new_entry->target_ino = info.target_ino; + new_entry->info.target_ino = info.target_ino; + hash_del_rcu(&tmp_entry->node); + spin_unlock(&susfs_spin_lock_sus_kstat); + synchronize_rcu(); + kfree(tmp_entry); + goto out_add_new_entry; + } + } + spin_unlock(&susfs_spin_lock_sus_kstat); + } + +out_add_new_entry: +#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) +#ifdef CONFIG_MIPS + info.spoofed_dev = new_decode_dev(info.spoofed_dev); +#else + info.spoofed_dev = huge_decode_dev(info.spoofed_dev); +#endif /* CONFIG_MIPS */ +#else + info.spoofed_dev = old_decode_dev(info.spoofed_dev); +#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */ + + new_entry->target_ino = info.target_ino; + memcpy(&new_entry->info, &info, sizeof(info)); + + info.err = susfs_mark_inode_sus_kstat(new_entry->info.target_pathname, new_entry); + if (info.err) { + kfree(new_entry); + goto out_copy_to_user; + } + + spin_lock(&susfs_spin_lock_sus_kstat); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + SUSFS_LOGI("is_fuse: %d, is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%llu', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n", + new_entry->is_fuse, + new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, + new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, + new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, + new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, + new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, + new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); +#else + SUSFS_LOGI("is_fuse: %d, is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n", + new_entry->is_fuse, + new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, + new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, + new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, + new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, + new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, + new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); +#endif + hash_add_rcu(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino); + spin_unlock(&susfs_spin_lock_sus_kstat); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_kstat __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + if (!info.is_statically) { + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_KSTAT -> ret: %d\n", info.err); + } else { + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY -> ret: %d\n", info.err); + } +} + +void susfs_update_sus_kstat(void __user **user_info) { + struct st_susfs_sus_kstat info = {0}; + struct st_susfs_sus_kstat_hlist *new_entry, *tmp_entry; + + if (copy_from_user(&info, (struct st_susfs_sus_kstat __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + new_entry = kzalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL); + if (!new_entry) { + info.err = -ENOMEM; + goto out_copy_to_user; + } + + spin_lock(&susfs_spin_lock_sus_kstat); + hash_for_each_possible(SUS_KSTAT_HLIST, tmp_entry, node, info.target_ino) { + if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) { + memcpy(&new_entry->info, &tmp_entry->info, sizeof(tmp_entry->info)); + new_entry->target_ino = info.target_ino; + new_entry->info.target_ino = info.target_ino; + hash_del_rcu(&tmp_entry->node); + spin_unlock(&susfs_spin_lock_sus_kstat); + synchronize_rcu(); + kfree(tmp_entry); + goto out_add_new_entry; + } + } + spin_unlock(&susfs_spin_lock_sus_kstat); + info.err = -ENOENT; + goto out_copy_to_user; + +out_add_new_entry: + info.err = susfs_mark_inode_sus_kstat(new_entry->info.target_pathname, new_entry); + if (info.err) { + kfree(new_entry); + goto out_copy_to_user; + } + SUSFS_LOGI("updating target_ino from '%lu' to '%lu' for pathname: '%s' in SUS_KSTAT_HLIST\n", + new_entry->info.target_ino, info.target_ino, info.target_pathname); + spin_lock(&susfs_spin_lock_sus_kstat); + hash_add_rcu(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino); + spin_unlock(&susfs_spin_lock_sus_kstat); + info.err = 0; + +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_kstat __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_UPDATE_SUS_KSTAT -> ret: %d\n", info.err); +} + +void susfs_sus_kstat_spoof_generic_fillattr(struct inode *inode, struct kstat *stat) +{ + struct st_susfs_sus_kstat_hlist *entry = NULL; + struct fuse_inode *fi = NULL; + unsigned long target_ino = 0; + dev_t target_dev = 0; + bool is_fuse = false; + + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi || !fi->inode.i_mapping) { + SUSFS_LOGE("fi || fi->inode.i_mapping is NULL\n"); + return; + } + if (!test_bit(AS_FLAGS_SUS_KSTAT, &fi->inode.i_mapping->flags) || + !susfs_is_current_proc_umounted_app()) + return; + target_ino = fi->inode.i_ino; + target_dev = fi->inode.i_sb->s_dev; + is_fuse = true; + goto out_spoof_kstat; + } + + if (!inode->i_mapping) { + SUSFS_LOGE("inode->i_mapping is NULL\n"); + return; + } + + if (!test_bit(AS_FLAGS_SUS_KSTAT, &inode->i_mapping->flags) || + !susfs_is_current_proc_umounted_app()) + return; + + target_ino = inode->i_ino; + target_dev = inode->i_sb->s_dev; + +out_spoof_kstat: + rcu_read_lock(); + hash_for_each_possible_rcu(SUS_KSTAT_HLIST, entry, node, target_ino) { + if (entry->target_ino == target_ino && + entry->target_dev == target_dev && + entry->is_fuse == is_fuse) + { + SUSFS_LOGI("spoofing kstat for path: %s, target_ino: %lu, target_dev: %u\n", + entry->info.target_pathname, target_ino, target_dev); + if (entry->info.flags & KSTAT_SPOOF_INO) + stat->ino = entry->info.spoofed_ino; + if (entry->info.flags & KSTAT_SPOOF_DEV) + stat->dev = entry->info.spoofed_dev; + if (entry->info.flags & KSTAT_SPOOF_NLINK) + stat->nlink = entry->info.spoofed_nlink; + if (entry->info.flags & KSTAT_SPOOF_SIZE) + stat->size = entry->info.spoofed_size; + if (entry->info.flags & KSTAT_SPOOF_ATIME_TV_SEC) + stat->atime.tv_sec = entry->info.spoofed_atime_tv_sec; + if (entry->info.flags & KSTAT_SPOOF_ATIME_TV_NSEC) + stat->atime.tv_nsec = entry->info.spoofed_atime_tv_nsec; + if (entry->info.flags & KSTAT_SPOOF_MTIME_TV_SEC) + stat->mtime.tv_sec = entry->info.spoofed_mtime_tv_sec; + if (entry->info.flags & KSTAT_SPOOF_MTIME_TV_NSEC) + stat->mtime.tv_nsec = entry->info.spoofed_mtime_tv_nsec; + if (entry->info.flags & KSTAT_SPOOF_CTIME_TV_SEC) + stat->ctime.tv_sec = entry->info.spoofed_ctime_tv_sec; + if (entry->info.flags & KSTAT_SPOOF_CTIME_TV_NSEC) + stat->ctime.tv_nsec = entry->info.spoofed_ctime_tv_nsec; + if (entry->info.flags & KSTAT_SPOOF_BLKSIZE) + stat->blksize = entry->info.spoofed_blksize; + if (entry->info.flags & KSTAT_SPOOF_BLOCKS) + stat->blocks = entry->info.spoofed_blocks; + rcu_read_unlock(); + return; + } + } + rcu_read_unlock(); +} + +void susfs_sus_kstat_spoof_show_map_vma(struct inode *inode, dev_t *out_dev, unsigned long *out_ino) { + struct st_susfs_sus_kstat_hlist *entry = NULL; + struct fuse_inode *fi = NULL; + unsigned long target_ino = 0; + dev_t target_dev = 0; + bool is_fuse = false; + + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi || !fi->inode.i_mapping) { + SUSFS_LOGE("fi || fi->inode.i_mapping is NULL\n"); + return; + } + if (!test_bit(AS_FLAGS_SUS_KSTAT, &fi->inode.i_mapping->flags) || + !susfs_is_current_proc_umounted_app()) + return; + target_ino = fi->inode.i_ino; + target_dev = fi->inode.i_sb->s_dev; + is_fuse = true; + goto out_spoof_kstat; + } + + if (!inode->i_mapping) { + SUSFS_LOGE("inode->i_mapping is NULL\n"); + return; + } + + if (!test_bit(AS_FLAGS_SUS_KSTAT, &inode->i_mapping->flags) || + !susfs_is_current_proc_umounted_app()) + return; + + target_ino = inode->i_ino; + target_dev = inode->i_sb->s_dev; + +out_spoof_kstat: + rcu_read_lock(); + hash_for_each_possible_rcu(SUS_KSTAT_HLIST, entry, node, target_ino) { + if (entry->target_ino == target_ino && + entry->target_dev == target_dev && + entry->is_fuse == is_fuse) + { + SUSFS_LOGI("spoofing kstat for target_ino: %lu, target_dev: %u\n", target_ino, target_dev); + *out_dev = entry->info.spoofed_dev; + *out_ino = entry->info.spoofed_ino; + rcu_read_unlock(); + return; + } + } + rcu_read_unlock(); +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + +/* spoof_uname */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +static struct st_susfs_uname my_uname = {0}; +static bool is_susfs_uname_set = false; +static DEFINE_SEQLOCK(susfs_uname_seqlock); + +void susfs_set_uname(void __user **user_info) { + struct st_susfs_uname info = {0}; + + if (copy_from_user(&info, (struct st_susfs_uname __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + if (*info.release == '\0' || *info.version == '\0') { + info.err = -EFAULT; + goto out_copy_to_user; + } + + write_seqlock(&susfs_uname_seqlock); + if (!strcmp(info.release, "default")) { + strscpy(my_uname.release, utsname()->release, __NEW_UTS_LEN); + } else { + strncpy(my_uname.release, info.release, __NEW_UTS_LEN); + } + if (!strcmp(info.version, "default")) { + strscpy(my_uname.version, utsname()->version, __NEW_UTS_LEN); + } else { + strncpy(my_uname.version, info.version, __NEW_UTS_LEN); + } + is_susfs_uname_set = true; + write_sequnlock(&susfs_uname_seqlock); + SUSFS_LOGI("set spoofed release: '%s', version: '%s'\n", + my_uname.release, my_uname.version); + + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_uname __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SET_UNAME -> ret: %d\n", info.err); +} + +void susfs_spoof_uname(struct new_utsname* tmp) { + unsigned seq; + + do { + seq = read_seqbegin(&susfs_uname_seqlock); + if (is_susfs_uname_set) { + strncpy(tmp->release, my_uname.release, __NEW_UTS_LEN); + strncpy(tmp->version, my_uname.version, __NEW_UTS_LEN); + } + } while (read_seqretry(&susfs_uname_seqlock, seq)); +} +#endif // #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + +/* enable_log */ +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +void susfs_enable_log(void __user **user_info) { + struct st_susfs_log info = {0}; + + if (copy_from_user(&info, (struct st_susfs_log __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + WRITE_ONCE(susfs_is_log_enabled, info.enabled); + + if (info.enabled) { + pr_info("susfs: enable logging to kernel"); + } else { + pr_info("susfs: disable logging to kernel"); + } + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_log __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ENABLE_LOG -> ret: %d\n", info.err); +} +#endif // #ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + +/* spoof_cmdline_or_bootconfig */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +static char *fake_cmdline_or_bootconfig = NULL; +static bool susfs_is_fake_cmdline_or_bootconfig_set = false; +static DEFINE_SEQLOCK(susfs_fake_cmdline_or_bootconfig_seqlock); + +void susfs_set_cmdline_or_bootconfig(void __user **user_info) { + struct st_susfs_spoof_cmdline_or_bootconfig *info = (struct st_susfs_spoof_cmdline_or_bootconfig *)kzalloc(sizeof(struct st_susfs_spoof_cmdline_or_bootconfig), GFP_KERNEL); + + if (!info) { + info->err = -ENOMEM; + goto out_copy_to_user; + } + + if (copy_from_user(info, (struct st_susfs_spoof_cmdline_or_bootconfig __user*)*user_info, sizeof(struct st_susfs_spoof_cmdline_or_bootconfig))) { + info->err = -EFAULT; + goto out_copy_to_user; + } + + if (*info->fake_cmdline_or_bootconfig == '\0') { + info->err = -EINVAL; + goto out_copy_to_user; + } + + if (!fake_cmdline_or_bootconfig) { + fake_cmdline_or_bootconfig = (char *)kzalloc(SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE, GFP_KERNEL); + if (!fake_cmdline_or_bootconfig) { + info->err = -ENOMEM; + goto out_copy_to_user; + } + } + + write_seqlock(&susfs_fake_cmdline_or_bootconfig_seqlock); + strncpy(fake_cmdline_or_bootconfig, + info->fake_cmdline_or_bootconfig, + SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE - 1); + susfs_is_fake_cmdline_or_bootconfig_set = true; + write_sequnlock(&susfs_fake_cmdline_or_bootconfig_seqlock); + + SUSFS_LOGI("fake_cmdline_or_bootconfig is set\n"); + info->err = 0; + +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_spoof_cmdline_or_bootconfig __user*)*user_info)->err, &info->err, sizeof(info->err))) { + info->err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG -> ret: %d\n", info->err); + if (info) { + kfree(info); + } +} + +int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m) { + unsigned seq; + int err = -EINVAL; + + do { + seq = read_seqbegin(&susfs_fake_cmdline_or_bootconfig_seqlock); + if (susfs_is_fake_cmdline_or_bootconfig_set) { + seq_puts(m, fake_cmdline_or_bootconfig); + err = 0; + } + } while (read_seqretry(&susfs_fake_cmdline_or_bootconfig_seqlock, seq)); + + return err; +} +#endif + +/* open_redirect */ +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +static DEFINE_SPINLOCK(susfs_spin_lock_open_redirect); +static DEFINE_HASHTABLE(OPEN_REDIRECT_HLIST, 10); +DEFINE_STATIC_SRCU(susfs_srcu_open_redirect); + +void susfs_add_open_redirect(void __user **user_info) { + struct st_susfs_open_redirect info = {0}; + struct st_susfs_open_redirect_hlist *new_entry_target, *new_entry_redirected, *tmp_entry_target, *tmp_entry_redirected; + struct path target_path, redirected_path; + struct inode *target_inode, *redirected_inode; + bool is_first_dup_found = false; + bool is_second_dup_found = false; + + if (copy_from_user(&info, (struct st_susfs_open_redirect __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + if (*info.target_pathname == '\0') { + info.err = -EINVAL; + SUSFS_LOGE("empty target_pathname\n"); + goto out_copy_to_user; + } + + if (info.uid_scheme < UID_NON_APP_PROC || info.uid_scheme > UID_UMOUNTED_PROC) { + info.err = -EINVAL; + SUSFS_LOGE("invalid uid scheme: %d\n", info.uid_scheme); + goto out_copy_to_user; + } + + info.err = kern_path(info.redirected_pathname, 0, &redirected_path); + if (info.err) { + SUSFS_LOGE("failed opening redirected file '%s'\n", info.redirected_pathname); + goto out_copy_to_user; + } + + info.err = kern_path(info.target_pathname, 0, &target_path); + if (info.err) { + SUSFS_LOGE("failed opening target file '%s'\n", info.target_pathname); + goto out_path_put_redirected_path; + } + + redirected_inode = d_backing_inode(redirected_path.dentry); + if (!redirected_inode || !redirected_inode->i_mapping) { + SUSFS_LOGE("redirected_inode || redirected_inode->i_mapping is NULL\n"); + info.err = -ENOENT; + goto out_path_put_target_path; + } + + target_inode = d_backing_inode(target_path.dentry); + if (!target_inode || !target_inode->i_mapping) { + SUSFS_LOGE("target_inode || target_inode->i_mapping is NULL\n"); + info.err = -ENOENT; + goto out_path_put_target_path; + } + + if (redirected_inode->i_sb->s_magic == FUSE_SUPER_MAGIC || + target_inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + SUSFS_LOGE("FUSE fs is not supported for open_redirect feature\n"); + info.err = -EINVAL; + goto out_path_put_target_path; + } + + new_entry_target = kzalloc(sizeof(struct st_susfs_open_redirect_hlist), GFP_KERNEL); + if (!new_entry_target) { + info.err = -ENOMEM; + goto out_path_put_target_path; + } + + new_entry_redirected = kzalloc(sizeof(struct st_susfs_open_redirect_hlist), GFP_KERNEL); + if (!new_entry_redirected) { + info.err = -ENOMEM; + kfree(new_entry_target); + goto out_path_put_target_path; + } + + // check for existing entries, delete it first if so + spin_lock(&susfs_spin_lock_open_redirect); + hash_for_each_possible(OPEN_REDIRECT_HLIST, tmp_entry_target, node, target_inode->i_ino) { + if (!strcmp(tmp_entry_target->info.target_pathname, info.target_pathname)) { + if (tmp_entry_target->reversed_lookup_only) { + SUSFS_LOGE("duplicated '%s' cannot be removed/added because it is used for reversed lookup only\n", info.target_pathname); + spin_unlock(&susfs_spin_lock_open_redirect); + info.err = -EINVAL; + kfree(new_entry_redirected); + kfree(new_entry_target); + goto out_path_put_target_path; + } + is_first_dup_found = true; + hash_del_rcu(&tmp_entry_target->node); + break; + } + } + + if (is_first_dup_found) { + hash_for_each_possible(OPEN_REDIRECT_HLIST, tmp_entry_redirected, node, redirected_inode->i_ino) { + if (!strcmp(tmp_entry_redirected->info.target_pathname, info.redirected_pathname)) { + is_second_dup_found = true; + hash_del_rcu(&tmp_entry_redirected->node); + break; + } + } + spin_unlock(&susfs_spin_lock_open_redirect); + synchronize_rcu(); + if (is_second_dup_found) + kfree(tmp_entry_redirected); + kfree(tmp_entry_target); + goto out_add_new_entry; + } + spin_unlock(&susfs_spin_lock_open_redirect); + +out_add_new_entry: + new_entry_target->target_ino = target_inode->i_ino; + new_entry_target->target_dev = target_inode->i_sb->s_dev; + new_entry_target->redirected_ino = redirected_inode->i_ino; + new_entry_target->redirected_dev = redirected_inode->i_sb->s_dev; + new_entry_target->info.uid_scheme = info.uid_scheme; + new_entry_target->reversed_lookup_only = false; + new_entry_target->spoofed_mnt_id = real_mount(target_path.mnt)->mnt_id; + (void)vfs_statfs(&target_path, &new_entry_target->spoofed_kstatfs); + memcpy(&new_entry_target->info, &info, sizeof(info)); + + new_entry_redirected->target_ino = redirected_inode->i_ino; + new_entry_redirected->target_dev = redirected_inode->i_sb->s_dev; + new_entry_redirected->redirected_ino = target_inode->i_ino; + new_entry_redirected->redirected_dev = target_inode->i_sb->s_dev; + new_entry_redirected->info.uid_scheme = info.uid_scheme; + new_entry_redirected->reversed_lookup_only = true; + new_entry_redirected->spoofed_mnt_id = real_mount(target_path.mnt)->mnt_id; + memcpy(&new_entry_redirected->spoofed_kstatfs, &new_entry_target->spoofed_kstatfs, sizeof(struct kstatfs)); + strncpy(new_entry_redirected->info.target_pathname, info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + strncpy(new_entry_redirected->info.redirected_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + + spin_lock(&susfs_spin_lock_open_redirect); + SUSFS_LOGI("target_pathname: '%s', redirected_pathname: '%s', target_i_ino: '%lu', redirected_i_ino: '%lu', target_s_dev: '%lu', redirected_s_dev: '%lu', uid_scheme: '%d', reversed_lookup_only: %d, spoofed_mnt_id: %d, is successfully added to OPEN_REDIRECT_HLIST\n", + new_entry_target->info.target_pathname, new_entry_target->info.redirected_pathname, new_entry_target->target_ino, new_entry_target->redirected_ino, new_entry_target->target_dev, new_entry_target->redirected_dev, new_entry_target->info.uid_scheme, new_entry_target->reversed_lookup_only, new_entry_target->spoofed_mnt_id); + SUSFS_LOGI("target_pathname: '%s', redirected_pathname: '%s', target_i_ino: '%lu', redirected_i_ino: '%lu', target_s_dev: '%lu', redirected_s_dev: '%lu', uid_scheme: '%d', reversed_lookup_only: %d, spoofed_mnt_id: %d, is successfully added to OPEN_REDIRECT_HLIST\n", + new_entry_redirected->info.target_pathname, new_entry_redirected->info.redirected_pathname, new_entry_redirected->target_ino, new_entry_redirected->redirected_ino, new_entry_redirected->target_dev, new_entry_redirected->redirected_dev, new_entry_redirected->info.uid_scheme, new_entry_redirected->reversed_lookup_only, new_entry_redirected->spoofed_mnt_id); + hash_add_rcu(OPEN_REDIRECT_HLIST, &new_entry_target->node, new_entry_target->target_ino); + hash_add_rcu(OPEN_REDIRECT_HLIST, &new_entry_redirected->node, new_entry_redirected->target_ino); + // we need to mark both target and redirected path inode just for spoofing readlink as well + set_bit(AS_FLAGS_OPEN_REDIRECT, &redirected_inode->i_mapping->flags); + set_bit(AS_FLAGS_OPEN_REDIRECT, &target_inode->i_mapping->flags); + spin_unlock(&susfs_spin_lock_open_redirect); + + info.err = 0; +out_path_put_target_path: + path_put(&target_path); +out_path_put_redirected_path: + path_put(&redirected_path); +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_open_redirect __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_OPEN_REDIRECT -> ret: %d\n", info.err); +} + +struct filename *susfs_open_redirect_spoof_do_sys_openat(struct inode *inode) { + struct st_susfs_open_redirect_hlist *entry = NULL; + struct filename *new_filename = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (!entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + switch(entry->info.uid_scheme) { + case UID_NON_APP_PROC: + if (current_uid().val % 100000 < 10000) + break; + goto out_srcu_read_unlock; + case UID_ROOT_PROC_EXCEPT_SU_PROC: + if (current_uid().val == 0 && !susfs_is_current_ksu_domain()) + break; + goto out_srcu_read_unlock; + case UID_NON_SU_PROC: + if (!susfs_is_current_ksu_domain()) + break; + goto out_srcu_read_unlock; + case UID_UMOUNTED_APP_PROC: + if (susfs_is_current_proc_umounted_app()) + break; + goto out_srcu_read_unlock; + case UID_UMOUNTED_PROC: + if (susfs_is_current_proc_umounted()) + break; + goto out_srcu_read_unlock; + default: + goto out_srcu_read_unlock; + } + SUSFS_LOGI("redirect path '%s' to '%s', uid_scheme: %d\n", + entry->info.target_pathname, entry->info.redirected_pathname, entry->info.uid_scheme); + new_filename = getname_kernel(entry->info.redirected_pathname); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return new_filename; + } + } +out_srcu_read_unlock: + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return new_filename; +} + +int susfs_open_redirect_spoof_vfs_readlink(struct inode *inode, char __user *buffer, int buflen) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + SUSFS_LOGI("spoof path '%s' to '%s'\n", + entry->info.target_pathname, entry->info.redirected_pathname); + if (strlen(entry->info.redirected_pathname) >= buflen) { + SUSFS_LOGE("buflen not big enough\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENAMETOOLONG; + } + if (copy_to_user(buffer, entry->info.redirected_pathname, strlen(entry->info.redirected_pathname))) { + SUSFS_LOGE("copy_to_user() failed\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EFAULT; + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENOENT; +} + +int susfs_open_redirect_spoof_do_proc_readlink(struct inode *inode, char *tmp_buf, int buflen) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + SUSFS_LOGI("spoof path '%s' to '%s'\n", + entry->info.target_pathname, entry->info.redirected_pathname); + if (strlen(entry->info.redirected_pathname) >= buflen) { + SUSFS_LOGE("buflen not big enough\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENAMETOOLONG; + } + strncpy(tmp_buf, entry->info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENOENT; +} + +int susfs_open_redirect_spoof_vfs_statfs(struct inode *inode, struct kstatfs *buf) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + SUSFS_LOGI("spoof kstatfs for redirected path: '%s'\n", + entry->info.target_pathname); + memcpy(buf, &entry->spoofed_kstatfs, sizeof(struct kstatfs)); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EINVAL; +} + +int susfs_open_redirect_spoof_seq_show(struct inode *inode, int *out_mnt_id, unsigned long *out_ino) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + *out_mnt_id = entry->spoofed_mnt_id; + *out_ino = entry->redirected_ino; + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EINVAL; +} + +int susfs_open_redirect_spoof_show_map_vma(struct inode *inode, unsigned long *out_ino, dev_t *out_dev, char *spoofed_name) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + if (spoofed_name) { + SUSFS_LOGE("spoofed_name must be NULL first!\n"); + return -EINVAL; + } + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + spoofed_name = kzalloc(SUSFS_MAX_LEN_PATHNAME, GFP_KERNEL); + if (!spoofed_name) { + SUSFS_LOGE("no enough memeory\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENOMEM; + } + SUSFS_LOGI("spoof maps ino/dev/name for redirected path: '%s'\n", + entry->info.target_pathname); + *out_ino = entry->redirected_ino; + *out_dev = entry->redirected_dev; + strncpy(spoofed_name, entry->info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EINVAL; +} +#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + +/* sus_map */ +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +void susfs_add_sus_map(void __user **user_info) { + struct st_susfs_sus_map info = {0}; + struct path path; + struct inode *inode = NULL; + + if (copy_from_user(&info, (struct st_susfs_sus_map __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + info.err = kern_path(info.target_pathname, LOOKUP_FOLLOW, &path); + if (info.err) { + SUSFS_LOGE("failed opening file '%s'\n", info.target_pathname); + goto out_copy_to_user; + } + + inode = d_backing_inode(path.dentry); + if (!inode || !inode->i_mapping) { + SUSFS_LOGE("inode || inode->i_mapping is NULL\n"); + info.err = -ENOENT; + goto out_path_put_path; + } + set_bit(AS_FLAGS_SUS_MAP, &inode->i_mapping->flags); + SUSFS_LOGI("pathname: '%s', is flagged as AS_FLAGS_SUS_MAP\n", info.target_pathname); + info.err = 0; +out_path_put_path: + path_put(&path); +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_map __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_MAP -> ret: %d\n", info.err); +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MAP + +/* susfs avc log spoofing */ +bool susfs_is_avc_log_spoofing_enabled = false; + +void susfs_set_avc_log_spoofing(void __user **user_info) { + struct st_susfs_avc_log_spoofing info = {0}; + + if (copy_from_user(&info, (struct st_susfs_avc_log_spoofing __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + WRITE_ONCE(susfs_is_avc_log_spoofing_enabled, info.enabled); + SUSFS_LOGI("susfs_is_avc_log_spoofing_enabled: %d\n", info.enabled); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_avc_log_spoofing __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING -> ret: %d\n", info.err); +} + +/* get susfs enabled features */ +static int copy_config_to_buf(const char *config_string, char *buf_ptr, size_t *copied_size, size_t bufsize) { + size_t tmp_size = strlen(config_string); + + *copied_size += tmp_size; + if (*copied_size >= bufsize) { + SUSFS_LOGE("bufsize is not big enough to hold the string.\n"); + return -EINVAL; + } + strncpy(buf_ptr, config_string, tmp_size); + return 0; +} + +void susfs_get_enabled_features(void __user **user_info) { + struct st_susfs_enabled_features *info = (struct st_susfs_enabled_features *)kzalloc(sizeof(struct st_susfs_enabled_features), GFP_KERNEL); + char *buf_ptr = NULL; + size_t copied_size = 0; + + if (!info) { + info->err = -ENOMEM; + goto out_copy_to_user; + } + + if (copy_from_user(info, (struct st_susfs_enabled_features __user*)*user_info, sizeof(struct st_susfs_enabled_features))) { + info->err = -EFAULT; + goto out_copy_to_user; + } + + buf_ptr = info->enabled_features; + +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_PATH\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_MOUNT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_KSTAT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SPOOF_UNAME\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_ENABLE_LOG\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_OPEN_REDIRECT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_MAP\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif + + info->err = 0; +out_copy_to_user: + if (copy_to_user((struct st_susfs_enabled_features __user*)*user_info, info, sizeof(struct st_susfs_enabled_features))) { + info->err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SHOW_ENABLED_FEATURES -> ret: %d\n", info->err); + if (info) { + kfree(info); + } +} + +/* show_variant */ +void susfs_show_variant(void __user **user_info) { + struct st_susfs_variant info = {0}; + + if (copy_from_user(&info, (struct st_susfs_variant __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + strncpy(info.susfs_variant, SUSFS_VARIANT, SUSFS_MAX_VARIANT_BUFSIZE-1); + info.err = 0; +out_copy_to_user: + if (copy_to_user((struct st_susfs_variant __user*)*user_info, &info, sizeof(info))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SHOW_VARIANT -> ret: %d\n", info.err); +} + +/* show version */ +void susfs_show_version(void __user **user_info) { + struct st_susfs_version info = {0}; + + if (copy_from_user(&info, (struct st_susfs_version __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + strncpy(info.susfs_version, SUSFS_VERSION, SUSFS_MAX_VERSION_BUFSIZE-1); + info.err = 0; +out_copy_to_user: + if (copy_to_user((struct st_susfs_version __user*)*user_info, &info, sizeof(info))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SHOW_VERSION -> ret: %d\n", info.err); +} + +/* kthread for checking if /sdcard/Android is accessible via fsnoitfy */ +/* code is straightly borrowed from KernelSU's pkg_observer.c */ +#define SDCARD_ANDROID_PATH "/data/media/0/Android" +extern void setup_selinux(const char *domain, struct cred *cred); +bool susfs_is_sdcard_android_data_decrypted __read_mostly = false; + +struct watch_dir { + const char *path; + u32 mask; + struct path kpath; + struct inode *inode; + struct fsnotify_mark *mark; +}; + +static struct fsnotify_group *g; + +static struct watch_dir g_watch = { .path = "/data/media/0", // we choose the underlying f2fs /data/media/0 instead of the FUSE /sdcard + .mask = (FS_EVENT_ON_CHILD | FS_ISDIR | FS_OPEN_PERM) }; + +static int add_mark_on_inode(struct inode *inode, u32 mask, + struct fsnotify_mark **out); + +static unsigned long sdcard_cleanup_scheduled; +static struct delayed_work sdcard_cleanup_dwork; + +static void susfs_sdcard_cleanup_fn(struct work_struct *work) +{ + struct fsnotify_group *grp; + struct inode *inode; + + SUSFS_LOGI("set susfs_is_sdcard_android_data_decrypted to true\n"); + WRITE_ONCE(susfs_is_sdcard_android_data_decrypted, true); + + SUSFS_LOGI("cleaning up fsnotify sdcard watch\n"); + + grp = xchg(&g, NULL); + if (grp) + fsnotify_destroy_group(grp); + + inode = xchg(&g_watch.inode, NULL); + if (inode) + iput(inode); + + if (g_watch.kpath.mnt) { + path_put(&g_watch.kpath); + memset(&g_watch.kpath, 0, sizeof(g_watch.kpath)); + } +} + +static int watch_one_dir(struct watch_dir *wd) +{ + int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath); + if (ret) { + SUSFS_LOGI("path not ready: %s (%d)\n", wd->path, ret); + return ret; + } + wd->inode = d_backing_inode(wd->kpath.dentry); + if (!wd->inode) { + SUSFS_LOGE("wd->inode is NULL\n"); + path_put(&wd->kpath); + return -ENOENT; + } + ihold(wd->inode); + + ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark); + if (ret) { + SUSFS_LOGE("add mark failed for %s (%d)\n", wd->path, ret); + iput(wd->inode); + wd->inode = NULL; + path_put(&wd->kpath); + return ret; + } + SUSFS_LOGI("watching %s\n", wd->path); + return 0; +} + +/* + * fsnotify handler — runs inside an SRCU read section held by fsnotify(). + * Must not block or call fsnotify_destroy_group() (which internally calls + * synchronize_srcu on the same SRCU struct, causing a permanent deadlock). + * Cleanup is deferred to a delayed_work that runs outside the SRCU context. + */ +static SUSFS_DECL_FSNOTIFY_OPS(susfs_handle_sdcard_inode_event) +{ + if (!file_name || strlen((const char *)file_name) != 7 || + memcmp(file_name, "Android", 7)) + return 0; + + if (test_and_set_bit(0, &sdcard_cleanup_scheduled)) + return 0; + + SUSFS_LOGI("'%s' detected, mask: 0x%x\n", SDCARD_ANDROID_PATH, mask); + SUSFS_LOGI("deferring cleanup for 5 seconds\n"); + queue_delayed_work(system_unbound_wq, &sdcard_cleanup_dwork, 5 * HZ); + return 0; +} + +static const struct fsnotify_ops fsnotify_ops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .handle_inode_event = susfs_handle_sdcard_inode_event, +#else + .handle_event = susfs_handle_sdcard_inode_event, +#endif +}; + +static void __maybe_unused m_free(struct fsnotify_mark *m) +{ + if (m) { + kfree(m); + } +} + +static int add_mark_on_inode(struct inode *inode, u32 mask, + struct fsnotify_mark **out) +{ + struct fsnotify_mark *m; + int ret; + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + +/* From KernelSU */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) + fsnotify_init_mark(m, g); + m->mask = mask; + ret = fsnotify_add_inode_mark(m, inode, 0); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + fsnotify_init_mark(m, g); + m->mask = mask; + ret = fsnotify_add_mark(m, inode, NULL, 0); +#else + fsnotify_init_mark(m, m_free); + m->mask = mask; + ret = fsnotify_add_mark(m, g, inode, NULL, 0); +#endif + + if (ret) { + fsnotify_put_mark(m); + return -EINVAL; + } + *out = m; + return 0; +} + +static int susfs_sdcard_monitor_fn(void *data) +{ + struct cred *cred = prepare_creds(); + int ret = 0; + + if (!cred) { + SUSFS_LOGE("failed to prepare creds!\n"); + return -ENOMEM; + } + + setup_selinux("u:r:ksu:s0", cred); + commit_creds(cred); + + if (!susfs_is_current_ksu_domain()) { + SUSFS_LOGE("domain is not su, exiting the thread\n"); + return -EINVAL; + } + + SUSFS_LOGI("start monitoring path '%s' using fsnotify\n", + SDCARD_ANDROID_PATH); + + INIT_DELAYED_WORK(&sdcard_cleanup_dwork, susfs_sdcard_cleanup_fn); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + g = fsnotify_alloc_group(&fsnotify_ops, 0); +#else + g = fsnotify_alloc_group(&fsnotify_ops); +#endif + if (IS_ERR(g)) { + return PTR_ERR(g); + } + + ret = watch_one_dir(&g_watch); + + SUSFS_LOGI("ret: %d\n", ret); + + return 0; +} + +void susfs_start_sdcard_monitor_fn(void) { + if (IS_ERR(kthread_run(susfs_sdcard_monitor_fn, NULL, "susfs_sdcard_monitor"))) { + SUSFS_LOGE("failed to create thread susfs_sdcard_monitor\n"); + SUSFS_LOGI("set susfs_is_sdcard_android_data_decrypted to true\n"); + WRITE_ONCE(susfs_is_sdcard_android_data_decrypted, true); + } +} + +/* susfs_init */ +void susfs_init(void) { + SUSFS_LOGI("susfs is initialized! version: " SUSFS_VERSION " \n"); +} + +/* No module exit is needed becuase it should never be a loadable kernel module */ +//void __init susfs_exit(void) \ No newline at end of file diff --git a/include/linux/chg-tcpc_info.h b/include/linux/chg-tcpc_info.h new file mode 100644 index 000000000000..099d8e9f028e --- /dev/null +++ b/include/linux/chg-tcpc_info.h @@ -0,0 +1,30 @@ +/* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 start */ +#ifndef _LINUX_CHG_TCPC_INFO_H +#define _LINUX_CHG_TCPC_INFO_H + +enum chg_ic_supplier { + UNKNOWN_CHG, + SGM41513, + UPM6910, + SC89601, + ETA6963, + SY6970, + BQ25601, + RT9467, + SC89890H, + SGM41511, + SGM41541, +}; + +enum tcpc_cc_supplier { + UNKNOWN_CC, + HUSB311, + FUSB302, + ET7304, + SGM7220, + WUSB3801, + FUSB301A, +}; + +#endif +/* hs14 code for SR-AL6528A-01-258 by shanxinkai at 2022/09/13 end */ \ No newline at end of file diff --git a/include/linux/mount.h b/include/linux/mount.h index adcc8f01c316..f605a2018e64 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -72,7 +72,14 @@ struct vfsmount { ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); +#ifdef CONFIG_KSU_SUSFS + ANDROID_KABI_USE(4, u64 susfs_mnt_id_backup); +#else ANDROID_KABI_RESERVE(4); +#endif +#if defined(CONFIG_KSU_SUSFS) && !defined(ANDROID_KABI_RESERVE) + u64 susfs_mnt_id_backup; +#endif void *data; } __randomize_layout; diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index e5320f6c8654..e77f7b4f39ca 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -11,6 +11,7 @@ #ifdef CONFIG_SECCOMP #include +#include #include struct seccomp_filter; @@ -27,6 +28,7 @@ struct seccomp_filter; */ struct seccomp { int mode; + atomic_t filter_count; struct seccomp_filter *filter; }; diff --git a/include/linux/susfs.h b/include/linux/susfs.h new file mode 100644 index 000000000000..225c8afe45eb --- /dev/null +++ b/include/linux/susfs.h @@ -0,0 +1,242 @@ +#ifndef KSU_SUSFS_H +#define KSU_SUSFS_H + +#include +#include +#include +#include +#include +#include +#include + +#define SUSFS_VERSION "v2.1.0" +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) +#define SUSFS_VARIANT "NON-GKI" +#else +#define SUSFS_VARIANT "GKI" +#endif + +/*********/ +/* MACRO */ +/*********/ +#define getname_safe(name) (name == NULL ? ERR_PTR(-EINVAL) : getname(name)) +#define putname_safe(name) (IS_ERR(name) ? NULL : putname(name)) + +/********/ +/* ENUM */ +/********/ +enum UID_SCHEME { + UID_NON_APP_PROC = 0, + UID_ROOT_PROC_EXCEPT_SU_PROC, + UID_NON_SU_PROC, + UID_UMOUNTED_APP_PROC, + UID_UMOUNTED_PROC, +}; + +/**********/ +/* STRUCT */ +/**********/ +/* sus_path */ +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +struct st_susfs_sus_path { + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + int err; +}; + +struct st_susfs_sus_path_list { + struct list_head list; + struct st_susfs_sus_path info; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; +}; +#endif + +/* sus_mount */ +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +struct st_susfs_hide_sus_mnts_for_non_su_procs { + bool enabled; + int err; +}; +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + +/* sus_kstat */ +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#define KSTAT_SPOOF_INO (1 << 0) +#define KSTAT_SPOOF_DEV (1 << 1) +#define KSTAT_SPOOF_NLINK (1 << 2) +#define KSTAT_SPOOF_SIZE (1 << 3) +#define KSTAT_SPOOF_ATIME_TV_SEC (1 << 4) +#define KSTAT_SPOOF_ATIME_TV_NSEC (1 << 5) +#define KSTAT_SPOOF_MTIME_TV_SEC (1 << 6) +#define KSTAT_SPOOF_MTIME_TV_NSEC (1 << 7) +#define KSTAT_SPOOF_CTIME_TV_SEC (1 < 8) +#define KSTAT_SPOOF_CTIME_TV_NSEC (1 << 9) +#define KSTAT_SPOOF_BLOCKS (1 << 10) +#define KSTAT_SPOOF_BLKSIZE (1 << 11) + +struct st_susfs_sus_kstat { + int is_statically; + unsigned long target_ino; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + unsigned long spoofed_ino; + unsigned long spoofed_dev; + unsigned int spoofed_nlink; + long long spoofed_size; + long spoofed_atime_tv_sec; + unsigned long spoofed_atime_tv_nsec; + long spoofed_mtime_tv_sec; + unsigned long spoofed_mtime_tv_nsec; + long spoofed_ctime_tv_sec; + unsigned long spoofed_ctime_tv_nsec; + long long spoofed_blocks; + long spoofed_blksize; + int flags; + int err; +}; + +struct st_susfs_sus_kstat_hlist { + unsigned long target_ino; + unsigned long target_dev; + bool is_fuse; + struct st_susfs_sus_kstat info; + struct hlist_node node; +}; +#endif + +/* spoof_uname */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +struct st_susfs_uname { + char release[__NEW_UTS_LEN+1]; + char version[__NEW_UTS_LEN+1]; + int err; +}; +#endif + +/* enable_log */ +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +struct st_susfs_log { + bool enabled; + int err; +}; +#endif + +/* spoof_cmdline_or_bootconfig */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +struct st_susfs_spoof_cmdline_or_bootconfig { + char fake_cmdline_or_bootconfig[SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE]; + int err; +}; +#endif + +/* open_redirect */ +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +struct st_susfs_open_redirect { + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + char redirected_pathname[SUSFS_MAX_LEN_PATHNAME]; + int uid_scheme; + int err; +}; + +struct st_susfs_open_redirect_hlist { + unsigned long target_ino; + unsigned long target_dev; + unsigned long redirected_ino; + unsigned long redirected_dev; + int spoofed_mnt_id; + struct kstatfs spoofed_kstatfs; + struct st_susfs_open_redirect info; + bool reversed_lookup_only; + struct hlist_node node; +}; +#endif + +/* sus_map */ +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +struct st_susfs_sus_map { + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + int err; +}; +#endif + +/* avc log spoofing */ +struct st_susfs_avc_log_spoofing { + bool enabled; + int err; +}; + +/* get enabled features */ +struct st_susfs_enabled_features { + char enabled_features[SUSFS_ENABLED_FEATURES_SIZE]; + int err; +}; + +/* show variant */ +struct st_susfs_variant { + char susfs_variant[16]; + int err; +}; + +/* show version */ +struct st_susfs_version { + char susfs_version[16]; + int err; +}; + +/***********************/ +/* FORWARD DECLARATION */ +/***********************/ +/* sus_path */ +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +void susfs_add_sus_path(void __user **user_info); +void susfs_add_sus_path_loop(void __user **user_info); +#endif + +/* sus_mount */ +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +void susfs_set_hide_sus_mnts_for_non_su_procs(void __user **user_info); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + +/* sus_kstat */ +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +void susfs_add_sus_kstat(void __user **user_info); +void susfs_update_sus_kstat(void __user **user_info); +#endif + +/* spoof_uname */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +void susfs_set_uname(void __user **user_info); +void susfs_spoof_uname(struct new_utsname* tmp); +#endif + +/* enable_log */ +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +void susfs_enable_log(void __user **user_info); +#endif + +/* spoof_cmdline_or_bootconfig */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +void susfs_set_cmdline_or_bootconfig(void __user **user_info); +int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m); +#endif + +/* open_redirect */ +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +void susfs_add_open_redirect(void __user **user_info); +#endif + +/* sus_map */ +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +void susfs_add_sus_map(void __user **user_info); +#endif + +void susfs_set_avc_log_spoofing(void __user **user_info); + +void susfs_get_enabled_features(void __user **user_info); +void susfs_show_variant(void __user **user_info); +void susfs_show_version(void __user **user_info); + +void susfs_start_sdcard_monitor_fn(void); + +/* susfs_init */ +void susfs_init(void); + +#endif \ No newline at end of file diff --git a/include/linux/susfs_def.h b/include/linux/susfs_def.h new file mode 100644 index 000000000000..21783ed1eb28 --- /dev/null +++ b/include/linux/susfs_def.h @@ -0,0 +1,143 @@ +#ifndef KSU_SUSFS_DEF_H +#define KSU_SUSFS_DEF_H + +#include +#include // We need check kernel version. +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +#include +#endif + +/********/ +/* ENUM */ +/********/ +/* shared with userspace ksu_susfs tool */ +#define SUSFS_MAGIC 0xFAFAFAFA +#define CMD_SUSFS_ADD_SUS_PATH 0x55550 +#define CMD_SUSFS_SET_ANDROID_DATA_ROOT_PATH 0x55551 /* deprecated */ +#define CMD_SUSFS_SET_SDCARD_ROOT_PATH 0x55552 /* deprecated */ +#define CMD_SUSFS_ADD_SUS_PATH_LOOP 0x55553 +#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 /* deprecated */ +#define CMD_SUSFS_HIDE_SUS_MNTS_FOR_NON_SU_PROCS 0x55561 +#define CMD_SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE 0x55562 /* deprecated */ +#define CMD_SUSFS_ADD_SUS_KSTAT 0x55570 +#define CMD_SUSFS_UPDATE_SUS_KSTAT 0x55571 +#define CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY 0x55572 +#define CMD_SUSFS_ADD_TRY_UMOUNT 0x55580 /* deprecated */ +#define CMD_SUSFS_SET_UNAME 0x55590 +#define CMD_SUSFS_ENABLE_LOG 0x555a0 +#define CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG 0x555b0 +#define CMD_SUSFS_ADD_OPEN_REDIRECT 0x555c0 +#define CMD_SUSFS_SHOW_VERSION 0x555e1 +#define CMD_SUSFS_SHOW_ENABLED_FEATURES 0x555e2 +#define CMD_SUSFS_SHOW_VARIANT 0x555e3 +#define CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE 0x555e4 /* deprecated */ +#define CMD_SUSFS_IS_SUS_SU_READY 0x555f0 /* deprecated */ +#define CMD_SUSFS_SUS_SU 0x60000 /* deprecated */ +#define CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING 0x60010 +#define CMD_SUSFS_ADD_SUS_MAP 0x60020 + +#define SUSFS_MAX_LEN_PATHNAME 256 // 256 should address many paths already unless you are doing some strange experimental stuff, then set your own desired length +#define SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE 8192 // 8192 is enough I guess +#define SUSFS_ENABLED_FEATURES_SIZE 8192 // 8192 is enough I guess +#define SUSFS_MAX_VERSION_BUFSIZE 16 +#define SUSFS_MAX_VARIANT_BUFSIZE 16 + +#define TRY_UMOUNT_DEFAULT 0 /* used by susfs_try_umount() */ +#define TRY_UMOUNT_DETACH 1 /* used by susfs_try_umount() */ + +#define VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT 0x80000000 /* used for mounts that are unshared by ksu process */ +#define DEFAULT_KSU_MNT_ID 500000 /* used for mounts created or single cloned by ksu process */ +#define DEFAULT_KSU_MNT_GROUP_ID 5000 /* used by mount->mnt_group_id */ + +/* + * inode->i_mapping->flags => A 'unsigned long' type storing flag 'AS_FLAGS_', bit 1 to 31 is not usable since 6.12 + * nd->state => storing flag 'ND_STATE_' + * nd->flags => storing flag 'ND_FLAGS_' + * task_struct->thread_info.flags => storing flag 'TIF_' + */ + // thread_info->flags is unsigned long :D +#define TIF_PROC_UMOUNTED 33 + +#define AS_FLAGS_SUS_PATH 33 +#define AS_FLAGS_SUS_MOUNT 34 +#define AS_FLAGS_SUS_KSTAT 35 +#define AS_FLAGS_OPEN_REDIRECT 36 +#define AS_FLAGS_SUS_MAP 39 + +#define ND_STATE_LOOKUP_LAST 32 +#define ND_STATE_OPEN_LAST 64 +#define ND_FLAGS_LOOKUP_LAST 0x2000000 + +#define MAGIC_MOUNT_WORKDIR "/debug_ramdisk/workdir" + +/* From KernelSU */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) +typedef const struct qstr *susfs_fname_t; +#define susfs_fname_len(f) ((f)->len) +#define susfs_fname_arg(f) ((f)->name) +#else +typedef const unsigned char *susfs_fname_t; +#define susfs_fname_len(f) (strlen(f)) +#define susfs_fname_arg(f) (f) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) +#define SUSFS_DECL_FSNOTIFY_OPS(name) \ +int name(struct fsnotify_mark *mark, u32 mask, struct inode *inode, \ +struct inode *dir, const struct qstr *file_name, u32 cookie) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) +#define SUSFS_DECL_FSNOTIFY_OPS(name) \ +int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \ +const void *data, int data_type, susfs_fname_t file_name, \ +u32 cookie, struct fsnotify_iter_info *iter_info) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +#define SUSFS_DECL_FSNOTIFY_OPS(name) \ +int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \ +const void *data, int data_type, susfs_fname_t file_name, \ +u32 cookie, struct fsnotify_iter_info *iter_info) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +#define SUSFS_DECL_FSNOTIFY_OPS(name) \ +int name(struct fsnotify_group *group, struct inode *inode, \ +struct fsnotify_mark *inode_mark, \ +struct fsnotify_mark *vfsmount_mark, u32 mask, \ +const void *data, int data_type, susfs_fname_t file_name, \ +u32 cookie, struct fsnotify_iter_info *iter_info) +#else +#define SUSFS_DECL_FSNOTIFY_OPS(name) \ +int name(struct fsnotify_group *group, struct inode *inode, \ +struct fsnotify_mark *inode_mark, \ +struct fsnotify_mark *vfsmount_mark, u32 mask, void *data, \ +int data_type, susfs_fname_t file_name, u32 cookie) +#endif + +static inline bool susfs_is_current_proc_umounted(void) { + return test_ti_thread_flag(¤t->thread_info, TIF_PROC_UMOUNTED); +} + +static inline void susfs_set_current_proc_umounted(void) { + set_ti_thread_flag(¤t->thread_info, TIF_PROC_UMOUNTED); +} + +static inline bool susfs_is_current_proc_umounted_app(void) { + return (test_ti_thread_flag(¤t->thread_info, TIF_PROC_UMOUNTED) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + __kuid_val(current_uid()) >= 10000); +#else + current_uid().val >= 10000); +#endif +} + +#define SUSFS_IS_INODE_SUS_MAP(inode) \ + inode && inode->i_mapping && \ + unlikely(test_bit(AS_FLAGS_SUS_MAP, &inode->i_mapping->flags)) && \ + susfs_is_current_proc_umounted_app() + +#define SUSFS_IS_INODE_OPEN_REDIRECT_WITHOUT_UID_CHECK(inode) \ + inode && inode->i_mapping && \ + unlikely(test_bit(AS_FLAGS_OPEN_REDIRECT, &inode->i_mapping->flags)) + +#define SUSFS_IS_INODE_OPEN_REDIRECT(inode) \ + inode && inode->i_mapping && \ + unlikely(test_bit(AS_FLAGS_OPEN_REDIRECT, &inode->i_mapping->flags)) && \ + susfs_is_current_proc_umounted_app() +#endif // #ifndef KSU_SUSFS_DEF_H \ No newline at end of file diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index b8ec1dbd1be5..de6d69c08040 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -634,6 +634,10 @@ static void s_stop(struct seq_file *m, void *p) { } +#ifdef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS +extern bool susfs_starts_with(const char *str, const char *prefix); +#endif + static int s_show(struct seq_file *m, void *p) { void *value; @@ -657,8 +661,35 @@ static int s_show(struct seq_file *m, void *p) seq_printf(m, "%px %c %s\t[%s]\n", value, type, iter->name, iter->module_name); } else +#ifndef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS + seq_printf(m, "%px %c %s\n", value, + iter->type, iter->name); +#else + { + if (susfs_starts_with(iter->name, "ksu_") || + susfs_starts_with(iter->name, "__ksu_") || + susfs_starts_with(iter->name, "susfs_") || + susfs_starts_with(iter->name, "ksud") || + susfs_starts_with(iter->name, "is_ksu_") || + susfs_starts_with(iter->name, "is_manager_") || + susfs_starts_with(iter->name, "escape_to_") || + susfs_starts_with(iter->name, "setup_selinux") || + susfs_starts_with(iter->name, "track_throne") || + susfs_starts_with(iter->name, "on_post_fs_data") || + susfs_starts_with(iter->name, "try_umount") || + susfs_starts_with(iter->name, "kernelsu") || + susfs_starts_with(iter->name, "__initcall__kmod_kernelsu") || + susfs_starts_with(iter->name, "apply_kernelsu") || + susfs_starts_with(iter->name, "handle_sepolicy") || + susfs_starts_with(iter->name, "getenforce") || + susfs_starts_with(iter->name, "setenforce") || + susfs_starts_with(iter->name, "is_zygote")){ + return 0; + } seq_printf(m, "%px %c %s\n", value, iter->type, iter->name); + } +#endif return 0; } diff --git a/kernel/reboot.c b/kernel/reboot.c index 3a87fee47d3a..25f6b79316d1 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -305,6 +305,10 @@ DEFINE_MUTEX(system_transition_mutex); * * reboot doesn't sync: do that yourself before calling this. */ +#ifdef CONFIG_KSU +extern int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, void __user **arg); +#endif + SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { @@ -312,6 +316,10 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, char buffer[256]; int ret = 0; +#ifdef CONFIG_KSU + ksu_handle_sys_reboot(magic1, magic2, cmd, &arg); +#endif + /* We only trust the superuser with rebooting the system. */ if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) return -EPERM; diff --git a/kernel/sys.c b/kernel/sys.c index 33c05fdd280f..9f09396cd4f9 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1262,12 +1262,18 @@ static int override_version(struct new_utsname __user *name) #endif } +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +extern void susfs_spoof_uname(struct new_utsname* tmp); +#endif SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) { struct new_utsname tmp; down_read(&uts_sem); memcpy(&tmp, utsname(), sizeof(tmp)); +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + susfs_spoof_uname(&tmp); +#endif up_read(&uts_sem); if (copy_to_user(name, &tmp, sizeof(tmp))) return -EFAULT; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 583c6c9597d4..29aa16c602b1 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -165,6 +165,11 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) audit_log_format(ab, " }"); } +#ifdef CONFIG_KSU_SUSFS +extern u32 susfs_ksu_sid; +extern u32 susfs_priv_app_sid; +extern bool susfs_is_avc_log_spoofing_enabled; +#endif /** * avc_dump_query - Display a SID pair and a class in human-readable form. @@ -180,6 +185,7 @@ static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state, u32 scontext_len; rc = security_sid_to_context(state, ssid, &scontext, &scontext_len); + if (rc) audit_log_format(ab, "ssid=%d", ssid); else { @@ -187,6 +193,15 @@ static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state, kfree(scontext); } +#ifdef CONFIG_KSU_SUSFS +if (unlikely(tsid == susfs_ksu_sid && READ_ONCE(susfs_is_avc_log_spoofing_enabled))) { + if (rc) + audit_log_format(ab, " tsid=%d", susfs_priv_app_sid); + else + audit_log_format(ab, " tcontext=%s", "u:r:priv_app:s0:c512,c768"); + goto bypass_orig_flow; +} +#endif rc = security_sid_to_context(state, tsid, &scontext, &scontext_len); if (rc) audit_log_format(ab, " tsid=%d", tsid); @@ -195,6 +210,9 @@ static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state, kfree(scontext); } +#ifdef CONFIG_KSU_SUSFS +bypass_orig_flow: +#endif BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index eb9bf9b52d15..6c9f2a7ab1e8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -280,7 +280,7 @@ static int __inode_security_revalidate(struct inode *inode, struct dentry *dentry, bool may_sleep) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); might_sleep_if(may_sleep); @@ -301,7 +301,7 @@ static int __inode_security_revalidate(struct inode *inode, static struct inode_security_struct *inode_security_novalidate(struct inode *inode) { - return inode->i_security; + return selinux_inode(inode); } static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) @@ -311,7 +311,7 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo error = __inode_security_revalidate(inode, NULL, !rcu); if (error) return ERR_PTR(error); - return inode->i_security; + return selinux_inode(inode); } /* @@ -320,14 +320,14 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo static struct inode_security_struct *inode_security(struct inode *inode) { __inode_security_revalidate(inode, NULL, true); - return inode->i_security; + return selinux_inode(inode); } static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); - return inode->i_security; + return selinux_inode(inode); } /* @@ -338,7 +338,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr struct inode *inode = d_backing_inode(dentry); __inode_security_revalidate(inode, dentry, true); - return inode->i_security; + return selinux_inode(inode); } static void inode_free_rcu(struct rcu_head *head) @@ -351,7 +351,7 @@ static void inode_free_rcu(struct rcu_head *head) static void inode_free_security(struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); struct superblock_security_struct *sbsec = inode->i_sb->s_security; /* @@ -1549,7 +1549,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry, static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) { struct superblock_security_struct *sbsec = NULL; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); u32 task_sid, sid = 0; u16 sclass; struct dentry *dentry; @@ -1854,7 +1854,7 @@ static int inode_has_perm(const struct cred *cred, return 0; sid = cred_sid(cred); - isec = inode->i_security; + isec = selinux_inode(inode); return avc_has_perm(&selinux_state, sid, isec->sid, isec->sclass, perms, adp); @@ -3097,7 +3097,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, /* Possibly defer initialization to selinux_complete_init. */ if (sbsec->flags & SE_SBINITIALIZED) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; @@ -3133,7 +3133,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, if (unlikely(!selinux_state.initialized)) return 0; - isec = inode->i_security; + isec = selinux_inode(inode); /* * We only get here once per ephemeral inode. The inode has @@ -3253,7 +3253,7 @@ static noinline int audit_inode_permission(struct inode *inode, { #ifdef CONFIG_AUDIT struct common_audit_data ad; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); int rc; ad.type = LSM_AUDIT_DATA_INODE; @@ -4332,7 +4332,7 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); u32 sid = task_sid(p); spin_lock(&isec->lock); @@ -6734,7 +6734,7 @@ static void selinux_release_secctx(char *secdata, u32 seclen) static void selinux_inode_invalidate_secctx(struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); spin_lock(&isec->lock); isec->initialized = LABEL_INVALID; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index c43cd13d8064..6dfeee8e7e45 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -142,4 +142,15 @@ struct perf_event_security_struct { u32 sid; /* SID of perf_event obj creator */ }; +static inline struct inode_security_struct *selinux_inode( + const struct inode *inode) +{ + return inode->i_security; +} + +static inline struct task_security_struct *selinux_cred(const struct cred *cred) +{ + return cred->security; +} + #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 359d3d372d1c..6ea32b098e27 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1378,7 +1378,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi) goto out; } - isec = (struct inode_security_struct *)inode->i_security; + isec = selinux_inode(inode); ret = security_genfs_sid(fsi->state, "selinuxfs", page, SECCLASS_FILE, &sid); if (ret) { @@ -2011,7 +2011,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) } inode->i_ino = ++fsi->last_ino; - isec = (struct inode_security_struct *)inode->i_security; + isec = selinux_inode(inode); isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; isec->initialized = LABEL_INITIALIZED; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 9e803d2a687a..f6d41a65784d 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -79,7 +79,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, gfp_t gfp) { int rc; - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct xfrm_sec_ctx *ctx = NULL; u32 str_len; @@ -138,7 +138,7 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) */ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); if (!ctx) return 0;