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